Completed
Push — master ( 9b8b24...6e1754 )
by Julito
58:58
created

UserManager::create_username()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 27
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 5
nop 2
dl 0
loc 27
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
                'Chamilo\\UserBundle\\Entity\\User'
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 = array(
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"] = array($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 = array(
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(array(
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 = array(), $active = null, $order = null, $limit = null)
1499
    {
1500
        if (empty($ids)) {
1501
            return array();
1502
        }
1503
1504
        $ids = is_array($ids) ? $ids : array($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 = array();
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 = 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 = array(),
1595
        $order_by = array(),
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 = 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 = array();
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 = array(
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 array(
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 = array(
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 array(
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 = array();
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 = array();
2250
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2251
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2252
        $columns = array(
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), array('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']] = array(
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 => array(),
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']] = array(
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 array();
2483
        }
2484
        $extra_data = array();
2485
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2486
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2487
        $user_id = intval($user_id);
2488
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2489
                FROM $t_uf f
2490
                WHERE
2491
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2492
                ";
2493
        $filter_cond = '';
2494
2495
        if (!$all_visibility) {
2496
            if (isset($field_filter)) {
2497
                $field_filter = intval($field_filter);
2498
                $filter_cond .= " AND filter = $field_filter ";
2499
            }
2500
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2501
        } else {
2502
            if (isset($field_filter)) {
2503
                $field_filter = intval($field_filter);
2504
                $sql .= " AND filter = $field_filter ";
2505
            }
2506
        }
2507
2508
        $sql .= " ORDER BY f.field_order";
2509
2510
        $res = Database::query($sql);
2511
        if (Database::num_rows($res) > 0) {
2512
            while ($row = Database::fetch_array($res)) {
2513
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
2514
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2515
                    $extra_data['extra_'.$row['fvar']] = $tags;
2516
                } else {
2517
                    $sqlu = "SELECT value as fval
2518
                            FROM $t_ufv
2519
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2520
                    $resu = Database::query($sqlu);
2521
                    // get default value
2522
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2523
                               WHERE id=".$row['id'];
2524
                    $res_df = Database::query($sql_df);
2525
2526
                    if (Database::num_rows($resu) > 0) {
2527
                        $rowu = Database::fetch_array($resu);
2528
                        $fval = $rowu['fval'];
2529
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2530
                            $fval = explode(';', $rowu['fval']);
2531
                        }
2532
                    } else {
2533
                        $row_df = Database::fetch_array($res_df);
2534
                        $fval = $row_df['fval_df'];
2535
                    }
2536
                    // We get here (and fill the $extra_data array) even if there
2537
                    // is no user with data (we fill it with default values)
2538
                    if ($prefix) {
2539
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2540
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2541
                        } else {
2542
                            $extra_data['extra_'.$row['fvar']] = $fval;
2543
                        }
2544
                    } else {
2545
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2546
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2547
                        } else {
2548
                            $extra_data[$row['fvar']] = $fval;
2549
                        }
2550
                    }
2551
                }
2552
            }
2553
        }
2554
2555
        return $extra_data;
2556
    }
2557
2558
    /** Get extra user data by field
2559
     * @param int    user ID
2560
     * @param string the internal variable name of the field
2561
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2562
     */
2563
    public static function get_extra_user_data_by_field(
2564
        $user_id,
2565
        $field_variable,
2566
        $prefix = false,
2567
        $all_visibility = true,
2568
        $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

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

2691
     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...
2692
    {
2693
        $extraFieldValue = new ExtraFieldValue('user');
2694
        $extraField = new ExtraField('user');
2695
2696
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2697
2698
        if (false === $info) {
2699
            return [];
2700
        }
2701
2702
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2703
            $variable,
2704
            $value,
2705
            false,
2706
            false,
2707
            true
2708
        );
2709
2710
        $result = [];
2711
        if (!empty($data)) {
2712
            foreach ($data as $item) {
2713
                $result[] = $item['item_id'];
2714
            }
2715
        }
2716
2717
        return $result;
2718
    }
2719
2720
    /**
2721
     * Get extra user data by tags value
2722
     *
2723
     * @param int $fieldId the ID of the field we want to know everything of
2724
     * @param string $tag the tag name for search
2725
     * @return array with extra data info of a user
2726
     * @author José Loguercio
2727
     * @since v1.11.0
2728
     */
2729
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2730
    {
2731
        $extraField = new ExtraField('user');
2732
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2733
        $array = [];
2734
        foreach ($result as $index => $user) {
2735
            $array[] = $user['user_id'];
2736
        }
2737
        return $array;
2738
    }
2739
2740
    /**
2741
     * Get extra user data by field variable
2742
     * @param string    $variable field variable
2743
     * @return array    data
2744
     */
2745
    public static function get_extra_user_data_by_field_variable($variable)
2746
    {
2747
        $extraInfo = self::get_extra_field_information_by_name($variable);
2748
        $field_id = intval($extraInfo['id']);
2749
2750
        $extraField = new ExtraFieldValue('user');
2751
        $data = $extraField->getValuesByFieldId($field_id);
2752
2753
        if (!empty($data) > 0) {
2754
            foreach ($data as $row) {
2755
                $user_id = $row['item_id'];
2756
                $data[$user_id] = $row;
2757
            }
2758
        }
2759
2760
        return $data;
2761
    }
2762
2763
    /**
2764
     * Get extra user data tags by field variable
2765
     *
2766
     * @param string $variable field variable
2767
     * @return array
2768
     */
2769
    public static function get_extra_user_data_for_tags($variable)
2770
    {
2771
        $data = self::get_extra_field_tags_information_by_name($variable);
2772
2773
        return $data;
2774
    }
2775
2776
    /**
2777
     * Gives a list of [session_category][session_id] for the current user.
2778
     * @param integer $user_id
2779
     * @param boolean $is_time_over whether to fill the first element or not (to give space for courses out of categories)
2780
     * @param boolean $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2781
     * @param boolean $ignoreTimeLimit ignore time start/end
2782
     * @return array  list of statuses [session_category][session_id]
2783
     *
2784
     * @todo ensure multiple access urls are managed correctly
2785
     */
2786
    public static function get_sessions_by_category(
2787
        $user_id,
2788
        $is_time_over = true,
2789
        $ignore_visibility_for_admins = false,
2790
        $ignoreTimeLimit = false
2791
    ) {
2792
        if ($user_id != strval(intval($user_id))) {
2793
            return [];
2794
        }
2795
2796
        $allowOrder = api_get_configuration_value('session_list_order');
2797
        $position = '';
2798
        if ($allowOrder) {
2799
            $position = ', s.position AS position ';
2800
        }
2801
2802
        // Get the list of sessions per user
2803
        $now = new DateTime('now', new DateTimeZone('UTC'));
2804
2805
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2806
        // join would not catch session-courses where the user is general
2807
        // session coach but which do not have students nor coaches registered
2808
        $dql = "SELECT DISTINCT
2809
                    s.id,
2810
                    s.name,
2811
                    s.accessStartDate AS access_start_date,
2812
                    s.accessEndDate AS access_end_date,
2813
                    s.duration,
2814
                    sc.id AS session_category_id,
2815
                    sc.name AS session_category_name,
2816
                    sc.dateStart AS session_category_date_start,
2817
                    sc.dateEnd AS session_category_date_end,
2818
                    s.coachAccessStartDate AS coach_access_start_date,
2819
                    s.coachAccessEndDate AS coach_access_end_date
2820
                    $position
2821
                FROM ChamiloCoreBundle:Session AS s
2822
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2823
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.sessionId = s.id
2824
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc
2825
                WHERE (scu.user = :user OR s.generalCoach = :user) AND url.accessUrlId = :url ";
2826
2827
        $order = "ORDER BY sc.name, s.name";
2828
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
2829
        if ($showAllSessions) {
2830
            $order = "ORDER BY s.accessStartDate";
2831
        }
2832
2833
        if ($allowOrder) {
2834
            $order = "ORDER BY s.position";
2835
        }
2836
2837
        $dql .= $order;
2838
2839
        $dql = Database::getManager()
2840
            ->createQuery($dql)
2841
            ->setParameters(
2842
                ['user' => $user_id, 'url' => api_get_current_access_url_id()]
2843
            )
2844
        ;
2845
2846
        $sessionData = $dql->getResult();
2847
        $categories = [];
2848
2849
        foreach ($sessionData as $row) {
2850
            $session_id = $row['id'];
2851
            $coachList = SessionManager::getCoachesBySession($session_id);
2852
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
2853
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
2854
            $courseList = self::get_courses_list_by_session(
2855
                $user_id,
2856
                $session_id
2857
            );
2858
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
2859
2860
            // User portal filters:
2861
            if ($ignoreTimeLimit === false) {
2862
                if ($is_time_over) {
2863
                    // History
2864
                    if ($row['duration']) {
2865
                        if ($daysLeft >= 0) {
2866
                            continue;
2867
                        }
2868
                    } else {
2869
                        if (empty($row['access_end_date'])) {
2870
                            continue;
2871
                        } else {
2872
                            if ($row['access_end_date'] > $now) {
2873
                                continue;
2874
                            }
2875
                        }
2876
                    }
2877
                } else {
2878
                    // Current user portal
2879
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
2880
                    $isCoachOfCourse = in_array($user_id, $coachList);
2881
2882
                    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...
2883
                        // Teachers can access the session depending in the access_coach date
2884
                    } else {
2885
                        if ($row['duration']) {
2886
                            if ($daysLeft <= 0) {
2887
                                continue;
2888
                            }
2889
                        } else {
2890
                            if (isset($row['access_end_date']) &&
2891
                                !empty($row['access_end_date'])
2892
                            ) {
2893
                                if ($row['access_end_date'] <= $now) {
2894
                                    continue;
2895
                                }
2896
                            }
2897
                        }
2898
                    }
2899
                }
2900
            }
2901
2902
            $categories[$row['session_category_id']]['session_category'] = array(
2903
                'id' => $row['session_category_id'],
2904
                'name' => $row['session_category_name'],
2905
                'date_start' => $categoryStart,
2906
                'date_end' => $categoryEnd
2907
            );
2908
2909
            $visibility = api_get_session_visibility(
2910
                $session_id,
2911
                null,
2912
                $ignore_visibility_for_admins
2913
            );
2914
2915
            if ($visibility != SESSION_VISIBLE) {
2916
                // Course Coach session visibility.
2917
                $blockedCourseCount = 0;
2918
                $closedVisibilityList = array(
2919
                    COURSE_VISIBILITY_CLOSED,
2920
                    COURSE_VISIBILITY_HIDDEN
2921
                );
2922
2923
                foreach ($courseList as $course) {
2924
                    // Checking session visibility
2925
                    $sessionCourseVisibility = api_get_session_visibility(
2926
                        $session_id,
2927
                        $course['real_id'],
2928
                        $ignore_visibility_for_admins
2929
                    );
2930
2931
                    $courseIsVisible = !in_array(
2932
                        $course['visibility'],
2933
                        $closedVisibilityList
2934
                    );
2935
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
2936
                        $blockedCourseCount++;
2937
                    }
2938
                }
2939
2940
                // If all courses are blocked then no show in the list.
2941
                if ($blockedCourseCount === count($courseList)) {
2942
                    $visibility = SESSION_INVISIBLE;
2943
                } else {
2944
                    $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...
2945
                }
2946
            }
2947
2948
            switch ($visibility) {
2949
                case SESSION_VISIBLE_READ_ONLY:
2950
                case SESSION_VISIBLE:
2951
                case SESSION_AVAILABLE:
2952
                    break;
2953
                case SESSION_INVISIBLE:
2954
                    if ($ignore_visibility_for_admins === false) {
2955
                        continue 2;
2956
                    }
2957
            }
2958
2959
            $categories[$row['session_category_id']]['sessions'][$row['id']] = array(
2960
                'session_name' => $row['name'],
2961
                'session_id' => $row['id'],
2962
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
2963
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
2964
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
2965
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
2966
                'courses' => $courseList
2967
            );
2968
        }
2969
2970
        return $categories;
2971
    }
