Completed
Push — master ( c10731...579cac )
by Julito
10:11 queued 13s
created

UserManager::cleanUserRequestsOfRemoval()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 14
nc 3
nop 1
dl 0
loc 20
rs 9.7998
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
5
use Chamilo\CoreBundle\Entity\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...
6
use Chamilo\CoreBundle\Entity\SkillRelUserComment;
7
use Chamilo\CoreBundle\Framework\Container;
8
use Chamilo\CoreBundle\Repository\AccessUrlRepository;
9
use Chamilo\UserBundle\Entity\User;
10
use Chamilo\UserBundle\Repository\UserRepository;
11
use ChamiloSession as Session;
12
13
/**
14
 * Class UserManager.
15
 *
16
 * This library provides functions for user management.
17
 * Include/require it in your code to use its functionality.
18
 *
19
 * @package chamilo.library
20
 *
21
 * @author Julio Montoya <[email protected]> Social network groups added 2009/12
22
 */
23
class UserManager
24
{
25
    // This constants are deprecated use the constants located in ExtraField
26
    public const USER_FIELD_TYPE_TEXT = 1;
27
    public const USER_FIELD_TYPE_TEXTAREA = 2;
28
    public const USER_FIELD_TYPE_RADIO = 3;
29
    public const USER_FIELD_TYPE_SELECT = 4;
30
    public const USER_FIELD_TYPE_SELECT_MULTIPLE = 5;
31
    public const USER_FIELD_TYPE_DATE = 6;
32
    public const USER_FIELD_TYPE_DATETIME = 7;
33
    public const USER_FIELD_TYPE_DOUBLE_SELECT = 8;
34
    public const USER_FIELD_TYPE_DIVIDER = 9;
35
    public const USER_FIELD_TYPE_TAG = 10;
36
    public const USER_FIELD_TYPE_TIMEZONE = 11;
37
    public const USER_FIELD_TYPE_SOCIAL_PROFILE = 12;
38
    public const USER_FIELD_TYPE_FILE = 13;
39
    public const USER_FIELD_TYPE_MOBILE_PHONE_NUMBER = 14;
40
41
    private static $encryptionMethod;
42
43
    /**
44
     * Constructor.
45
     *
46
     * @assert () === null
47
     */
48
    public function __construct()
49
    {
50
    }
51
52
    /**
53
     * Repository is use to query the DB, selects, etc.
54
     *
55
     * @return UserRepository
56
     */
57
    public static function getRepository()
58
    {
59
        return Database::getManager()->getRepository('ChamiloUserBundle:User');
60
    }
61
62
    /**
63
     * Create/update/delete methods are available in the UserManager
64
     * (based in the Sonata\UserBundle\Entity\UserManager).
65
     *
66
     * @return \Sonata\UserBundle\Entity\UserManager
67
     */
68
    public static function getManager()
69
    {
70
        return Container::getUserManager();
71
    }
72
73
    /**
74
     * @param string $encryptionMethod
75
     */
76
    public static function setPasswordEncryption($encryptionMethod)
77
    {
78
        self::$encryptionMethod = $encryptionMethod;
79
    }
80
81
    /**
82
     * @return bool|mixed
83
     */
84
    public static function getPasswordEncryption()
85
    {
86
        $encryptionMethod = self::$encryptionMethod;
87
        if (empty($encryptionMethod)) {
88
            $encryptionMethod = api_get_configuration_value('password_encryption');
89
        }
90
91
        return $encryptionMethod;
92
    }
93
94
    /**
95
     * Validates the password.
96
     *
97
     * @param $encoded
98
     * @param $raw
99
     * @param $salt
100
     *
101
     * @return bool
102
     */
103
    public static function isPasswordValid($encoded, $raw, $salt)
104
    {
105
        $encoder = new \Chamilo\UserBundle\Security\Encoder(self::getPasswordEncryption());
0 ignored issues
show
Bug introduced by
It seems like self::getPasswordEncryption() can also be of type boolean; however, parameter $passwordEncrypt of Chamilo\UserBundle\Security\Encoder::__construct() does only seem to accept string, 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

105
        $encoder = new \Chamilo\UserBundle\Security\Encoder(/** @scrutinizer ignore-type */ self::getPasswordEncryption());
Loading history...
106
        $validPassword = $encoder->isPasswordValid($encoded, $raw, $salt);
107
108
        return $validPassword;
109
    }
110
111
    /**
112
     * @param int    $userId
113
     * @param string $password
114
     */
115
    public static function updatePassword($userId, $password)
116
    {
117
        $repository = self::getRepository();
118
        /** @var User $user */
119
        $user = $repository->find($userId);
120
        $userManager = self::getManager();
121
        $user->setPlainPassword($password);
122
        $userManager->updateUser($user, true);
123
    }
124
125
    /**
126
     * Creates a new user for the platform.
127
     *
128
     * @author Hugues Peeters <[email protected]>,
129
     * @author Roan Embrechts <[email protected]>
130
     *
131
     * @param string        $firstName
132
     * @param string        $lastName
133
     * @param int           $status               (1 for course tutor, 5 for student, 6 for anonymous)
134
     * @param string        $email
135
     * @param string        $loginName
136
     * @param string        $password
137
     * @param string        $official_code        Any official code (optional)
138
     * @param string        $language             User language    (optional)
139
     * @param string        $phone                Phone number    (optional)
140
     * @param string        $picture_uri          Picture URI        (optional)
141
     * @param string        $authSource           Authentication source (defaults to 'platform', dependind on constant)
142
     * @param string        $expirationDate       Account expiration date (optional, defaults to null)
143
     * @param int           $active               Whether the account is enabled or disabled by default
144
     * @param int           $hr_dept_id           The department of HR in which the user is registered (defaults to 0)
145
     * @param array         $extra                Extra fields
146
     * @param string        $encrypt_method       Used if password is given encrypted. Set to an empty string by default
147
     * @param bool          $send_mail
148
     * @param bool          $isAdmin
149
     * @param string        $address
150
     * @param bool          $sendEmailToAllAdmins
151
     * @param FormValidator $form
152
     * @param int           $creatorId
153
     * @param array         $emailTemplate
154
     *
155
     * @return mixed new user id - if the new user creation succeeds, false otherwise
156
     * @desc The function tries to retrieve user id from the session.
157
     * If it exists, the current user id is the creator id. If a problem arises,
158
     * @assert ('Sam','Gamegie',5,'[email protected]','jo','jo') > 1
159
     * @assert ('Pippin','Took',null,null,'jo','jo') === false
160
     */
161
    public static function create_user(
162
        $firstName,
163
        $lastName,
164
        $status,
165
        $email,
166
        $loginName,
167
        $password,
168
        $official_code = '',
169
        $language = '',
170
        $phone = '',
171
        $picture_uri = '',
172
        $authSource = PLATFORM_AUTH_SOURCE,
173
        $expirationDate = null,
174
        $active = 1,
175
        $hr_dept_id = 0,
176
        $extra = [],
177
        $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

177
        /** @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...
178
        $send_mail = false,
179
        $isAdmin = false,
180
        $address = '',
181
        $sendEmailToAllAdmins = false,
182
        $form = null,
183
        $creatorId = 0,
184
        $emailTemplate = []
185
    ) {
186
        $creatorId = empty($creatorId) ? api_get_user_id() : 0;
187
        $hook = HookCreateUser::create();
188
        if (!empty($hook)) {
189
            $hook->notifyCreateUser(HOOK_EVENT_TYPE_PRE);
190
        }
191
192
        // First check wether the login already exists
193
        if (!self::is_username_available($loginName)) {
194
            Display::addFlash(
195
                Display::return_message(get_lang('LoginAlreadyTaken'))
196
            );
197
198
            return false;
199
        }
200
201
        global $_configuration;
202
        $original_password = $password;
203
204
        $access_url_id = 1;
205
        if (api_get_multiple_access_url()) {
206
            $access_url_id = api_get_current_access_url_id();
207
        } else {
208
            // In some cases, the first access_url ID might be different from 1
209
            // for example when using a DB cluster or hacking the DB manually.
210
            // In this case, we want the first row, not necessarily "1".
211
            $dbm = Database::getManager();
212
            /** @var AccessUrlRepository $accessUrlRepository */
213
            $accessUrlRepository = $dbm->getRepository('ChamiloCoreBundle:AccessUrl');
214
            $accessUrl = $accessUrlRepository->getFirstId();
215
            if (!empty($accessUrl[0]) && !empty($accessUrl[0][1])) {
216
                $access_url_id = $accessUrl[0][1];
217
            }
218
        }
219
220
        if (isset($_configuration[$access_url_id]) &&
221
            is_array($_configuration[$access_url_id]) &&
222
            isset($_configuration[$access_url_id]['hosting_limit_users']) &&
223
            $_configuration[$access_url_id]['hosting_limit_users'] > 0) {
224
            $num = self::get_number_of_users();
225
            if ($num >= $_configuration[$access_url_id]['hosting_limit_users']) {
226
                api_warn_hosting_contact('hosting_limit_users');
227
                Display::addFlash(
228
                    Display::return_message(
229
                        get_lang('PortalUsersLimitReached'),
230
                        'warning'
231
                    )
232
                );
233
234
                return false;
235
            }
236
        }
237
238
        if ($status === 1 &&
239
            isset($_configuration[$access_url_id]) &&
240
            is_array($_configuration[$access_url_id]) &&
241
            isset($_configuration[$access_url_id]['hosting_limit_teachers']) &&
242
            $_configuration[$access_url_id]['hosting_limit_teachers'] > 0
243
        ) {
244
            $num = self::get_number_of_users(1);
245
            if ($num >= $_configuration[$access_url_id]['hosting_limit_teachers']) {
246
                Display::addFlash(
247
                    Display::return_message(
248
                        get_lang('PortalTeachersLimitReached'),
249
                        'warning'
250
                    )
251
                );
252
                api_warn_hosting_contact('hosting_limit_teachers');
253
254
                return false;
255
            }
256
        }
257
258
        if (empty($password)) {
259
            if ($authSource === PLATFORM_AUTH_SOURCE) {
260
                Display::addFlash(
261
                    Display::return_message(
262
                        get_lang('ThisFieldIsRequired').': '.get_lang(
263
                            'Password'
264
                        ),
265
                        'warning'
266
                    )
267
                );
268
269
                return false;
270
            }
271
272
            // We use the authSource as password.
273
            // The real validation will be by processed by the auth
274
            // source not Chamilo
275
            $password = $authSource;
276
        }
277
278
        // database table definition
279
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
280
281
        // Checking the user language
282
        $languages = api_get_languages();
283
        $language = strtolower($language);
284
        if (isset($languages)) {
285
            if (!in_array($language, $languages)) {
286
                $language = api_get_setting('platformLanguage');
287
            }
288
        }
289
290
        $currentDate = api_get_utc_datetime();
291
        $now = new DateTime();
292
293
        if (empty($expirationDate) || $expirationDate == '0000-00-00 00:00:00') {
294
            // Default expiration date
295
            // if there is a default duration of a valid account then
296
            // we have to change the expiration_date accordingly
297
            // Accept 0000-00-00 00:00:00 as a null value to avoid issues with
298
            // third party code using this method with the previous (pre-1.10)
299
            // value of 0000...
300
            if (api_get_setting('account_valid_duration') != '') {
301
                $expirationDate = new DateTime($currentDate);
302
                $days = (int) api_get_setting('account_valid_duration');
303
                $expirationDate->modify('+'.$days.' day');
304
            }
305
        } else {
306
            $expirationDate = api_get_utc_datetime($expirationDate);
307
            $expirationDate = new \DateTime($expirationDate, new DateTimeZone('UTC'));
308
        }
309
310
        $userManager = self::getManager();
311
312
        /** @var User $user */
313
        $user = $userManager->createUser();
314
315
        $user
316
            ->setLastname($lastName)
317
            ->setFirstname($firstName)
318
            ->setUsername($loginName)
319
            ->setStatus($status)
320
            ->setPlainPassword($password)
321
            ->setEmail($email)
322
            ->setOfficialCode($official_code)
323
            ->setPictureUri($picture_uri)
324
            ->setCreatorId($creatorId)
325
            ->setAuthSource($authSource)
326
            ->setPhone($phone)
327
            ->setAddress($address)
328
            ->setLanguage($language)
329
            ->setRegistrationDate($now)
330
            ->setHrDeptId($hr_dept_id)
331
            ->setActive($active)
332
            ->setEnabled($active)
333
        ;
334
335
        if (!empty($expirationDate)) {
336
            $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

336
            $user->setExpirationDate(/** @scrutinizer ignore-type */ $expirationDate);
Loading history...
337
        }
338
        try {
339
            $userManager->updateUser($user);
340
            $userId = $user->getId();
341
342
            // Add user to a group
343
            $statusToGroup = [
344
                COURSEMANAGER => 'TEACHER',
345
                STUDENT => 'STUDENT',
346
                DRH => 'RRHH',
347
                SESSIONADMIN => 'SESSION_ADMIN',
348
                STUDENT_BOSS => 'STUDENT_BOSS',
349
                INVITEE => 'INVITEE',
350
            ];
351
352
            $group = Container::$container->get('fos_user.group_manager')->findGroupBy(['code' => $statusToGroup[$status]]);
353
            if ($group) {
354
                $user->addGroup($group);
355
                $userManager->updateUser($user);
356
            }
357
        } catch (Exception $e) {
358
            error_log($e->getMessage());
359
        }
360
361
        if (!empty($userId)) {
362
            $return = $userId;
363
            $sql = "UPDATE $table_user SET user_id = $return WHERE id = $return";
364
            Database::query($sql);
365
366
            if ($isAdmin) {
367
                self::add_user_as_admin($user);
368
            }
369
370
            if (api_get_multiple_access_url()) {
371
                UrlManager::add_user_to_url($userId, api_get_current_access_url_id());
372
            } else {
373
                //we are adding by default the access_url_user table with access_url_id = 1
374
                UrlManager::add_user_to_url($userId, 1);
375
            }
376
377
            $extra['item_id'] = $userId;
378
379
            if (is_array($extra) && count($extra) > 0) {
380
                $courseFieldValue = new ExtraFieldValue('user');
381
                $courseFieldValue->saveFieldValues($extra);
382
            } else {
383
                // Create notify settings by default
384
                self::update_extra_field_value(
385
                    $userId,
386
                    'mail_notify_invitation',
387
                    '1'
388
                );
389
                self::update_extra_field_value(
390
                    $userId,
391
                    'mail_notify_message',
392
                    '1'
393
                );
394
                self::update_extra_field_value(
395
                    $userId,
396
                    'mail_notify_group_message',
397
                    '1'
398
                );
399
            }
400
401
            self::update_extra_field_value(
402
                $userId,
403
                'already_logged_in',
404
                'false'
405
            );
406
407
            if (!empty($email) && $send_mail) {
408
                $recipient_name = api_get_person_name(
409
                    $firstName,
410
                    $lastName,
411
                    null,
412
                    PERSON_NAME_EMAIL_ADDRESS
413
                );
414
                $tplSubject = new Template(
415
                    null,
416
                    false,
417
                    false,
418
                    false,
419
                    false,
420
                    false
421
                );
422
                $layoutSubject = $tplSubject->get_template('mail/subject_registration_platform.tpl');
423
                $emailSubject = $tplSubject->fetch($layoutSubject);
424
                $sender_name = api_get_person_name(
425
                    api_get_setting('administratorName'),
426
                    api_get_setting('administratorSurname'),
427
                    null,
428
                    PERSON_NAME_EMAIL_ADDRESS
429
                );
430
                $email_admin = api_get_setting('emailAdministrator');
431
432
                $url = api_get_path(WEB_PATH);
433
                if (api_is_multiple_url_enabled()) {
434
                    $access_url_id = api_get_current_access_url_id();
435
                    if ($access_url_id != -1) {
436
                        $urlInfo = api_get_access_url($access_url_id);
437
                        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...
438
                            $url = $urlInfo['url'];
439
                        }
440
                    }
441
                }
442
443
                $tplContent = new Template(
444
                    null,
445
                    false,
446
                    false,
447
                    false,
448
                    false,
449
                    false
450
                );
451
                // variables for the default template
452
                $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
453
                $tplContent->assign('login_name', $loginName);
454
                $tplContent->assign('original_password', stripslashes($original_password));
455
                $tplContent->assign('mailWebPath', $url);
456
                $tplContent->assign('new_user', $user);
457
458
                $layoutContent = $tplContent->get_template('mail/content_registration_platform.tpl');
459
                $emailBody = $tplContent->fetch($layoutContent);
460
461
                $userInfo = api_get_user_info($userId);
462
                $mailTemplateManager = new MailTemplateManager();
463
464
                $phoneNumber = isset($extra['mobile_phone_number']) ? $extra['mobile_phone_number'] : null;
465
466
                $additionalParameters = [
467
                    'smsType' => SmsPlugin::WELCOME_LOGIN_PASSWORD,
468
                    'userId' => $return,
469
                    'mobilePhoneNumber' => $phoneNumber,
470
                    'password' => $original_password,
471
                ];
472
473
                $emailBodyTemplate = '';
474
                if (!empty($emailTemplate)) {
475
                    if (isset($emailTemplate['content_registration_platform.tpl']) &&
476
                        !empty($emailTemplate['content_registration_platform.tpl'])
477
                    ) {
478
                        $emailBodyTemplate = $mailTemplateManager->parseTemplate(
479
                            $emailTemplate['content_registration_platform.tpl'],
480
                            $userInfo
481
                        );
482
                    }
483
                }
484
485
                $twoEmail = api_get_configuration_value('send_two_inscription_confirmation_mail');
486
                if ($twoEmail === true) {
487
                    $layoutContent = $tplContent->get_template('mail/new_user_first_email_confirmation.tpl');
488
                    $emailBody = $tplContent->fetch($layoutContent);
489
490
                    if (!empty($emailBodyTemplate) &&
491
                        isset($emailTemplate['new_user_first_email_confirmation.tpl']) &&
492
                        !empty($emailTemplate['new_user_first_email_confirmation.tpl'])
493
                    ) {
494
                        $emailBody = $mailTemplateManager->parseTemplate(
495
                            $emailTemplate['new_user_first_email_confirmation.tpl'],
496
                            $userInfo
497
                        );
498
                    }
499
500
                    api_mail_html(
501
                        $recipient_name,
502
                        $email,
503
                        $emailSubject,
504
                        $emailBody,
505
                        $sender_name,
506
                        $email_admin,
507
                        null,
508
                        null,
509
                        null,
510
                        $additionalParameters
511
                    );
512
513
                    $layoutContent = $tplContent->get_template('mail/new_user_second_email_confirmation.tpl');
514
                    $emailBody = $tplContent->fetch($layoutContent);
515
516
                    if (!empty($emailBodyTemplate) &&
517
                        isset($emailTemplate['new_user_second_email_confirmation.tpl']) &&
518
                        !empty($emailTemplate['new_user_second_email_confirmation.tpl'])
519
                    ) {
520
                        $emailBody = $mailTemplateManager->parseTemplate(
521
                            $emailTemplate['new_user_second_email_confirmation.tpl'],
522
                            $userInfo
523
                        );
524
                    }
525
526
                    api_mail_html(
527
                        $recipient_name,
528
                        $email,
529
                        $emailSubject,
530
                        $emailBody,
531
                        $sender_name,
532
                        $email_admin,
533
                        null,
534
                        null,
535
                        null,
536
                        $additionalParameters
537
                    );
538
                } else {
539
                    if (!empty($emailBodyTemplate)) {
540
                        $emailBody = $emailBodyTemplate;
541
                    }
542
                    $sendToInbox = api_get_configuration_value('send_inscription_msg_to_inbox');
543
                    if ($sendToInbox) {
544
                        $adminList = self::get_all_administrators();
545
                        $senderId = 1;
546
                        if (!empty($adminList)) {
547
                            $adminInfo = current($adminList);
548
                            $senderId = $adminInfo['user_id'];
549
                        }
550
551
                        MessageManager::send_message_simple(
552
                            $userId,
553
                            $emailSubject,
554
                            $emailBody,
555
                            $senderId
556
                        );
557
                    } else {
558
                        api_mail_html(
559
                            $recipient_name,
560
                            $email,
561
                            $emailSubject,
562
                            $emailBody,
563
                            $sender_name,
564
                            $email_admin,
565
                            null,
566
                            null,
567
                            null,
568
                            $additionalParameters
569
                        );
570
                    }
571
                }
572
573
                $notification = api_get_configuration_value('send_notification_when_user_added');
574
                if (!empty($notification) && isset($notification['admins']) && is_array($notification['admins'])) {
575
                    foreach ($notification['admins'] as $adminId) {
576
                        $emailSubjectToAdmin = get_lang('UserAdded').': '.api_get_person_name($firstName, $lastName);
577
                        MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, $emailBody, $userId);
0 ignored issues
show
Bug introduced by
It seems like $emailBody can also be of type false; however, parameter $message of MessageManager::send_message_simple() does only seem to accept string, 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

577
                        MessageManager::send_message_simple($adminId, $emailSubjectToAdmin, /** @scrutinizer ignore-type */ $emailBody, $userId);
Loading history...
578
                    }
579
                }
580
581
                if ($sendEmailToAllAdmins) {
582
                    $adminList = self::get_all_administrators();
583
584
                    $tplContent = new Template(
585
                        null,
586
                        false,
587
                        false,
588
                        false,
589
                        false,
590
                        false
591
                    );
592
                    // variables for the default template
593
                    $tplContent->assign('complete_name', stripslashes(api_get_person_name($firstName, $lastName)));
594
                    $tplContent->assign('user_added', $user);
595
                    $renderer = FormValidator::getDefaultRenderer();
596
                    // Form template
597
                    $elementTemplate = ' {label}: {element} <br />';
598
                    $renderer->setElementTemplate($elementTemplate);
599
                    /** @var FormValidator $form */
600
                    $form->freeze(null, $elementTemplate);
601
                    $form->removeElement('submit');
602
                    $formData = $form->returnForm();
603
                    $url = api_get_path(WEB_CODE_PATH).'admin/user_information.php?user_id='.$user->getId();
604
                    $tplContent->assign('link', Display::url($url, $url));
605
                    $tplContent->assign('form', $formData);
606
607
                    $layoutContent = $tplContent->get_template('mail/content_registration_platform_to_admin.tpl');
608
                    $emailBody = $tplContent->fetch($layoutContent);
609
610
                    if (!empty($emailBodyTemplate) &&
611
                        isset($emailTemplate['content_registration_platform_to_admin.tpl']) &&
612
                        !empty($emailTemplate['content_registration_platform_to_admin.tpl'])
613
                    ) {
614
                        $emailBody = $mailTemplateManager->parseTemplate(
615
                            $emailTemplate['content_registration_platform_to_admin.tpl'],
616
                            $userInfo
617
                        );
618
                    }
619
620
                    $subject = get_lang('UserAdded');
621
622
                    foreach ($adminList as $adminId => $data) {
623
                        MessageManager::send_message_simple(
624
                            $adminId,
625
                            $subject,
626
                            $emailBody,
627
                            $userId
628
                        );
629
                    }
630
                }
631
                /* ENDS MANAGE EVENT WITH MAIL */
632
            }
633
634
            if (!empty($hook)) {
635
                $hook->setEventData([
636
                    'return' => $userId,
637
                    'originalPassword' => $original_password,
638
                ]);
639
                $hook->notifyCreateUser(HOOK_EVENT_TYPE_POST);
640
            }
641
            Event::addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);
0 ignored issues
show
Bug introduced by
The method addEvent() does not exist on Event. ( Ignorable by Annotation )

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

641
            Event::/** @scrutinizer ignore-call */ 
642
                   addEvent(LOG_USER_CREATE, LOG_USER_ID, $userId, null, $creatorId);

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

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

Loading history...
642
        } else {
643
            Display::addFlash(
644
                Display::return_message(get_lang('ErrorContactPlatformAdmin'))
645
            );
646
647
            return false;
648
        }
649
650
        return $return;
651
    }
652
653
    /**
654
     * Can user be deleted? This function checks whether there's a course
655
     * in which the given user is the
656
     * only course administrator. If that is the case, the user can't be
657
     * deleted because the course would remain without a course admin.
658
     *
659
     * @param int $user_id The user id
660
     *
661
     * @return bool true if user can be deleted
662
     *
663
     * @assert (null) === false
664
     * @assert (-1) === false
665
     * @assert ('abc') === false
666
     */
667
    public static function canDeleteUser($user_id)
668
    {
669
        $deny = api_get_configuration_value('deny_delete_users');
670
671
        if ($deny) {
672
            return false;
673
        }
674
675
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
676
        if ($user_id != strval(intval($user_id))) {
677
            return false;
678
        }
679
        if ($user_id === false) {
680
            return false;
681
        }
682
        $sql = "SELECT * FROM $table_course_user
683
                WHERE status = 1 AND user_id = ".$user_id;
684
        $res = Database::query($sql);
685
        while ($course = Database::fetch_object($res)) {
686
            $sql = "SELECT id FROM $table_course_user
687
                    WHERE status=1 AND c_id = ".intval($course->c_id);
688
            $res2 = Database::query($sql);
689
            if (Database::num_rows($res2) == 1) {
690
                return false;
691
            }
692
        }
693
694
        return true;
695
    }
696
697
    /**
698
     * Delete a user from the platform, and all its belongings. This is a
699
     * very dangerous function that should only be accessible by
700
     * super-admins. Other roles should only be able to disable a user,
701
     * which removes access to the platform but doesn't delete anything.
702
     *
703
     * @param int The ID of th user to be deleted
704
     *
705
     * @throws Exception
706
     *
707
     * @return bool true if user is successfully deleted, false otherwise
708
     * @assert (null) === false
709
     * @assert ('abc') === false
710
     */
711
    public static function delete_user($user_id)