2972
2973
    /**
2974
     * Gives a list of [session_id-course_code] => [status] for the current user.
2975
     * @param integer $user_id
2976
     * @param int $sessionLimit
2977
     * @return array  list of statuses (session_id-course_code => status)
2978
     */
2979
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
2980
    {
2981
        // Database Table Definitions
2982
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
2983
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
2984
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
2985
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
2986
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2987
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2988
2989
        if ($user_id != strval(intval($user_id))) {
2990
            return array();
2991
        }
2992
2993
        // We filter the courses from the URL
2994
        $join_access_url = $where_access_url = '';
2995
2996
        if (api_get_multiple_access_url()) {
2997
            $access_url_id = api_get_current_access_url_id();
2998
            if ($access_url_id != -1) {
2999
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3000
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3001
                $where_access_url = " AND access_url_id = $access_url_id ";
3002
            }
3003
        }
3004
3005
        // Courses in which we subscribed out of any session
3006
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3007
3008
        $sql = "SELECT
3009
                    course.code,
3010
                    course_rel_user.status course_rel_status,
3011
                    course_rel_user.sort sort,
3012
                    course_rel_user.user_course_cat user_course_cat
3013
                 FROM $tbl_course_user course_rel_user
3014
                 LEFT JOIN $tbl_course course
3015
                 ON course.id = course_rel_user.c_id
3016
                 LEFT JOIN $tbl_user_course_category user_course_category
3017
                 ON course_rel_user.user_course_cat = user_course_category.id
3018
                 $join_access_url
3019
                 WHERE
3020
                    course_rel_user.user_id = '".$user_id."' AND
3021
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3022
                    $where_access_url
3023
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3024
3025
        $course_list_sql_result = Database::query($sql);
3026
3027
        $personal_course_list = array();
3028
        if (Database::num_rows($course_list_sql_result) > 0) {
3029
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3030
                $course_info = api_get_course_info($result_row['code']);
3031
                $result_row['course_info'] = $course_info;
3032
                $personal_course_list[] = $result_row;
3033
            }
3034
        }
3035
3036
        $coachCourseConditions = '';
3037
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3038
        if (api_is_allowed_to_create_course()) {
3039
            $sessionListFromCourseCoach = array();
3040
            $sql = " SELECT DISTINCT session_id
3041
                    FROM $tbl_session_course_user
3042
                    WHERE user_id = $user_id AND status = 2 ";
3043
3044
            $result = Database::query($sql);
3045
            if (Database::num_rows($result)) {
3046
                $result = Database::store_result($result);
3047
                foreach ($result as $session) {
3048
                    $sessionListFromCourseCoach[] = $session['session_id'];
3049
                }
3050
            }
3051
            if (!empty($sessionListFromCourseCoach)) {
3052
                $condition = implode("','", $sessionListFromCourseCoach);
3053
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3054
            }
3055
        }
3056
3057
        // Get the list of sessions where the user is subscribed
3058
        // This is divided into two different queries
3059
        $sessions = array();
3060
        $sessionLimitRestriction = '';
3061
        if (!empty($sessionLimit)) {
3062
            $sessionLimit = (int) $sessionLimit;
3063
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3064
        }
3065
3066
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3067
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3068
                ON (s.id = su.session_id)
3069
                WHERE (
3070
                    su.user_id = $user_id AND
3071
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3072
                )
3073
                $coachCourseConditions
3074
                ORDER BY access_start_date, access_end_date, name
3075
                $sessionLimitRestriction
3076
        ";
3077
3078
        $result = Database::query($sql);
3079
        if (Database::num_rows($result) > 0) {
3080
            while ($row = Database::fetch_assoc($result)) {
3081
                $sessions[$row['id']] = $row;
3082
            }
3083
        }
3084
3085
        $sql = "SELECT DISTINCT
3086
                id, name, access_start_date, access_end_date
3087
                FROM $tbl_session s
3088
                WHERE (
3089
                    id_coach = $user_id
3090
                )
3091
                $coachCourseConditions
3092
                ORDER BY access_start_date, access_end_date, name";
3093
3094
        $result = Database::query($sql);
3095
        if (Database::num_rows($result) > 0) {
3096
            while ($row = Database::fetch_assoc($result)) {
3097
                if (empty($sessions[$row['id']])) {
3098
                    $sessions[$row['id']] = $row;
3099
                }
3100
            }
3101
        }
3102
3103
        if (api_is_allowed_to_create_course()) {
3104
            foreach ($sessions as $enreg) {
3105
                $session_id = $enreg['id'];
3106
                $session_visibility = api_get_session_visibility($session_id);
3107
3108
                if ($session_visibility == SESSION_INVISIBLE) {
3109
                    continue;
3110
                }
3111
3112
                // This query is horribly slow when more than a few thousand
3113
                // users and just a few sessions to which they are subscribed
3114
                $sql = "SELECT DISTINCT
3115
                        course.code code,
3116
                        course.title i,
3117
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3118
                        email, course.course_language l,
3119
                        1 sort,
3120
                        category_code user_course_cat,
3121
                        access_start_date,
3122
                        access_end_date,
3123
                        session.id as session_id,
3124
                        session.name as session_name
3125
                    FROM $tbl_session_course_user as session_course_user
3126
                    INNER JOIN $tbl_course AS course
3127
                        ON course.id = session_course_user.c_id
3128
                    INNER JOIN $tbl_session as session
3129
                        ON session.id = session_course_user.session_id
3130
                    LEFT JOIN $tbl_user as user
3131
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3132
                    WHERE
3133
                        session_course_user.session_id = $session_id AND (
3134
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3135
                            OR session.id_coach = $user_id
3136
                        )
3137
                    ORDER BY i";
3138
                $course_list_sql_result = Database::query($sql);
3139
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3140
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3141
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3142
                    $personal_course_list[$key] = $result_row;
3143
                }
3144
            }
3145
        }
3146
3147
        foreach ($sessions as $enreg) {
3148
            $session_id = $enreg['id'];
3149
            $session_visibility = api_get_session_visibility($session_id);
3150
            if ($session_visibility == SESSION_INVISIBLE) {
3151
                continue;
3152
            }
3153
3154
            /* This query is very similar to the above query,
3155
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3156
            $sql = "SELECT DISTINCT
3157
                course.code code,
3158
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3159
                email,
3160
                course.course_language l,
3161
                1 sort,
3162
                category_code user_course_cat,
3163
                access_start_date,
3164
                access_end_date,
3165
                session.id as session_id,
3166
                session.name as session_name,
3167
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3168
            FROM $tbl_session_course_user as session_course_user
3169
            INNER JOIN $tbl_course AS course
3170
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3171
            INNER JOIN $tbl_session as session 
3172
            ON session_course_user.session_id = session.id
3173
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3174
            WHERE session_course_user.user_id = $user_id
3175
            ORDER BY i";
3176
3177
            $course_list_sql_result = Database::query($sql);
3178
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3179
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3180
                $key = $result_row['session_id'].' - '.$result_row['code'];
3181
                if (!isset($personal_course_list[$key])) {
3182
                    $personal_course_list[$key] = $result_row;
3183
                }
3184
            }
3185
        }
3186
3187
        return $personal_course_list;
3188
    }
3189
3190
    /**
3191
     * Gives a list of courses for the given user in the given session
3192
     * @param integer $user_id
3193
     * @param integer $session_id
3194
     * @return array  list of statuses (session_id-course_code => status)
3195
     */
3196
    public static function get_courses_list_by_session($user_id, $session_id)
3197
    {
3198
        // Database Table Definitions
3199
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3200
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3201
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3202
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3203
3204
        $user_id = intval($user_id);
3205
        $session_id = intval($session_id);
3206
        //we filter the courses from the URL
3207
        $join_access_url = $where_access_url = '';
3208
3209
        if (api_get_multiple_access_url()) {
3210
            $urlId = api_get_current_access_url_id();
3211
            if ($urlId != -1) {
3212
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3213
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3214
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3215
            }
3216
        }
3217
3218
        /* This query is very similar to the query below, but it will check the
3219
        session_rel_course_user table if there are courses registered
3220
        to our user or not */
3221
        $sql = "SELECT DISTINCT
3222
                    c.visibility,
3223
                    c.id as real_id,
3224
                    c.code as course_code,
3225
                    sc.position
3226
                FROM $tbl_session_course_user as scu
3227
                INNER JOIN $tbl_session_course sc
3228
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3229
                INNER JOIN $tableCourse as c
3230
                ON (scu.c_id = c.id)
3231
                $join_access_url
3232
                WHERE
3233
                    scu.user_id = $user_id AND
3234
                    scu.session_id = $session_id
3235
                    $where_access_url
3236
                ORDER BY sc.position ASC, c.id";
3237
3238
        $personal_course_list = array();
3239
        $courses = array();
3240
3241
        $result = Database::query($sql);
3242
        if (Database::num_rows($result) > 0) {
3243
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3244
                $result_row['status'] = 5;
3245
                if (!in_array($result_row['real_id'], $courses)) {
3246
                    $personal_course_list[] = $result_row;
3247
                    $courses[] = $result_row['real_id'];
3248
                }
3249
            }
3250
        }
3251
3252
        if (api_is_allowed_to_create_course()) {
3253
            $sql = "SELECT DISTINCT
3254
                        c.visibility, 
3255
                        c.id as real_id,
3256
                        c.code as course_code,
3257
                        sc.position
3258
                    FROM $tbl_session_course_user as scu
3259
                    INNER JOIN $tbl_session as s
3260
                    ON (scu.session_id = s.id)
3261
                    INNER JOIN $tbl_session_course sc
3262
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3263
                    INNER JOIN $tableCourse as c
3264
                    ON (scu.c_id = c.id)
3265
                    $join_access_url
3266
                    WHERE
3267
                      s.id = $session_id AND
3268
                      (
3269
                        (scu.user_id = $user_id AND scu.status = 2) OR
3270
                        s.id_coach = $user_id
3271
                      )
3272
                    $where_access_url
3273
                    ORDER BY sc.position ASC";
3274
            $result = Database::query($sql);
3275
3276
            if (Database::num_rows($result) > 0) {
3277
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3278
                    $result_row['status'] = 2;
3279
                    if (!in_array($result_row['real_id'], $courses)) {
3280
                        $personal_course_list[] = $result_row;
3281
                        $courses[] = $result_row['real_id'];
3282
                    }
3283
                }
3284
            }
3285
        }
3286
3287
        if (api_is_drh()) {
3288
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3289
            $sessionList = array_keys($sessionList);
3290
            if (in_array($session_id, $sessionList)) {
3291
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3292
                if (!empty($courseList)) {
3293
                    foreach ($courseList as $course) {
3294
                        if (!in_array($course['id'], $courses)) {
3295
                            $personal_course_list[] = $course;
3296
                        }
3297
                    }
3298
                }
3299
            }
3300
        } else {
3301
            //check if user is general coach for this session
3302
            $sessionInfo = api_get_session_info($session_id);
3303
            if ($sessionInfo['id_coach'] == $user_id) {
3304
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3305
                if (!empty($courseList)) {
3306
                    foreach ($courseList as $course) {
3307
                        if (!in_array($course['id'], $courses)) {
3308
                            $personal_course_list[] = $course;
3309
                        }
3310
                    }
3311
                }
3312
            }
3313
        }
3314
3315
        return $personal_course_list;
3316
    }
3317
3318
    /**
3319
     * Get user id from a username
3320
     * @param string $username
3321
     * @return int User ID (or false if not found)
3322
     */
3323
    public static function get_user_id_from_username($username)
3324
    {
3325
        if (empty($username)) {
3326
3327
            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...
3328
        }
3329
        $username = trim($username);
3330
        $username = Database::escape_string($username);
3331
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3332
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3333
        $res = Database::query($sql);
3334
3335
        if ($res === false) {
3336
            return false;
3337
        }
3338
        if (Database::num_rows($res) !== 1) {
3339
            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...
3340
        }
3341
        $row = Database::fetch_array($res);
3342
3343
        return $row['id'];
3344
    }
3345
3346
    /**
3347
     * Get the users files upload from his share_folder
3348
     * @param    string  $user_id   User ID
3349
     * @param   string  $course course directory
3350
     * @param   string  $resourcetype resourcetype: images, all
3351
     * @return    string
3352
     */
3353
    public static function get_user_upload_files_by_course(
3354
        $user_id,
3355
        $course,
3356
        $resourcetype = 'all'
3357
    ) {
3358
        $return = '';
3359
        if (!empty($user_id) && !empty($course)) {
3360
            $user_id = intval($user_id);
3361
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3362
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3363
            $file_list = array();
3364
3365
            if (is_dir($path)) {
3366
                $handle = opendir($path);
3367
                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

3367
                while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
3368
                    if ($file == '.' || $file == '..' || $file == '.htaccess' || is_dir($path.$file)) {
3369
                        continue; // skip current/parent directory and .htaccess
3370
                    }
3371
                    $file_list[] = $file;
3372
                }
3373
                if (count($file_list) > 0) {
3374
                    $return = "<h4>$course</h4>";
3375
                    $return .= '<ul class="thumbnails">';
3376
                }
3377
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3378
                foreach ($file_list as $file) {
3379
                    if ($resourcetype == "all") {
3380
                        $return .= '<li><a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3381
                    } elseif ($resourcetype == "images") {
3382
                        //get extension
3383
                        $ext = explode('.', $file);
3384
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3385
                            $return .= '<li class="span2">
3386
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3387
                                                <img src="'.$web_path.urlencode($file).'" >
3388
                                            </a>
3389
                                        </li>';
3390
                        }
3391
                    }
3392
                }
3393
                if (count($file_list) > 0) {
3394
                    $return .= '</ul>';
3395
                }
3396
            }
3397
        }
3398
3399
        return $return;
3400
    }
3401
3402
    /**
3403
     * Gets the API key (or keys) and return them into an array
3404
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3405
     * @param string $api_service
3406
     * @return  array   Non-indexed array containing the list of API keys for this user, or FALSE on error
3407
     */
3408
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
3409
    {
3410
        if ($user_id != strval(intval($user_id))) {
3411
            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...
3412
        }
3413
        if (empty($user_id)) {
3414
            $user_id = api_get_user_id();
3415
        }
3416
        if ($user_id === false) {
3417
            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...
3418
        }
3419
        $service_name = Database::escape_string($api_service);
3420
        if (is_string($service_name) === false) {
3421
            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...
3422
        }
3423
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3424
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3425
        $res = Database::query($sql);
3426
        if ($res === false) {
3427
            return false;
3428
        } //error during query
3429
        $num = Database::num_rows($res);
3430
        if ($num == 0) {
3431
            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...
3432
        }
3433
        $list = array();
3434
        while ($row = Database::fetch_array($res)) {
3435
            $list[$row['id']] = $row['api_key'];
3436
        }
3437
        return $list;
3438
    }
3439
3440
    /**
3441
     * Adds a new API key to the users' account
3442
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3443
     * @param string $api_service
3444
     * @return  boolean True on success, false on failure
3445
     */
3446
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
3447
    {
3448
        if ($user_id != strval(intval($user_id))) {
3449
            return false;
3450
        }
3451
        if (empty($user_id)) {
3452
            $user_id = api_get_user_id();
3453
        }
3454
        if ($user_id === false) {
3455
            return false;
3456
        }
3457
        $service_name = Database::escape_string($api_service);
3458
        if (is_string($service_name) === false) {
3459
            return false;
3460
        }
3461
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3462
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3463
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3464
        $res = Database::query($sql);
3465
        if ($res === false) {
3466
            return false;
3467
        } //error during query
3468
        $num = Database::insert_id();
3469
        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...
3470
    }
3471
3472
    /**
3473
     * Deletes an API key from the user's account
3474
     * @param   int     API key's internal ID
3475
     * @return  boolean True on success, false on failure
3476
     */
3477
    public static function delete_api_key($key_id)
3478
    {
3479
        if ($key_id != strval(intval($key_id))) {
3480
            return false;
3481
        }
3482
        if ($key_id === false) {
3483
            return false;
3484
        }
3485
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3486
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3487
        $res = Database::query($sql);
3488
        if ($res === false) {
3489
            return false;
3490
        } //error during query
3491
        $num = Database::num_rows($res);
3492
        if ($num !== 1) {
3493
            return false;
3494
        }
3495
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3496
        $res = Database::query($sql);
3497
        if ($res === false) {
3498
            return false;
3499
        } //error during query
3500
3501
        return true;
3502
    }
3503
3504
    /**
3505
     * Regenerate an API key from the user's account
3506
     * @param   int     user ID (defaults to the results of api_get_user_id())
3507
     * @param   string  API key's internal ID
3508
     * @return  int        num
3509
     */
3510
    public static function update_api_key($user_id, $api_service)