712
    {
713
        $user_id = (int) $user_id;
714
715
        if (empty($user_id)) {
716
            return false;
717
        }
718
719
        if (!self::canDeleteUser($user_id)) {
720
            return false;
721
        }
722
723
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
724
        $usergroup_rel_user = Database::get_main_table(TABLE_USERGROUP_REL_USER);
725
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
726
        $table_course = Database::get_main_table(TABLE_MAIN_COURSE);
727
        $table_session = Database::get_main_table(TABLE_MAIN_SESSION);
728
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
729
        $table_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
730
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
731
        $table_group = Database::get_course_table(TABLE_GROUP_USER);
732
        $table_work = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
733
734
        $user = api_get_user_entity($user_id);
735
736
        // Unsubscribe the user from all groups in all his courses
737
        $sql = "SELECT c.id 
738
                FROM $table_course c 
739
                INNER JOIN $table_course_user cu
740
                ON (c.id = cu.c_id)
741
                WHERE
742
                    cu.user_id = '".$user_id."' AND
743
                    relation_type<>".COURSE_RELATION_TYPE_RRHH."
744
                ";
745
746
        $res = Database::query($sql);
747
        while ($course = Database::fetch_object($res)) {
748
            $sql = "DELETE FROM $table_group
749
                    WHERE c_id = {$course->id} AND user_id = $user_id";
750
            Database::query($sql);
751
        }
752
753
        // Unsubscribe user from usergroup_rel_user
754
        $sql = "DELETE FROM $usergroup_rel_user WHERE user_id = '".$user_id."'";
755
        Database::query($sql);
756
757
        // Unsubscribe user from all courses
758
        $sql = "DELETE FROM $table_course_user WHERE user_id = '".$user_id."'";
759
        Database::query($sql);
760
761
        // Unsubscribe user from all courses in sessions
762
        $sql = "DELETE FROM $table_session_course_user WHERE user_id = '".$user_id."'";
763
        Database::query($sql);
764
765
        // If the user was added as a id_coach then set the current admin as coach see BT#
766
        $currentUserId = api_get_user_id();
767
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
768
                WHERE id_coach = '".$user_id."'";
769
        Database::query($sql);
770
771
        $sql = "UPDATE $table_session SET id_coach = $currentUserId
772
                WHERE session_admin_id = '".$user_id."'";
773
        Database::query($sql);
774
775
        // Unsubscribe user from all sessions
776
        $sql = "DELETE FROM $table_session_user
777
                WHERE user_id = '".$user_id."'";
778
        Database::query($sql);
779
780
        // Delete user picture
781
        /* TODO: Logic about api_get_setting('split_users_upload_directory') == 'true'
782
        a user has 4 different sized photos to be deleted. */
783
        $user_info = api_get_user_info($user_id);
784
785
        if (strlen($user_info['picture_uri']) > 0) {
786
            $path = self::getUserPathById($user_id, 'system');
787
            $img_path = $path.$user_info['picture_uri'];
788
            if (file_exists($img_path)) {
789
                unlink($img_path);
790
            }
791
        }
792
793
        // Delete the personal course categories
794
        $course_cat_table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
795
        $sql = "DELETE FROM $course_cat_table WHERE user_id = '".$user_id."'";
796
        Database::query($sql);
797
798
        // Delete user from the admin table
799
        $sql = "DELETE FROM $table_admin WHERE user_id = '".$user_id."'";
800
        Database::query($sql);
801
802
        // Delete the personal agenda-items from this user
803
        $agenda_table = Database::get_main_table(TABLE_PERSONAL_AGENDA);
804
        $sql = "DELETE FROM $agenda_table WHERE user = '".$user_id."'";
805
        Database::query($sql);
806
807
        $gradebook_results_table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_RESULT);
808
        $sql = 'DELETE FROM '.$gradebook_results_table.' WHERE user_id = '.$user_id;
809
        Database::query($sql);
810
811
        $extraFieldValue = new ExtraFieldValue('user');
812
        $extraFieldValue->deleteValuesByItem($user_id);
813
814
        UrlManager::deleteUserFromAllUrls($user_id);
815
816
        if (api_get_setting('allow_social_tool') === 'true') {
817
            $userGroup = new UserGroup();
818
            //Delete user from portal groups
819
            $group_list = $userGroup->get_groups_by_user($user_id);
820
            if (!empty($group_list)) {
821
                foreach ($group_list as $group_id => $data) {
822
                    $userGroup->delete_user_rel_group($user_id, $group_id);
823
                }
824
            }
825
826
            // Delete user from friend lists
827
            SocialManager::remove_user_rel_user($user_id, true);
828
        }
829
830
        // Removing survey invitation
831
        SurveyManager::delete_all_survey_invitations_by_user($user_id);
832
833
        // Delete students works
834
        $sql = "DELETE FROM $table_work WHERE user_id = $user_id AND c_id <> 0";
835
        Database::query($sql);
836
837
        $sql = "UPDATE c_item_property SET to_user_id = NULL
838
                WHERE to_user_id = '".$user_id."'";
839
        Database::query($sql);
840
841
        $sql = "UPDATE c_item_property SET insert_user_id = NULL
842
                WHERE insert_user_id = '".$user_id."'";
843
        Database::query($sql);
844
845
        $sql = "UPDATE c_item_property SET lastedit_user_id = NULL
846
                WHERE lastedit_user_id = '".$user_id."'";
847
        Database::query($sql);
848
849
        // Skills
850
        $em = Database::getManager();
851
852
        $criteria = ['user' => $user_id];
853
        $skills = $em->getRepository('ChamiloCoreBundle:SkillRelUser')->findBy($criteria);
854
        if ($skills) {
855
            /** @var SkillRelUser $skill */
856
            foreach ($skills as $skill) {
857
                $comments = $skill->getComments();
858
                if ($comments) {
859
                    /** @var SkillRelUserComment $comment */
860
                    foreach ($comments as $comment) {
861
                        $em->remove($comment);
862
                    }
863
                }
864
                $em->remove($skill);
865
            }
866
            $em->flush();
867
        }
868
869
        // ExtraFieldSavedSearch
870
        $criteria = ['user' => $user_id];
871
        $searchList = $em->getRepository('ChamiloCoreBundle:ExtraFieldSavedSearch')->findBy($criteria);
872
        if ($searchList) {
873
            foreach ($searchList as $search) {
874
                $em->remove($search);
875
            }
876
            $em->flush();
877
        }
878
879
        $connection = Database::getManager()->getConnection();
880
        $tableExists = $connection->getSchemaManager()->tablesExist(['plugin_bbb_room']);
881
        if ($tableExists) {
882
            // Delete user from database
883
            $sql = "DELETE FROM plugin_bbb_room WHERE participant_id = $user_id";
884
            Database::query($sql);
885
        }
886
887
        // Delete user/ticket relationships :(
888
        $tableExists = $connection->getSchemaManager()->tablesExist(['ticket_ticket']);
889
        if ($tableExists) {
890
            TicketManager::deleteUserFromTicketSystem($user_id);
891
        }
892
893
        $tableExists = $connection->getSchemaManager()->tablesExist(['c_lp_category_user']);
894
        if ($tableExists) {
895
            $sql = "DELETE FROM c_lp_category_user WHERE user_id = $user_id";
896
            Database::query($sql);
897
        }
898
899
        $app_plugin = new AppPlugin();
900
        $app_plugin->performActionsWhenDeletingItem('user', $user_id);
901
902
        // Delete user from database
903
        $em->remove($user);
904
905
        $em->flush();
906
907
        // Add event to system log
908
        $user_id_manager = api_get_user_id();
909
910
        Event::addEvent(
911
            LOG_USER_DELETE,
912
            LOG_USER_ID,
913
            $user_id,
914
            api_get_utc_datetime(),
915
            $user_id_manager
916
        );
917
918
        Event::addEvent(
919
            LOG_USER_DELETE,
920
            LOG_USER_OBJECT,
921
            $user_info,
922
            api_get_utc_datetime(),
923
            $user_id_manager
924
        );
925
        $cacheAvailable = api_get_configuration_value('apc');
926
        if ($cacheAvailable === true) {
927
            $apcVar = api_get_configuration_value('apc_prefix').'userinfo_'.$user_id;
928
            if (apcu_exists($apcVar)) {
929
                apcu_delete($apcVar);
930
            }
931
        }
932
933
        return true;
934
    }
935
936
    /**
937
     * Deletes users completely. Can be called either as:
938
     * - UserManager::delete_users(1, 2, 3); or
939
     * - UserManager::delete_users(array(1, 2, 3));.
940
     *
941
     * @param array|int $ids
942
     *
943
     * @return bool True if at least one user was successfuly deleted. False otherwise.
944
     *
945
     * @author Laurent Opprecht
946
     *
947
     * @uses \UserManager::delete_user() to actually delete each user
948
     * @assert (null) === false
949
     * @assert (-1) === false
950
     * @assert (array(-1)) === false
951
     */
952
    public static function delete_users($ids = [])
953
    {
954
        $result = false;
955
        $ids = is_array($ids) ? $ids : func_get_args();
956
        if (!is_array($ids) || count($ids) == 0) {
957
            return false;
958
        }
959
        $ids = array_map('intval', $ids);
960
        foreach ($ids as $id) {
961
            if (empty($id) || $id < 1) {
962
                continue;
963
            }
964
            $deleted = self::delete_user($id);
965
            $result = $deleted || $result;
966
        }
967
968
        return $result;
969
    }
970
971
    /**
972
     * Disable users. Can be called either as:
973
     * - UserManager::deactivate_users(1, 2, 3);
974
     * - UserManager::deactivate_users(array(1, 2, 3));.
975
     *
976
     * @param array|int $ids
977
     *
978
     * @return bool
979
     *
980
     * @author Laurent Opprecht
981
     * @assert (null) === false
982
     * @assert (array(-1)) === false
983
     */
984
    public static function deactivate_users($ids = [])
985
    {
986
        if (empty($ids)) {
987
            return false;
988
        }
989
990
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
991
992
        $ids = is_array($ids) ? $ids : func_get_args();
993
        $ids = array_map('intval', $ids);
994
        $ids = implode(',', $ids);
995
996
        $sql = "UPDATE $table_user SET active = 0 WHERE id IN ($ids)";
997
        $r = Database::query($sql);
998
        if ($r !== false) {
999
            Event::addEvent(LOG_USER_DISABLE, LOG_USER_ID, $ids);
1000
1001
            return true;
1002
        }
1003
1004
        return false;
1005
    }
1006
1007
    /**
1008
     * Enable users. Can be called either as:
1009
     * - UserManager::activate_users(1, 2, 3);
1010
     * - UserManager::activate_users(array(1, 2, 3));.
1011
     *
1012
     * @param array|int IDs of the users to enable
1013
     *
1014
     * @return bool
1015
     *
1016
     * @author Laurent Opprecht
1017
     * @assert (null) === false
1018
     * @assert (array(-1)) === false
1019
     */
1020
    public static function activate_users($ids = [])
1021
    {
1022
        if (empty($ids)) {
1023
            return false;
1024
        }
1025
1026
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
1027
1028
        $ids = is_array($ids) ? $ids : func_get_args();
1029
        $ids = array_map('intval', $ids);
1030
        $ids = implode(',', $ids);
1031
1032
        $sql = "UPDATE $table_user SET active = 1 WHERE id IN ($ids)";
1033
        $r = Database::query($sql);
1034
        if ($r !== false) {
1035
            Event::addEvent(LOG_USER_ENABLE, LOG_USER_ID, $ids);
1036
1037
            return true;
1038
        }
1039
1040
        return false;
1041
    }
1042
1043
    /**
1044
     * Update user information with all the parameters passed to this function.
1045
     *
1046
     * @param int    $user_id         The ID of the user to be updated
1047
     * @param string $firstname       The user's firstname
1048
     * @param string $lastname        The user's lastname
1049
     * @param string $username        The user's username (login)
1050
     * @param string $password        The user's password
1051
     * @param string $auth_source     The authentication source (default: "platform")
1052
     * @param string $email           The user's e-mail address
1053
     * @param int    $status          The user's status
1054
     * @param string $official_code   The user's official code (usually just an internal institutional code)
1055
     * @param string $phone           The user's phone number
1056
     * @param string $picture_uri     The user's picture URL (internal to the Chamilo directory)
1057
     * @param string $expiration_date The date at which this user will be automatically disabled
1058
     * @param int    $active          Whether this account needs to be enabled (1) or disabled (0)
1059
     * @param int    $creator_id      The user ID of the person who registered this user (optional, defaults to null)
1060
     * @param int    $hr_dept_id      The department of HR in which the user is registered (optional, defaults to 0)
1061
     * @param array  $extra           Additional fields to add to this user as extra fields (defaults to null)
1062
     * @param string $language        The language to which the user account will be set
1063
     * @param string $encrypt_method  The cipher method. This parameter is deprecated. It will use the system's default
1064
     * @param bool   $send_email      Whether to send an e-mail to the user after the update is complete
1065
     * @param int    $reset_password  Method used to reset password (0, 1, 2 or 3 - see usage examples for details)
1066
     * @param string $address
1067
     * @param array  $emailTemplate
1068
     *
1069
     * @return bool|int False on error, or the user ID if the user information was updated
1070
     * @assert (false, false, false, false, false, false, false, false, false, false, false, false, false) === false
1071
     */
1072
    public static function update_user(
1073
        $user_id,
1074
        $firstname,
1075
        $lastname,
1076
        $username,
1077
        $password = null,
1078
        $auth_source = null,
1079
        $email,
1080
        $status,
1081
        $official_code,
1082
        $phone,
1083
        $picture_uri,
1084
        $expiration_date,
1085
        $active,
1086
        $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

1086
        /** @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...
1087
        $hr_dept_id = 0,
1088
        $extra = null,
1089
        $language = 'english',
1090
        $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

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

2076
                /** @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...
2077
                @rename($path.'medium_'.$old_file, $path.$prefix.'medium_'.$old_file);
2078
                @rename($path.'big_'.$old_file, $path.$prefix.'big_'.$old_file);
2079
                @rename($path.$old_file, $path.$prefix.$old_file);
2080
            } else {
2081
                @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

2081
                /** @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...
2082
                @unlink($path.'medium_'.$old_file);
2083
                @unlink($path.'big_'.$old_file);
2084
                @unlink($path.$old_file);
2085
            }
2086
        }
2087
2088
        // Exit if only deletion has been requested. Return an empty picture name.
2089
        if ($delete) {
2090
            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...
2091
        }
2092
2093
        // Validation 2.
2094
        $allowed_types = api_get_supported_image_extensions();
2095
        $file = str_replace('\\', '/', $file);
2096
        $filename = (($pos = strrpos($file, '/')) !== false) ? substr($file, $pos + 1) : $file;
2097
        $extension = strtolower(substr(strrchr($filename, '.'), 1));
2098
        if (!in_array($extension, $allowed_types)) {
2099
            return false;
2100
        }
2101
2102
        // This is the common name for the new photos.
2103
        if ($old_file != 'unknown.jpg') {
2104
            $old_extension = strtolower(substr(strrchr($old_file, '.'), 1));
2105
            $filename = in_array($old_extension, $allowed_types) ? substr($old_file, 0, -strlen($old_extension)) : $old_file;
2106
            $filename = (substr($filename, -1) == '.') ? $filename.$extension : $filename.'.'.$extension;
2107
        } else {
2108
            $filename = api_replace_dangerous_char($filename);
2109
            $filename = uniqid('').'_'.$filename;
2110
            // We always prefix user photos with user ids, so on setting
2111
            // api_get_setting('split_users_upload_directory') === 'true'
2112
            // the correspondent directories to be found successfully.
2113
            $filename = $user_id.'_'.$filename;
2114
        }
2115
2116
        //Crop the image to adjust 1:1 ratio
2117
        $image = new Image($source_file);
2118
        $image->crop($cropParameters);
2119
2120
        // Storing the new photos in 4 versions with various sizes.
2121
        $userPath = self::getUserPathById($user_id, 'system');
2122
2123
        // If this path does not exist - we create it.
2124
        if (!file_exists($userPath)) {
2125
            mkdir($userPath, api_get_permissions_for_new_directories(), true);
2126
        }
2127
        $small = new Image($source_file);
2128
        $small->resize(32);
2129
        $small->send_image($userPath.'small_'.$filename);
2130
        $medium = new Image($source_file);
2131
        $medium->resize(85);
2132
        $medium->send_image($userPath.'medium_'.$filename);
2133
        $normal = new Image($source_file);
2134
        $normal->resize(200);
2135
        $normal->send_image($userPath.$filename);
2136
2137
        $big = new Image($source_file); // This is the original picture.
2138
        $big->send_image($userPath.'big_'.$filename);
2139
2140
        $result = $small && $medium && $normal && $big;
0 ignored issues
show
introduced by
$normal is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$big is of type Image, thus it always evaluated to true.
Loading history...
introduced by
$medium is of type Image, thus it always evaluated to true.
Loading history...
2141
2142
        return $result ? $filename : false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result ? $filename : false returns the type string which is incompatible with the documented return type boolean.
Loading history...
2143
    }
2144
2145
    /**
2146
     * Update User extra field file type into {user_folder}/{$extra_field}.
2147
     *
2148
     * @param int    $user_id     The user internal identification number
2149
     * @param string $extra_field The $extra_field The extra field name
2150
     * @param null   $file        The filename
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $file is correct as it would always require null to be passed?
Loading history...
2151
     * @param null   $source_file The temporal filename
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $source_file is correct as it would always require null to be passed?
Loading history...
2152
     *
2153
     * @return bool|null return filename if success, but false
2154
     */
2155
    public static function update_user_extra_file(
2156
        $user_id,
2157
        $extra_field = '',
2158
        $file = null,
2159
        $source_file = null
2160
    ) {
2161
        // Add Filter
2162
        $source_file = Security::filter_filename($source_file);
2163
        $file = Security::filter_filename($file);
2164
2165
        if (empty($user_id)) {
2166
            return false;
2167
        }
2168
2169
        if (empty($source_file)) {
2170
            $source_file = $file;
2171
        }
2172
2173
        // User-reserved directory where extra file have to be placed.
2174
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2175
        $path = $path_info['dir'];
2176
        if (!empty($extra_field)) {
2177
            $path .= $extra_field.'/';
2178
        }
2179
        // If this directory does not exist - we create it.
2180
        if (!file_exists($path)) {
2181
            @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

2181
            /** @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...
2182
        }
2183
2184
        if (filter_extension($file)) {
2185
            if (@move_uploaded_file($source_file, $path.$file)) {
2186
                if ($extra_field) {
2187
                    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 boolean|null.
Loading history...
2188
                } else {
2189
                    return $file;
2190
                }
2191
            }
2192
        }
2193
2194
        return false; // this should be returned if anything went wrong with the upload
2195
    }
2196
2197
    /**
2198
     * Deletes user photos.
2199
     * Note: This method relies on configuration setting from main/inc/conf/profile.conf.php.
2200
     *
2201
     * @param int $userId the user internal identification number
2202
     *
2203
     * @return mixed returns empty string on success, FALSE on error
2204
     */
2205
    public static function deleteUserPicture($userId)
2206
    {
2207
        return self::update_user_picture($userId);
2208
    }
2209
2210
    /**
2211
     * Returns an XHTML formatted list of productions for a user, or FALSE if he
2212
     * doesn't have any.
2213
     *
2214
     * If there has been a request to remove a production, the function will return
2215
     * without building the list unless forced to do so by the optional second
2216
     * parameter. This increases performance by avoiding to read through the
2217
     * productions on the filesystem before the removal request has been carried
2218
     * out because they'll have to be re-read afterwards anyway.
2219
     *
2220
     * @param int  $user_id    User id
2221
     * @param bool $force      Optional parameter to force building after a removal request
2222
     * @param bool $showDelete
2223
     *
2224
     * @return string A string containing the XHTML code to display the production list, or FALSE
2225
     */
2226
    public static function build_production_list($user_id, $force = false, $showDelete = false)
2227
    {
2228
        if (!$force && !empty($_POST['remove_production'])) {
2229
            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...
2230
        }
2231
2232
        $productions = self::get_user_productions($user_id);
2233
2234
        if (empty($productions)) {
2235
            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...
2236
        }
2237
2238
        $production_dir = self::getUserPathById($user_id, 'web');
2239
        $del_image = Display::returnIconPath('delete.png');
2240
        $add_image = Display::returnIconPath('archive.png');
2241
        $del_text = get_lang('Delete');
2242
        $production_list = '';
2243
        if (count($productions) > 0) {
2244
            $production_list = '<div class="files-production"><ul id="productions">';
2245
            foreach ($productions as $file) {
2246
                $production_list .= '<li>
2247
                    <img src="'.$add_image.'" />
2248
                    <a href="'.$production_dir.urlencode($file).'" target="_blank">
2249
                        '.htmlentities($file).'
2250
                    </a>';
2251
                if ($showDelete) {
2252
                    $production_list .= '&nbsp;&nbsp;
2253
                        <input 
2254
                            style="width:16px;" 
2255
                            type="image" 
2256
                            name="remove_production['.urlencode($file).']" 
2257
                            src="'.$del_image.'" 
2258
                            alt="'.$del_text.'" 
2259
                            title="'.$del_text.' '.htmlentities($file).'" 
2260
                            onclick="javascript: return confirmation(\''.htmlentities($file).'\');" /></li>';
2261
                }
2262
            }
2263
            $production_list .= '</ul></div>';
2264
        }
2265
2266
        return $production_list;
2267
    }
2268
2269
    /**
2270
     * Returns an array with the user's productions.
2271
     *
2272
     * @param int $user_id User id
2273
     *
2274
     * @return array An array containing the user's productions
2275
     */
2276
    public static function get_user_productions($user_id)
2277
    {
2278
        $production_repository = self::getUserPathById($user_id, 'system');
2279
        $productions = [];
2280
2281
        if (is_dir($production_repository)) {
2282
            $handle = opendir($production_repository);
2283
            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

2283
            while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
2284
                if ($file == '.' ||
2285
                    $file == '..' ||
2286
                    $file == '.htaccess' ||
2287
                    is_dir($production_repository.$file)
2288
                ) {
2289
                    // skip current/parent directory and .htaccess
2290
                    continue;
2291
                }
2292
2293
                if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
2294
                    // User's photos should not be listed as productions.
2295
                    continue;
2296
                }
2297
                $productions[] = $file;
2298
            }
2299
        }
2300
2301
        return $productions;
2302
    }
2303
2304
    /**
2305
     * Remove a user production.
2306
     *
2307
     * @param int    $user_id    User id
2308
     * @param string $production The production to remove
2309
     *
2310
     * @return bool
2311
     */
2312
    public static function remove_user_production($user_id, $production)
2313
    {
2314
        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
2315
        $production_file = $production_path['dir'].$production;
2316
        if (is_file($production_file)) {
2317
            unlink($production_file);
2318
2319
            return true;
2320
        }
2321
2322
        return false;
2323
    }
2324
2325
    /**
2326
     * Update an extra field value for a given user.
2327
     *
2328
     * @param int    $userId   User ID
2329
     * @param string $variable Field variable name
2330
     * @param string $value    Field value
2331
     *
2332
     * @return bool true if field updated, false otherwise
2333
     */
2334
    public static function update_extra_field_value($userId, $variable, $value = '')
2335
    {
2336
        $extraFieldValue = new ExtraFieldValue('user');
2337
        $params = [
2338
            'item_id' => $userId,
2339
            'variable' => $variable,
2340
            'value' => $value,
2341
        ];
2342
2343
        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...
2344
    }
2345
2346
    /**
2347
     * Get an array of extra fields with field details (type, default value and options).
2348
     *
2349
     * @param    int    Offset (from which row)
2350
     * @param    int    Number of items
2351
     * @param    int    Column on which sorting is made
2352
     * @param    string    Sorting direction
2353
     * @param    bool    Optional. Whether we get all the fields or just the visible ones
0 ignored issues
show
Documentation Bug introduced by
The doc comment Optional. at position 0 could not be parsed: Unknown type name 'Optional.' at position 0 in Optional..
Loading history...
2354
     * @param    int        Optional. Whether we get all the fields with field_filter 1 or 0 or everything
2355
     *
2356
     * @return array Extra fields details (e.g. $list[2]['type'], $list[4]['options'][2]['title']
2357
     */
2358
    public static function get_extra_fields(
2359
        $from = 0,
2360
        $number_of_items = 0,
2361
        $column = 5,
2362
        $direction = 'ASC',
2363
        $all_visibility = true,
2364
        $field_filter = null
2365
    ) {
2366
        $fields = [];
2367
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2368
        $t_ufo = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
2369
        $columns = [
2370
            'id',
2371
            'variable',
2372
            'field_type',
2373
            'display_text',
2374
            'default_value',
2375
            'field_order',
2376
            'filter',
2377
        ];
2378
        $column = (int) $column;
2379
        $sort_direction = '';
2380
        if (in_array(strtoupper($direction), ['ASC', 'DESC'])) {
2381
            $sort_direction = strtoupper($direction);
2382
        }
2383
        $extraFieldType = EntityExtraField::USER_FIELD_TYPE;
2384
        $sqlf = "SELECT * FROM $t_uf WHERE extra_field_type = $extraFieldType ";
2385
        if (!$all_visibility) {
2386
            $sqlf .= " AND visible_to_self = 1 ";
2387
        }
2388
        if (!is_null($field_filter)) {
2389
            $field_filter = (int) $field_filter;
2390
            $sqlf .= " AND filter = $field_filter ";
2391
        }
2392
        $sqlf .= " ORDER BY ".$columns[$column]." $sort_direction ";
2393
        if ($number_of_items != 0) {
2394
            $sqlf .= " LIMIT ".intval($from).','.intval($number_of_items);
2395
        }
2396
        $resf = Database::query($sqlf);
2397
        if (Database::num_rows($resf) > 0) {
2398
            while ($rowf = Database::fetch_array($resf)) {
2399
                $fields[$rowf['id']] = [
2400
                    0 => $rowf['id'],
2401
                    1 => $rowf['variable'],
2402
                    2 => $rowf['field_type'],
2403
                    3 => empty($rowf['display_text']) ? '' : $rowf['display_text'],
2404
                    4 => $rowf['default_value'],
2405
                    5 => $rowf['field_order'],
2406
                    6 => $rowf['visible_to_self'],
2407
                    7 => $rowf['changeable'],
2408
                    8 => $rowf['filter'],
2409
                    9 => [],
2410
                    10 => '<a name="'.$rowf['id'].'"></a>',
2411
                ];
2412
2413
                $sqlo = "SELECT * FROM $t_ufo
2414
                         WHERE field_id = ".$rowf['id']."
2415
                         ORDER BY option_order ASC";
2416
                $reso = Database::query($sqlo);
2417
                if (Database::num_rows($reso) > 0) {
2418
                    while ($rowo = Database::fetch_array($reso)) {
2419
                        $fields[$rowf['id']][9][$rowo['id']] = [
2420
                            0 => $rowo['id'],
2421
                            1 => $rowo['option_value'],
2422
                            2 => empty($rowo['display_text']) ? '' : $rowo['display_text'],
2423
                            3 => $rowo['option_order'],
2424
                        ];
2425
                    }
2426
                }
2427
            }
2428
        }
2429
2430
        return $fields;
2431
    }
2432
2433
    /**
2434
     * Build a list of extra file already uploaded in $user_folder/{$extra_field}/.
2435
     *
2436
     * @param $user_id
2437
     * @param $extra_field
2438
     * @param bool $force
2439
     * @param bool $showDelete
2440
     *
2441
     * @return bool|string
2442
     */
2443
    public static function build_user_extra_file_list(
2444
        $user_id,
2445
        $extra_field,
2446
        $force = false,
2447
        $showDelete = false
2448
    ) {
2449
        if (!$force && !empty($_POST['remove_'.$extra_field])) {
2450
            return true; // postpone reading from the filesystem
2451
        }
2452
2453
        $extra_files = self::get_user_extra_files($user_id, $extra_field);
2454
        if (empty($extra_files)) {
2455
            return false;
2456
        }
2457
2458
        $path_info = self::get_user_picture_path_by_id($user_id, 'web');
2459
        $path = $path_info['dir'];
2460
        $del_image = Display::returnIconPath('delete.png');
2461
2462
        $del_text = get_lang('Delete');
2463
        $extra_file_list = '';
2464
        if (count($extra_files) > 0) {
2465
            $extra_file_list = '<div class="files-production"><ul id="productions">';
2466
            foreach ($extra_files as $file) {
2467
                $filename = substr($file, strlen($extra_field) + 1);
2468
                $extra_file_list .= '<li>'.Display::return_icon('archive.png').
2469
                    '<a href="'.$path.$extra_field.'/'.urlencode($filename).'" target="_blank">
2470
                        '.htmlentities($filename).
2471
                    '</a> ';
2472
                if ($showDelete) {
2473
                    $extra_file_list .= '<input 
2474
                        style="width:16px;" 
2475
                        type="image" 
2476
                        name="remove_extra_'.$extra_field.'['.urlencode($file).']" 
2477
                        src="'.$del_image.'" 
2478
                        alt="'.$del_text.'"
2479
                        title="'.$del_text.' '.htmlentities($filename).'" 
2480
                        onclick="javascript: return confirmation(\''.htmlentities($filename).'\');" /></li>';
2481
                }
2482
            }
2483
            $extra_file_list .= '</ul></div>';
2484
        }
2485
2486
        return $extra_file_list;
2487
    }
2488
2489
    /**
2490
     * Get valid filenames in $user_folder/{$extra_field}/.
2491
     *
2492
     * @param $user_id
2493
     * @param $extra_field
2494
     * @param bool $full_path
2495
     *
2496
     * @return array
2497
     */
2498
    public static function get_user_extra_files($user_id, $extra_field, $full_path = false)
2499
    {
2500
        if (!$full_path) {
2501
            // Nothing to do
2502
        } else {
2503
            $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2504
            $path = $path_info['dir'];
2505
        }
2506
        $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
2507
        $extra_files = $extra_data[$extra_field];
2508
        if (is_array($extra_files)) {
2509
            foreach ($extra_files as $key => $value) {
2510
                if (!$full_path) {
2511
                    // Relative path from user folder
2512
                    $files[] = $value;
2513
                } else {
2514
                    $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...
2515
                }
2516
            }
2517
        } elseif (!empty($extra_files)) {
2518
            if (!$full_path) {
2519
                // Relative path from user folder
2520
                $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...
2521
            } else {
2522
                $files[] = $path.$extra_files;
2523
            }
2524
        }
2525
2526
        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...
2527
    }
2528
2529
    /**
2530
     * Remove an {$extra_file} from the user folder $user_folder/{$extra_field}/.
2531
     *
2532
     * @param int    $user_id
2533
     * @param string $extra_field
2534
     * @param string $extra_file
2535
     *
2536
     * @return bool
2537
     */
2538
    public static function remove_user_extra_file($user_id, $extra_field, $extra_file)
2539
    {
2540
        $extra_file = Security::filter_filename($extra_file);
2541
        $path_info = self::get_user_picture_path_by_id($user_id, 'system');
2542
        if (strpos($extra_file, $extra_field) !== false) {
2543
            $path_extra_file = $path_info['dir'].$extra_file;
2544
        } else {
2545
            $path_extra_file = $path_info['dir'].$extra_field.'/'.$extra_file;
2546
        }
2547
        if (is_file($path_extra_file)) {
2548
            unlink($path_extra_file);
2549
2550
            return true;
2551
        }
2552
2553
        return false;
2554
    }
2555
2556
    /**
2557
     * Creates a new extra field.
2558
     *
2559
     * @param string $variable    Field's internal variable name
2560
     * @param int    $fieldType   Field's type
2561
     * @param string $displayText Field's language var name
2562
     * @param string $default     Field's default value
2563
     *
2564
     * @return int
2565
     */
2566
    public static function create_extra_field(
2567
        $variable,
2568
        $fieldType,
2569
        $displayText,
2570
        $default
2571
    ) {
2572
        $extraField = new ExtraField('user');
2573
        $params = [
2574
            'variable' => $variable,
2575
            'field_type' => $fieldType,
2576
            'display_text' => $displayText,
2577
            'default_value' => $default,
2578
        ];
2579
2580
        return $extraField->save($params);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $extraField->save($params) also could return the type boolean which is incompatible with the documented return type integer.
Loading history...
2581
    }
2582
2583
    /**
2584
     * Check if a field is available.
2585
     *
2586
     * @param string $variable
2587
     *
2588
     * @return bool
2589
     */
2590
    public static function is_extra_field_available($variable)
2591
    {
2592
        $extraField = new ExtraField('user');
2593
        $data = $extraField->get_handler_field_info_by_field_variable($variable);
2594
2595
        return !empty($data) ? true : false;
2596
    }
2597
2598
    /**
2599
     * Gets user extra fields data.
2600
     *
2601
     * @param    int    User ID
2602
     * @param    bool    Whether to prefix the fields indexes with "extra_" (might be used by formvalidator)
2603
     * @param    bool    Whether to return invisible fields as well
2604
     * @param    bool    Whether to split multiple-selection fields or not
2605
     *
2606
     * @return array Array of fields => value for the given user
2607
     */
2608
    public static function get_extra_user_data(
2609
        $user_id,
2610
        $prefix = false,
2611
        $allVisibility = true,
2612
        $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

2612
        /** @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...
2613
        $fieldFilter = null
2614
    ) {
2615
        $user_id = (int) $user_id;
2616
2617
        if (empty($user_id)) {
2618
            return [];
2619
        }
2620
2621
        $extra_data = [];
2622
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2623
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2624
        $user_id = (int) $user_id;
2625
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2626
                FROM $t_uf f
2627
                WHERE
2628
                    extra_field_type = ".EntityExtraField::USER_FIELD_TYPE."
2629
                ";
2630
        $filter_cond = '';
2631
2632
        if (!$allVisibility) {
2633
            if (isset($fieldFilter)) {
2634
                $fieldFilter = (int) $fieldFilter;
2635
                $filter_cond .= " AND filter = $fieldFilter ";
2636
            }
2637
            $sql .= " AND f.visible_to_self = 1 $filter_cond ";
2638
        } else {
2639
            if (isset($fieldFilter)) {
2640
                $fieldFilter = (int) $fieldFilter;
2641
                $sql .= " AND filter = $fieldFilter ";
2642
            }
2643
        }
2644
2645
        $sql .= ' ORDER BY f.field_order';
2646
2647
        $res = Database::query($sql);
2648
        if (Database::num_rows($res) > 0) {
2649
            while ($row = Database::fetch_array($res)) {
2650
                if ($row['type'] == self::USER_FIELD_TYPE_TAG) {
2651
                    $tags = self::get_user_tags_to_string($user_id, $row['id'], false);
2652
                    $extra_data['extra_'.$row['fvar']] = $tags;
2653
                } else {
2654
                    $sqlu = "SELECT value as fval
2655
                            FROM $t_ufv
2656
                            WHERE field_id=".$row['id']." AND item_id = ".$user_id;
2657
                    $resu = Database::query($sqlu);
2658
                    // get default value
2659
                    $sql_df = "SELECT default_value as fval_df FROM $t_uf
2660
                               WHERE id=".$row['id'];
2661
                    $res_df = Database::query($sql_df);
2662
2663
                    if (Database::num_rows($resu) > 0) {
2664
                        $rowu = Database::fetch_array($resu);
2665
                        $fval = $rowu['fval'];
2666
                        if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2667
                            $fval = explode(';', $rowu['fval']);
2668
                        }
2669
                    } else {
2670
                        $row_df = Database::fetch_array($res_df);
2671
                        $fval = $row_df['fval_df'];
2672
                    }
2673
                    // We get here (and fill the $extra_data array) even if there
2674
                    // is no user with data (we fill it with default values)
2675
                    if ($prefix) {
2676
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2677
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2678
                        } else {
2679
                            $extra_data['extra_'.$row['fvar']] = $fval;
2680
                        }
2681
                    } else {
2682
                        if ($row['type'] == self::USER_FIELD_TYPE_RADIO) {
2683
                            $extra_data['extra_'.$row['fvar']]['extra_'.$row['fvar']] = $fval;
2684
                        } else {
2685
                            $extra_data[$row['fvar']] = $fval;
2686
                        }
2687
                    }
2688
                }
2689
            }
2690
        }
2691
2692
        return $extra_data;
2693
    }
2694
2695
    /** Get extra user data by field
2696
     * @param int    user ID
2697
     * @param string the internal variable name of the field
2698
     *
2699
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2700
     */
2701
    public static function get_extra_user_data_by_field(
2702
        $user_id,
2703
        $field_variable,
2704
        $prefix = false,
2705
        $all_visibility = true,
2706
        $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

2706
        /** @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...
2707
    ) {
2708
        // A sanity check.
2709
        if (empty($user_id)) {
2710
            $user_id = 0;
2711
        } else {
2712
            if ($user_id != strval(intval($user_id))) {
2713
                return [];
2714
            }
2715
        }
2716
        $extra_data = [];
2717
        $t_uf = Database::get_main_table(TABLE_EXTRA_FIELD);
2718
        $t_ufv = Database::get_main_table(TABLE_EXTRA_FIELD_VALUES);
2719
        $user_id = (int) $user_id;
2720
2721
        $sql = "SELECT f.id as id, f.variable as fvar, f.field_type as type
2722
                FROM $t_uf f
2723
                WHERE f.variable = '$field_variable' ";
2724
2725
        if (!$all_visibility) {
2726
            $sql .= " AND f.visible_to_self = 1 ";
2727
        }
2728
2729
        $sql .= " AND extra_field_type = ".EntityExtraField::USER_FIELD_TYPE;
2730
        $sql .= " ORDER BY f.field_order";
2731
2732
        $res = Database::query($sql);
2733
        if (Database::num_rows($res) > 0) {
2734
            while ($row = Database::fetch_array($res)) {
2735
                $sqlu = "SELECT value as fval FROM $t_ufv v 
2736
                         INNER JOIN $t_uf f
2737
                         ON (v.field_id = f.id)
2738
                         WHERE
2739
                            extra_field_type = ".EntityExtraField::USER_FIELD_TYPE." AND
2740
                            field_id = ".$row['id']." AND
2741
                            item_id = ".$user_id;
2742
                $resu = Database::query($sqlu);
2743
                $fval = '';
2744
                if (Database::num_rows($resu) > 0) {
2745
                    $rowu = Database::fetch_array($resu);
2746
                    $fval = $rowu['fval'];
2747
                    if ($row['type'] == self::USER_FIELD_TYPE_SELECT_MULTIPLE) {
2748
                        $fval = explode(';', $rowu['fval']);
2749
                    }
2750
                }
2751
                if ($prefix) {
2752
                    $extra_data['extra_'.$row['fvar']] = $fval;
2753
                } else {
2754
                    $extra_data[$row['fvar']] = $fval;
2755
                }
2756
            }
2757
        }
2758
2759
        return $extra_data;
2760
    }
2761
2762
    /**
2763
     * Get the extra field information for a certain field (the options as well).
2764
     *
2765
     * @param int $variable The name of the field we want to know everything about
2766
     *
2767
     * @return array Array containing all the information about the extra profile field
2768
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2769
     *               as returned by the database)
2770
     *
2771
     * @author Julio Montoya
2772
     *
2773
     * @since v1.8.6
2774
     */
2775
    public static function get_extra_field_information_by_name($variable)
2776
    {
2777
        $extraField = new ExtraField('user');
2778
2779
        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...
2780
    }
2781
2782
    /**
2783
     * Get the extra field information for user tag (the options as well).
2784
     *
2785
     * @param int $variable The name of the field we want to know everything about
2786
     *
2787
     * @return array Array containing all the information about the extra profile field
2788
     *               (first level of array contains field details, then 'options' sub-array contains options details,
2789
     *               as returned by the database)
2790
     *
2791
     * @author José Loguercio
2792
     *
2793
     * @since v1.11.0
2794
     */
2795
    public static function get_extra_field_tags_information_by_name($variable)
2796
    {
2797
        $extraField = new ExtraField('user');
2798
2799
        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...
2800
    }
2801
2802
    /**
2803
     * @param string $type
2804
     *
2805
     * @return array
2806
     */
2807
    public static function get_all_extra_field_by_type($type)
2808
    {
2809
        $extraField = new ExtraField('user');
2810
2811
        return $extraField->get_all_extra_field_by_type($type);
2812
    }
2813
2814
    /**
2815
     * Get all the extra field information of a certain field (also the options).
2816
     *
2817
     * @param int $fieldId the ID of the field we want to know everything of
2818
     *
2819
     * @return array $return containing all th information about the extra profile field
2820
     *
2821
     * @author Julio Montoya
2822
     *
2823
     * @deprecated
2824
     * @since v1.8.6
2825
     */
2826
    public static function get_extra_field_information($fieldId)
2827
    {
2828
        $extraField = new ExtraField('user');
2829
2830
        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...
2831
    }
2832
2833
    /**
2834
     * Get extra user data by value.
2835
     *
2836
     * @param string $variable       the internal variable name of the field
2837
     * @param string $value          the internal value of the field
2838
     * @param bool   $all_visibility
2839
     *
2840
     * @return array with extra data info of a user i.e array('field_variable'=>'value');
2841
     */
2842
    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

2842
    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...
2843
    {
2844
        $extraFieldValue = new ExtraFieldValue('user');
2845
        $extraField = new ExtraField('user');
2846
2847
        $info = $extraField->get_handler_field_info_by_field_variable($variable);
2848
2849
        if (false === $info) {
2850
            return [];
2851
        }
2852
2853
        $data = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
2854
            $variable,
2855
            $value,
2856
            false,
2857
            false,
2858
            true
2859
        );
2860
2861
        $result = [];
2862
        if (!empty($data)) {
2863
            foreach ($data as $item) {
2864
                $result[] = $item['item_id'];
2865
            }
2866
        }
2867
2868
        return $result;
2869
    }
2870
2871
    /**
2872
     * Get extra user data by tags value.
2873
     *
2874
     * @param int    $fieldId the ID of the field we want to know everything of
2875
     * @param string $tag     the tag name for search
2876
     *
2877
     * @return array with extra data info of a user
2878
     *
2879
     * @author José Loguercio
2880
     *
2881
     * @since v1.11.0
2882
     */
2883
    public static function get_extra_user_data_by_tags($fieldId, $tag)
2884
    {
2885
        $extraField = new ExtraField('user');
2886
        $result = $extraField->getAllUserPerTag($fieldId, $tag);
2887
        $array = [];
2888
        foreach ($result as $index => $user) {
2889
            $array[] = $user['user_id'];
2890
        }
2891
2892
        return $array;
2893
    }
2894
2895
    /**
2896
     * Get extra user data by field variable.
2897
     *
2898
     * @param string $variable field variable
2899
     *
2900
     * @return array data
2901
     */
2902
    public static function get_extra_user_data_by_field_variable($variable)
2903
    {
2904
        $extraInfo = self::get_extra_field_information_by_name($variable);
2905
        $field_id = (int) $extraInfo['id'];
2906
2907
        $extraField = new ExtraFieldValue('user');
2908
        $data = $extraField->getValuesByFieldId($field_id);
2909
2910
        if (!empty($data)) {
2911
            foreach ($data as $row) {
2912
                $user_id = $row['item_id'];
2913
                $data[$user_id] = $row;
2914
            }
2915
        }
2916
2917
        return $data;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $data could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
2918
    }
2919
2920
    /**
2921
     * Get extra user data tags by field variable.
2922
     *
2923
     * @param string $variable field variable
2924
     *
2925
     * @return array
2926
     */
2927
    public static function get_extra_user_data_for_tags($variable)
2928
    {
2929
        $data = self::get_extra_field_tags_information_by_name($variable);
2930
2931
        return $data;
2932
    }
2933
2934
    /**
2935
     * Gives a list of [session_category][session_id] for the current user.
2936
     *
2937
     * @param int  $user_id
2938
     * @param bool $is_time_over                 whether to fill the first element or not
2939
     *                                           (to give space for courses out of categories)
2940
     * @param bool $ignore_visibility_for_admins optional true if limit time from session is over, false otherwise
2941
     * @param bool $ignoreTimeLimit              ignore time start/end
2942
     *
2943
     * @return array list of statuses [session_category][session_id]
2944
     *
2945
     * @todo ensure multiple access urls are managed correctly
2946
     */
2947
    public static function get_sessions_by_category(
2948
        $user_id,
2949
        $is_time_over = true,
2950
        $ignore_visibility_for_admins = false,
2951
        $ignoreTimeLimit = false,
2952
        $getCount = false
2953
    ) {
2954
        if ($user_id != strval(intval($user_id))) {
2955
            return [];
2956
        }
2957
2958
        $allowOrder = api_get_configuration_value('session_list_order');
2959
        $position = '';
2960
        if ($allowOrder) {
2961
            $position = ', s.position AS position ';
2962
        }
2963
2964
        // Get the list of sessions per user
2965
        $now = new DateTime('now', new DateTimeZone('UTC'));
2966
2967
        // LEFT JOIN is used for session_rel_course_rel_user because an inner
2968
        // join would not catch session-courses where the user is general
2969
        // session coach but which do not have students nor coaches registered
2970
        $dqlSelect = " COUNT(DISTINCT s.id) ";
2971
2972
        if (!$getCount) {
2973
            $dqlSelect = " DISTINCT
2974
                s.id,
2975
                s.name,
2976
                s.accessStartDate AS access_start_date,
2977
                s.accessEndDate AS access_end_date,
2978
                s.duration,
2979
                sc.id AS session_category_id,
2980
                sc.name AS session_category_name,
2981
                sc.dateStart AS session_category_date_start,
2982
                sc.dateEnd AS session_category_date_end,
2983
                s.coachAccessStartDate AS coach_access_start_date,
2984
                s.coachAccessEndDate AS coach_access_end_date,
2985
                CASE WHEN s.accessEndDate IS NULL THEN 1 ELSE 0 END HIDDEN _isFieldNull
2986
                $position
2987
            ";
2988
        }
2989
2990
        $dql = "SELECT $dqlSelect
2991
                FROM ChamiloCoreBundle:Session AS s
2992
                LEFT JOIN ChamiloCoreBundle:SessionRelCourseRelUser AS scu WITH scu.session = s
2993
                INNER JOIN ChamiloCoreBundle:AccessUrlRelSession AS url WITH url.session = s.id
2994
                LEFT JOIN ChamiloCoreBundle:SessionCategory AS sc WITH s.category = sc ";
2995
2996
        // A single OR operation on scu.user = :user OR s.generalCoach = :user
2997
        // is awfully inefficient for large sets of data (1m25s for 58K
2998
        // sessions, BT#14115) but executing a similar query twice and grouping
2999
        // the results afterwards in PHP takes about 1/1000th of the time
3000
        // (0.1s + 0.0s) for the same set of data, so we do it this way...
3001
        $dqlStudent = $dql." WHERE scu.user = :user AND url.url = :url ";
3002
        $dqlCoach = $dql." WHERE s.generalCoach = :user AND url.url = :url ";
3003
3004
        // Default order
3005
        $order = 'ORDER BY sc.name, s.name';
3006
3007
        // Order by date if showing all sessions
3008
        $showAllSessions = api_get_configuration_value('show_all_sessions_on_my_course_page') === true;
3009
        if ($showAllSessions) {
3010
            $order = 'ORDER BY s.accessStartDate';
3011
        }
3012
3013
        // Order by position
3014
        if ($allowOrder) {
3015
            $order = 'ORDER BY s.position';
3016
        }
3017
3018
        // Order by dates according to settings
3019
        $orderBySettings = api_get_configuration_value('my_courses_session_order');
3020
        if (!empty($orderBySettings) && isset($orderBySettings['field']) && isset($orderBySettings['order'])) {
3021
            $field = $orderBySettings['field'];
3022
            $orderSetting = $orderBySettings['order'];
3023
            switch ($field) {
3024
                case 'start_date':
3025
                    $order = " ORDER BY s.accessStartDate $orderSetting";
3026
                    break;
3027
                case 'end_date':
3028
                    $order = " ORDER BY s.accessEndDate $orderSetting ";
3029
                    if ($orderSetting == 'asc') {
3030
                        // Put null values at the end
3031
                        // https://stackoverflow.com/questions/12652034/how-can-i-order-by-null-in-dql
3032
                        $order = ' ORDER BY _isFieldNull asc, s.accessEndDate asc';
3033
                    }
3034
                    break;
3035
            }
3036
        }
3037
3038
        $dqlStudent .= $order;
3039
        $dqlCoach .= $order;
3040
3041
        $accessUrlId = api_get_current_access_url_id();
3042
        $dqlStudent = Database::getManager()
3043
            ->createQuery($dqlStudent)
3044
            ->setParameters(
3045
                ['user' => $user_id, 'url' => $accessUrlId]
3046
            )
3047
        ;
3048
        $dqlCoach = Database::getManager()
3049
            ->createQuery($dqlCoach)
3050
            ->setParameters(
3051
                ['user' => $user_id, 'url' => $accessUrlId]
3052
            )
3053
        ;
3054
3055
        if ($getCount) {
3056
            return $dqlStudent->getSingleScalarResult() + $dqlCoach->getSingleScalarResult();
3057
        }
3058
3059
        $sessionDataStudent = $dqlStudent->getResult();
3060
        $sessionDataCoach = $dqlCoach->getResult();
3061
3062
        $sessionData = [];
3063
        // First fill $sessionData with student sessions
3064
        foreach ($sessionDataStudent as $row) {
3065
            $sessionData[$row['id']] = $row;
3066
        }
3067
        // Overwrite session data of the user as a student with session data
3068
        // of the user as a coach.
3069
        // There shouldn't be such duplicate rows, but just in case...
3070
        foreach ($sessionDataCoach as $row) {
3071
            $sessionData[$row['id']] = $row;
3072
        }
3073
3074
        $collapsable = api_get_configuration_value('allow_user_session_collapsable');
3075
        $extraField = new ExtraFieldValue('session');
3076
        $collapsableLink = api_get_path(WEB_PATH).'user_portal.php?action=collapse_session';
3077
3078
        $categories = [];
3079
        foreach ($sessionData as $row) {
3080
            $session_id = $row['id'];
3081
            $coachList = SessionManager::getCoachesBySession($session_id);
3082
            $categoryStart = $row['session_category_date_start'] ? $row['session_category_date_start']->format('Y-m-d') : '';
3083
            $categoryEnd = $row['session_category_date_end'] ? $row['session_category_date_end']->format('Y-m-d') : '';
3084
            $courseList = self::get_courses_list_by_session(
3085
                $user_id,
3086
                $session_id
3087
            );
3088
            $daysLeft = SessionManager::getDayLeftInSession($row, $user_id);
3089
3090
            // User portal filters:
3091
            if ($ignoreTimeLimit === false) {
3092
                if ($is_time_over) {
3093
                    // History
3094
                    if ($row['duration']) {
3095
                        if ($daysLeft >= 0) {
3096
                            continue;
3097
                        }
3098
                    } else {
3099
                        if (empty($row['access_end_date'])) {
3100
                            continue;
3101
                        } else {
3102
                            if ($row['access_end_date'] > $now) {
3103
                                continue;
3104
                            }
3105
                        }
3106
                    }
3107
                } else {
3108
                    // Current user portal
3109
                    $isGeneralCoach = SessionManager::user_is_general_coach($user_id, $row['id']);
3110
                    $isCoachOfCourse = in_array($user_id, $coachList);
3111
3112
                    if (api_is_platform_admin() || $isGeneralCoach || $isCoachOfCourse) {
3113
                        // Teachers can access the session depending in the access_coach date
3114
                    } else {
3115
                        if ($row['duration']) {
3116
                            if ($daysLeft <= 0) {
3117
                                continue;
3118
                            }
3119
                        } else {
3120
                            if (isset($row['access_end_date']) &&
3121
                                !empty($row['access_end_date'])
3122
                            ) {
3123
                                if ($row['access_end_date'] <= $now) {
3124
                                    continue;
3125
                                }
3126
                            }
3127
                        }
3128
                    }
3129
                }
3130
            }
3131
3132
            $categories[$row['session_category_id']]['session_category'] = [
3133
                'id' => $row['session_category_id'],
3134
                'name' => $row['session_category_name'],
3135
                'date_start' => $categoryStart,
3136
                'date_end' => $categoryEnd,
3137
            ];
3138
3139
            $visibility = api_get_session_visibility(
3140
                $session_id,
3141
                null,
3142
                $ignore_visibility_for_admins
3143
            );
3144
3145
            if ($visibility != SESSION_VISIBLE) {
3146
                // Course Coach session visibility.
3147
                $blockedCourseCount = 0;
3148
                $closedVisibilityList = [
3149
                    COURSE_VISIBILITY_CLOSED,
3150
                    COURSE_VISIBILITY_HIDDEN,
3151
                ];
3152
3153
                foreach ($courseList as $course) {
3154
                    // Checking session visibility
3155
                    $sessionCourseVisibility = api_get_session_visibility(
3156
                        $session_id,
3157
                        $course['real_id'],
3158
                        $ignore_visibility_for_admins
3159
                    );
3160
3161
                    $courseIsVisible = !in_array($course['visibility'], $closedVisibilityList);
3162
                    if ($courseIsVisible === false || $sessionCourseVisibility == SESSION_INVISIBLE) {
3163
                        $blockedCourseCount++;
3164
                    }
3165
                }
3166
3167
                // If all courses are blocked then no show in the list.
3168
                if ($blockedCourseCount === count($courseList)) {
3169
                    $visibility = SESSION_INVISIBLE;
3170
                } else {
3171
                    $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...
3172
                }
3173
            }
3174
3175
            switch ($visibility) {
3176
                case SESSION_VISIBLE_READ_ONLY:
3177
                case SESSION_VISIBLE:
3178
                case SESSION_AVAILABLE:
3179
                    break;
3180
                case SESSION_INVISIBLE:
3181
                    if ($ignore_visibility_for_admins === false) {
3182
                        continue 2;
3183
                    }
3184
            }
3185
3186
            $collapsed = '';
3187
            $collapsedAction = '';
3188
            if ($collapsable) {
3189
                $collapsableData = Sessionmanager::getCollapsableData(
3190
                    $user_id,
3191
                    $session_id,
3192
                    $extraField,
3193
                    $collapsableLink
3194
                );
3195
                $collapsed = $collapsableData['collapsed'];
3196
                $collapsedAction = $collapsableData['collapsable_link'];
3197
            }
3198
3199
            $categories[$row['session_category_id']]['sessions'][] = [
3200
                'session_name' => $row['name'],
3201
                'session_id' => $row['id'],
3202
                'access_start_date' => $row['access_start_date'] ? $row['access_start_date']->format('Y-m-d H:i:s') : null,
3203
                'access_end_date' => $row['access_end_date'] ? $row['access_end_date']->format('Y-m-d H:i:s') : null,
3204
                'coach_access_start_date' => $row['coach_access_start_date'] ? $row['coach_access_start_date']->format('Y-m-d H:i:s') : null,
3205
                'coach_access_end_date' => $row['coach_access_end_date'] ? $row['coach_access_end_date']->format('Y-m-d H:i:s') : null,
3206
                'courses' => $courseList,
3207
                'collapsed' => $collapsed,
3208
                'collapsable_link' => $collapsedAction,
3209
                'duration' => $row['duration'],
3210
            ];
3211
        }
3212
3213
        return $categories;
3214
    }
3215
3216
    /**
3217
     * Gives a list of [session_id-course_code] => [status] for the current user.
3218
     *
3219
     * @param int $user_id
3220
     * @param int $sessionLimit
3221
     *
3222
     * @return array list of statuses (session_id-course_code => status)
3223
     */
3224
    public static function get_personal_session_course_list($user_id, $sessionLimit = null)
3225
    {
3226
        // Database Table Definitions
3227
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3228
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
3229
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3230
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3231
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3232
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3233
3234
        if ($user_id != strval(intval($user_id))) {
3235
            return [];
3236
        }
3237
3238
        // We filter the courses from the URL
3239
        $join_access_url = $where_access_url = '';
3240
3241
        if (api_get_multiple_access_url()) {
3242
            $access_url_id = api_get_current_access_url_id();
3243
            if ($access_url_id != -1) {
3244
                $tbl_url_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3245
                $join_access_url = "LEFT JOIN $tbl_url_course url_rel_course ON url_rel_course.c_id = course.id";
3246
                $where_access_url = " AND access_url_id = $access_url_id ";
3247
            }
3248
        }
3249
3250
        // Courses in which we subscribed out of any session
3251
        $tbl_user_course_category = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
3252
3253
        $sql = "SELECT
3254
                    course.code,
3255
                    course_rel_user.status course_rel_status,
3256
                    course_rel_user.sort sort,
3257
                    course_rel_user.user_course_cat user_course_cat
3258
                 FROM $tbl_course_user course_rel_user
3259
                 LEFT JOIN $tbl_course course
3260
                 ON course.id = course_rel_user.c_id
3261
                 LEFT JOIN $tbl_user_course_category user_course_category
3262
                 ON course_rel_user.user_course_cat = user_course_category.id
3263
                 $join_access_url
3264
                 WHERE
3265
                    course_rel_user.user_id = '".$user_id."' AND
3266
                    course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
3267
                    $where_access_url
3268
                 ORDER BY user_course_category.sort, course_rel_user.sort, course.title ASC";
3269
3270
        $course_list_sql_result = Database::query($sql);
3271
3272
        $personal_course_list = [];
3273
        if (Database::num_rows($course_list_sql_result) > 0) {
3274
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3275
                $course_info = api_get_course_info($result_row['code']);
3276
                $result_row['course_info'] = $course_info;
3277
                $personal_course_list[] = $result_row;
3278
            }
3279
        }
3280
3281
        $coachCourseConditions = '';
3282
        // Getting sessions that are related to a coach in the session_rel_course_rel_user table
3283
        if (api_is_allowed_to_create_course()) {
3284
            $sessionListFromCourseCoach = [];
3285
            $sql = " SELECT DISTINCT session_id
3286
                    FROM $tbl_session_course_user
3287
                    WHERE user_id = $user_id AND status = 2 ";
3288
3289
            $result = Database::query($sql);
3290
            if (Database::num_rows($result)) {
3291
                $result = Database::store_result($result);
3292
                foreach ($result as $session) {
3293
                    $sessionListFromCourseCoach[] = $session['session_id'];
3294
                }
3295
            }
3296
            if (!empty($sessionListFromCourseCoach)) {
3297
                $condition = implode("','", $sessionListFromCourseCoach);
3298
                $coachCourseConditions = " OR ( s.id IN ('$condition'))";
3299
            }
3300
        }
3301
3302
        // Get the list of sessions where the user is subscribed
3303
        // This is divided into two different queries
3304
        $sessions = [];
3305
        $sessionLimitRestriction = '';
3306
        if (!empty($sessionLimit)) {
3307
            $sessionLimit = (int) $sessionLimit;
3308
            $sessionLimitRestriction = "LIMIT $sessionLimit";
3309
        }
3310
3311
        $sql = "SELECT DISTINCT s.id, name, access_start_date, access_end_date
3312
                FROM $tbl_session_user su INNER JOIN $tbl_session s
3313
                ON (s.id = su.session_id)
3314
                WHERE (
3315
                    su.user_id = $user_id AND
3316
                    su.relation_type <> ".SESSION_RELATION_TYPE_RRHH."
3317
                )
3318
                $coachCourseConditions
3319
                ORDER BY access_start_date, access_end_date, name
3320
                $sessionLimitRestriction
3321
        ";
3322
3323
        $result = Database::query($sql);
3324
        if (Database::num_rows($result) > 0) {
3325
            while ($row = Database::fetch_assoc($result)) {
3326
                $sessions[$row['id']] = $row;
3327
            }
3328
        }
3329
3330
        $sql = "SELECT DISTINCT
3331
                id, name, access_start_date, access_end_date
3332
                FROM $tbl_session s
3333
                WHERE (
3334
                    id_coach = $user_id
3335
                )
3336
                $coachCourseConditions
3337
                ORDER BY access_start_date, access_end_date, name";
3338
3339
        $result = Database::query($sql);
3340
        if (Database::num_rows($result) > 0) {
3341
            while ($row = Database::fetch_assoc($result)) {
3342
                if (empty($sessions[$row['id']])) {
3343
                    $sessions[$row['id']] = $row;
3344
                }
3345
            }
3346
        }
3347
3348
        if (api_is_allowed_to_create_course()) {
3349
            foreach ($sessions as $enreg) {
3350
                $session_id = $enreg['id'];
3351
                $session_visibility = api_get_session_visibility($session_id);
3352
3353
                if ($session_visibility == SESSION_INVISIBLE) {
3354
                    continue;
3355
                }
3356
3357
                // This query is horribly slow when more than a few thousand
3358
                // users and just a few sessions to which they are subscribed
3359
                $sql = "SELECT DISTINCT
3360
                        course.code code,
3361
                        course.title i,
3362
                        ".(api_is_western_name_order() ? "CONCAT(user.firstname,' ',user.lastname)" : "CONCAT(user.lastname,' ',user.firstname)")." t,
3363
                        email, course.course_language l,
3364
                        1 sort,
3365
                        category_code user_course_cat,
3366
                        access_start_date,
3367
                        access_end_date,
3368
                        session.id as session_id,
3369
                        session.name as session_name
3370
                    FROM $tbl_session_course_user as session_course_user
3371
                    INNER JOIN $tbl_course AS course
3372
                        ON course.id = session_course_user.c_id
3373
                    INNER JOIN $tbl_session as session
3374
                        ON session.id = session_course_user.session_id
3375
                    LEFT JOIN $tbl_user as user
3376
                        ON user.id = session_course_user.user_id OR session.id_coach = user.id
3377
                    WHERE
3378
                        session_course_user.session_id = $session_id AND (
3379
                            (session_course_user.user_id = $user_id AND session_course_user.status = 2)
3380
                            OR session.id_coach = $user_id
3381
                        )
3382
                    ORDER BY i";
3383
                $course_list_sql_result = Database::query($sql);
3384
                while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3385
                    $result_row['course_info'] = api_get_course_info($result_row['code']);
3386
                    $key = $result_row['session_id'].' - '.$result_row['code'];
3387
                    $personal_course_list[$key] = $result_row;
3388
                }
3389
            }
3390
        }
3391
3392
        foreach ($sessions as $enreg) {
3393
            $session_id = $enreg['id'];
3394
            $session_visibility = api_get_session_visibility($session_id);
3395
            if ($session_visibility == SESSION_INVISIBLE) {
3396
                continue;
3397
            }
3398
3399
            /* This query is very similar to the above query,
3400
               but it will check the session_rel_course_user table if there are courses registered to our user or not */
3401
            $sql = "SELECT DISTINCT
3402
                course.code code,
3403
                course.title i, CONCAT(user.lastname,' ',user.firstname) t,
3404
                email,
3405
                course.course_language l,
3406
                1 sort,
3407
                category_code user_course_cat,
3408
                access_start_date,
3409
                access_end_date,
3410
                session.id as session_id,
3411
                session.name as session_name,
3412
                IF((session_course_user.user_id = 3 AND session_course_user.status=2),'2', '5')
3413
            FROM $tbl_session_course_user as session_course_user
3414
            INNER JOIN $tbl_course AS course
3415
            ON course.id = session_course_user.c_id AND session_course_user.session_id = $session_id
3416
            INNER JOIN $tbl_session as session 
3417
            ON session_course_user.session_id = session.id
3418
            LEFT JOIN $tbl_user as user ON user.id = session_course_user.user_id
3419
            WHERE session_course_user.user_id = $user_id
3420
            ORDER BY i";
3421
3422
            $course_list_sql_result = Database::query($sql);
3423
            while ($result_row = Database::fetch_array($course_list_sql_result, 'ASSOC')) {
3424
                $result_row['course_info'] = api_get_course_info($result_row['code']);
3425
                $key = $result_row['session_id'].' - '.$result_row['code'];
3426
                if (!isset($personal_course_list[$key])) {
3427
                    $personal_course_list[$key] = $result_row;
3428
                }
3429
            }
3430
        }
3431
3432
        return $personal_course_list;
3433
    }