3511
    {
3512
        if ($user_id != strval(intval($user_id))) {
3513
            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...
3514
        }
3515
        if ($user_id === false) {
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
        $service_name = Database::escape_string($api_service);
3519
        if (is_string($service_name) === false) {
3520
            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...
3521
        }
3522
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3523
        $sql = "SELECT id FROM $t_api 
3524
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3525
        $res = Database::query($sql);
3526
        $num = Database::num_rows($res);
3527
        if ($num == 1) {
3528
            $id_key = Database::fetch_array($res, 'ASSOC');
3529
            self::delete_api_key($id_key['id']);
3530
            $num = self::add_api_key($user_id, $api_service);
3531
        } elseif ($num == 0) {
3532
            $num = self::add_api_key($user_id, $api_service);
3533
        }
3534
3535
        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...
3536
    }
3537
3538
    /**
3539
     * @param   int     user ID (defaults to the results of api_get_user_id())
3540
     * @param   string    API key's internal ID
3541
     * @return  int    row ID, or return false if not found
3542
     */
3543
    public static function get_api_key_id($user_id, $api_service)
3544
    {
3545
        if ($user_id != strval(intval($user_id))) {
3546
            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...
3547
        }
3548
        if ($user_id === false) {
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 (empty($api_service)) {
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
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3555
        $api_service = Database::escape_string($api_service);
3556
        $sql = "SELECT id FROM $t_api 
3557
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3558
        $res = Database::query($sql);
3559
        if (Database::num_rows($res) < 1) {
3560
            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...
3561
        }
3562
        $row = Database::fetch_array($res, 'ASSOC');
3563
3564
        return $row['id'];
3565
    }
3566
3567
    /**
3568
     * Checks if a user_id is platform admin
3569
     * @param   int user ID
3570
     * @return  boolean True if is admin, false otherwise
3571
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3572
     */
3573
    public static function is_admin($user_id)
3574
    {
3575
        if (empty($user_id) || $user_id != strval(intval($user_id))) {
3576
            return false;
3577
        }
3578
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3579
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3580
        $res = Database::query($sql);
3581
3582
        return Database::num_rows($res) === 1;
3583
    }
3584
3585
    /**
3586
     * Get the total count of users
3587
     * @param   int     Status of users to be counted
3588
     * @param   int     Access URL ID (optional)
3589
     * @return    mixed    Number of users or false on error
3590
     */
3591
    public static function get_number_of_users($status = 0, $access_url_id = 1)
3592
    {
3593
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
3594
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3595
3596
        if (api_is_multiple_url_enabled()) {
3597
            $sql = "SELECT count(u.id) 
3598
                    FROM $t_u u 
3599
                    INNER JOIN $t_a url_user
3600
                    ON (u.id = url_user.user_id)
3601
                    WHERE url_user.access_url_id = $access_url_id                
3602
            ";
3603
        } else {
3604
            $sql = "SELECT count(u.id) 
3605
                    FROM $t_u u
3606
                    WHERE 1 = 1 ";
3607
        }
3608
        if (is_int($status) && $status > 0) {
3609
            $sql .= " AND u.status = $status ";
3610
        }
3611
        $res = Database::query($sql);
3612
        if (Database::num_rows($res) === 1) {
3613
            return (int) Database::result($res, 0, 0);
3614
        }
3615
        return false;
3616
    }
3617
3618
    /**
3619
     * @author Isaac flores <[email protected]>
3620
     * @param string The email administrator
3621
     * @param integer The user id
3622
     * @param string The message title
3623
     * @param string The content message
3624
     */
3625
    public static function send_message_in_outbox(
3626
        $email_administrator,
3627
        $user_id,
3628
        $title,
3629
        $content
3630
    ) {
3631
        $table_message = Database::get_main_table(TABLE_MESSAGE);
3632
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3633
        $title = api_utf8_decode($title);
3634
        $content = api_utf8_decode($content);
3635
        $email_administrator = Database::escape_string($email_administrator);
3636
        //message in inbox
3637
        $sql_message_outbox = 'SELECT id from '.$table_user.' WHERE email="'.$email_administrator.'" ';
3638
        //$num_row_query = Database::num_rows($sql_message_outbox);
3639
        $res_message_outbox = Database::query($sql_message_outbox);
3640
        $array_users_administrator = array();
3641
        while ($row_message_outbox = Database::fetch_array($res_message_outbox, 'ASSOC')) {
3642
            $array_users_administrator[] = $row_message_outbox['id'];
3643
        }
3644
        //allow to insert messages in outbox
3645
        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...
3646
            $sql = "INSERT INTO $table_message (user_sender_id, user_receiver_id, msg_status, send_date, title, content ) ".
3647
                " VALUES (".
3648
                "'".(int) $user_id."', '".(int) ($array_users_administrator[$i])."', '4', '".api_get_utc_datetime()."','".Database::escape_string($title)."','".Database::escape_string($content)."'".
3649
                ")";
3650
            Database::query($sql);
3651
        }
3652
    }
3653
3654
    /**
3655
     * Gets the tags of a specific field_id
3656
     * USER TAGS
3657
     *
3658
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3659
     *
3660
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3661
     *    Called it "books" for example.
3662
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3663
     * 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
3664
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3665
     * 5. Test and enjoy.
3666
     *
3667
     * @param string $tag
3668
     * @param int $field_id field_id
3669
     * @param string $return_format how we are going to result value in array or in a string (json)
3670
     * @param $limit
3671
     *
3672
     * @return mixed
3673
     *
3674
     */
3675
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3676
    {
3677
        // database table definition
3678
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3679
        $field_id = intval($field_id);
3680
        $limit = intval($limit);
3681
        $tag = trim(Database::escape_string($tag));
3682
3683
        // all the information of the field
3684
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3685
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3686
        $result = Database::query($sql);
3687
        $return = array();
3688
        if (Database::num_rows($result) > 0) {
3689
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3690
                $return[] = array('id' => $row['tag'], 'text' => $row['tag']);
3691
            }
3692
        }
3693
        if ($return_format === 'json') {
3694
            $return = json_encode($return);
3695
        }
3696
3697
        return $return;
3698
    }
3699
3700
    /**
3701
     * @param int $field_id
3702
     * @param int $limit
3703
     *
3704
     * @return array
3705
     */
3706
    public static function get_top_tags($field_id, $limit = 100)
3707
    {
3708
        // database table definition
3709
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3710
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3711
        $field_id = intval($field_id);
3712
        $limit = intval($limit);
3713
        // all the information of the field
3714
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3715
                INNER JOIN $table_user_tag ut
3716
                ON(ut.id = uv.tag_id)
3717
                WHERE field_id = $field_id
3718
                GROUP BY tag_id
3719
                ORDER BY count DESC
3720
                LIMIT $limit";
3721
        $result = Database::query($sql);
3722
        $return = array();
3723
        if (Database::num_rows($result) > 0) {
3724
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3725
                $return[] = $row;
3726
            }
3727
        }
3728
        return $return;
3729
    }
3730
3731
    /**
3732
     * Get user's tags
3733
     * @param int $user_id
3734
     * @param int $field_id
3735
     *
3736
     * @return array
3737
     */
3738
    public static function get_user_tags($user_id, $field_id)
3739
    {
3740
        // database table definition
3741
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3742
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3743
        $field_id = intval($field_id);
3744
        $user_id = intval($user_id);
3745
3746
        // all the information of the field
3747
        $sql = "SELECT ut.id, tag, count
3748
                FROM $table_user_tag ut
3749
                INNER JOIN $table_user_tag_values uv
3750
                ON (uv.tag_id=ut.ID)
3751
                WHERE field_id = $field_id AND user_id = $user_id
3752
                ORDER BY tag";
3753
        $result = Database::query($sql);
3754
        $return = array();
3755
        if (Database::num_rows($result) > 0) {
3756
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3757
                $return[$row['id']] = array('tag' => $row['tag'], 'count' => $row['count']);
3758
            }
3759
        }
3760
3761
        return $return;
3762
    }
3763
3764
    /**
3765
     * Get user's tags
3766
     * @param int $user_id
3767
     * @param int $field_id
3768
     * @param bool $show_links show links or not
3769
     *
3770
     * @return array
3771
     */
3772
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
3773
    {
3774
        // database table definition
3775
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3776
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3777
        $field_id = intval($field_id);
3778
        $user_id = intval($user_id);
3779
3780
        // all the information of the field
3781
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
3782
                INNER JOIN $table_user_tag_values uv
3783
                ON (uv.tag_id = ut.id)
3784
                WHERE field_id = $field_id AND user_id = $user_id
3785
                ORDER BY tag";
3786
3787
        $result = Database::query($sql);
3788
        $return = array();
3789
        if (Database::num_rows($result) > 0) {
3790
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3791
                $return[$row['id']] = array('tag' => $row['tag'], 'count' => $row['count']);
3792
            }
3793
        }
3794
        $user_tags = $return;
3795
        $tag_tmp = array();
3796
        foreach ($user_tags as $tag) {
3797
            if ($show_links) {
3798
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
3799
                    $tag['tag'].
3800
                '</a>';
3801
            } else {
3802
                $tag_tmp[] = $tag['tag'];
3803
            }
3804
        }
3805
3806
        if (is_array($user_tags) && count($user_tags) > 0) {
3807
            $return = implode(', ', $tag_tmp);
3808
        } else {
3809
3810
            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...
3811
        }
3812
3813
        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...
3814
    }
3815
3816
    /**
3817
     * Get the tag id
3818
     * @param int $tag
3819
     * @param int $field_id
3820
     * @return int returns 0 if fails otherwise the tag id
3821
     */
3822
    public static function get_tag_id($tag, $field_id)
3823
    {
3824
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3825
        $tag = Database::escape_string($tag);
3826
        $field_id = intval($field_id);
3827
        //with COLLATE latin1_bin to select query in a case sensitive mode
3828
        $sql = "SELECT id FROM $table_user_tag
3829
                WHERE tag LIKE '$tag' AND field_id = $field_id";
3830
        $result = Database::query($sql);
3831
        if (Database::num_rows($result) > 0) {
3832
            $row = Database::fetch_array($result, 'ASSOC');
3833
3834
            return $row['id'];
3835
        } else {
3836
3837
            return 0;
3838
        }
3839
    }
3840
3841
    /**
3842
     * Get the tag id
3843
     * @param int $tag_id
3844
     * @param int $field_id
3845
     *
3846
     * @return int 0 if fails otherwise the tag id
3847
     */
3848
    public static function get_tag_id_from_id($tag_id, $field_id)
3849
    {
3850
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3851
        $tag_id = intval($tag_id);
3852
        $field_id = intval($field_id);
3853
        $sql = "SELECT id FROM $table_user_tag
3854
                WHERE id = '$tag_id' AND field_id = $field_id";
3855
        $result = Database::query($sql);
3856
        if (Database::num_rows($result) > 0) {
3857
            $row = Database::fetch_array($result, 'ASSOC');
3858
            return $row['id'];
3859
        } else {
3860
            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...
3861
        }
3862
    }
3863
3864
    /**
3865
     * Adds a user-tag value
3866
     * @param mixed $tag
3867
     * @param int $user_id
3868
     * @param int $field_id field id of the tag
3869
     * @return bool
3870
     */
3871
    public static function add_tag($tag, $user_id, $field_id)