3434
3435
    /**
3436
     * Gives a list of courses for the given user in the given session.
3437
     *
3438
     * @param int $user_id
3439
     * @param int $session_id
3440
     *
3441
     * @return array list of statuses (session_id-course_code => status)
3442
     */
3443
    public static function get_courses_list_by_session($user_id, $session_id)
3444
    {
3445
        // Database Table Definitions
3446
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3447
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
3448
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3449
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3450
3451
        $user_id = (int) $user_id;
3452
        $session_id = (int) $session_id;
3453
        //we filter the courses from the URL
3454
        $join_access_url = $where_access_url = '';
3455
3456
        if (api_get_multiple_access_url()) {
3457
            $urlId = api_get_current_access_url_id();
3458
            if ($urlId != -1) {
3459
                $tbl_url_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3460
                $join_access_url = " ,  $tbl_url_session url_rel_session ";
3461
                $where_access_url = " AND access_url_id = $urlId AND url_rel_session.session_id = $session_id ";
3462
            }
3463
        }
3464
3465
        /* This query is very similar to the query below, but it will check the
3466
        session_rel_course_user table if there are courses registered
3467
        to our user or not */
3468
        $sql = "SELECT DISTINCT
3469
                    c.visibility,
3470
                    c.id as real_id,
3471
                    c.code as course_code,
3472
                    sc.position
3473
                FROM $tbl_session_course_user as scu
3474
                INNER JOIN $tbl_session_course sc
3475
                ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3476
                INNER JOIN $tableCourse as c
3477
                ON (scu.c_id = c.id)
3478
                $join_access_url
3479
                WHERE
3480
                    scu.user_id = $user_id AND
3481
                    scu.session_id = $session_id
3482
                    $where_access_url
3483
                ORDER BY sc.position ASC";
3484
3485
        $myCourseList = [];
3486
        $courses = [];
3487
        $result = Database::query($sql);
3488
        if (Database::num_rows($result) > 0) {
3489
            while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3490
                $result_row['status'] = 5;
3491
                if (!in_array($result_row['real_id'], $courses)) {
3492
                    $position = $result_row['position'];
3493
                    if (!isset($myCourseList[$position])) {
3494
                        $myCourseList[$position] = $result_row;
3495
                    } else {
3496
                        $myCourseList[] = $result_row;
3497
                    }
3498
                    $courses[] = $result_row['real_id'];
3499
                }
3500
            }
3501
        }
3502
3503
        if (api_is_allowed_to_create_course()) {
3504
            $sql = "SELECT DISTINCT
3505
                        c.visibility, 
3506
                        c.id as real_id,
3507
                        c.code as course_code,
3508
                        sc.position
3509
                    FROM $tbl_session_course_user as scu
3510
                    INNER JOIN $tbl_session as s
3511
                    ON (scu.session_id = s.id)
3512
                    INNER JOIN $tbl_session_course sc
3513
                    ON (scu.session_id = sc.session_id AND scu.c_id = sc.c_id)
3514
                    INNER JOIN $tableCourse as c
3515
                    ON (scu.c_id = c.id)
3516
                    $join_access_url
3517
                    WHERE
3518
                      s.id = $session_id AND
3519
                      (
3520
                        (scu.user_id = $user_id AND scu.status = 2) OR
3521
                        s.id_coach = $user_id
3522
                      )
3523
                    $where_access_url
3524
                    ORDER BY sc.position ASC";
3525
            $result = Database::query($sql);
3526
3527
            if (Database::num_rows($result) > 0) {
3528
                while ($result_row = Database::fetch_array($result, 'ASSOC')) {
3529
                    $result_row['status'] = 2;
3530
                    if (!in_array($result_row['real_id'], $courses)) {
3531
                        $position = $result_row['position'];
3532
                        if (!isset($myCourseList[$position])) {
3533
                            $myCourseList[$position] = $result_row;
3534
                        } else {
3535
                            $myCourseList[] = $result_row;
3536
                        }
3537
                        $courses[] = $result_row['real_id'];
3538
                    }
3539
                }
3540
            }
3541
        }
3542
3543
        if (api_is_drh()) {
3544
            $sessionList = SessionManager::get_sessions_followed_by_drh($user_id);
3545
            $sessionList = array_keys($sessionList);
3546
            if (in_array($session_id, $sessionList)) {
3547
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3548
                if (!empty($courseList)) {
3549
                    foreach ($courseList as $course) {
3550
                        if (!in_array($course['id'], $courses)) {
3551
                            $position = $course['position'];
3552
                            if (!isset($myCourseList[$position])) {
3553
                                $myCourseList[$position] = $course;
3554
                            } else {
3555
                                $myCourseList[] = $course;
3556
                            }
3557
                        }
3558
                    }
3559
                }
3560
            }
3561
        } else {
3562
            //check if user is general coach for this session
3563
            $sessionInfo = api_get_session_info($session_id);
3564
            if ($sessionInfo['id_coach'] == $user_id) {
3565
                $courseList = SessionManager::get_course_list_by_session_id($session_id);
3566
                if (!empty($courseList)) {
3567
                    foreach ($courseList as $course) {
3568
                        if (!in_array($course['id'], $courses)) {
3569
                            $position = $course['position'];
3570
                            if (!isset($myCourseList[$position])) {
3571
                                $myCourseList[$position] = $course;
3572
                            } else {
3573
                                $myCourseList[] = $course;
3574
                            }
3575
                        }
3576
                    }
3577
                }
3578
            }
3579
        }
3580
3581
        if (!empty($myCourseList)) {
3582
            ksort($myCourseList);
3583
        }
3584
3585
        return $myCourseList;
3586
    }
3587
3588
    /**
3589
     * Get user id from a username.
3590
     *
3591
     * @param string $username
3592
     *
3593
     * @return int User ID (or false if not found)
3594
     */
3595
    public static function get_user_id_from_username($username)
3596
    {
3597
        if (empty($username)) {
3598
            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...
3599
        }
3600
        $username = trim($username);
3601
        $username = Database::escape_string($username);
3602
        $t_user = Database::get_main_table(TABLE_MAIN_USER);
3603
        $sql = "SELECT id FROM $t_user WHERE username = '$username'";
3604
        $res = Database::query($sql);
3605
3606
        if ($res === false) {
3607
            return false;
3608
        }
3609
        if (Database::num_rows($res) !== 1) {
3610
            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...
3611
        }
3612
        $row = Database::fetch_array($res);
3613
3614
        return $row['id'];
3615
    }
3616
3617
    /**
3618
     * Get the users files upload from his share_folder.
3619
     *
3620
     * @param string $user_id      User ID
3621
     * @param string $course       course directory
3622
     * @param string $resourceType resource type: images, all
3623
     *
3624
     * @return string
3625
     */
3626
    public static function get_user_upload_files_by_course(
3627
        $user_id,
3628
        $course,
3629
        $resourceType = 'all'
3630
    ) {
3631
        $return = '';
3632
        if (!empty($user_id) && !empty($course)) {
3633
            $user_id = (int) $user_id;
3634
            $path = api_get_path(SYS_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3635
            $web_path = api_get_path(WEB_COURSE_PATH).$course.'/document/shared_folder/sf_user_'.$user_id.'/';
3636
            $file_list = [];
3637
3638
            if (is_dir($path)) {
3639
                $handle = opendir($path);
3640
                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

3640
                while ($file = readdir(/** @scrutinizer ignore-type */ $handle)) {
Loading history...
3641
                    if ($file == '.' || $file == '..' || $file == '.htaccess' || is_dir($path.$file)) {
3642
                        continue; // skip current/parent directory and .htaccess
3643
                    }
3644
                    $file_list[] = $file;
3645
                }
3646
                if (count($file_list) > 0) {
3647
                    $return = "<h4>$course</h4>";
3648
                    $return .= '<ul class="thumbnails">';
3649
                }
3650
                $extensionList = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'tif'];
3651
                foreach ($file_list as $file) {
3652
                    if ($resourceType == 'all') {
3653
                        $return .= '<li>
3654
                            <a href="'.$web_path.urlencode($file).'" target="_blank">'.htmlentities($file).'</a></li>';
3655
                    } elseif ($resourceType == 'images') {
3656
                        //get extension
3657
                        $ext = explode('.', $file);
3658
                        if (isset($ext[1]) && in_array($ext[1], $extensionList)) {
3659
                            $return .= '<li class="span2">
3660
                                            <a class="thumbnail" href="'.$web_path.urlencode($file).'" target="_blank">
3661
                                                <img src="'.$web_path.urlencode($file).'" >
3662
                                            </a>
3663
                                        </li>';
3664
                        }
3665
                    }
3666
                }
3667
                if (count($file_list) > 0) {
3668
                    $return .= '</ul>';
3669
                }
3670
            }
3671
        }
3672
3673
        return $return;
3674
    }
3675
3676
    /**
3677
     * Gets the API key (or keys) and return them into an array.
3678
     *
3679
     * @param int     Optional user id (defaults to the result of api_get_user_id())
3680
     * @param string $api_service
3681
     *
3682
     * @return mixed Non-indexed array containing the list of API keys for this user, or FALSE on error
3683
     */
3684
    public static function get_api_keys($user_id = null, $api_service = 'dokeos')
3685
    {
3686
        if ($user_id != strval(intval($user_id))) {
3687
            return false;
3688
        }
3689
        if (empty($user_id)) {
3690
            $user_id = api_get_user_id();
3691
        }
3692
        if ($user_id === false) {
3693
            return false;
3694
        }
3695
        $service_name = Database::escape_string($api_service);
3696
        if (is_string($service_name) === false) {
3697
            return false;
3698
        }
3699
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3700
        $sql = "SELECT * FROM $t_api WHERE user_id = $user_id AND api_service='$api_service';";
3701
        $res = Database::query($sql);
3702
        if ($res === false) {
3703
            return false;
3704
        } //error during query
3705
        $num = Database::num_rows($res);
3706
        if ($num == 0) {
3707
            return false;
3708
        }
3709
        $list = [];
3710
        while ($row = Database::fetch_array($res)) {
3711
            $list[$row['id']] = $row['api_key'];
3712
        }
3713
3714
        return $list;
3715
    }
3716
3717
    /**
3718
     * Adds a new API key to the users' account.
3719
     *
3720
     * @param   int     Optional user ID (defaults to the results of api_get_user_id())
3721
     * @param string $api_service
3722
     *
3723
     * @return bool True on success, false on failure
3724
     */
3725
    public static function add_api_key($user_id = null, $api_service = 'dokeos')
3726
    {
3727
        if ($user_id != strval(intval($user_id))) {
3728
            return false;
3729
        }
3730
        if (empty($user_id)) {
3731
            $user_id = api_get_user_id();
3732
        }
3733
        if ($user_id === false) {
3734
            return false;
3735
        }
3736
        $service_name = Database::escape_string($api_service);
3737
        if (is_string($service_name) === false) {
3738
            return false;
3739
        }
3740
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3741
        $md5 = md5((time() + ($user_id * 5)) - rand(10000, 10000)); //generate some kind of random key
3742
        $sql = "INSERT INTO $t_api (user_id, api_key,api_service) VALUES ($user_id,'$md5','$service_name')";
3743
        $res = Database::query($sql);
3744
        if ($res === false) {
3745
            return false;
3746
        } //error during query
3747
        $num = Database::insert_id();
3748
3749
        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...
3750
    }
3751
3752
    /**
3753
     * Deletes an API key from the user's account.
3754
     *
3755
     * @param   int     API key's internal ID
3756
     *
3757
     * @return bool True on success, false on failure
3758
     */
3759
    public static function delete_api_key($key_id)
3760
    {
3761
        if ($key_id != strval(intval($key_id))) {
3762
            return false;
3763
        }
3764
        if ($key_id === false) {
3765
            return false;
3766
        }
3767
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3768
        $sql = "SELECT * FROM $t_api WHERE id = ".$key_id;
3769
        $res = Database::query($sql);
3770
        if ($res === false) {
3771
            return false;
3772
        } //error during query
3773
        $num = Database::num_rows($res);
3774
        if ($num !== 1) {
3775
            return false;
3776
        }
3777
        $sql = "DELETE FROM $t_api WHERE id = ".$key_id;
3778
        $res = Database::query($sql);
3779
        if ($res === false) {
3780
            return false;
3781
        } //error during query
3782
3783
        return true;
3784
    }
3785
3786
    /**
3787
     * Regenerate an API key from the user's account.
3788
     *
3789
     * @param   int     user ID (defaults to the results of api_get_user_id())
3790
     * @param   string  API key's internal ID
3791
     *
3792
     * @return int num
3793
     */
3794
    public static function update_api_key($user_id, $api_service)
3795
    {
3796
        if ($user_id != strval(intval($user_id))) {
3797
            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...
3798
        }
3799
        if ($user_id === false) {
3800
            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...
3801
        }
3802
        $service_name = Database::escape_string($api_service);
3803
        if (is_string($service_name) === false) {
3804
            return false;
3805
        }
3806
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3807
        $sql = "SELECT id FROM $t_api 
3808
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3809
        $res = Database::query($sql);
3810
        $num = Database::num_rows($res);
3811
        if ($num == 1) {
3812
            $id_key = Database::fetch_array($res, 'ASSOC');
3813
            self::delete_api_key($id_key['id']);
3814
            $num = self::add_api_key($user_id, $api_service);
3815
        } elseif ($num == 0) {
3816
            $num = self::add_api_key($user_id, $api_service);
3817
        }
3818
3819
        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...
3820
    }