3872
    {
3873
        // database table definition
3874
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3875
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3876
        $tag = trim(Database::escape_string($tag));
3877
        $user_id = intval($user_id);
3878
        $field_id = intval($field_id);
3879
3880
        $tag_id = self::get_tag_id($tag, $field_id);
3881
3882
        /* IMPORTANT
3883
         *  @todo we don't create tags with numbers
3884
         *
3885
         */
3886
        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...
3887
            //the form is sending an id this means that the user select it from the list so it MUST exists
3888
            /* $new_tag_id = self::get_tag_id_from_id($tag,$field_id);
3889
              if ($new_tag_id !== false) {
3890
              $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
3891
              $result = Database::query($sql);
3892
              $last_insert_id = $new_tag_id;
3893
              } else {
3894
              $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3895
              $result = Database::query($sql);
3896
              $last_insert_id = Database::insert_id();
3897
              } */
3898
        }
3899
3900
        //this is a new tag
3901
        if ($tag_id == 0) {
3902
            //the tag doesn't exist
3903
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
3904
             Database::query($sql);
3905
            $last_insert_id = Database::insert_id();
3906
        } else {
3907
            //the tag exists we update it
3908
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
3909
             Database::query($sql);
3910
            $last_insert_id = $tag_id;
3911
        }
3912
3913
        if (!empty($last_insert_id) && ($last_insert_id != 0)) {
3914
            //we insert the relationship user-tag
3915
            $sql = "SELECT tag_id FROM $table_user_tag_values
3916
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
3917
            $result = Database::query($sql);
3918
            //if the relationship does not exist we create it
3919
            if (Database::num_rows($result) == 0) {
3920
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
3921
                Database::query($sql);
3922
            }
3923
        }
3924
    }
3925
3926
    /**
3927
     * Deletes an user tag
3928
     * @param int $user_id
3929
     * @param int $field_id
3930
     **/
3931
    public static function delete_user_tags($user_id, $field_id)
3932
    {
3933
        // database table definition
3934
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3935
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3936
        $tags = self::get_user_tags($user_id, $field_id);
3937
        if (is_array($tags) && count($tags) > 0) {
3938
            foreach ($tags as $key => $tag) {
3939
                if ($tag['count'] > '0') {
3940
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
3941
                    Database::query($sql);
3942
                }
3943
                $sql = "DELETE FROM $table_user_tag_values
3944
                        WHERE user_id = $user_id AND tag_id = $key";
3945
                Database::query($sql);
3946
            }
3947
        }
3948
    }
3949
3950
    /**
3951
     * Process the tag list comes from the UserManager::update_extra_field_value() function
3952
     * @param array $tags the tag list that will be added
3953
     * @param int $user_id
3954
     * @param int $field_id
3955
     *
3956
     * @return bool
3957
     */
3958
    public static function process_tags($tags, $user_id, $field_id)
3959
    {
3960
        // We loop the tags and add it to the DB
3961
        if (is_array($tags)) {
3962
            foreach ($tags as $tag) {
3963
                self::add_tag($tag, $user_id, $field_id);
3964
            }
3965
        } else {
3966
            self::add_tag($tags, $user_id, $field_id);
3967
        }
3968
3969
        return true;
3970
    }
3971
3972
    /**
3973
     * Returns a list of all administrators
3974
     *
3975
     * @return array
3976
     */
3977
    public static function get_all_administrators()
3978
    {
3979
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
3980
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
3981
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3982
        $access_url_id = api_get_current_access_url_id();
3983
        if (api_get_multiple_access_url()) {
3984
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3985
                    FROM $tbl_url_rel_user as url
3986
                    INNER JOIN $table_admin as admin
3987
                    ON (admin.user_id=url.user_id)
3988
                    INNER JOIN $table_user u
3989
                    ON (u.id=admin.user_id)
3990
                    WHERE access_url_id ='".$access_url_id."'";
3991
        } else {
3992
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
3993
                    FROM $table_admin as admin
3994
                    INNER JOIN $table_user u
3995
                    ON (u.id=admin.user_id)";
3996
        }
3997
        $result = Database::query($sql);
3998
        $return = array();
3999
        if (Database::num_rows($result) > 0) {
4000
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4001
                $return[$row['user_id']] = $row;
4002
            }
4003
        }
4004
4005
        return $return;
4006
    }
4007
4008
    /**
4009
     * Search an user (tags, first name, last name and email )
4010
     * @param string $tag
4011
     * @param int $field_id field id of the tag
4012
     * @param int $from where to start in the query
4013
     * @param int $number_of_items
4014
     * @param bool $getCount get count or not
4015
     * @return array
4016
     */
4017
    public static function get_all_user_tags(
4018
        $tag,
4019
        $field_id = 0,
4020
        $from = 0,
4021
        $number_of_items = 10,
4022
        $getCount = false
4023
    ) {
4024
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
4025
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4026
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4027
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4028
4029
        $field_id = intval($field_id);
4030
        $from = intval($from);
4031
        $number_of_items = intval($number_of_items);
4032
4033
        $where_field = "";
4034
        $where_extra_fields = self::get_search_form_where_extra_fields();
4035
        if ($field_id != 0) {
4036
            $where_field = " field_id = $field_id AND ";
4037
        }
4038
4039
        // all the information of the field
4040
        if ($getCount) {
4041
            $select = "SELECT count(DISTINCT u.id) count";
4042
        } else {
4043
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
4044
        }
4045
4046
        $sql = " $select
4047
                FROM $user_table u
4048
                INNER JOIN $access_url_rel_user_table url_rel_user
4049
                ON (u.id = url_rel_user.user_id)
4050
                LEFT JOIN $table_user_tag_values uv
4051
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
4052
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
4053
                WHERE
4054
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
4055
                    (
4056
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
4057
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
4058
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
4059
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
4060
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
4061
                     )
4062
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
4063
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
4064
4065
        $keyword_active = true;
4066
        // only active users
4067
        if ($keyword_active) {
4068
            $sql .= " AND u.active='1'";
4069
        }
4070
        // avoid anonymous
4071
        $sql .= " AND u.status <> 6 ";
4072
        $sql .= " ORDER BY username";
4073
        $sql .= " LIMIT $from , $number_of_items";
4074
4075
        $result = Database::query($sql);
4076
        $return = array();
4077
4078
        if (Database::num_rows($result) > 0) {
4079
            if ($getCount) {
4080
                $row = Database::fetch_array($result, 'ASSOC');
4081
                return $row['count'];
4082
            }
4083
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4084
                if (isset($return[$row['id']]) &&
4085
                    !empty($return[$row['id']]['tag'])
4086
                ) {
4087
                    $url = Display::url(
4088
                        $row['tag'],
4089
                        api_get_path(WEB_PATH).'main/social/search.php?q='.$row['tag'],
4090
                        array('class' => 'tag')
4091
                    );
4092
                    $row['tag'] = $url;
4093
                }
4094
                $return[$row['id']] = $row;
4095
            }
4096
        }
4097
4098
        return $return;
4099
    }
4100
4101
    /**
4102
      * Get extra filterable user fields (only type select)
4103
      * @return array
4104
      */
4105
    public static function getExtraFilterableFields()
4106
    {
4107
        $extraFieldList = self::get_extra_fields();
4108
        $fields = array();
4109
        if (is_array($extraFieldList)) {
4110
            foreach ($extraFieldList as $extraField) {
4111
                // If is enabled to filter and is a "<select>" field type
4112
                if ($extraField[8] == 1 && $extraField[2] == 4) {
4113
                    $fields[] = array(
4114
                        'name' => $extraField[3],
4115
                        'variable' => $extraField[1],
4116
                        'data' => $extraField[9]
4117
                    );
4118
                }
4119
            }
4120
        }
4121
4122
        if (is_array($fields) && count($fields) > 0) {
4123
            return $fields;
4124
        }
4125
    }
4126
4127
    /**
4128
      * Get extra where clauses for finding users based on extra filtrable user fields (type select)
4129
      * @return string With AND clauses based on user's ID which have the values to search in extra user fields
4130
      */
4131
    public static function get_search_form_where_extra_fields()
4132
    {
4133
        $useExtraFields = false;
4134
        $extraFields = self::getExtraFilterableFields();
4135
        $extraFieldResult = array();
4136
        if (is_array($extraFields) && count($extraFields) > 0) {
4137
            foreach ($extraFields as $extraField) {
4138
                $varName = 'field_'.$extraField['variable'];
4139
                if (self::is_extra_field_available($extraField['variable'])) {
4140
                    if (isset($_GET[$varName]) && $_GET[$varName] != '0') {
4141
                        $useExtraFields = true;
4142
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
4143
                            $extraField['variable'],
4144
                            $_GET[$varName]
4145
                        );
4146
                    }
4147
                }
4148
            }
4149
        }
4150
4151
        if ($useExtraFields) {
4152
            $finalResult = array();
4153
            if (count($extraFieldResult) > 1) {
4154
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
4155
                if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
4156
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
4157
                    }
4158
                }
4159
            } else {
4160
                $finalResult = $extraFieldResult[0];
4161
            }
4162
4163
            if (is_array($finalResult) && count($finalResult) > 0) {
4164
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
4165
            } else {
4166
                //no results
4167
                $whereFilter = " AND u.id  = -1 ";
4168
            }
4169
4170
            return $whereFilter;
4171
        }
4172
    }
4173
4174
    /**
4175
     * Show the search form
4176
     * @param string $query the value of the search box
4177
     * @return string HTML form
4178
     */
4179
    public static function get_search_form($query, $defaultParams = [])