3821
3822
    /**
3823
     * @param   int     user ID (defaults to the results of api_get_user_id())
3824
     * @param   string    API key's internal ID
3825
     *
3826
     * @return int row ID, or return false if not found
3827
     */
3828
    public static function get_api_key_id($user_id, $api_service)
3829
    {
3830
        if ($user_id != strval(intval($user_id))) {
3831
            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...
3832
        }
3833
        if ($user_id === false) {
3834
            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...
3835
        }
3836
        if (empty($api_service)) {
3837
            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...
3838
        }
3839
        $t_api = Database::get_main_table(TABLE_MAIN_USER_API_KEY);
3840
        $api_service = Database::escape_string($api_service);
3841
        $sql = "SELECT id FROM $t_api 
3842
                WHERE user_id=".$user_id." AND api_service='".$api_service."'";
3843
        $res = Database::query($sql);
3844
        if (Database::num_rows($res) < 1) {
3845
            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...
3846
        }
3847
        $row = Database::fetch_array($res, 'ASSOC');
3848
3849
        return $row['id'];
3850
    }
3851
3852
    /**
3853
     * Checks if a user_id is platform admin.
3854
     *
3855
     * @param   int user ID
3856
     *
3857
     * @return bool True if is admin, false otherwise
3858
     *
3859
     * @see main_api.lib.php::api_is_platform_admin() for a context-based check
3860
     */
3861
    public static function is_admin($user_id)
3862
    {
3863
        if (empty($user_id) || $user_id != strval(intval($user_id))) {
3864
            return false;
3865
        }
3866
        $admin_table = Database::get_main_table(TABLE_MAIN_ADMIN);
3867
        $sql = "SELECT * FROM $admin_table WHERE user_id = $user_id";
3868
        $res = Database::query($sql);
3869
3870
        return Database::num_rows($res) === 1;
3871
    }
3872
3873
    /**
3874
     * Get the total count of users.
3875
     *
3876
     * @param   int     Status of users to be counted
3877
     * @param   int     Access URL ID (optional)
3878
     *
3879
     * @return mixed Number of users or false on error
3880
     */
3881
    public static function get_number_of_users($status = 0, $access_url_id = 1)
3882
    {
3883
        $t_u = Database::get_main_table(TABLE_MAIN_USER);
3884
        $t_a = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
3885
3886
        if (api_is_multiple_url_enabled()) {
3887
            $sql = "SELECT count(u.id) 
3888
                    FROM $t_u u 
3889
                    INNER JOIN $t_a url_user
3890
                    ON (u.id = url_user.user_id)
3891
                    WHERE url_user.access_url_id = $access_url_id                
3892
            ";
3893
        } else {
3894
            $sql = "SELECT count(u.id) 
3895
                    FROM $t_u u
3896
                    WHERE 1 = 1 ";
3897
        }
3898
        if (is_int($status) && $status > 0) {
3899
            $sql .= " AND u.status = $status ";
3900
        }
3901
        $res = Database::query($sql);
3902
        if (Database::num_rows($res) === 1) {
3903
            return (int) Database::result($res, 0, 0);
3904
        }
3905
3906
        return false;
3907
    }
3908
3909
    /**
3910
     * Gets the tags of a specific field_id
3911
     * USER TAGS.
3912
     *
3913
     * Instructions to create a new user tag by Julio Montoya <[email protected]>
3914
     *
3915
     * 1. Create a new extra field in main/admin/user_fields.php with the "TAG" field type make it available and visible.
3916
     *    Called it "books" for example.
3917
     * 2. Go to profile main/auth/profile.php There you will see a special input (facebook style) that will show suggestions of tags.
3918
     * 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
3919
     * 4. Tags are independent this means that tags can't be shared between tags + book + hobbies.
3920
     * 5. Test and enjoy.
3921
     *
3922
     * @param string $tag
3923
     * @param int    $field_id      field_id
3924
     * @param string $return_format how we are going to result value in array or in a string (json)
3925
     * @param $limit
3926
     *
3927
     * @return mixed
3928
     */
3929
    public static function get_tags($tag, $field_id, $return_format = 'json', $limit = 10)
3930
    {
3931
        // database table definition
3932
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3933
        $field_id = (int) $field_id;
3934
        $limit = (int) $limit;
3935
        $tag = trim(Database::escape_string($tag));
3936
3937
        // all the information of the field
3938
        $sql = "SELECT DISTINCT id, tag from $table_user_tag
3939
                WHERE field_id = $field_id AND tag LIKE '$tag%' ORDER BY tag LIMIT $limit";
3940
        $result = Database::query($sql);
3941
        $return = [];
3942
        if (Database::num_rows($result) > 0) {
3943
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3944
                $return[] = ['id' => $row['tag'], 'text' => $row['tag']];
3945
            }
3946
        }
3947
        if ($return_format === 'json') {
3948
            $return = json_encode($return);
3949
        }
3950
3951
        return $return;
3952
    }
3953
3954
    /**
3955
     * @param int $field_id
3956
     * @param int $limit
3957
     *
3958
     * @return array
3959
     */
3960
    public static function get_top_tags($field_id, $limit = 100)
3961
    {
3962
        // database table definition
3963
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3964
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3965
        $field_id = (int) $field_id;
3966
        $limit = (int) $limit;
3967
        // all the information of the field
3968
        $sql = "SELECT count(*) count, tag FROM $table_user_tag_values  uv
3969
                INNER JOIN $table_user_tag ut
3970
                ON (ut.id = uv.tag_id)
3971
                WHERE field_id = $field_id
3972
                GROUP BY tag_id
3973
                ORDER BY count DESC
3974
                LIMIT $limit";
3975
        $result = Database::query($sql);
3976
        $return = [];
3977
        if (Database::num_rows($result) > 0) {
3978
            while ($row = Database::fetch_array($result, 'ASSOC')) {
3979
                $return[] = $row;
3980
            }
3981
        }
3982
3983
        return $return;
3984
    }
3985
3986
    /**
3987
     * Get user's tags.
3988
     *
3989
     * @param int $user_id
3990
     * @param int $field_id
3991
     *
3992
     * @return array
3993
     */
3994
    public static function get_user_tags($user_id, $field_id)
3995
    {
3996
        // database table definition
3997
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
3998
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
3999
        $field_id = (int) $field_id;
4000
        $user_id = (int) $user_id;
4001
4002
        // all the information of the field
4003
        $sql = "SELECT ut.id, tag, count
4004
                FROM $table_user_tag ut
4005
                INNER JOIN $table_user_tag_values uv
4006
                ON (uv.tag_id=ut.ID)
4007
                WHERE field_id = $field_id AND user_id = $user_id
4008
                ORDER BY tag";
4009
        $result = Database::query($sql);
4010
        $return = [];
4011
        if (Database::num_rows($result) > 0) {
4012
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4013
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4014
            }
4015
        }
4016
4017
        return $return;
4018
    }
4019
4020
    /**
4021
     * Get user's tags.
4022
     *
4023
     * @param int  $user_id
4024
     * @param int  $field_id
4025
     * @param bool $show_links show links or not
4026
     *
4027
     * @return string
4028
     */
4029
    public static function get_user_tags_to_string($user_id, $field_id, $show_links = true)
4030
    {
4031
        // database table definition
4032
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4033
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4034
        $field_id = (int) $field_id;
4035
        $user_id = (int) $user_id;
4036
4037
        // all the information of the field
4038
        $sql = "SELECT ut.id, tag,count FROM $table_user_tag ut
4039
                INNER JOIN $table_user_tag_values uv
4040
                ON (uv.tag_id = ut.id)
4041
                WHERE field_id = $field_id AND user_id = $user_id
4042
                ORDER BY tag";
4043
4044
        $result = Database::query($sql);
4045
        $return = [];
4046
        if (Database::num_rows($result) > 0) {
4047
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4048
                $return[$row['id']] = ['tag' => $row['tag'], 'count' => $row['count']];
4049
            }
4050
        }
4051
        $user_tags = $return;
4052
        $tag_tmp = [];
4053
        foreach ($user_tags as $tag) {
4054
            if ($show_links) {
4055
                $tag_tmp[] = '<a href="'.api_get_path(WEB_PATH).'main/search/index.php?q='.$tag['tag'].'">'.
4056
                    $tag['tag'].
4057
                '</a>';
4058
            } else {
4059
                $tag_tmp[] = $tag['tag'];
4060
            }
4061
        }
4062
4063
        if (is_array($user_tags) && count($user_tags) > 0) {
4064
            return implode(', ', $tag_tmp);
4065
        } else {
4066
            return '';
4067
        }
4068
    }
4069
4070
    /**
4071
     * Get the tag id.
4072
     *
4073
     * @param int $tag
4074
     * @param int $field_id
4075
     *
4076
     * @return int returns 0 if fails otherwise the tag id
4077
     */
4078
    public static function get_tag_id($tag, $field_id)
4079
    {
4080
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4081
        $tag = Database::escape_string($tag);
4082
        $field_id = (int) $field_id;
4083
        //with COLLATE latin1_bin to select query in a case sensitive mode
4084
        $sql = "SELECT id FROM $table_user_tag
4085
                WHERE tag LIKE '$tag' AND field_id = $field_id";
4086
        $result = Database::query($sql);
4087
        if (Database::num_rows($result) > 0) {
4088
            $row = Database::fetch_array($result, 'ASSOC');
4089
4090
            return $row['id'];
4091
        } else {
4092
            return 0;
4093
        }
4094
    }
4095
4096
    /**
4097
     * Get the tag id.
4098
     *
4099
     * @param int $tag_id
4100
     * @param int $field_id
4101
     *
4102
     * @return int 0 if fails otherwise the tag id
4103
     */
4104
    public static function get_tag_id_from_id($tag_id, $field_id)
4105
    {
4106
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4107
        $tag_id = (int) $tag_id;
4108
        $field_id = (int) $field_id;
4109
        $sql = "SELECT id FROM $table_user_tag
4110
                WHERE id = '$tag_id' AND field_id = $field_id";
4111
        $result = Database::query($sql);
4112
        if (Database::num_rows($result) > 0) {
4113
            $row = Database::fetch_array($result, 'ASSOC');
4114
4115
            return $row['id'];
4116
        } else {
4117
            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...
4118
        }
4119
    }
4120
4121
    /**
4122
     * Adds a user-tag value.
4123
     *
4124
     * @param mixed $tag
4125
     * @param int   $user_id
4126
     * @param int   $field_id field id of the tag
4127
     *
4128
     * @return bool True if the tag was inserted or updated. False otherwise.
4129
     *              The return value doesn't take into account *values* added to the tag.
4130
     *              Only the creation/update of the tag field itself.
4131
     */
4132
    public static function add_tag($tag, $user_id, $field_id)
4133
    {
4134
        // database table definition
4135
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4136
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4137
        $tag = trim(Database::escape_string($tag));
4138
        $user_id = (int) $user_id;
4139
        $field_id = (int) $field_id;
4140
4141
        $tag_id = self::get_tag_id($tag, $field_id);
4142
4143
        /* IMPORTANT
4144
         *  @todo we don't create tags with numbers
4145
         *
4146
         */
4147
        if (is_numeric($tag)) {
4148
            //the form is sending an id this means that the user select it from the list so it MUST exists
4149
            /* $new_tag_id = self::get_tag_id_from_id($tag,$field_id);
4150
              if ($new_tag_id !== false) {
4151
              $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $new_tag_id";
4152
              $result = Database::query($sql);
4153
              $last_insert_id = $new_tag_id;
4154
              } else {
4155
              $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4156
              $result = Database::query($sql);
4157
              $last_insert_id = Database::insert_id();
4158
              } */
4159
        }
4160
4161
        //this is a new tag
4162
        if ($tag_id == 0) {
4163
            //the tag doesn't exist
4164
            $sql = "INSERT INTO $table_user_tag (tag, field_id,count) VALUES ('$tag','$field_id', count + 1)";
4165
            Database::query($sql);
4166
            $last_insert_id = Database::insert_id();
4167
        } else {
4168
            //the tag exists we update it
4169
            $sql = "UPDATE $table_user_tag SET count = count + 1 WHERE id  = $tag_id";
4170
            Database::query($sql);
4171
            $last_insert_id = $tag_id;
4172
        }
4173
4174
        if (!empty($last_insert_id) && ($last_insert_id != 0)) {
4175
            //we insert the relationship user-tag
4176
            $sql = "SELECT tag_id FROM $table_user_tag_values
4177
                    WHERE user_id = $user_id AND tag_id = $last_insert_id ";
4178
            $result = Database::query($sql);
4179
            //if the relationship does not exist we create it
4180
            if (Database::num_rows($result) == 0) {
4181
                $sql = "INSERT INTO $table_user_tag_values SET user_id = $user_id, tag_id = $last_insert_id";
4182
                Database::query($sql);
4183
            }
4184
4185
            return true;
4186
        }
4187
4188
        return false;
4189
    }
4190
4191
    /**
4192
     * Deletes an user tag.
4193
     *
4194
     * @param int $user_id
4195
     * @param int $field_id
4196
     */
4197
    public static function delete_user_tags($user_id, $field_id)
4198
    {
4199
        // database table definition
4200
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4201
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4202
        $tags = self::get_user_tags($user_id, $field_id);
4203
        if (is_array($tags) && count($tags) > 0) {
4204
            foreach ($tags as $key => $tag) {
4205
                if ($tag['count'] > '0') {
4206
                    $sql = "UPDATE $table_user_tag SET count = count - 1  WHERE id = $key ";
4207
                    Database::query($sql);
4208
                }
4209
                $sql = "DELETE FROM $table_user_tag_values
4210
                        WHERE user_id = $user_id AND tag_id = $key";
4211
                Database::query($sql);
4212
            }
4213
        }
4214
    }
4215
4216
    /**
4217
     * Process the tag list comes from the UserManager::update_extra_field_value() function.
4218
     *
4219
     * @param array $tags     the tag list that will be added
4220
     * @param int   $user_id
4221
     * @param int   $field_id
4222
     *
4223
     * @return bool
4224
     */
4225
    public static function process_tags($tags, $user_id, $field_id)
4226
    {
4227
        // We loop the tags and add it to the DB
4228
        if (is_array($tags)) {
4229
            foreach ($tags as $tag) {
4230
                self::add_tag($tag, $user_id, $field_id);
4231
            }
4232
        } else {
4233
            self::add_tag($tags, $user_id, $field_id);
4234
        }
4235
4236
        return true;
4237
    }
4238
4239
    /**
4240
     * Returns a list of all administrators.
4241
     *
4242
     * @return array
4243
     */
4244
    public static function get_all_administrators()
4245
    {
4246
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
4247
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
4248
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4249
        $access_url_id = api_get_current_access_url_id();
4250
        if (api_get_multiple_access_url()) {
4251
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4252
                    FROM $tbl_url_rel_user as url
4253
                    INNER JOIN $table_admin as admin
4254
                    ON (admin.user_id=url.user_id)
4255
                    INNER JOIN $table_user u
4256
                    ON (u.id=admin.user_id)
4257
                    WHERE access_url_id ='".$access_url_id."'";
4258
        } else {
4259
            $sql = "SELECT admin.user_id, username, firstname, lastname, email, active
4260
                    FROM $table_admin as admin
4261
                    INNER JOIN $table_user u
4262
                    ON (u.id=admin.user_id)";
4263
        }
4264
        $result = Database::query($sql);
4265
        $return = [];
4266
        if (Database::num_rows($result) > 0) {
4267
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4268
                $return[$row['user_id']] = $row;
4269
            }
4270
        }
4271
4272
        return $return;
4273
    }
4274
4275
    /**
4276
     * Search an user (tags, first name, last name and email ).
4277
     *
4278
     * @param string $tag
4279
     * @param int    $field_id        field id of the tag
4280
     * @param int    $from            where to start in the query
4281
     * @param int    $number_of_items
4282
     * @param bool   $getCount        get count or not
4283
     *
4284
     * @return array
4285
     */
4286
    public static function get_all_user_tags(
4287
        $tag,
4288
        $field_id = 0,
4289
        $from = 0,
4290
        $number_of_items = 10,
4291
        $getCount = false
4292
    ) {
4293
        $user_table = Database::get_main_table(TABLE_MAIN_USER);
4294
        $table_user_tag = Database::get_main_table(TABLE_MAIN_TAG);
4295
        $table_user_tag_values = Database::get_main_table(TABLE_MAIN_USER_REL_TAG);
4296
        $access_url_rel_user_table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4297
4298
        $field_id = intval($field_id);
4299
        $from = intval($from);
4300
        $number_of_items = intval($number_of_items);
4301
4302
        $where_field = "";
4303
        $where_extra_fields = self::get_search_form_where_extra_fields();
4304
        if ($field_id != 0) {
4305
            $where_field = " field_id = $field_id AND ";
4306
        }
4307
4308
        // all the information of the field
4309
        if ($getCount) {
4310
            $select = "SELECT count(DISTINCT u.id) count";
4311
        } else {
4312
            $select = "SELECT DISTINCT u.id, u.username, firstname, lastname, email, tag, picture_uri";
4313
        }
4314
4315
        $sql = " $select
4316
                FROM $user_table u
4317
                INNER JOIN $access_url_rel_user_table url_rel_user
4318
                ON (u.id = url_rel_user.user_id)
4319
                LEFT JOIN $table_user_tag_values uv
4320
                ON (u.id AND uv.user_id AND uv.user_id = url_rel_user.user_id)
4321
                LEFT JOIN $table_user_tag ut ON (uv.tag_id = ut.id)
4322
                WHERE
4323
                    ($where_field tag LIKE '".Database::escape_string($tag."%")."') OR
4324
                    (
4325
                        u.firstname LIKE '".Database::escape_string("%".$tag."%")."' OR
4326
                        u.lastname LIKE '".Database::escape_string("%".$tag."%")."' OR
4327
                        u.username LIKE '".Database::escape_string("%".$tag."%")."' OR
4328
                        concat(u.firstname, ' ', u.lastname) LIKE '".Database::escape_string("%".$tag."%")."' OR
4329
                        concat(u.lastname, ' ', u.firstname) LIKE '".Database::escape_string("%".$tag."%")."'
4330
                     )
4331
                     ".(!empty($where_extra_fields) ? $where_extra_fields : '')."
4332
                     AND url_rel_user.access_url_id=".api_get_current_access_url_id();
4333
4334
        $keyword_active = true;
4335
        // only active users
4336
        if ($keyword_active) {
4337
            $sql .= " AND u.active='1'";
4338
        }
4339
        // avoid anonymous
4340
        $sql .= " AND u.status <> 6 ";
4341
        $sql .= " ORDER BY username";
4342
        $sql .= " LIMIT $from , $number_of_items";
4343
4344
        $result = Database::query($sql);
4345
        $return = [];
4346
4347
        if (Database::num_rows($result) > 0) {
4348
            if ($getCount) {
4349
                $row = Database::fetch_array($result, 'ASSOC');
4350
4351
                return $row['count'];
4352
            }
4353
            while ($row = Database::fetch_array($result, 'ASSOC')) {
4354
                if (isset($return[$row['id']]) &&
4355
                    !empty($return[$row['id']]['tag'])
4356
                ) {
4357
                    $url = Display::url(
4358
                        $row['tag'],
4359
                        api_get_path(WEB_PATH).'main/social/search.php?q='.$row['tag'],
4360
                        ['class' => 'tag']
4361
                    );
4362
                    $row['tag'] = $url;
4363
                }
4364
                $return[$row['id']] = $row;
4365
            }
4366
        }
4367
4368
        return $return;
4369
    }
4370
4371
    /**
4372
     * Get extra filterable user fields (only type select).
4373
     *
4374
     * @return array Array of extra fields as [int => ['name' => ..., 'variable' => ..., 'data' => ...]] (
4375
     *               or empty array if no extra field)
4376
     */
4377
    public static function getExtraFilterableFields()
4378
    {
4379
        $extraFieldList = self::get_extra_fields();
4380
        $fields = [];
4381
        if (is_array($extraFieldList)) {
4382
            foreach ($extraFieldList as $extraField) {
4383
                // If is enabled to filter and is a "<select>" field type
4384
                if ($extraField[8] == 1 && $extraField[2] == 4) {
4385
                    $fields[] = [
4386
                        'name' => $extraField[3],
4387
                        'variable' => $extraField[1],
4388
                        'data' => $extraField[9],
4389
                    ];
4390
                }
4391
            }
4392
        }
4393
4394
        return $fields;
4395
    }
4396
4397
    /**
4398
     * Get extra where clauses for finding users based on extra filterable user fields (type select).
4399
     *
4400
     * @return string With AND clauses based on user's ID which have the values to search in extra user fields
4401
     *                (or empty if no extra field exists)
4402
     */
4403
    public static function get_search_form_where_extra_fields()
4404
    {
4405
        $useExtraFields = false;
4406
        $extraFields = self::getExtraFilterableFields();
4407
        $extraFieldResult = [];
4408
        if (is_array($extraFields) && count($extraFields) > 0) {
4409
            foreach ($extraFields as $extraField) {
4410
                $varName = 'field_'.$extraField['variable'];
4411
                if (self::is_extra_field_available($extraField['variable'])) {
4412
                    if (isset($_GET[$varName]) && $_GET[$varName] != '0') {
4413
                        $useExtraFields = true;
4414
                        $extraFieldResult[] = self::get_extra_user_data_by_value(
4415
                            $extraField['variable'],
4416
                            $_GET[$varName]
4417
                        );
4418
                    }
4419
                }
4420
            }
4421
        }
4422
4423
        if ($useExtraFields) {
4424
            $finalResult = [];
4425
            if (count($extraFieldResult) > 1) {
4426
                for ($i = 0; $i < count($extraFieldResult) - 1; $i++) {
4427
                    if (is_array($extraFieldResult[$i]) && is_array($extraFieldResult[$i + 1])) {
4428
                        $finalResult = array_intersect($extraFieldResult[$i], $extraFieldResult[$i + 1]);
4429
                    }
4430
                }
4431
            } else {
4432
                $finalResult = $extraFieldResult[0];
4433
            }
4434
4435
            if (is_array($finalResult) && count($finalResult) > 0) {
4436
                $whereFilter = " AND u.id IN  ('".implode("','", $finalResult)."') ";
4437
            } else {
4438
                //no results
4439
                $whereFilter = " AND u.id  = -1 ";
4440
            }
4441
4442
            return $whereFilter;
4443
        }
4444
4445
        return '';
4446
    }
4447
4448
    /**
4449
     * Show the search form.
4450
     *
4451
     * @param string $query the value of the search box
4452
     *
4453
     * @throws Exception
4454
     *
4455
     * @return string HTML form
4456
     */
4457
    public static function get_search_form($query, $defaultParams = [])
4458
    {
4459
        $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : null;
4460
        $form = new FormValidator(
4461
            'search_user',
4462
            'get',
4463
            api_get_path(WEB_PATH).'main/social/search.php',
4464
            '',
4465
            [],
4466
            FormValidator::LAYOUT_HORIZONTAL
4467
        );
4468
4469
        $query = Security::remove_XSS($query);
4470
4471
        if (!empty($query)) {
4472
            $form->addHeader(get_lang('Results').' "'.$query.'"');
4473
        }
4474
4475
        $form->addText(
4476
            'q',
4477
            get_lang('UsersGroups'),
4478
            false,
4479
            [
4480
                'id' => 'q',
4481
            ]
4482
        );
4483
        $options = [
4484
            0 => get_lang('Select'),
4485
            1 => get_lang('User'),
4486
            2 => get_lang('Group'),
4487
        ];
4488
        $form->addSelect(
4489
            'search_type',
4490
            get_lang('Type'),
4491
            $options,
4492
            ['onchange' => 'javascript: extra_field_toogle();', 'id' => 'search_type']
4493
        );
4494
4495
        // Extra fields
4496
        $extraFields = self::getExtraFilterableFields();
4497
        $defaults = [];
4498
        if (is_array($extraFields) && count($extraFields) > 0) {
4499
            foreach ($extraFields as $extraField) {
4500
                $varName = 'field_'.$extraField['variable'];
4501
                $options = [
4502
                    0 => get_lang('Select'),
4503
                ];
4504
                foreach ($extraField['data'] as $option) {
4505
                    $checked = '';
4506
                    if (isset($_GET[$varName])) {
4507
                        if ($_GET[$varName] == $option[1]) {
4508
                            $defaults[$option[1]] = true;
4509
                        }
4510
                    }
4511
4512
                    $options[$option[1]] = $option[1];
4513
                }
4514
                $form->addSelect($varName, $extraField['name'], $options);
4515
            }
4516
        }
4517
4518
        $defaults['search_type'] = (int) $searchType;
4519
        $defaults['q'] = $query;
4520
4521
        if (!empty($defaultParams)) {
4522
            $defaults = array_merge($defaults, $defaultParams);
4523
        }
4524
        $form->setDefaults($defaults);
4525
        $form->addButtonSearch(get_lang('Search'));
4526
4527
        $js = '<script>
4528
        extra_field_toogle();
4529
        function extra_field_toogle() {
4530
            if (jQuery("select[name=search_type]").val() != "1") { 
4531
                jQuery(".extra_field").hide(); 
4532
            } else { 
4533
                jQuery(".extra_field").show(); 
4534
            }
4535
        }
4536
        </script>';
4537
4538
        return $js.$form->returnForm();
4539
    }
4540
4541
    /**
4542
     * Shows the user menu.
4543
     */
4544
    public static function show_menu()
4545
    {
4546
        echo '<div class="actions">';
4547
        echo '<a href="/main/auth/profile.php">'.
4548
            Display::return_icon('profile.png').' '.get_lang('PersonalData').'</a>';
4549
        echo '<a href="/main/messages/inbox.php">'.
4550
            Display::return_icon('inbox.png').' '.get_lang('Inbox').'</a>';
4551
        echo '<a href="/main/messages/outbox.php">'.
4552
            Display::return_icon('outbox.png').' '.get_lang('Outbox').'</a>';
4553
        echo '<span style="float:right; padding-top:7px;">'.
4554
        '<a href="/main/auth/profile.php?show=1">'.
4555
            Display::return_icon('edit.gif').' '.get_lang('Configuration').'</a>';
4556
        echo '</span>';
4557
        echo '</div>';
4558
    }
4559
4560
    /**
4561
     * Allow to register contact to social network.
4562
     *
4563
     * @param int $friend_id     user friend id
4564
     * @param int $my_user_id    user id
4565
     * @param int $relation_type relation between users see constants definition
4566
     *
4567
     * @return bool
4568
     */
4569
    public static function relate_users($friend_id, $my_user_id, $relation_type)
4570
    {
4571
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4572
4573
        $friend_id = (int) $friend_id;
4574
        $my_user_id = (int) $my_user_id;
4575
        $relation_type = (int) $relation_type;
4576
4577
        $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4578
                WHERE
4579
                    friend_user_id='.$friend_id.' AND
4580
                    user_id='.$my_user_id.' AND
4581
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4582
        $result = Database::query($sql);
4583
        $row = Database::fetch_array($result, 'ASSOC');
4584
        $current_date = api_get_utc_datetime();
4585
4586
        if ($row['count'] == 0) {
4587
            $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4588
                    VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4589
            Database::query($sql);
4590
4591
            return true;
4592
        }
4593
4594
        $sql = 'SELECT COUNT(*) as count, relation_type  FROM '.$tbl_my_friend.'
4595
                WHERE
4596
                    friend_user_id='.$friend_id.' AND
4597
                    user_id='.$my_user_id.' AND
4598
                    relation_type NOT IN('.USER_RELATION_TYPE_RRHH.', '.USER_RELATION_TYPE_BOSS.') ';
4599
        $result = Database::query($sql);
4600
        $row = Database::fetch_array($result, 'ASSOC');
4601
4602
        if ($row['count'] == 1) {
4603
            //only for the case of a RRHH or a Student BOSS
4604
            if ($row['relation_type'] != $relation_type &&
4605
                ($relation_type == USER_RELATION_TYPE_RRHH || $relation_type == USER_RELATION_TYPE_BOSS)
4606
            ) {
4607
                $sql = 'INSERT INTO '.$tbl_my_friend.'(friend_user_id,user_id,relation_type,last_edit)
4608
                        VALUES ('.$friend_id.','.$my_user_id.','.$relation_type.',"'.$current_date.'")';
4609
            } else {
4610
                $sql = 'UPDATE '.$tbl_my_friend.' SET relation_type='.$relation_type.'
4611
                        WHERE friend_user_id='.$friend_id.' AND user_id='.$my_user_id;
4612
            }
4613
            Database::query($sql);
4614
4615
            return true;
4616
        }
4617
4618
        return false;
4619
    }
4620
4621
    /**
4622
     * Deletes a contact.
4623
     *
4624
     * @param bool   $friend_id
4625
     * @param bool   $real_removed          true will delete ALL friends relationship
4626
     * @param string $with_status_condition
4627
     *
4628
     * @author isaac flores paz <[email protected]>
4629
     * @author Julio Montoya <[email protected]> Cleaning code
4630
     */
4631
    public static function remove_user_rel_user(
4632
        $friend_id,
4633
        $real_removed = false,
4634
        $with_status_condition = ''
4635
    ) {
4636
        $tbl_my_friend = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4637
        $tbl_my_message = Database::get_main_table(TABLE_MESSAGE);
4638
        $friend_id = (int) $friend_id;
4639
        $user_id = api_get_user_id();
4640
4641
        if ($real_removed) {
4642
            $extra_condition = '';
4643
            if ($with_status_condition != '') {
4644
                $extra_condition = ' AND relation_type = '.intval($with_status_condition);
4645
            }
4646
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4647
                    WHERE 
4648
                        relation_type <> '.USER_RELATION_TYPE_RRHH.' AND 
4649
                        friend_user_id='.$friend_id.' '.$extra_condition;
4650
            Database::query($sql);
4651
            $sql = 'DELETE FROM '.$tbl_my_friend.'
4652
                   WHERE 
4653
                    relation_type <> '.USER_RELATION_TYPE_RRHH.' AND 
4654
                    user_id='.$friend_id.' '.$extra_condition;
4655
            Database::query($sql);
4656
        } else {
4657
            $sql = 'SELECT COUNT(*) as count FROM '.$tbl_my_friend.'
4658
                    WHERE
4659
                        user_id='.$user_id.' AND
4660
                        relation_type NOT IN('.USER_RELATION_TYPE_DELETED.', '.USER_RELATION_TYPE_RRHH.') AND
4661
                        friend_user_id='.$friend_id;
4662
            $result = Database::query($sql);
4663
            $row = Database::fetch_array($result, 'ASSOC');
4664
            if ($row['count'] == 1) {
4665
                //Delete user rel user
4666
                $sql_i = 'UPDATE '.$tbl_my_friend.' SET relation_type='.USER_RELATION_TYPE_DELETED.'
4667
                          WHERE user_id='.$user_id.' AND friend_user_id='.$friend_id;
4668
4669
                $sql_j = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4670
                          WHERE 
4671
                                user_receiver_id='.$user_id.' AND 
4672
                                user_sender_id='.$friend_id.' AND update_date="0000-00-00 00:00:00" ';
4673
                //Delete user
4674
                $sql_ij = 'UPDATE '.$tbl_my_friend.'  SET relation_type='.USER_RELATION_TYPE_DELETED.'
4675
                           WHERE user_id='.$friend_id.' AND friend_user_id='.$user_id;
4676
                $sql_ji = 'UPDATE '.$tbl_my_message.' SET msg_status='.MESSAGE_STATUS_INVITATION_DENIED.'
4677
                           WHERE 
4678
                                user_receiver_id='.$friend_id.' AND 
4679
                                user_sender_id='.$user_id.' AND 
4680
                                update_date="0000-00-00 00:00:00" ';
4681
                Database::query($sql_i);
4682
                Database::query($sql_j);
4683
                Database::query($sql_ij);
4684
                Database::query($sql_ji);
4685
            }
4686
        }
4687
4688
        // Delete accepted invitations
4689
        $sql = "DELETE FROM $tbl_my_message 
4690
                WHERE
4691
                    msg_status = ".MESSAGE_STATUS_INVITATION_ACCEPTED." AND
4692
                    (
4693
                        user_receiver_id = $user_id AND 
4694
                        user_sender_id = $friend_id
4695
                    ) OR 
4696
                    (
4697
                        user_sender_id = $user_id AND 
4698
                        user_receiver_id = $friend_id
4699
                    )
4700
        ";
4701
        Database::query($sql);
4702
    }
4703
4704
    /**
4705
     * @param int $userId
4706
     *
4707
     * @return array
4708
     */
4709
    public static function getDrhListFromUser($userId)
4710
    {
4711
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
4712
        $tblUserRelUser = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4713
        $tblUserRelAccessUrl = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4714
        $userId = (int) $userId;
4715
4716
        $orderBy = null;
4717
        if (api_is_western_name_order()) {
4718
            $orderBy .= ' ORDER BY firstname, lastname ';
4719
        } else {
4720
            $orderBy .= ' ORDER BY lastname, firstname ';
4721
        }
4722
4723
        $sql = "SELECT u.id, username, u.firstname, u.lastname
4724
                FROM $tblUser u
4725
                INNER JOIN $tblUserRelUser uru ON (uru.friend_user_id = u.id)
4726
                INNER JOIN $tblUserRelAccessUrl a ON (a.user_id = u.id)
4727
                WHERE
4728
                    access_url_id = ".api_get_current_access_url_id()." AND
4729
                    uru.user_id = '$userId' AND
4730
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4731
                $orderBy
4732
                ";
4733
        $result = Database::query($sql);
4734
4735
        return Database::store_result($result);
4736
    }
4737
4738
    /**
4739
     * get users followed by human resource manager.
4740
     *
4741
     * @param int    $userId
4742
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
4743
     * @param bool   $getOnlyUserId
4744
     * @param bool   $getSql
4745
     * @param bool   $getCount
4746
     * @param int    $from
4747
     * @param int    $numberItems
4748
     * @param int    $column
4749
     * @param string $direction
4750
     * @param int    $active
4751
     * @param string $lastConnectionDate
4752
     *
4753
     * @return array users
4754
     */
4755
    public static function get_users_followed_by_drh(
4756
        $userId,
4757
        $userStatus = 0,
4758
        $getOnlyUserId = false,
4759
        $getSql = false,
4760
        $getCount = false,
4761
        $from = null,
4762
        $numberItems = null,
4763
        $column = null,
4764
        $direction = null,
4765
        $active = null,
4766
        $lastConnectionDate = null
4767
    ) {
4768
        return self::getUsersFollowedByUser(
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::getUsersFol...astConnectionDate, DRH) also could return the type string which is incompatible with the documented return type array.
Loading history...
4769
            $userId,
4770
            $userStatus,
4771
            $getOnlyUserId,
4772
            $getSql,
4773
            $getCount,
4774
            $from,
4775
            $numberItems,
4776
            $column,
4777
            $direction,
4778
            $active,
4779
            $lastConnectionDate,
4780
            DRH
4781
        );
4782
    }
4783
4784
    /**
4785
     * Get users followed by human resource manager.
4786
     *
4787
     * @param int    $userId
4788
     * @param int    $userStatus         Filter users by status (STUDENT, COURSEMANAGER, etc)
4789
     * @param bool   $getOnlyUserId
4790
     * @param bool   $getSql
4791
     * @param bool   $getCount
4792
     * @param int    $from
4793
     * @param int    $numberItems
4794
     * @param int    $column
4795
     * @param string $direction
4796
     * @param int    $active
4797
     * @param string $lastConnectionDate
4798
     * @param int    $status             the function is called by who? COURSEMANAGER, DRH?
4799
     * @param string $keyword
4800
     *
4801
     * @return mixed Users list (array) or the SQL query if $getSQL was set to true
4802
     */
4803
    public static function getUsersFollowedByUser(
4804
        $userId,
4805
        $userStatus = null,
4806
        $getOnlyUserId = false,
4807
        $getSql = false,
4808
        $getCount = false,
4809
        $from = null,
4810
        $numberItems = null,
4811
        $column = null,
4812
        $direction = null,
4813
        $active = null,
4814
        $lastConnectionDate = null,
4815
        $status = null,
4816
        $keyword = null
4817
    ) {
4818
        // Database Table Definitions
4819
        $tbl_user = Database::get_main_table(TABLE_MAIN_USER);
4820
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
4821
        $tbl_user_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
4822
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4823
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4824
        $tbl_session_rel_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4825
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4826
        $tbl_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4827
4828
        $userId = (int) $userId;
4829
        $limitCondition = '';
4830
4831
        if (isset($from) && isset($numberItems)) {
4832
            $from = (int) $from;
4833
            $numberItems = (int) $numberItems;
4834
            $limitCondition = "LIMIT $from, $numberItems";
4835
        }
4836
4837
        $column = Database::escape_string($column);
4838
        $direction = in_array(strtolower($direction), ['asc', 'desc']) ? $direction : null;
4839
4840
        $userConditions = '';
4841
        if (!empty($userStatus)) {
4842
            $userConditions .= ' AND u.status = '.intval($userStatus);
4843
        }
4844
4845
        $select = " SELECT DISTINCT u.id user_id, u.username, u.lastname, u.firstname, u.email ";
4846
        if ($getOnlyUserId) {
4847
            $select = " SELECT DISTINCT u.id user_id";
4848
        }
4849
4850
        $masterSelect = "SELECT DISTINCT * FROM ";
4851
4852
        if ($getCount) {
4853
            $masterSelect = "SELECT COUNT(DISTINCT(user_id)) as count FROM ";
4854
            $select = " SELECT DISTINCT(u.id) user_id";
4855
        }
4856
4857
        if (!is_null($active)) {
4858
            $active = intval($active);
4859
            $userConditions .= " AND u.active = $active ";
4860
        }
4861
4862
        if (!empty($keyword)) {
4863
            $keyword = Database::escape_string($keyword);
4864
            $userConditions .= " AND (
4865
                u.username LIKE '%$keyword%' OR
4866
                u.firstname LIKE '%$keyword%' OR
4867
                u.lastname LIKE '%$keyword%' OR
4868
                u.official_code LIKE '%$keyword%' OR
4869
                u.email LIKE '%$keyword%'
4870
            )";
4871
        }
4872
4873
        if (!empty($lastConnectionDate)) {
4874
            $lastConnectionDate = Database::escape_string($lastConnectionDate);
4875
            $userConditions .= " AND u.last_login <= '$lastConnectionDate' ";
4876
        }
4877
4878
        $courseConditions = null;
4879
        $sessionConditionsCoach = null;
4880
        $sessionConditionsTeacher = null;
4881
        $drhConditions = null;
4882
        $teacherSelect = null;
4883
4884
        switch ($status) {
4885
            case DRH:
4886
                $drhConditions .= " AND
4887
                    friend_user_id = '$userId' AND
4888
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4889
                ";
4890
                break;
4891
            case COURSEMANAGER:
4892
                $drhConditions .= " AND
4893
                    friend_user_id = '$userId' AND
4894
                    relation_type = '".USER_RELATION_TYPE_RRHH."'
4895
                ";
4896
4897
                $sessionConditionsCoach .= " AND
4898
                    (s.id_coach = '$userId')
4899
                ";
4900
4901
                $sessionConditionsTeacher .= " AND
4902
                    (scu.status = 2 AND scu.user_id = '$userId')
4903
                ";
4904
4905
                $teacherSelect =
4906
                "UNION ALL (
4907
                        $select
4908
                        FROM $tbl_user u
4909
                        INNER JOIN $tbl_session_rel_user sru ON (sru.user_id = u.id)
4910
                        WHERE
4911
                            (
4912
                                sru.session_id IN (
4913
                                    SELECT DISTINCT(s.id) FROM $tbl_session s INNER JOIN
4914
                                    $tbl_session_rel_access_url session_rel_access_rel_user
4915
                                    ON session_rel_access_rel_user.session_id = s.id
4916
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4917
                                    $sessionConditionsCoach                                  
4918
                                ) OR sru.session_id IN (
4919
                                    SELECT DISTINCT(s.id) FROM $tbl_session s
4920
                                    INNER JOIN $tbl_session_rel_access_url url
4921
                                    ON (url.session_id = s.id)
4922
                                    INNER JOIN $tbl_session_rel_course_rel_user scu
4923
                                    ON (scu.session_id = s.id)
4924
                                    WHERE access_url_id = ".api_get_current_access_url_id()."
4925
                                    $sessionConditionsTeacher
4926
                                )
4927
                            )                            
4928
                            $userConditions
4929
                    )
4930
                    UNION ALL(
4931
                        $select
4932
                        FROM $tbl_user u
4933
                        INNER JOIN $tbl_course_user cu ON (cu.user_id = u.id)
4934
                        WHERE cu.c_id IN (
4935
                            SELECT DISTINCT(c_id) FROM $tbl_course_user
4936
                            WHERE user_id = $userId AND status = ".COURSEMANAGER."
4937
                        )
4938
                        $userConditions
4939
                    )"
4940
                ;
4941
                break;
4942
            case STUDENT_BOSS:
4943
                $drhConditions = " AND friend_user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
4944
                break;
4945
            case HRM_REQUEST:
4946
                $drhConditions .= " AND
4947
                    friend_user_id = '$userId' AND
4948
                    relation_type = '".USER_RELATION_TYPE_HRM_REQUEST."'
4949
                ";
4950
                break;
4951
        }
4952
4953
        $join = null;
4954
        $sql = " $masterSelect
4955
                (
4956
                    (
4957
                        $select
4958
                        FROM $tbl_user u
4959
                        INNER JOIN $tbl_user_rel_user uru ON (uru.user_id = u.id)
4960
                        LEFT JOIN $tbl_user_rel_access_url a ON (a.user_id = u.id)
4961
                        $join
4962
                        WHERE
4963
                            access_url_id = ".api_get_current_access_url_id()."
4964
                            $drhConditions
4965
                            $userConditions
4966
                    )
4967
                    $teacherSelect
4968
4969
                ) as t1";
4970
4971
        if ($getSql) {
4972
            return $sql;
4973
        }
4974
        if ($getCount) {
4975
            $result = Database::query($sql);
4976
            $row = Database::fetch_array($result);
4977
4978
            return $row['count'];
4979
        }
4980
4981
        $orderBy = null;
4982
        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...
4983
            if (api_is_western_name_order()) {
4984
                $orderBy .= " ORDER BY firstname, lastname ";
4985
            } else {
4986
                $orderBy .= " ORDER BY lastname, firstname ";
4987
            }
4988
4989
            if (!empty($column) && !empty($direction)) {
4990
                // Fixing order due the UNIONs
4991
                $column = str_replace('u.', '', $column);
4992
                $orderBy = " ORDER BY $column $direction ";
4993
            }
4994
        }
4995
4996
        $sql .= $orderBy;
4997
        $sql .= $limitCondition;
4998
4999
        $result = Database::query($sql);
5000
        $users = [];
5001
        if (Database::num_rows($result) > 0) {
5002
            while ($row = Database::fetch_array($result)) {
5003
                $users[$row['user_id']] = $row;
5004
            }
5005
        }
5006
5007
        return $users;
5008
    }
5009
5010
    /**
5011
     * Subscribes users to human resource manager (Dashboard feature).
5012
     *
5013
     * @param int   $hr_dept_id
5014
     * @param array $users_id
5015
     * @param bool  $deleteOtherAssignedUsers
5016
     *
5017
     * @return int
5018
     */
5019
    public static function subscribeUsersToHRManager(
5020
        $hr_dept_id,
5021
        $users_id,
5022
        $deleteOtherAssignedUsers = true
5023
    ) {
5024
        return self::subscribeUsersToUser(
5025
            $hr_dept_id,
5026
            $users_id,
5027
            USER_RELATION_TYPE_RRHH,
5028
            false,
5029
            $deleteOtherAssignedUsers
5030
        );
5031
    }
5032
5033
    /**
5034
     * Register request to assign users to HRM.
5035
     *
5036
     * @param int   $hrmId   The HRM ID
5037
     * @param array $usersId The users IDs
5038
     *
5039
     * @return int
5040
     */
5041
    public static function requestUsersToHRManager($hrmId, $usersId)
5042
    {
5043
        return self::subscribeUsersToUser(
5044
            $hrmId,
5045
            $usersId,
5046
            USER_RELATION_TYPE_HRM_REQUEST,
5047
            false,
5048
            false
5049
        );
5050
    }
5051
5052
    /**
5053
     * Remove the requests for assign a user to a HRM.
5054
     *
5055
     * @param User  $hrmId
5056
     * @param array $usersId List of user IDs from whom to remove all relations requests with HRM
5057
     */
5058
    public static function clearHrmRequestsForUser(User $hrmId, $usersId)