4180
    {
4181
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
4182
        $form = new FormValidator(
4183
            'search_user',
4184
            'get',
4185
            api_get_path(WEB_PATH).'main/social/search.php',
4186
            '',
4187
            array(),
4188
            FormValidator::LAYOUT_HORIZONTAL
4189
        );
4190
4191
        $form->addText('q', get_lang('UsersGroups'), false, array(
4192
            "id" => "q"
4193
        ));
4194
        $options = array(
4195
            0 => get_lang('Select'),
4196
            1 => get_lang('User'),
4197
            2 => get_lang('Group'),
4198
        );
4199
        $form->addSelect(
4200
            'search_type',
4201
            get_lang('Type'),
4202
            $options,
4203
            array('onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type')
4204
        );
4205
4206
        // Extra fields
4207
        $extraFields = self::getExtraFilterableFields();
4208
        $defaults = [];
4209
        if (is_array($extraFields) && count($extraFields) > 0) {
4210
            foreach ($extraFields as $extraField) {
4211
                $varName = 'field_'.$extraField['variable'];
4212
                $options = [
4213
                    0 => get_lang('Select')
4214
                ];
4215
                foreach ($extraField['data'] as $option) {
4216
                    $checked = '';
4217
                    if (isset($_GET[$varName])) {
4218
                        if ($_GET[$varName] == $option[1]) {
4219
                            $defaults[$option[1]] = true;
4220
                        }
4221
                    }
4222
4223
                    $options[$option[1]] = $option[1];
4224
                }
4225
                $form->addSelect($varName, $extraField['name'], $options);
4226
            }
4227
        }
4228
4229
        $defaults['search_type'] = intval($searchType);
4230
        $defaults['q'] = api_htmlentities(Security::remove_XSS($query));
4231
4232
        if (!empty($defaultParams)) {
4233
            $defaults = array_merge($defaults, $defaultParams);
4234
        }
4235
        $form->setDefaults($defaults);
4236
        $form->addButtonSearch(get_lang('Search'));
4237
4238
        $js = '<script>
4239
        extra_field_toogle();
4240
        function extra_field_toogle() {
4241
            if (jQuery("select[name=search_type]").val() != "1") { jQuery(".extra_field").hide(); } else { jQuery(".extra_field").show(); }
4242
        }
4243
        </script>';
4244
4245
        return $js.$form->returnForm();
4246
    }
4247
4248
    /**
4249
     * Shows the user menu
4250
     */
4251
    public static function show_menu()
4252
    {
4253
        echo '<div class="actions">';
4254
        echo '<a href="/main/auth/profile.php">'.Display::return_icon('profile.png').' '.get_lang('PersonalData').'</a>';
4255
        echo '<a href="/main/messages/inbox.php">'.Display::return_icon('inbox.png').' '.get_lang('Inbox').'</a>';
4256
        echo '<a href="/main/messages/outbox.php">'.Display::return_icon('outbox.png').' '.get_lang('Outbox').'</a>';
4257
        echo '<span style="float:right; padding-top:7px;">'.
4258
        '<a href="/main/auth/profile.php?show=1">'.Display::return_icon('edit.gif').' '.get_lang('Configuration').'</a>';
4259
        '</span>';
4260
        echo '</div>';
4261
    }
4262
4263
    /**
4264
     * Allow to register contact to social network
4265
     * @param int $friend_id user friend id
4266
     * @param int $my_user_id user id
4267
     * @param int $relation_type relation between users see constants definition
4268
     * @return bool
4269
     */
4270
    public static function relate_users($friend_id, $my_user_id, $relation_type)
4271
    {
4272
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4273
4274
        $friend_id = intval($friend_id);
4275
        $my_user_id = intval($my_user_id);
4276
        $relation_type = intval($relation_type);
4277
4278
        $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4279
                WHERE
4280
                    friend_user_id='.$friend_id.' AND
4281
                    user_id='.$my_user_id.' AND
4282
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4283
        $result = Database::query($sql);
4284
        $row = Database::fetch_array($result, 'ASSOC');
4285
        $current_date = api_get_utc_datetime();
4286
4287
        if ($row['count'] == 0) {
4288
            $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4289
                    VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4290
            Database::query($sql);
4291
            return true;
4292
        }
4293
4294
        $sql = 'SELECT COUNT(*) as count, relation_type  FROM '.$tbl_my_friend.'
4295
                WHERE
4296
                    friend_user_id='.$friend_id.' AND
4297
                    user_id='.$my_user_id.' AND
4298
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4299
        $result = Database::query($sql);
4300
        $row = Database::fetch_array($result, 'ASSOC');
4301
4302
        if ($row['count'] == 1) {
4303
            //only for the case of a RRHH or a Student BOSS
4304
            if ($row['relation_type'] != $relation_type && ($relation_type == USER_RELATION_TYPE_RRHH || $relation_type == USER_RELATION_TYPE_BOSS)) {
4305
                $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4306
                        VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4307
            } else {
4308
                $sql = 'UPDATE '.$tbl_my_friend.' SET relation_type='.$relation_type.'
4309
                        WHERE friend_user_id='.$friend_id.' AND user_id='.$my_user_id;
4310
            }
4311
            Database::query($sql);
4312
4313
            return true;
4314
        }
4315
4316
        return false;
4317
    }
4318
4319
    /**
4320
     * Deletes a contact
4321
     * @param int user friend id
4322
     * @param bool true will delete ALL friends relationship from $friend_id
4323
     * @param string $with_status_condition
4324
     * @author isaac flores paz <[email protected]>
4325
     * @author Julio Montoya <[email protected]> Cleaning code
4326
     */
4327
    public static function remove_user_rel_user(
4328
        $friend_id,
4329
        $real_removed = false,
4330
        $with_status_condition = ''
4331
    ) {
4332
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4333
        $tbl_my_message = Database::get_main_table(TABLE_MESSAGE);
4334
        $friend_id = intval($friend_id);
4335
4336
        if ($real_removed) {
4337
            $extra_condition = '';
4338
            if ($with_status_condition != '') {
4339
                $extra_condition = ' AND relation_type = '.intval($with_status_condition);
4340
            }
4341
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4342
                    WHERE 
4343
                        relation_type <> '.USER_RELATION_TYPE_RRHH.' AND 
4344
                        friend_user_id='.$friend_id.' '.$extra_condition;
4345
            Database::query($sql);
4346
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4347
                   WHERE 
4348
                    relation_type <> '.USER_RELATION_TYPE_RRHH.' AND 
4349
                    user_id='.$friend_id.' '.$extra_condition;
4350
            Database::query($sql);
4351
        } else {
4352
            $user_id = api_get_user_id();
4353
            $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4354
                    WHERE
4355
                        user_id='.$user_id.' AND
4356
                        relation_type NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.') AND
4357
                        friend_user_id='.$friend_id;
4358
            $result = Database::query($sql);
4359
            $row = Database::fetch_array($result, 'ASSOC');
4360
            if ($row['count'] == 1) {
4361
                //Delete user rel user
4362
                $sql_i = 'UPDATE '.$tbl_my_friend.' SET relation_type='.USER_RELATION_TYPE_DELETED.'
4363
                          WHERE user_id='.$user_id.' AND friend_user_id='.$friend_id;
4364
                $sql_j = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4365
                          WHERE user_receiver_id='.$user_id.' AND user_sender_id='.$friend_id.' AND update_date="0000-00-00 00:00:00" ';
4366
                //Delete user
4367
                $sql_ij = 'UPDATE '.$tbl_my_friend.'  SET relation_type='.USER_RELATION_TYPE_DELETED.'
4368
                           WHERE user_id='.$friend_id.' AND friend_user_id='.$user_id;
4369
                $sql_ji = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4370
                           WHERE user_receiver_id='.$friend_id.' AND user_sender_id='.$user_id.' AND update_date="0000-00-00 00:00:00" ';
4371
                Database::query($sql_i);
4372
                Database::query($sql_j);
4373
                Database::query($sql_ij);
4374
                Database::query($sql_ji);
4375
            }
4376
        }
4377
    }
4378
4379
    /**
4380
     * @param int $userId
4381
     * @return array
4382
     */
4383
    public static function getDrhListFromUser($userId)
4384
    {
4385
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4386
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4387
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4388
        $userId = intval($userId);
4389
4390
        $orderBy = null;
4391
        if (api_is_western_name_order()) {
4392
            $orderBy .= " ORDER BY firstname, lastname ";
4393
        } else {
4394
            $orderBy .= " ORDER BY lastname, firstname ";
4395
        }
4396
4397
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4398
                FROM $tblUser u
4399
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4400
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4401
                WHERE
4402
                    access_url_id = ".api_get_current_access_url_id()." AND
4403
                    uru.user_id = '$userId' AND
4404
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4405
                $orderBy
4406
                ";
4407
        $result = Database::query($sql);
4408
4409
        return Database::store_result($result);
4410
    }
4411
4412
    /**
4413
     * get users followed by human resource manager
4414
     * @param int $userId
4415
     * @param int $userStatus (STUDENT, COURSEMANAGER, etc)
4416
     * @param bool $getOnlyUserId
4417
     * @param bool $getSql
4418
     * @param bool $getCount
4419
     * @param int $from
4420
     * @param int $numberItems
4421
     * @param int $column
4422
     * @param string $direction
4423
     * @param int $active
4424
     * @param string $lastConnectionDate
4425
     * @return array     users
4426
     */
4427
    public static function get_users_followed_by_drh(
4428
        $userId,
4429
        $userStatus = 0,
4430
        $getOnlyUserId = false,
4431
        $getSql = false,
4432
        $getCount = false,
4433
        $from = null,
4434
        $numberItems = null,
4435
        $column = null,
4436
        $direction = null,
4437
        $active = null,
4438
        $lastConnectionDate = null
4439
    ) {
4440
        return self::getUsersFollowedByUser(
4441
            $userId,
4442
            $userStatus,
4443
            $getOnlyUserId,
4444
            $getSql,
4445
            $getCount,
4446
            $from,
4447
            $numberItems,
4448
            $column,
4449
            $direction,
4450
            $active,
4451
            $lastConnectionDate,
4452
            DRH
4453
        );
4454
    }
4455
4456
    /**
4457
    * Get users followed by human resource manager
4458
    * @param int $userId
4459
    * @param int  $userStatus Filter users by status (STUDENT, COURSEMANAGER, etc)
4460
    * @param bool $getOnlyUserId
4461
    * @param bool $getSql
4462
    * @param bool $getCount
4463
    * @param int $from
4464
    * @param int $numberItems
4465
    * @param int $column
4466
    * @param string $direction
4467
    * @param int $active
4468
    * @param string $lastConnectionDate
4469
    * @param int $status the function is called by who? COURSEMANAGER, DRH?
4470
    * @param string $keyword
4471
     *
4472
    * @return array user list
4473
    */
4474
    public static function getUsersFollowedByUser(
4475
        $userId,
4476
        $userStatus = null,
4477
        $getOnlyUserId = false,
4478
        $getSql = false,
4479
        $getCount = false,
4480
        $from = null,
4481
        $numberItems = null,
4482
        $column = null,
4483
        $direction = null,
4484
        $active = null,
4485
        $lastConnectionDate = null,
4486
        $status = null,
4487
        $keyword = null
4488
    ) {
4489
        // Database Table Definitions
4490
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4491
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4492
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4493
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4494
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4495
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4496
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4497
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4498
4499
        $userId = intval($userId);
4500
        $limitCondition = '';
4501
4502
        if (isset($from) && isset($numberItems)) {
4503
            $from = intval($from);
4504
            $numberItems = intval($numberItems);
4505
            $limitCondition = "LIMIT $from, $numberItems";
4506
        }
4507
4508
        $column = Database::escape_string($column);
4509
        $direction = in_array(strtolower($direction), array('asc', 'desc')) ? $direction : null;
4510
4511
        $userConditions = '';
4512
        if (!empty($userStatus)) {
4513
            $userConditions .= ' AND u.status = '.intval($userStatus);
4514
        }
4515
4516
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4517
        if ($getOnlyUserId) {
4518
            $select = " SELECT DISTINCT u.id user_id";
4519
        }
4520
4521
        $masterSelect = "SELECT DISTINCT * FROM ";
4522
4523
        if ($getCount) {
4524
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4525
            $select = " SELECT DISTINCT(u.id) user_id";
4526
        }
4527
4528
        if (!is_null($active)) {
4529
            $active = intval($active);
4530
            $userConditions .= " AND u.active = $active ";
4531
        }
4532
4533
        if (!empty($keyword)) {
4534
            $keyword = Database::escape_string($keyword);
4535
            $userConditions .= " AND (
4536
                u.username LIKE '%$keyword%' OR
4537
                u.firstname LIKE '%$keyword%' OR
4538
                u.lastname LIKE '%$keyword%' OR
4539
                u.official_code LIKE '%$keyword%' OR
4540
                u.email LIKE '%$keyword%'
4541
            )";
4542
        }
4543
4544
        if (!empty($lastConnectionDate)) {
4545
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4546
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4547
        }
4548
4549
        $courseConditions = null;
4550
        $sessionConditionsCoach = null;
4551
        $sessionConditionsTeacher = null;
4552
        $drhConditions = null;
4553
        $teacherSelect = null;
4554
4555
        switch ($status) {
4556
            case DRH:
4557
                $drhConditions .= " AND
4558
                    friend_user_id = '$userId' AND
4559
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4560
                ";
4561
                break;
4562
            case COURSEMANAGER:
4563
                $drhConditions .= " AND
4564
                    friend_user_id = '$userId' AND
4565
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4566
                ";
4567
4568
                $sessionConditionsCoach .= " AND
4569
                    (s.id_coach = '$userId')
4570
                ";
4571
4572
                $sessionConditionsTeacher .= " AND
4573
                    (scu.status = 2 AND scu.user_id = '$userId')
4574
                ";
4575
4576
                $teacherSelect =
4577
                "UNION ALL (
4578
                        $select
4579
                        FROM $tbl_user u
4580
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4581
                        WHERE
4582
                            (
4583
                                sru.session_id IN (
4584
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4585
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4586
                                    ON session_rel_access_rel_user.session_id = s.id
4587
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4588
                                    $sessionConditionsCoach                                  
4589
                                ) OR sru.session_id IN (
4590
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4591
                                    INNER JOIN $tbl_session_rel_access_url url
4592
                                    ON (url.session_id = s.id)
4593
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4594
                                    ON (scu.session_id = s.id)
4595
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4596
                                    $sessionConditionsTeacher
4597
                                )
4598
                            )                            
4599
                            $userConditions
4600
                    )
4601
                    UNION ALL(
4602
                        $select
4603
                        FROM $tbl_user u
4604
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4605
                        WHERE cu.c_id IN (
4606
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4607
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4608
                        )
4609
                        $userConditions
4610
                    )"
4611
                ;
4612
                break;
4613
            case STUDENT_BOSS:
4614
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
4615
                break;
4616
            case HRM_REQUEST:
4617
                $drhConditions .= " AND
4618
                    friend_user_id = '$userId' AND
4619
                    relation_type = '".USER_RELATION_TYPE_HRM_REQUEST."'
4620
                ";
4621
                break;
4622
        }
4623
4624
        $join = null;
4625
        $sql = " $masterSelect
4626
                (
4627
                    (
4628
                        $select
4629
                        FROM $tbl_user u
4630
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4631
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4632
                        $join
4633
                        WHERE
4634
                            access_url_id = ".api_get_current_access_url_id()."
4635
                            $drhConditions
4636
                            $userConditions
4637
                    )
4638
                    $teacherSelect
4639
4640
                ) as t1";
4641
4642
        if ($getSql) {
4643
            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...
4644
        }
4645
        if ($getCount) {
4646
            $result = Database::query($sql);
4647
            $row = Database::fetch_array($result);
4648
            return $row['count'];
4649
        }
4650
4651
        $orderBy = null;
4652
        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...
4653
            if (api_is_western_name_order()) {
4654
                $orderBy .= " ORDER BY firstname, lastname ";
4655
            } else {
4656
                $orderBy .= " ORDER BY lastname, firstname ";
4657
            }
4658
4659
            if (!empty($column) && !empty($direction)) {
4660
                // Fixing order due the UNIONs
4661
                $column = str_replace('u.', '', $column);
4662
                $orderBy = " ORDER BY $column $direction ";
4663
            }
4664
        }
4665
4666
        $sql .= $orderBy;
4667
        $sql .= $limitCondition;
4668
4669
        $result = Database::query($sql);
4670
        $users = array();
4671
        if (Database::num_rows($result) > 0) {
4672
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
                    array($score, $cat[0]->get_weight()),
4951
                    SCORE_DIV_PERCENT_WITH_CUSTOM
4952
                );
4953
            } else {
4954
                $grade = $displayscore->display_score(
4955
                    array($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 = array();
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 = array();
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
            array(
5299
            'where' => array(
5300
                'status = ?' => COURSEMANAGER
5301
            )
5302
        ));
5303
5304
        foreach ($resultData as &$teacherData) {
5305
            $teacherData['completeName'] = api_get_person_name(
5306
                $teacherData['firstname'],
5307
                $teacherData['lastname']
5308
            );
5309
        }
5310
5311
        return $resultData;
5312
    }
5313
5314
    /**
5315
     * @return array
5316
     */
5317
    public static function getOfficialCodeGrouped()
5318
    {
5319
        $user = Database::get_main_table(TABLE_MAIN_USER);
5320
        $sql = "SELECT DISTINCT official_code
5321
                FROM $user
5322
                GROUP BY official_code";
5323
        $result = Database::query($sql);
5324
        $values = Database::store_result($result, 'ASSOC');
5325
        $result = array();
5326
        foreach ($values as $value) {
5327
            $result[$value['official_code']] = $value['official_code'];
5328
        }
5329
        return $result;
5330
    }
5331
5332
    /**
5333
     * @param string $officialCode
5334
     * @return array
5335
     */
5336
    public static function getUsersByOfficialCode($officialCode)
5337
    {
5338
        $user = Database::get_main_table(TABLE_MAIN_USER);
5339
        $officialCode = Database::escape_string($officialCode);
5340
5341
        $sql = "SELECT DISTINCT id
5342
                FROM $user
5343
                WHERE official_code = '$officialCode'
5344
                ";
5345
        $result = Database::query($sql);
5346
5347
        $users = array();
5348
        while ($row = Database::fetch_array($result)) {
5349
            $users[] = $row['id'];
5350
        }
5351
        return $users;
5352
    }
5353
5354
    /**
5355
     * Calc the expended time (in seconds) by a user in a course
5356
     * @param int $userId The user id
5357
     * @param int $courseId The course id
5358
     * @param int $sessionId Optional. The session id
5359
     * @param string $from Optional. From date
5360
     * @param string $until Optional. Until date
5361
     * @return int The time
5362
     */
5363
    public static function getTimeSpentInCourses(
5364
        $userId,
5365
        $courseId,
5366
        $sessionId = 0,
5367
        $from = '',
5368
        $until = ''
5369
    ) {
5370
        $userId = intval($userId);
5371
        $sessionId = intval($sessionId);
5372
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5373
        $whereConditions = array(
5374
            'user_id = ? ' => $userId,
5375
            'AND c_id = ? ' => $courseId,
5376
            'AND session_id = ? ' => $sessionId
5377
        );
5378
5379
        if (!empty($from) && !empty($until)) {
5380
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5381
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5382
        }
5383
5384
        $trackResult = Database::select(
5385
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5386
            $trackCourseAccessTable,
5387
            array(
5388
                'where' => $whereConditions
5389
            ), 'first'
5390
        );
5391
5392
        if ($trackResult != false) {
5393
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5394
        }
5395
5396
        return 0;
5397
    }
5398
5399
    /**
5400
     * Get the boss user ID from a followed user id
5401
     * @param $userId
5402
     * @return bool
5403
     */
5404
    public static function getFirstStudentBoss($userId)
5405
    {
5406
        $userId = intval($userId);
5407
        if ($userId > 0) {
5408
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5409
            $row = Database::select(
5410
                'DISTINCT friend_user_id AS boss_id',
5411
                $userRelTable,
5412
                array(
5413
                    'where' => array(
5414
                        'user_id = ? AND relation_type = ? LIMIT 1' => array(
5415
                            $userId,
5416
                            USER_RELATION_TYPE_BOSS,
5417
                        )
5418
                    )
5419
                )
5420
            );
5421
            if (!empty($row)) {
5422
5423
                return $row[0]['boss_id'];
5424
            }
5425
        }
5426
5427
        return false;
5428
    }
5429
5430
    /**
5431
     * Get the boss user ID from a followed user id
5432
     * @param $userId
5433
     * @return array
5434
     */
5435
    public static function getStudentBossList($userId)
5436
    {
5437
        $userId = (int) $userId;
5438
        if ($userId > 0) {
5439
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5440
            $result = Database::select(
5441
                'DISTINCT friend_user_id AS boss_id',
5442
                $userRelTable,
5443
                array(
5444
                    'where' => array(
5445
                        'user_id = ? AND relation_type = ? ' => array(
5446
                            $userId,
5447
                            USER_RELATION_TYPE_BOSS,
5448
                        )
5449
                    )
5450
                )
5451
            );
5452
5453
            return $result;
5454
        }
5455
5456
        return [];
5457
    }
5458
5459
    /**
5460
     * @param int $bossId
5461
     * @param int $studentId
5462
     *
5463
     * @return bool
5464
     */
5465
    public static function userIsBossOfStudent($bossId, $studentId)
5466
    {
5467
        $result = false;
5468
        $bossList = self::getStudentBossList($studentId);
5469
        if (!empty($bossList)) {
5470
            $bossList = array_column($bossList, 'boss_id');
5471
            if (in_array($bossId, $bossList)) {
5472
                $result = true;
5473
            }
5474
        }
5475
5476
        return $result;
5477
    }
5478
5479
    /**
5480
     * Get either a Gravatar URL or complete image tag for a specified email address.
5481
     *
5482
     * @param string $email The email address
5483
     * @param string $s Size in pixels, defaults to 80px [ 1 - 2048 ]
5484
     * @param string $d Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
5485
     * @param string $r Maximum rating (inclusive) [ g | pg | r | x ]
5486
     * @param bool $img True to return a complete IMG tag False for just the URL
5487
     * @param array $atts Optional, additional key/value attributes to include in the IMG tag
5488
     * @return String containing either just a URL or a complete image tag
5489
     * @source http://gravatar.com/site/implement/images/php/
5490
     */
5491
    private static function getGravatar(
5492
        $email,
5493
        $s = 80,
5494
        $d = 'mm',
5495
        $r = 'g',
5496
        $img = false,
5497
        $atts = array()
5498
    ) {
5499
        $url = 'http://www.gravatar.com/avatar/';
5500
        if (!empty($_SERVER['HTTPS'])) {
5501
            $url = 'https://secure.gravatar.com/avatar/';
5502
        }
5503
        $url .= md5(strtolower(trim($email)));
5504
        $url .= "?s=$s&d=$d&r=$r";
5505
        if ($img) {
5506
            $url = '<img src="'.$url.'"';
5507
            foreach ($atts as $key => $val)
5508
                $url .= ' '.$key.'="'.$val.'"';
5509
            $url .= ' />';
5510
        }
5511
        return $url;
5512
    }
5513
5514
    /**
5515
     * Displays the name of the user and makes the link to the user profile
5516
     * @param array $userInfo
5517
     *
5518
     * @return string
5519
     */
5520
    public static function getUserProfileLink($userInfo)
5521
    {
5522
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5523
            return Display::url(
5524
                $userInfo['complete_name_with_username'],
5525
                $userInfo['profile_url']
5526
            );
5527
        } else {
5528
            return get_lang('Anonymous');
5529
        }
5530
    }