5059
    {
5060
        $users = implode(', ', $usersId);
5061
        Database::getManager()
5062
            ->createQuery('
5063
                DELETE FROM ChamiloCoreBundle:UserRelUser uru
5064
                WHERE uru.friendUserId = :hrm_id AND uru.relationType = :relation_type AND uru.userId IN (:users_ids)
5065
            ')
5066
            ->execute(['hrm_id' => $hrmId, 'relation_type' => USER_RELATION_TYPE_HRM_REQUEST, 'users_ids' => $users]);
5067
    }
5068
5069
    /**
5070
     * Add subscribed users to a user by relation type.
5071
     *
5072
     * @param int    $userId                   The user id
5073
     * @param array  $subscribedUsersId        The id of subscribed users
5074
     * @param string $relationType             The relation type
5075
     * @param bool   $deleteUsersBeforeInsert
5076
     * @param bool   $deleteOtherAssignedUsers
5077
     *
5078
     * @return int
5079
     */
5080
    public static function subscribeUsersToUser(
5081
        $userId,
5082
        $subscribedUsersId,
5083
        $relationType,
5084
        $deleteUsersBeforeInsert = false,
5085
        $deleteOtherAssignedUsers = true
5086
    ) {
5087
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5088
        $userRelAccessUrlTable = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
5089
5090
        $userId = intval($userId);
5091
        $relationType = intval($relationType);
5092
        $affectedRows = 0;
5093
5094
        if ($deleteOtherAssignedUsers) {
5095
            if (api_get_multiple_access_url()) {
5096
                // Deleting assigned users to hrm_id
5097
                $sql = "SELECT s.user_id 
5098
                        FROM $userRelUserTable s 
5099
                        INNER JOIN $userRelAccessUrlTable a
5100
                        ON (a.user_id = s.user_id) 
5101
                        WHERE 
5102
                            friend_user_id = $userId AND 
5103
                            relation_type = $relationType AND 
5104
                            access_url_id = ".api_get_current_access_url_id();
5105
            } else {
5106
                $sql = "SELECT user_id 
5107
                        FROM $userRelUserTable 
5108
                        WHERE 
5109
                            friend_user_id = $userId AND 
5110
                            relation_type = $relationType";
5111
            }
5112
            $result = Database::query($sql);
5113
5114
            if (Database::num_rows($result) > 0) {
5115
                while ($row = Database::fetch_array($result)) {
5116
                    $sql = "DELETE FROM $userRelUserTable 
5117
                            WHERE
5118
                                user_id = {$row['user_id']} AND 
5119
                                friend_user_id = $userId AND 
5120
                                relation_type = $relationType";
5121
                    Database::query($sql);
5122
                }
5123
            }
5124
        }
5125
5126
        if ($deleteUsersBeforeInsert) {
5127
            $sql = "DELETE FROM $userRelUserTable 
5128
                    WHERE 
5129
                        user_id = $userId AND
5130
                        relation_type = $relationType";
5131
            Database::query($sql);
5132
        }
5133
5134
        // Inserting new user list
5135
        if (is_array($subscribedUsersId)) {
5136
            foreach ($subscribedUsersId as $subscribedUserId) {
5137
                $subscribedUserId = intval($subscribedUserId);
5138
                $sql = "SELECT id FROM $userRelUserTable
5139
                        WHERE user_id = $subscribedUserId
5140
                        AND friend_user_id = $userId
5141
                        AND relation_type = $relationType";
5142
                $result = Database::query($sql);
5143
                $num = Database::num_rows($result);
5144
                if ($num === 0) {
5145
                    $date = api_get_utc_datetime();
5146
                    $sql = "INSERT INTO $userRelUserTable (user_id, friend_user_id, relation_type, last_edit)
5147
                        VALUES ($subscribedUserId, $userId, $relationType, '$date')";
5148
                    $result = Database::query($sql);
5149
                    $affectedRows += Database::affected_rows($result);
5150
                }
5151
            }
5152
        }
5153
5154
        return $affectedRows;
5155
    }
5156
5157
    /**
5158
     * This function check if an user is followed by human resources manager.
5159
     *
5160
     * @param int $user_id
5161
     * @param int $hr_dept_id Human resources manager
5162
     *
5163
     * @return bool
5164
     */
5165
    public static function is_user_followed_by_drh($user_id, $hr_dept_id)
5166
    {
5167
        // Database table and variables Definitions
5168
        $tbl_user_rel_user = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5169
        $user_id = intval($user_id);
5170
        $hr_dept_id = intval($hr_dept_id);
5171
        $result = false;
5172
5173
        $sql = "SELECT user_id FROM $tbl_user_rel_user
5174
                WHERE
5175
                    user_id = $user_id AND
5176
                    friend_user_id = $hr_dept_id AND
5177
                    relation_type = ".USER_RELATION_TYPE_RRHH;
5178
        $rs = Database::query($sql);
5179
        if (Database::num_rows($rs) > 0) {
5180
            $result = true;
5181
        }
5182
5183
        return $result;
5184
    }
5185
5186
    /**
5187
     * Return the user id of teacher or session administrator.
5188
     *
5189
     * @param array $courseInfo
5190
     *
5191
     * @return mixed The user id, or false if the session ID was negative
5192
     */
5193
    public static function get_user_id_of_course_admin_or_session_admin($courseInfo)
5194
    {
5195
        $session = api_get_session_id();
5196
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5197
        $table_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5198
        $table_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5199
        $courseId = $courseInfo['real_id'];
5200
5201
        if ($session == 0 || is_null($session)) {
5202
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5203
                    INNER JOIN '.$table_course_user.' ru
5204
                    ON ru.user_id = u.id
5205
                    WHERE
5206
                        ru.status = 1 AND
5207
                        ru.c_id = "'.$courseId.'" ';
5208
            $rs = Database::query($sql);
5209
            $num_rows = Database::num_rows($rs);
5210
            if ($num_rows == 1) {
5211
                $row = Database::fetch_array($rs);
5212
5213
                return $row['uid'];
5214
            } else {
5215
                $my_num_rows = $num_rows;
5216
                $my_user_id = Database::result($rs, $my_num_rows - 1, 'uid');
5217
5218
                return $my_user_id;
5219
            }
5220
        } elseif ($session > 0) {
5221
            $sql = 'SELECT u.id uid FROM '.$table_user.' u
5222
                    INNER JOIN '.$table_session_course_user.' sru
5223
                    ON sru.user_id=u.id
5224
                    WHERE
5225
                        sru.c_id="'.$courseId.'" AND
5226
                        sru.status=2';
5227
            $rs = Database::query($sql);
5228
            $row = Database::fetch_array($rs);
5229
5230
            return $row['uid'];
5231
        }
5232
5233
        return false;
5234
    }
5235
5236
    /**
5237
     * Determines if a user is a gradebook certified.
5238
     *
5239
     * @param int $cat_id  The category id of gradebook
5240
     * @param int $user_id The user id
5241
     *
5242
     * @return bool
5243
     */
5244
    public static function is_user_certified($cat_id, $user_id)
5245
    {
5246
        $table_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5247
        $sql = 'SELECT path_certificate FROM '.$table_certificate.'
5248
                WHERE
5249
                    cat_id="'.intval($cat_id).'" AND
5250
                    user_id="'.intval($user_id).'"';
5251
        $rs = Database::query($sql);
5252
        $row = Database::fetch_array($rs);
5253
        if ($row['path_certificate'] == '' || is_null($row['path_certificate'])) {
5254
            return false;
5255
        } else {
5256
            return true;
5257
        }
5258
    }
5259
5260
    /**
5261
     * Gets the info about a gradebook certificate for a user by course.
5262
     *
5263
     * @param string $course_code The course code
5264
     * @param int    $user_id     The user id
5265
     *
5266
     * @return array if there is not information return false
5267
     */
5268
    public static function get_info_gradebook_certificate($course_code, $user_id)
5269
    {
5270
        $tbl_grade_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5271
        $tbl_grade_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
5272
        $session_id = api_get_session_id();
5273
5274
        if (empty($session_id)) {
5275
            $session_condition = ' AND (session_id = "" OR session_id = 0 OR session_id IS NULL )';
5276
        } else {
5277
            $session_condition = " AND session_id = $session_id";
5278
        }
5279
5280
        $sql = 'SELECT * FROM '.$tbl_grade_certificate.' 
5281
                WHERE cat_id = (
5282
                    SELECT id FROM '.$tbl_grade_category.'
5283
                    WHERE
5284
                        course_code = "'.Database::escape_string($course_code).'" '.$session_condition.' 
5285
                    LIMIT 1 
5286
                ) AND user_id='.intval($user_id);
5287
5288
        $rs = Database::query($sql);
5289
        if (Database::num_rows($rs) > 0) {
5290
            $row = Database::fetch_array($rs, 'ASSOC');
5291
            $score = $row['score_certificate'];
5292
            $category_id = $row['cat_id'];
5293
            $cat = Category::load($category_id);
5294
            $displayscore = ScoreDisplay::instance();
5295
            if (isset($cat) && $displayscore->is_custom()) {
5296
                $grade = $displayscore->display_score(
5297
                    [$score, $cat[0]->get_weight()],
5298
                    SCORE_DIV_PERCENT_WITH_CUSTOM
5299
                );
5300
            } else {
5301
                $grade = $displayscore->display_score(
5302
                    [$score, $cat[0]->get_weight()]
5303
                );
5304
            }
5305
            $row['grade'] = $grade;
5306
5307
            return $row;
5308
        }
5309
5310
        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...
5311
    }
5312
5313
    /**
5314
     * Gets the user path of user certificated.
5315
     *
5316
     * @param int The user id
5317
     *
5318
     * @return array containing path_certificate and cat_id
5319
     */
5320
    public static function get_user_path_certificate($user_id)
5321
    {
5322
        $my_certificate = [];
5323
        $table_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
5324
        $table_gradebook_category = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
5325
5326
        $session_id = api_get_session_id();
5327
        $user_id = intval($user_id);
5328
        if ($session_id == 0 || is_null($session_id)) {
5329
            $sql_session = 'AND (session_id='.intval($session_id).' OR isnull(session_id)) ';
5330
        } elseif ($session_id > 0) {
5331
            $sql_session = 'AND session_id='.intval($session_id);
5332
        } else {
5333
            $sql_session = '';
5334
        }
5335
        $sql = "SELECT tc.path_certificate,tc.cat_id,tgc.course_code,tgc.name
5336
                FROM $table_certificate tc, $table_gradebook_category tgc
5337
                WHERE tgc.id = tc.cat_id AND tc.user_id = $user_id
5338
                ORDER BY tc.date_certificate DESC 
5339
                LIMIT 5";
5340
5341
        $rs = Database::query($sql);
5342
        while ($row = Database::fetch_array($rs)) {
5343
            $my_certificate[] = $row;
5344
        }
5345
5346
        return $my_certificate;
5347
    }
5348
5349
    /**
5350
     * This function check if the user is a coach inside session course.
5351
     *
5352
     * @param int $user_id    User id
5353
     * @param int $courseId
5354
     * @param int $session_id
5355
     *
5356
     * @return bool True if the user is a coach
5357
     */
5358
    public static function is_session_course_coach($user_id, $courseId, $session_id)
5359
    {
5360
        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5361
        // Protect data
5362
        $user_id = intval($user_id);
5363
        $courseId = intval($courseId);
5364
        $session_id = intval($session_id);
5365
        $result = false;
5366
5367
        $sql = "SELECT session_id FROM $table
5368
                WHERE
5369
                  session_id = $session_id AND
5370
                  c_id = $courseId AND
5371
                  user_id = $user_id AND
5372
                  status = 2 ";
5373
        $res = Database::query($sql);
5374
5375
        if (Database::num_rows($res) > 0) {
5376
            $result = true;
5377
        }
5378
5379
        return $result;
5380
    }
5381
5382
    /**
5383
     * This function returns an icon path that represents the favicon of the website of which the url given.
5384
     * Defaults to the current Chamilo favicon.
5385
     *
5386
     * @param string $url1 URL of website where to look for favicon.ico
5387
     * @param string $url2 Optional second URL of website where to look for favicon.ico
5388
     *
5389
     * @return string Path of icon to load
5390
     */
5391
    public static function get_favicon_from_url($url1, $url2 = null)
5392
    {
5393
        $icon_link = '';
5394
        $url = $url1;
5395
        if (empty($url1)) {
5396
            $url = $url2;
5397
            if (empty($url)) {
5398
                $url = api_get_access_url(api_get_current_access_url_id());
5399
                $url = $url[0];
5400
            }
5401
        }
5402
        if (!empty($url)) {
5403
            $pieces = parse_url($url);
5404
            $icon_link = $pieces['scheme'].'://'.$pieces['host'].'/favicon.ico';
5405
        }
5406
5407
        return $icon_link;
5408
    }
5409
5410
    /**
5411
     * Returns an array of the different types of user extra fields [id => title translation].
5412
     *
5413
     * @return array
5414
     */
5415
    public static function get_user_field_types()
5416
    {
5417
        $types = [];
5418
        $types[self::USER_FIELD_TYPE_TEXT] = get_lang('FieldTypeText');
5419
        $types[self::USER_FIELD_TYPE_TEXTAREA] = get_lang('FieldTypeTextarea');
5420
        $types[self::USER_FIELD_TYPE_RADIO] = get_lang('FieldTypeRadio');
5421
        $types[self::USER_FIELD_TYPE_SELECT] = get_lang('FieldTypeSelect');
5422
        $types[self::USER_FIELD_TYPE_SELECT_MULTIPLE] = get_lang('FieldTypeSelectMultiple');
5423
        $types[self::USER_FIELD_TYPE_DATE] = get_lang('FieldTypeDate');
5424
        $types[self::USER_FIELD_TYPE_DATETIME] = get_lang('FieldTypeDatetime');
5425
        $types[self::USER_FIELD_TYPE_DOUBLE_SELECT] = get_lang('FieldTypeDoubleSelect');
5426
        $types[self::USER_FIELD_TYPE_DIVIDER] = get_lang('FieldTypeDivider');
5427
        $types[self::USER_FIELD_TYPE_TAG] = get_lang('FieldTypeTag');
5428
        $types[self::USER_FIELD_TYPE_TIMEZONE] = get_lang('FieldTypeTimezone');
5429
        $types[self::USER_FIELD_TYPE_SOCIAL_PROFILE] = get_lang('FieldTypeSocialProfile');
5430
        $types[self::USER_FIELD_TYPE_FILE] = get_lang('FieldTypeFile');
5431
        $types[self::USER_FIELD_TYPE_MOBILE_PHONE_NUMBER] = get_lang('FieldTypeMobilePhoneNumber');
5432
5433
        return $types;
5434
    }
5435
5436
    /**
5437
     * @param User $user
5438
     */
5439
    public static function add_user_as_admin(User $user)
5440
    {
5441
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
5442
        if ($user) {
0 ignored issues
show
introduced by
$user is of type Chamilo\UserBundle\Entity\User, thus it always evaluated to true.
Loading history...
5443
            $userId = $user->getId();
5444
5445
            if (!self::is_admin($userId)) {
5446
                $sql = "INSERT INTO $table_admin SET user_id = $userId";
5447
                Database::query($sql);
5448
            }
5449
5450
            $group = Container::$container->get('fos_user.group_manager')->findGroupBy(['code' => 'ADMIN']);
5451
            if ($group) {
5452
                $user->addGroup($group);
5453
            }
5454
            self::getManager()->updateUser($user, true);
5455
        }
5456
    }
5457
5458
    /**
5459
     * @param int $userId
5460
     */
5461
    public static function remove_user_admin($userId)
5462
    {
5463
        $table_admin = Database::get_main_table(TABLE_MAIN_ADMIN);
5464
        $userId = intval($userId);
5465
        if (self::is_admin($userId)) {
5466
            $sql = "DELETE FROM $table_admin WHERE user_id = $userId";
5467
            Database::query($sql);
5468
        }
5469
    }
5470
5471
    /**
5472
     * @param string $from
5473
     * @param string $to
5474
     */
5475
    public static function update_all_user_languages($from, $to)
5476
    {
5477
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
5478
        $from = Database::escape_string($from);
5479
        $to = Database::escape_string($to);
5480
5481
        if (!empty($to) && !empty($from)) {
5482
            $sql = "UPDATE $table_user SET language = '$to'
5483
                    WHERE language = '$from'";
5484
            Database::query($sql);
5485
        }
5486
    }
5487
5488
    /**
5489
     * Subscribe boss to students.
5490
     *
5491
     * @param int   $bossId  The boss id
5492
     * @param array $usersId The users array
5493
     *
5494
     * @return int Affected rows
5495
     */
5496
    public static function subscribeBossToUsers($bossId, $usersId)
5497
    {
5498
        return self::subscribeUsersToUser(
5499
            $bossId,
5500
            $usersId,
5501
            USER_RELATION_TYPE_BOSS
5502
        );
5503
    }
5504
5505
    /**
5506
     * @param int $userId
5507
     *
5508
     * @return bool
5509
     */
5510
    public static function removeAllBossFromStudent($userId)
5511
    {
5512
        $userId = (int) $userId;
5513
5514
        if (empty($userId)) {
5515
            return false;
5516
        }
5517
5518
        $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5519
        $sql = "DELETE FROM $userRelUserTable 
5520
                WHERE user_id = $userId AND relation_type = ".USER_RELATION_TYPE_BOSS;
5521
        Database::query($sql);
5522
5523
        return true;
5524
    }
5525
5526
    /**
5527
     * Subscribe boss to students, if $bossList is empty then the boss list will be empty too.
5528
     *
5529
     * @param int   $studentId
5530
     * @param array $bossList
5531
     * @param bool  $sendNotification
5532
     *
5533
     * @return mixed Affected rows or false on failure
5534
     */
5535
    public static function subscribeUserToBossList(
5536
        $studentId,
5537
        $bossList,
5538
        $sendNotification = false
5539
    ) {
5540
        $inserted = 0;
5541
        if (!empty($bossList)) {
5542
            sort($bossList);
5543
            $studentId = (int) $studentId;
5544
            $studentInfo = api_get_user_info($studentId);
5545
5546
            if (empty($studentInfo)) {
5547
                return false;
5548
            }
5549
5550
            $previousBossList = self::getStudentBossList($studentId);
5551
            $previousBossList = !empty($previousBossList) ? array_column($previousBossList, 'boss_id') : [];
5552
            sort($previousBossList);
5553
5554
            // Boss list is the same, nothing changed.
5555
            if ($bossList == $previousBossList) {
5556
                return false;
5557
            }
5558
5559
            $userRelUserTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5560
            self::removeAllBossFromStudent($studentId);
5561
5562
            foreach ($bossList as $bossId) {
5563
                $bossId = (int) $bossId;
5564
                $sql = "INSERT IGNORE INTO $userRelUserTable (user_id, friend_user_id, relation_type)
5565
                        VALUES ($studentId, $bossId, ".USER_RELATION_TYPE_BOSS.")";
5566
                $insertId = Database::query($sql);
5567
5568
                if ($insertId) {
5569
                    if ($sendNotification) {
5570
                        $name = $studentInfo['complete_name'];
5571
                        $url = api_get_path(WEB_CODE_PATH).'mySpace/myStudents.php?student='.$studentId;
5572
                        $url = Display::url($url, $url);
5573
                        $subject = sprintf(get_lang('UserXHasBeenAssignedToBoss'), $name);
5574
                        $message = sprintf(get_lang('UserXHasBeenAssignedToBossWithUrlX'), $name, $url);
5575
                        MessageManager::send_message_simple(
5576
                            $bossId,
5577
                            $subject,
5578
                            $message
5579
                        );
5580
                    }
5581
                    $inserted++;
5582
                }
5583
            }
5584
        } else {
5585
            self::removeAllBossFromStudent($studentId);
5586
        }
5587
5588
        return $inserted;
5589
    }
5590
5591
    /**
5592
     * Get users followed by student boss.
5593
     *
5594
     * @param int    $userId
5595
     * @param int    $userStatus         (STUDENT, COURSEMANAGER, etc)
5596
     * @param bool   $getOnlyUserId
5597
     * @param bool   $getSql
5598
     * @param bool   $getCount
5599
     * @param int    $from
5600
     * @param int    $numberItems
5601
     * @param int    $column
5602
     * @param string $direction
5603
     * @param int    $active
5604
     * @param string $lastConnectionDate
5605
     *
5606
     * @return array users
5607
     */
5608
    public static function getUsersFollowedByStudentBoss(
5609
        $userId,
5610
        $userStatus = 0,
5611
        $getOnlyUserId = false,
5612
        $getSql = false,
5613
        $getCount = false,
5614
        $from = null,
5615
        $numberItems = null,
5616
        $column = null,
5617
        $direction = null,
5618
        $active = null,
5619
        $lastConnectionDate = null
5620
    ) {
5621
        return self::getUsersFollowedByUser(
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::getUsersFol...tionDate, STUDENT_BOSS) also could return the type string which is incompatible with the documented return type array.
Loading history...
5622
            $userId,
5623
            $userStatus,
5624
            $getOnlyUserId,
5625
            $getSql,
5626
            $getCount,
5627
            $from,
5628
            $numberItems,
5629
            $column,
5630
            $direction,
5631
            $active,
5632
            $lastConnectionDate,
5633
            STUDENT_BOSS
5634
        );
5635
    }
5636
5637
    /**
5638
     * Get the teacher (users with COURSEMANGER status) list.
5639
     *
5640
     * @return array The list
5641
     */
5642
    public static function getTeachersList()
5643
    {
5644
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5645
        $resultData = Database::select(
5646
            'user_id, lastname, firstname, username',
5647
            $userTable,
5648
            [
5649
            'where' => [
5650
                'status = ?' => COURSEMANAGER,
5651
            ],
5652
        ]
5653
        );
5654
5655
        foreach ($resultData as &$teacherData) {
5656
            $teacherData['completeName'] = api_get_person_name(
5657
                $teacherData['firstname'],
5658
                $teacherData['lastname']
5659
            );
5660
        }
5661
5662
        return $resultData;
5663
    }
5664
5665
    /**
5666
     * @return array
5667
     */
5668
    public static function getOfficialCodeGrouped()
5669
    {
5670
        $user = Database::get_main_table(TABLE_MAIN_USER);
5671
        $sql = "SELECT DISTINCT official_code
5672
                FROM $user
5673
                GROUP BY official_code";
5674
        $result = Database::query($sql);
5675
        $values = Database::store_result($result, 'ASSOC');
5676
        $result = [];
5677
        foreach ($values as $value) {
5678
            $result[$value['official_code']] = $value['official_code'];
5679
        }
5680
5681
        return $result;
5682
    }
5683
5684
    /**
5685
     * @param string $officialCode
5686
     *
5687
     * @return array
5688
     */
5689
    public static function getUsersByOfficialCode($officialCode)
5690
    {
5691
        $user = Database::get_main_table(TABLE_MAIN_USER);
5692
        $officialCode = Database::escape_string($officialCode);
5693
5694
        $sql = "SELECT DISTINCT id
5695
                FROM $user
5696
                WHERE official_code = '$officialCode'
5697
                ";
5698
        $result = Database::query($sql);
5699
5700
        $users = [];
5701
        while ($row = Database::fetch_array($result)) {
5702
            $users[] = $row['id'];
5703
        }
5704
5705
        return $users;
5706
    }
5707
5708
    /**
5709
     * Calc the expended time (in seconds) by a user in a course.
5710
     *
5711
     * @param int    $userId    The user id
5712
     * @param int    $courseId  The course id
5713
     * @param int    $sessionId Optional. The session id
5714
     * @param string $from      Optional. From date
5715
     * @param string $until     Optional. Until date
5716
     *
5717
     * @return int The time
5718
     */
5719
    public static function getTimeSpentInCourses(
5720
        $userId,
5721
        $courseId,
5722
        $sessionId = 0,
5723
        $from = '',
5724
        $until = ''
5725
    ) {
5726
        $userId = intval($userId);
5727
        $sessionId = intval($sessionId);
5728
        $trackCourseAccessTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5729
        $whereConditions = [
5730
            'user_id = ? ' => $userId,
5731
            'AND c_id = ? ' => $courseId,
5732
            'AND session_id = ? ' => $sessionId,
5733
        ];
5734
5735
        if (!empty($from) && !empty($until)) {
5736
            $whereConditions["AND (login_course_date >= '?' "] = $from;
5737
            $whereConditions["AND logout_course_date <= DATE_ADD('?', INTERVAL 1 DAY)) "] = $until;
5738
        }
5739
5740
        $trackResult = Database::select(
5741
            'SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as total_time',
5742
            $trackCourseAccessTable,
5743
            [
5744
                'where' => $whereConditions,
5745
            ],
5746
            'first'
5747
        );
5748
5749
        if ($trackResult != false) {
5750
            return $trackResult['total_time'] ? $trackResult['total_time'] : 0;
5751
        }
5752
5753
        return 0;
5754
    }
5755
5756
    /**
5757
     * Get the boss user ID from a followed user id.
5758
     *
5759
     * @param $userId
5760
     *
5761
     * @return bool
5762
     */
5763
    public static function getFirstStudentBoss($userId)
5764
    {
5765
        $userId = intval($userId);
5766
        if ($userId > 0) {
5767
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5768
            $row = Database::select(
5769
                'DISTINCT friend_user_id AS boss_id',
5770
                $userRelTable,
5771
                [
5772
                    'where' => [
5773
                        'user_id = ? AND relation_type = ? LIMIT 1' => [
5774
                            $userId,
5775
                            USER_RELATION_TYPE_BOSS,
5776
                        ],
5777
                    ],
5778
                ]
5779
            );
5780
            if (!empty($row)) {
5781
                return $row[0]['boss_id'];
5782
            }
5783
        }
5784
5785
        return false;
5786
    }
5787
5788
    /**
5789
     * Get the boss user ID from a followed user id.
5790
     *
5791
     * @param int $userId student id
5792
     *
5793
     * @return array
5794
     */
5795
    public static function getStudentBossList($userId)
5796
    {
5797
        $userId = (int) $userId;
5798
        if ($userId > 0) {
5799
            $userRelTable = Database::get_main_table(TABLE_MAIN_USER_REL_USER);
5800
            $result = Database::select(
5801
                'DISTINCT friend_user_id AS boss_id',
5802
                $userRelTable,
5803
                [
5804
                    'where' => [
5805
                        'user_id = ? AND relation_type = ? ' => [
5806
                            $userId,
5807
                            USER_RELATION_TYPE_BOSS,
5808
                        ],
5809
                    ],
5810
                ]
5811
            );
5812
5813
            return $result;
5814
        }
5815
5816
        return [];
5817
    }
5818
5819
    /**
5820
     * @param int $bossId
5821
     * @param int $studentId
5822
     *
5823
     * @return bool
5824
     */
5825
    public static function userIsBossOfStudent($bossId, $studentId)
5826
    {
5827
        $result = false;
5828
        $bossList = self::getStudentBossList($studentId);
5829
        if (!empty($bossList)) {
5830
            $bossList = array_column($bossList, 'boss_id');
5831
            if (in_array($bossId, $bossList)) {
5832
                $result = true;
5833
            }
5834
        }
5835
5836
        return $result;
5837
    }
5838
5839
    /**
5840
     * Displays the name of the user and makes the link to the user profile.
5841
     *
5842
     * @param array $userInfo
5843
     *
5844
     * @return string
5845
     */
5846
    public static function getUserProfileLink($userInfo)
5847
    {
5848
        if (isset($userInfo) && isset($userInfo['user_id'])) {
5849
            return Display::url(
5850
                $userInfo['complete_name_with_username'],
5851
                $userInfo['profile_url']
5852
            );
5853
        } else {
5854
            return get_lang('Anonymous');
5855
        }
5856
    }
5857
5858
    /**
5859
     * Displays the name of the user and makes the link to the user profile.
5860
     *
5861
     * @param $userInfo
5862
     *
5863
     * @return string
5864
     */
5865
    public static function getUserProfileLinkWithPicture($userInfo)
5866
    {
5867
        return Display::url(
5868
            Display::img($userInfo['avatar']),
5869
            $userInfo['profile_url']
5870
        );
5871
    }
5872
5873
    /**
5874
     * Get users whose name matches $firstname and $lastname.
5875
     *
5876
     * @param string $firstname Firstname to search
5877
     * @param string $lastname  Lastname to search
5878
     *
5879
     * @return array The user list
5880
     */
5881
    public static function getUsersByName($firstname, $lastname)
5882
    {
5883
        $firstname = Database::escape_string($firstname);
5884
        $lastname = Database::escape_string($lastname);
5885
        $userTable = Database::get_main_table(TABLE_MAIN_USER);
5886
5887
        $sql = <<<SQL
5888
            SELECT id, username, lastname, firstname
5889
            FROM $userTable
5890
            WHERE 
5891
                firstname LIKE '$firstname%' AND
5892
                lastname LIKE '$lastname%'
5893
SQL;
5894
        $result = Database::query($sql);
5895
        $users = [];
5896
        while ($resultData = Database::fetch_object($result)) {
5897
            $users[] = $resultData;
5898
        }
5899
5900
        return $users;
5901
    }
5902
5903
    /**
5904
     * @param int $optionSelected
5905
     *
5906
     * @return string
5907
     */
5908
    public static function getUserSubscriptionTab($optionSelected = 1)
5909
    {
5910
        $allowAdmin = api_get_setting('allow_user_course_subscription_by_course_admin');
5911
        if (($allowAdmin === 'true' && api_is_allowed_to_edit()) ||
5912
            api_is_platform_admin()
5913
        ) {
5914
            $userPath = api_get_path(WEB_CODE_PATH).'user/';
5915
5916
            $headers = [
5917
                [
5918
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.STUDENT,
5919
                    'content' => get_lang('Students'),
5920
                ],
5921
                [
5922
                    'url' => $userPath.'user.php?'.api_get_cidreq().'&type='.COURSEMANAGER,
5923
                    'content' => get_lang('Teachers'),
5924
                ],
5925
                /*[
5926
                    'url' => $userPath.'subscribe_user.php?'.api_get_cidreq(),
5927
                    'content' => get_lang('Students'),
5928
                ],
5929
                [
5930
                    'url' => $userPath.'subscribe_user.php?type=teacher&'.api_get_cidreq(),
5931
                    'content' => get_lang('Teachers'),
5932
                ],*/
5933
                [
5934
                    'url' => api_get_path(WEB_CODE_PATH).'group/group.php?'.api_get_cidreq(),
5935
                    'content' => get_lang('Groups'),
5936
                ],
5937
                [
5938
                    'url' => $userPath.'class.php?'.api_get_cidreq(),
5939
                    'content' => get_lang('Classes'),
5940
                ],
5941
            ];
5942
5943
            return Display::tabsOnlyLink($headers, $optionSelected);
5944
        }
5945
5946
        return '';
5947
    }
5948
5949
    /**
5950
     * Make sure this function is protected because it does NOT check password!
5951
     *
5952
     * This function defines globals.
5953
     *
5954
     * @param int  $userId
5955
     * @param bool $checkIfUserCanLoginAs
5956
     *
5957
     * @return bool
5958
     *
5959
     * @author Evie Embrechts
5960
     * @author Yannick Warnier <[email protected]>
5961
     */
5962
    public static function loginAsUser($userId, $checkIfUserCanLoginAs = true)
5963
    {
5964
        $userId = (int) $userId;
5965
        $userInfo = api_get_user_info($userId);
5966
5967
        // Check if the user is allowed to 'login_as'
5968
        $canLoginAs = true;
5969
        if ($checkIfUserCanLoginAs) {
5970
            $canLoginAs = api_can_login_as($userId);
5971
        }
5972
5973
        if (!$canLoginAs || empty($userInfo)) {
5974
            return false;
5975
        }
5976
5977
        if ($userId) {
5978
            $logInfo = [
5979
                'tool' => 'logout',
5980
                'tool_id' => 0,
5981
                'tool_id_detail' => 0,
5982
                'action' => '',
5983
                'info' => 'Change user (login as)',
5984
            ];
5985
            Event::registerLog($logInfo);
0 ignored issues
show
Bug introduced by
The method registerLog() does not exist on Event. ( Ignorable by Annotation )

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

5985
            Event::/** @scrutinizer ignore-call */ 
5986
                   registerLog($logInfo);

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

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

Loading history...
5986
5987
            // Logout the current user
5988
            self::loginDelete(api_get_user_id());
5989
5990
            Session::erase('_user');
5991
            Session::erase('is_platformAdmin');
5992
            Session::erase('is_allowedCreateCourse');
5993
            Session::erase('_uid');
5994
5995
            // Cleaning session variables
5996
            $_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...
5997
            $_user['lastName'] = $userInfo['lastname'];
5998
            $_user['mail'] = $userInfo['email'];
5999
            $_user['official_code'] = $userInfo['official_code'];
6000
            $_user['picture_uri'] = $userInfo['picture_uri'];
6001
            $_user['user_id'] = $userId;
6002
            $_user['id'] = $userId;
6003
            $_user['status'] = $userInfo['status'];
6004
6005
            // Filling session variables with new data
6006
            Session::write('_uid', $userId);
6007
            Session::write('_user', $userInfo);
6008
            Session::write('is_platformAdmin', (bool) self::is_admin($userId));
6009
            Session::write('is_allowedCreateCourse', $userInfo['status'] == 1);
6010
            // will be useful later to know if the user is actually an admin or not (example reporting)
6011
            Session::write('login_as', true);
6012
6013
            $logInfo = [
6014
                'tool' => 'login',
6015
                'tool_id' => 0,
6016
                'tool_id_detail' => 0,
6017
                'action' => '',
6018
                'info' => $userId,
6019
            ];
6020
            Event::registerLog($logInfo);
6021
6022
            return true;
6023
        }
6024
6025
        return false;
6026
    }