5531
5532
    /**
5533
     * Displays the name of the user and makes the link to the user profile
5534
     *
5535
     * @param $userInfo
5536
     *
5537
     * @return string
5538
     */
5539
    public static function getUserProfileLinkWithPicture($userInfo)
5540
    {
5541
        return Display::url(
5542
            Display::img($userInfo['avatar']),
5543
            $userInfo['profile_url']
5544
        );
5545
    }
5546
5547
    /**
5548
     * Get users whose name matches $firstname and $lastname
5549
     * @param string $firstname Firstname to search
5550
     * @param string $lastname Lastname to search
5551
     * @return array The user list
5552
     */
5553
    public static function getUsersByName($firstname, $lastname)
5554
    {
5555
        $firstname = Database::escape_string($firstname);
5556
        $lastname = Database::escape_string($lastname);
5557
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5558
5559
        $sql = <<<SQL
5560
            SELECT id, username, lastname, firstname
5561
            FROM $userTable
5562
            WHERE 
5563
                firstname LIKE '$firstname%' AND
5564
                lastname LIKE '$lastname%'
5565
SQL;
5566
        $result = Database::query($sql);
5567
        $users = [];
5568
        while ($resultData = Database::fetch_object($result)) {
5569
            $users[] = $resultData;
5570
        }
5571
5572
        return $users;
5573
    }
5574
5575
    /**
5576
     * @param int $optionSelected
5577
     * @return string
5578
     */
5579
    public static function getUserSubscriptionTab($optionSelected = 1)
5580
    {
5581
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5582
        if (($allowAdmin === 'true' && api_is_allowed_to_edit()) ||
5583
            api_is_platform_admin()
5584
        ) {
5585
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5586
5587
            $headers = [
5588
                [
5589
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5590
                    'content' => get_lang('Students'),
5591
                ],
5592
                [
5593
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5594
                    'content' => get_lang('Teachers'),
5595
                ],
5596
                /*[
5597
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5598
                    'content' => get_lang('Students'),
5599
                ],
5600
                [
5601
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5602
                    'content' => get_lang('Teachers'),
5603
                ],*/
5604
                [
5605
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5606
                    'content' => get_lang('Groups'),
5607
                ],
5608
                [
5609
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5610
                    'content' => get_lang('Classes'),
5611
                ]
5612
            ];
5613
5614
            return Display::tabsOnlyLink($headers, $optionSelected);
5615
        }
5616
    }
5617
5618
    /**
5619
     * Make sure this function is protected because it does NOT check password!
5620
     *
5621
     * This function defines globals.
5622
     * @param  int $userId
5623
     * @param bool $checkIfUserCanLoginAs
5624
     * @return bool
5625
     * @author Evie Embrechts
5626
     * @author Yannick Warnier <[email protected]>
5627
    */
5628
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5629
    {
5630
        $userId = intval($userId);
5631
        $userInfo = api_get_user_info($userId);
5632
5633
        // Check if the user is allowed to 'login_as'
5634
        $canLoginAs = true;
5635
        if ($checkIfUserCanLoginAs) {
5636
            $canLoginAs = api_can_login_as($userId);
5637
        }
5638
5639
        if (!$canLoginAs || empty($userInfo)) {
5640
            return false;
5641
        }
5642
5643
        if ($userId) {
5644
            // Logout the current user
5645
            self::loginDelete(api_get_user_id());
5646
5647
            Session::erase('_user');
5648
            Session::erase('is_platformAdmin');
5649
            Session::erase('is_allowedCreateCourse');
5650
            Session::erase('_uid');
5651
5652
            // Cleaning session variables
5653
            $_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...
5654
            $_user['lastName'] = $userInfo['lastname'];
5655
            $_user['mail'] = $userInfo['email'];
5656
            $_user['official_code'] = $userInfo['official_code'];
5657
            $_user['picture_uri'] = $userInfo['picture_uri'];
5658
            $_user['user_id'] = $userId;
5659
            $_user['id'] = $userId;
5660
            $_user['status'] = $userInfo['status'];
5661
5662
            // Filling session variables with new data
5663
            Session::write('_uid', $userId);
5664
            Session::write('_user', $userInfo);
5665
            Session::write('is_platformAdmin', (bool) UserManager::is_admin($userId));
5666
            Session::write('is_allowedCreateCourse', (bool) ($userInfo['status'] == 1));
5667
            // will be useful later to know if the user is actually an admin or not (example reporting)
5668
            Session::write('login_as', true);
5669
5670
            return true;
5671
        }
5672
5673
        return false;
5674
    }
5675
5676
    /**
5677
     * Remove all login records from the track_e_online stats table,
5678
     * for the given user ID.
5679
     * @param int $userId User ID
5680
     * @return void
5681
     */
5682
    public static function loginDelete($userId)
5683
    {
5684
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
5685
        $userId = intval($userId);
5686
        $query = "DELETE FROM ".$online_table." WHERE login_user_id = $userId";
5687
        Database::query($query);
5688
    }
5689
5690
    /**
5691
     * Login as first admin user registered in the platform
5692
     * @return array
5693
     */
5694
    public static function logInAsFirstAdmin()
5695
    {
5696
        $adminList = self::get_all_administrators();
5697
5698
        if (!empty($adminList)) {
5699
            $userInfo = current($adminList);
5700
            if (!empty($userInfo)) {
5701
                $result = self::loginAsUser($userInfo['user_id'], false);
5702
                if ($result && api_is_platform_admin()) {
5703
                    return api_get_user_info();
5704
                }
5705
            }
5706
        }
5707
5708
        return [];
5709
    }
5710
5711
    /**
5712
     * Check if user is teacher of a student based in their courses
5713
     * @param $teacherId
5714
     * @param $studentId
5715
     * @return array
5716
     */
5717
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
5718
    {
5719
        $courses = CourseManager::getCoursesFollowedByUser(
5720
            $teacherId,
5721
            COURSEMANAGER
5722
        );
5723
        if (empty($courses)) {
5724
            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...
5725
        }
5726
5727
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
5728
        if (empty($coursesFromUser)) {
5729
            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...
5730
        }
5731
5732
        $coursesCodeList = array_column($courses, 'code');
5733
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
5734
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
5735
        $commonCourses = array_filter($commonCourses);
5736
5737
        if (!empty($commonCourses)) {
5738
            return $commonCourses;
5739
        }
5740
5741
        return [];
5742
    }
5743
5744
    /**
5745
     * @param int $teacherId
5746
     * @param int $studentId
5747
     * @return bool
5748
     */
5749
    public static function isTeacherOfStudent($teacherId, $studentId)
5750
    {
5751
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
5752
            $teacherId,
5753
            $studentId
5754
        );
5755
5756
        if (!empty($courses)) {
5757
            return true;
5758
        }
5759
5760
        return false;
5761
    }
5762
5763
    /**
5764
     * Send user confirmation mail
5765
     *
5766
     * @param User $user
5767
     */
5768
    public static function sendUserConfirmationMail(User $user)
5769
    {
5770
        $uniqueId = api_get_unique_id();
5771
        $user->setConfirmationToken($uniqueId);
5772
5773
        Database::getManager()->persist($user);
5774
        Database::getManager()->flush();
5775
5776
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
5777
        $mailSubject = get_lang('RegistrationConfirmation');
5778
        $mailBody = get_lang('RegistrationConfirmationEmailMessage')
5779
            .PHP_EOL
5780
            .Display::url($url, $url);
5781
5782
        api_mail_html(
5783
            $user->getCompleteName(),
5784
            $user->getEmail(),
5785
            $mailSubject,
5786
            $mailBody
5787
        );
5788
        Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
5789
    }
5790
}
5791