6027
6028
    /**
6029
     * Remove all login records from the track_e_online stats table,
6030
     * for the given user ID.
6031
     *
6032
     * @param int $userId User ID
6033
     */
6034
    public static function loginDelete($userId)
6035
    {
6036
        $online_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6037
        $userId = (int) $userId;
6038
        $query = "DELETE FROM $online_table WHERE login_user_id = $userId";
6039
        Database::query($query);
6040
    }
6041
6042
    /**
6043
     * Login as first admin user registered in the platform.
6044
     *
6045
     * @return array
6046
     */
6047
    public static function logInAsFirstAdmin()
6048
    {
6049
        $adminList = self::get_all_administrators();
6050
6051
        if (!empty($adminList)) {
6052
            $userInfo = current($adminList);
6053
            if (!empty($userInfo)) {
6054
                $result = self::loginAsUser($userInfo['user_id'], false);
6055
                if ($result && api_is_platform_admin()) {
6056
                    return api_get_user_info();
6057
                }
6058
            }
6059
        }
6060
6061
        return [];
6062
    }
6063
6064
    /**
6065
     * Check if user is teacher of a student based in their courses.
6066
     *
6067
     * @param $teacherId
6068
     * @param $studentId
6069
     *
6070
     * @return array
6071
     */
6072
    public static function getCommonCoursesBetweenTeacherAndStudent($teacherId, $studentId)
6073
    {
6074
        $courses = CourseManager::getCoursesFollowedByUser(
6075
            $teacherId,
6076
            COURSEMANAGER
6077
        );
6078
        if (empty($courses)) {
6079
            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...
6080
        }
6081
6082
        $coursesFromUser = CourseManager::get_courses_list_by_user_id($studentId);
6083
        if (empty($coursesFromUser)) {
6084
            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...
6085
        }
6086
6087
        $coursesCodeList = array_column($courses, 'code');
6088
        $coursesCodeFromUserList = array_column($coursesFromUser, 'code');
6089
        $commonCourses = array_intersect($coursesCodeList, $coursesCodeFromUserList);
6090
        $commonCourses = array_filter($commonCourses);
6091
6092
        if (!empty($commonCourses)) {
6093
            return $commonCourses;
6094
        }
6095
6096
        return [];
6097
    }
6098
6099
    /**
6100
     * @param int $teacherId
6101
     * @param int $studentId
6102
     *
6103
     * @return bool
6104
     */
6105
    public static function isTeacherOfStudent($teacherId, $studentId)
6106
    {
6107
        $courses = self::getCommonCoursesBetweenTeacherAndStudent(
6108
            $teacherId,
6109
            $studentId
6110
        );
6111
6112
        if (!empty($courses)) {
6113
            return true;
6114
        }
6115
6116
        return false;
6117
    }
6118
6119
    /**
6120
     * Send user confirmation mail.
6121
     *
6122
     * @param User $user
6123
     *
6124
     * @throws Exception
6125
     */
6126
    public static function sendUserConfirmationMail(User $user)
6127
    {
6128
        $uniqueId = api_get_unique_id();
6129
        $user->setConfirmationToken($uniqueId);
6130
6131
        Database::getManager()->persist($user);
6132
        Database::getManager()->flush();
6133
6134
        $url = api_get_path(WEB_CODE_PATH).'auth/user_mail_confirmation.php?token='.$uniqueId;
6135
6136
        // Check if the user was originally set for an automated subscription to a course or session
6137
        $courseCodeToRedirect = Session::read('course_redirect');
6138
        $sessionToRedirect = Session::read('session_redirect');
6139
        if (!empty($courseCodeToRedirect)) {
6140
            $url .= '&c='.$courseCodeToRedirect;
6141
        }
6142
        if (!empty($sessionToRedirect)) {
6143
            $url .= '&s='.$sessionToRedirect;
6144
        }
6145
        $mailSubject = get_lang('RegistrationConfirmation');
6146
        $mailBody = get_lang('RegistrationConfirmationEmailMessage')
6147
            .PHP_EOL
6148
            .Display::url($url, $url);
6149
6150
        api_mail_html(
6151
            UserManager::formatUserFullName($user),
6152
            $user->getEmail(),
6153
            $mailSubject,
6154
            $mailBody
6155
        );
6156
        Display::addFlash(Display::return_message(get_lang('CheckYourEmailAndFollowInstructions')));
6157
    }
6158
6159
    /**
6160
     * Anonymize a user. Replace personal info by anonymous info.
6161
     *
6162
     * @param int  $userId   User id
6163
     * @param bool $deleteIP Whether to replace the IP address in logs tables by 127.0.0.1 or to leave as is
6164
     *
6165
     * @throws \Exception
6166
     *
6167
     * @return bool
6168
     * @assert (0) === false
6169
     */
6170
    public static function anonymize($userId, $deleteIP = true)
6171
    {
6172
        global $debug;
6173
        if (empty($userId)) {
6174
            return false;
6175
        }
6176
        $em = Database::getManager();
6177
        $user = api_get_user_entity($userId);
6178
        $uniqueId = uniqid('anon', true);
6179
        $user
6180
            ->setFirstname($uniqueId)
6181
            ->setLastname($uniqueId)
6182
            ->setBiography('')
6183
            ->setAddress('')
6184
            ->setCurriculumItems(null)
6185
            ->setDateOfBirth(null)
6186
            ->setCompetences('')
6187
            ->setDiplomas('')
6188
            ->setOpenarea('')
6189
            ->setTeach('')
6190
            ->setProductions(null)
6191
            ->setOpenid('')
6192
            ->setEmailCanonical($uniqueId.'@example.com')
6193
            ->setEmail($uniqueId.'@example.com')
6194
            ->setUsername($uniqueId)
6195
            ->setUsernameCanonical($uniqueId)
6196
            ->setPhone('')
6197
            ->setOfficialCode('')
6198
        ;
6199
6200
        self::deleteUserPicture($userId);
6201
        self::cleanUserRequestsOfRemoval($userId);
6202
6203
        // The IP address is a border-case personal data, as it does
6204
        // not directly allow for personal identification (it is not
6205
        // a completely safe value in most countries - the IP could
6206
        // be used by neighbours and crackers)
6207
        if ($deleteIP) {
6208
            $substitute = '127.0.0.1';
6209
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
6210
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE access_user_id = $userId";
6211
            $res = Database::query($sql);
6212
            if ($res === false && $debug > 0) {
6213
                error_log("Could not anonymize IP address for user $userId ($sql)");
6214
            }
6215
6216
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6217
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6218
            $res = Database::query($sql);
6219
            if ($res === false && $debug > 0) {
6220
                error_log("Could not anonymize IP address for user $userId ($sql)");
6221
            }
6222
6223
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6224
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE exe_user_id = $userId";
6225
            $res = Database::query($sql);
6226
            if ($res === false && $debug > 0) {
6227
                error_log("Could not anonymize IP address for user $userId ($sql)");
6228
            }
6229
6230
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6231
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6232
            $res = Database::query($sql);
6233
            if ($res === false && $debug > 0) {
6234
                error_log("Could not anonymize IP address for user $userId ($sql)");
6235
            }
6236
6237
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ONLINE);
6238
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE login_user_id = $userId";
6239
            $res = Database::query($sql);
6240
            if ($res === false && $debug > 0) {
6241
                error_log("Could not anonymize IP address for user $userId ($sql)");
6242
            }
6243
6244
            $table = Database::get_course_table(TABLE_WIKI);
6245
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6246
            $res = Database::query($sql);
6247
            if ($res === false && $debug > 0) {
6248
                error_log("Could not anonymize IP address for user $userId ($sql)");
6249
            }
6250
6251
            $table = Database::get_main_table(TABLE_TICKET_MESSAGE);
6252
            $sql = "UPDATE $table set ip_address = '$substitute' WHERE sys_insert_user_id = $userId";
6253
            $res = Database::query($sql);
6254
            if ($res === false && $debug > 0) {
6255
                error_log("Could not anonymize IP address for user $userId ($sql)");
6256
            }
6257
6258
            $table = Database::get_course_table(TABLE_WIKI);
6259
            $sql = "UPDATE $table set user_ip = '$substitute' WHERE user_id = $userId";
6260
            $res = Database::query($sql);
6261
            if ($res === false && $debug > 0) {
6262
                error_log("Could not anonymize IP address for user $userId ($sql)");
6263
            }
6264
        }
6265
        $em->persist($user);
6266
        $em->flush($user);
6267
        Event::addEvent(LOG_USER_ANONYMIZE, LOG_USER_ID, $userId);
6268
6269
        return true;
6270
    }
6271
6272
    /**
6273
     * @param int $userId
6274
     *
6275
     * @throws Exception
6276
     *
6277
     * @return string
6278
     */
6279
    public static function anonymizeUserWithVerification($userId)
6280
    {
6281
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6282
6283
        $message = '';
6284
        if (api_is_platform_admin() ||
6285
            ($allowDelete && api_is_session_admin())
6286
        ) {
6287
            $userToUpdateInfo = api_get_user_info($userId);
6288
            $currentUserId = api_get_user_id();
6289
6290
            if ($userToUpdateInfo &&
0 ignored issues
show
Bug Best Practice introduced by
The expression $userToUpdateInfo 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...
6291
                api_global_admin_can_edit_admin($userId, null, $allowDelete)
6292
            ) {
6293
                if ($userId != $currentUserId &&
6294
                    self::anonymize($userId)
6295
                ) {
6296
                    $message = Display::return_message(
6297
                        sprintf(get_lang('UserXAnonymized'), $userToUpdateInfo['complete_name_with_username']),
6298
                        'confirmation'
6299
                    );
6300
                } else {
6301
                    $message = Display::return_message(
6302
                        sprintf(get_lang('CannotAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6303
                        'error'
6304
                    );
6305
                }
6306
            } else {
6307
                $message = Display::return_message(
6308
                    sprintf(get_lang('NoPermissionToAnonymizeUserX'), $userToUpdateInfo['complete_name_with_username']),
6309
                    'error'
6310
                );
6311
            }
6312
        }
6313
6314
        return $message;
6315
    }
6316
6317
    /**
6318
     * @param int $userId
6319
     *
6320
     * @throws Exception
6321
     *
6322
     * @return string
6323
     */
6324
    public static function deleteUserWithVerification($userId)
6325
    {
6326
        $allowDelete = api_get_configuration_value('allow_delete_user_for_session_admin');
6327
        $message = Display::return_message(get_lang('CannotDeleteUser'), 'error');
6328
        $userToUpdateInfo = api_get_user_info($userId);
6329
6330
        // User must exist.
6331
        if (empty($userToUpdateInfo)) {
6332
            return $message;
6333
        }
6334
6335
        $currentUserId = api_get_user_id();
6336
6337
        // Cannot delete myself.
6338
        if ($userId == $currentUserId) {
6339
            return $message;
6340
        }
6341
6342
        if (api_is_platform_admin() ||
6343
            ($allowDelete && api_is_session_admin())
6344
        ) {
6345
            if (api_global_admin_can_edit_admin($userId, null, $allowDelete)) {
6346
                if (self::delete_user($userId)) {
6347
                    $message = Display::return_message(
6348
                        get_lang('UserDeleted').': '.$userToUpdateInfo['complete_name_with_username'],
6349
                        'confirmation'
6350
                    );
6351
                } else {
6352
                    $message = Display::return_message(get_lang('CannotDeleteUserBecauseOwnsCourse'), 'error');
6353
                }
6354
            }
6355
        }
6356
6357
        return $message;
6358
    }
6359
6360
    /**
6361
     * @return array
6362
     */
6363
    public static function createDataPrivacyExtraFields()
6364
    {
6365
        self::create_extra_field(
6366
            'request_for_legal_agreement_consent_removal_justification',
6367
            1, //text
6368
            'Request for legal agreement consent removal justification	',
6369
            ''
6370
        );
6371
6372
        self::create_extra_field(
6373
            'request_for_delete_account_justification',
6374
            1, //text
6375
            'Request for delete account justification',
6376
            ''
6377
        );
6378
6379
        $extraFieldId = self::create_extra_field(
6380
            'request_for_legal_agreement_consent_removal',
6381
            1, //text
6382
            'Request for legal agreement consent removal',
6383
            ''
6384
        );
6385
6386
        $extraFieldIdDeleteAccount = self::create_extra_field(
6387
            'request_for_delete_account',
6388
            1, //text
6389
            'Request for delete user account',
6390
            ''
6391
        );
6392
6393
        return [
6394
            'delete_account_extra_field' => $extraFieldIdDeleteAccount,
6395
            'delete_legal' => $extraFieldId,
6396
        ];
6397
    }
6398
6399
    /**
6400
     * @param int $userId
6401
     */
6402
    public static function cleanUserRequestsOfRemoval($userId)
6403
    {
6404
        $userId = (int) $userId;
6405
6406
        $extraFieldValue = new ExtraFieldValue('user');
6407
        $extraFieldsToDelete = [
6408
            'legal_accept',
6409
            'request_for_legal_agreement_consent_removal',
6410
            'request_for_legal_agreement_consent_removal_justification',
6411
            'request_for_delete_account_justification', // just in case delete also this
6412
            'request_for_delete_account',
6413
        ];
6414
6415
        foreach ($extraFieldsToDelete as $variable) {
6416
            $value = $extraFieldValue->get_values_by_handler_and_field_variable(
6417
                $userId,
6418
                $variable
6419
            );
6420
            if ($value && isset($value['id'])) {
6421
                $extraFieldValue->delete($value['id']);
6422
            }
6423
        }
6424
    }
6425
6426
    /**
6427
     * @return int
6428
     */
6429
    public static function getCountActiveUsers()
6430
    {
6431
        $table = Database::get_main_table(TABLE_MAIN_USER);
6432
        $sql = "SELECT count(id) count FROM $table WHERE active = 1 AND status <> ".ANONYMOUS;
6433
        $result = Database::query($sql);
6434
        $row = Database::fetch_array($result);
6435
6436
        return (int) $row['count'];
6437
    }
6438
6439
    /**
6440
     * @param array $userInfo
6441
     * @param int   $searchYear
6442
     *
6443
     * @throws Exception
6444
     *
6445
     * @return array
6446
     */
6447
    public static function getSubscribedSessionsByYear(array $userInfo, $searchYear)
6448
    {
6449
        $timezone = new DateTimeZone(api_get_timezone());
6450
6451
        $sessions = [];
6452
6453
        if (DRH == $userInfo['status']) {
6454
            $sessions = SessionManager::get_sessions_followed_by_drh($userInfo['id']);
6455
        } elseif (api_is_platform_admin(true)) {
6456
            $sessions = SessionManager::getSessionsForAdmin($userInfo['id']);
6457
        } else {
6458
            $sessionsByCategory = self::get_sessions_by_category($userInfo['id'], false, true, true);
6459
            $sessionsByCategory = array_column($sessionsByCategory, 'sessions');
6460
6461
            foreach ($sessionsByCategory as $sessionsInCategory) {
6462
                $sessions = array_merge($sessions, $sessionsInCategory);
6463
            }
6464
        }
6465
6466
        $sessions = array_map(
6467
            function ($sessionInfo) {
6468
                if (!isset($sessionInfo['session_id'])) {
6469
                    $sessionInfo['session_id'] = $sessionInfo['id'];
6470
                }
6471
                if (!isset($sessionInfo['session_name'])) {
6472
                    $sessionInfo['session_name'] = $sessionInfo['name'];
6473
                }
6474
6475
                return $sessionInfo;
6476
            },
6477
            $sessions
6478
        );
6479
6480
        $calendarSessions = [];
6481
6482
        foreach ($sessions as $sessionInfo) {
6483
            if (!empty($sessionInfo['duration'])) {
6484
                $courseAccess = CourseManager::getFirstCourseAccessPerSessionAndUser(
6485
                    $sessionInfo['session_id'],
6486
                    $userInfo['id']
6487
                );
6488
6489
                if (empty($courseAccess)) {
6490
                    continue;
6491
                }
6492
6493
                $firstAcessDate = new DateTime(api_get_local_time($courseAccess['login_course_date']), $timezone);
6494
                $lastAccessDate = clone $firstAcessDate;
6495
                $lastAccessDate->modify("+{$sessionInfo['duration']} days");
6496
6497
                $firstAccessYear = (int) $firstAcessDate->format('Y');
6498
                $lastAccessYear = (int) $lastAccessDate->format('Y');
6499
6500
                if ($firstAccessYear <= $searchYear && $lastAccessYear >= $searchYear) {
6501
                    $calendarSessions[$sessionInfo['session_id']] = [
6502
                        'name' => $sessionInfo['session_name'],
6503
                        'access_start_date' => $firstAcessDate->format('Y-m-d h:i:s'),
6504
                        'access_end_date' => $lastAccessDate->format('Y-m-d h:i:s'),
6505
                    ];
6506
                }
6507
6508
                continue;
6509
            }
6510
6511
            $accessStartDate = !empty($sessionInfo['access_start_date'])
6512
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6513
                : null;
6514
            $accessEndDate = !empty($sessionInfo['access_end_date'])
6515
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6516
                : null;
6517
            $accessStartYear = $accessStartDate ? (int) $accessStartDate->format('Y') : 0;
6518
            $accessEndYear = $accessEndDate ? (int) $accessEndDate->format('Y') : 0;
6519
6520
            $isValid = false;
6521
6522
            if ($accessStartYear && $accessEndYear) {
6523
                if ($accessStartYear <= $searchYear && $accessEndYear >= $searchYear) {
6524
                    $isValid = true;
6525
                }
6526
            }
6527
6528
            if ($accessStartYear && !$accessEndYear) {
6529
                if ($accessStartYear == $searchYear) {
6530
                    $isValid = true;
6531
                }
6532
            }
6533
6534
            if (!$accessStartYear && $accessEndYear) {
6535
                if ($accessEndYear == $searchYear) {
6536
                    $isValid = true;
6537
                }
6538
            }
6539
6540
            if ($isValid) {
6541
                $calendarSessions[$sessionInfo['session_id']] = [
6542
                    'name' => $sessionInfo['session_name'],
6543
                    'access_start_date' => $accessStartDate ? $accessStartDate->format('Y-m-d h:i:s') : null,
6544
                    'access_end_date' => $accessEndDate ? $accessEndDate->format('Y-m-d h:i:s') : null,
6545
                ];
6546
            }
6547
        }
6548
6549
        return $calendarSessions;
6550
    }
6551
6552
    /**
6553
     * Get sessions info for planification calendar.
6554
     *
6555
     * @param array $sessionsList Session list from UserManager::getSubscribedSessionsByYear
6556
     * @param int   $searchYear
6557
     *
6558
     * @throws Exception
6559
     *
6560
     * @return array
6561
     */
6562
    public static function getSessionsCalendarByYear(array $sessionsList, $searchYear)
6563
    {
6564
        $timezone = new DateTimeZone(api_get_timezone());
6565
        $calendar = [];
6566
6567
        foreach ($sessionsList as $sessionId => $sessionInfo) {
6568
            $startDate = $sessionInfo['access_start_date']
6569
                ? new DateTime(api_get_local_time($sessionInfo['access_start_date']), $timezone)
6570
                : null;
6571
            $endDate = $sessionInfo['access_end_date']
6572
                ? new DateTime(api_get_local_time($sessionInfo['access_end_date']), $timezone)
6573
                : null;
6574
6575
            $startYear = $startDate ? (int) $startDate->format('Y') : 0;
6576
            $startWeekYear = $startDate ? (int) $startDate->format('o') : 0;
6577
            $startWeek = $startDate ? (int) $startDate->format('W') : 0;
6578
            $endYear = $endDate ? (int) $endDate->format('Y') : 0;
6579
            $endWeekYear = $endDate ? (int) $endDate->format('o') : 0;
6580
            $endWeek = $endDate ? (int) $endDate->format('W') : 0;
6581
6582
            $start = $startWeekYear < $searchYear ? 0 : $startWeek - 1;
6583
            $duration = $endWeekYear > $searchYear ? 52 - $start : $endWeek - $start;
6584
6585
            $calendar[] = [
6586
                'id' => $sessionId,
6587
                'name' => $sessionInfo['name'],
6588
                'human_date' => SessionManager::convertSessionDateToString($startDate, $endDate, false, true),
0 ignored issues
show
Bug introduced by
It seems like $startDate can also be of type DateTime; however, parameter $startDate of SessionManager::convertSessionDateToString() does only seem to accept string, 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

6588
                'human_date' => SessionManager::convertSessionDateToString(/** @scrutinizer ignore-type */ $startDate, $endDate, false, true),
Loading history...
Bug introduced by
It seems like $endDate can also be of type DateTime; however, parameter $endDate of SessionManager::convertSessionDateToString() does only seem to accept string, 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

6588
                'human_date' => SessionManager::convertSessionDateToString($startDate, /** @scrutinizer ignore-type */ $endDate, false, true),
Loading history...
6589
                'start_in_last_year' => $startYear < $searchYear,
6590
                'end_in_next_year' => $endYear > $searchYear,
6591
                'no_start' => !$startWeek,
6592
                'no_end' => !$endWeek,
6593
                'start' => $start,
6594
                'duration' => $duration > 0 ? $duration : 1,
6595
            ];
6596
        }
6597
6598
        usort(
6599
            $calendar,
6600
            function ($sA, $sB) {
6601
                if ($sA['start'] == $sB['start']) {
6602
                    return 0;
6603
                }
6604
6605
                if ($sA['start'] < $sB['start']) {
6606
                    return -1;
6607
                }
6608
6609
                return 1;
6610
            }
6611
        );
6612
6613
        return $calendar;
6614
    }
6615
6616
    /**
6617
     * Return the user's full name. Optionally with the username.
6618
     *
6619
     * @param User $user
6620
     * @param bool $includeUsername Optional. By default username is not included.
6621
     *
6622
     * @return string
6623
     */
6624
    public static function formatUserFullName(User $user, $includeUsername = false): string
6625
    {
6626
        $fullName = api_get_person_name($user->getFirstname(), $user->getLastname());
6627
6628
        if ($includeUsername && api_get_setting('profile.hide_username_with_complete_name') === 'false') {
6629
            $username = $user->getUsername();
6630
6631
            return "$fullName ($username)";
6632
        }
6633
6634
        return $fullName;
6635
    }
6636
6637
    /**
6638
     * Disables or enables a user.
6639
     *
6640
     * @param int $user_id
6641
     * @param int $active  Enable or disable
6642
     *
6643
     * @return bool True on success, false on failure
6644
     * @assert (-1,0) === false
6645
     * @assert (1,1) === true
6646
     */
6647
    private static function change_active_state($user_id, $active)
6648
    {
6649
        if (strval(intval($user_id)) != $user_id) {
6650
            return false;
6651
        }
6652
        if ($user_id < 1) {
6653
            return false;
6654
        }
6655
        $user_id = intval($user_id);
6656
        $table_user = Database::get_main_table(TABLE_MAIN_USER);
6657
        $sql = "UPDATE $table_user SET active = '$active' WHERE id = $user_id";
6658
        $r = Database::query($sql);
6659
        $ev = LOG_USER_DISABLE;
6660
        if ($active == 1) {
6661
            $ev = LOG_USER_ENABLE;
6662
        }
6663
        if ($r !== false) {
6664
            Event::addEvent($ev, LOG_USER_ID, $user_id);
6665
        }
6666
6667
        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...
6668
    }
6669
6670
    /**
6671
     * Get either a Gravatar URL or complete image tag for a specified email address.
6672
     *
6673
     * @param string $email The email address
6674
     * @param int    $s     Size in pixels, defaults to 80px [ 1 - 2048 ]
6675
     * @param string $d     Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
6676
     * @param string $r     Maximum rating (inclusive) [ g | pg | r | x ]
6677
     * @param bool   $img   True to return a complete IMG tag False for just the URL
6678
     * @param array  $atts  Optional, additional key/value attributes to include in the IMG tag
6679
     *
6680
     * @return string containing either just a URL or a complete image tag
6681
     * @source http://gravatar.com/site/implement/images/php/
6682
     */
6683
    private static function getGravatar(
6684
        $email,
6685
        $s = 80,
6686
        $d = 'mm',
6687
        $r = 'g',
6688
        $img = false,
6689
        $atts = []
6690
    ) {
6691
        $url = 'http://www.gravatar.com/avatar/';
6692
        if (!empty($_SERVER['HTTPS'])) {
6693
            $url = 'https://secure.gravatar.com/avatar/';
6694
        }
6695
        $url .= md5(strtolower(trim($email)));
6696
        $url .= "?s=$s&d=$d&r=$r";
6697
        if ($img) {
6698
            $url = '<img src="'.$url.'"';
6699
            foreach ($atts as $key => $val) {
6700
                $url .= ' '.$key.'="'.$val.'"';
6701
            }
6702
            $url .= ' />';
6703
        }
6704
6705
        return $url;
6706
    }
6707
}
6708