Passed
Push — main ( d07ee1...38048d )
by Rafael
50:23
created

User::send_password()   C

Complexity

Conditions 10
Paths 80

Size

Total Lines 110
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 70
nc 80
nop 3
dl 0
loc 110
rs 6.7878
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* Copyright (c) 2002-2007  Rodolphe Quiedeville    <[email protected]>
4
 * Copyright (c) 2002-2003  Jean-Louis Bergamo      <[email protected]>
5
 * Copyright (c) 2004-2012  Laurent Destailleur     <[email protected]>
6
 * Copyright (C) 2004       Sebastien Di Cintio     <[email protected]>
7
 * Copyright (C) 2004       Benoit Mortier          <[email protected]>
8
 * Copyright (C) 2005-2017  Regis Houssin           <[email protected]>
9
 * Copyright (C) 2005       Lionel Cousteix         <[email protected]>
10
 * Copyright (C) 2011       Herve Prot              <[email protected]>
11
 * Copyright (C) 2013-2019  Philippe Grand          <[email protected]>
12
 * Copyright (C) 2013-2015  Alexandre Spangaro      <[email protected]>
13
 * Copyright (C) 2015       Marcos García           <[email protected]>
14
 * Copyright (C) 2018       charlene Benke          <[email protected]>
15
 * Copyright (C) 2018-2021  Nicolas ZABOURI         <[email protected]>
16
 * Copyright (C) 2019-2024  Frédéric France         <[email protected]>
17
 * Copyright (C) 2019       Abbes Bahfir            <[email protected]>
18
 * Copyright (C) 2024		MDW						<[email protected]>
19
 * Copyright (C) 2024       Rafael San José         <[email protected]>
20
 *
21
 * This program is free software; you can redistribute it and/or modify
22
 * it under the terms of the GNU General Public License as published by
23
 * the Free Software Foundation; either version 3 of the License, or
24
 * (at your option) any later version.
25
 *
26
 * This program is distributed in the hope that it will be useful,
27
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29
 * GNU General Public License for more details.
30
 *
31
 * You should have received a copy of the GNU General Public License
32
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
33
 */
34
35
namespace DoliModules\User\Model;
36
37
use DoliCore\Base\CommonPeople;
38
use DoliCore\Base\Config;
39
use DoliCore\Base\GenericDocument;
40
use DoliCore\Form\Form;
41
use DoliCore\Model\DefaultValues;
42
use DoliCore\Tools\Load;
43
use DoliDB;
44
use stdClass;
45
46
/**
47
 *  \file       htdocs/user/class/user.class.php
48
 *  \brief      File of class to manage users
49
 *  \ingroup    core
50
 */
51
52
require_once DOL_DOCUMENT_ROOT . '/../Dolibarr/Lib/Security.php';
53
54
/**
55
 *  Class to manage Dolibarr users
56
 */
57
class User extends GenericDocument
58
{
59
    use CommonPeople;
0 ignored issues
show
introduced by
The trait DoliCore\Base\CommonPeople requires some properties which are not provided by DoliModules\User\Model\User: $MAIN_SHOW_REGION_IN_STATE_SELECT, $phone_perso, $attributes, $company, $global, $nom, $use_javascript_ajax, $societe
Loading history...
60
61
    const STATUS_DISABLED = 0;
62
    const STATUS_ENABLED = 1;
63
    /**
64
     * @var string ID to identify managed object
65
     */
66
    public $element = 'user';
67
    /**
68
     * @var string Name of table without prefix where object is stored
69
     */
70
    public $table_element = 'user';
71
    /**
72
     * @var string Field with ID of parent key if this field has a parent
73
     */
74
    public $fk_element = 'fk_user';
75
    /**
76
     * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
77
     * @var int
78
     */
79
    public $ismultientitymanaged = 1;
80
    /**
81
     * @var int  Does object support extrafields ? 0=No, 1=Yes
82
     */
83
    public $isextrafieldmanaged = 1;
84
    /**
85
     * @var string picto
86
     */
87
    public $picto = 'user';
88
    public $id = 0;
89
    /**
90
     * @var User old copy of User
91
     */
92
    public $oldcopy;
93
    /**
94
     * @var int
95
     * @deprecated
96
     * @see $status
97
     */
98
    public $statut;
99
    public $status;
100
    /**
101
     * @var string      Open ID
102
     */
103
    public $openid;
104
    public $ldap_sid;
105
    public $search_sid;
106
    public $employee;
107
    public $civility_code;
108
    /**
109
     * @var string fullname
110
     */
111
    public $fullname;
112
    /**
113
     * @var string gender
114
     */
115
    public $gender;
116
    public $birth;
117
    /**
118
     * @var string email
119
     */
120
    public $email;
121
    /**
122
     * @var string email
123
     */
124
    public $email_oauth2;
125
    /**
126
     * @var string personal email
127
     */
128
    public $personal_email;
129
    /**
130
     * @var array array of socialnetwo18dprks
131
     */
132
    public $socialnetworks;
133
    /**
134
     * @var string job position
135
     */
136
    public $job;
137
    /**
138
     * @var string user signature
139
     */
140
    public $signature;
141
    /**
142
     * @var string office phone
143
     */
144
    public $office_phone;
145
    /**
146
     * @var string office fax
147
     */
148
    public $office_fax;
149
    /**
150
     * @var string phone mobile
151
     */
152
    public $user_mobile;
153
    /**
154
     * @var string personal phone mobile
155
     */
156
    public $personal_mobile;
157
    /**
158
     * @var int 1 if admin 0 if standard user
159
     */
160
    public $admin;
161
    /**
162
     * @var string user login
163
     */
164
    public $login;
165
    /**
166
     * @var string user apikey
167
     */
168
    public $api_key;
169
    /**
170
     * @var int Entity
171
     */
172
    public $entity;
173
    /**
174
     * @var string Clear password in memory
175
     */
176
    public $pass;
177
    /**
178
     * @var string Encrypted password in memory
179
     */
180
    public $pass_crypted;
181
    /**
182
     * @var string Clear password in database (defined if DATABASE_PWD_ENCRYPTED=0)
183
     */
184
    public $pass_indatabase;
185
    /**
186
     * @var string Encrypted password in database (always defined)
187
     */
188
    public $pass_indatabase_crypted;
189
    /**
190
     * @var string Temporary password
191
     */
192
    public $pass_temp;
193
    /**
194
     * Date creation record (datec)
195
     *
196
     * @var integer
197
     */
198
    public $datec;
199
    /**
200
     * Date modification record (tms)
201
     *
202
     * @var integer
203
     */
204
    public $datem;
205
    /**
206
     * @var int If this is defined, it is an external user
207
     */
208
    public $socid;
209
    /**
210
     * @var int If this is defined, it is a user created from a contact
211
     */
212
    public $contact_id;
213
    /**
214
     * @var int ID
215
     */
216
    public $fk_member;
217
    /**
218
     * @var int User ID of supervisor
219
     */
220
    public $fk_user;
221
    /**
222
     * @var int User ID of expense validator
223
     */
224
    public $fk_user_expense_validator;
225
    /**
226
     * @var int User ID of holidays validator
227
     */
228
    public $fk_user_holiday_validator;
229
    /**
230
     * @string clicktodial url
231
     */
232
    public $clicktodial_url;
233
    /**
234
     * @var string clicktodial login
235
     */
236
    public $clicktodial_login;
237
    /**
238
     * @var string clicktodial password
239
     */
240
    public $clicktodial_password;
241
    /**
242
     * @var string clicktodial poste
243
     */
244
    public $clicktodial_poste;
245
    /**
246
     * @var int     0 by default, 1 if click to dial data were already loaded for this user
247
     */
248
    public $clicktodial_loaded;
249
    public $datelastlogin;
250
    public $datepreviouslogin;
251
    public $flagdelsessionsbefore;
252
    public $iplastlogin;
253
    public $ippreviouslogin;
254
    public $datestartvalidity;
255
    public $dateendvalidity;
256
    /**
257
     * @var string photo filename
258
     */
259
    public $photo;
260
    /**
261
     * @var string default language
262
     */
263
    public $lang;
264
    /**
265
     * @var stdClass Class of permissions user->rights->permx
266
     */
267
    public $rights;
268
    /**
269
     * @var int  All permissions are loaded
270
     */
271
    public $all_permissions_are_loaded;
272
    /**
273
     * @var int Number of rights granted to the user. Value loaded after a getrights().
274
     */
275
    public $nb_rights;
276
    /**
277
     * @var array   To store list of groups of user (used by API /info for example)
278
     */
279
    public $user_group_list;
280
    /**
281
     * @var stdClass To store personal config
282
     */
283
    public $conf; // To store default values for user. Loaded by loadDefaultValues().
284
    public $default_values; // To store current search criteria for user
285
    public $lastsearch_values_tmp; // To store last saved search criteria for user
286
    public $lastsearch_values;
287
    /**
288
     * @var array<int,array{rowid:int,id:int,fk_user:int,fk_soc:int,firstname:string,lastname:string,login:string,statut:int,entity:string,email:string,gender:int,admin:string,photo:string,fullpath:string,fullname:string,level:int}>
289
     *       Store the entire hierarchy tree of users
290
     */
291
    public $users = []; // To store an array of all parents for all ids.
292
    public $parentof; // Cache array of already loaded children
293
    public $accountancy_code; // Accountancy code in prevision of the complete accountancy module
294
295
    public $thm; // Average cost of employee - Used for valuation of time spent
296
    public $tjm; // Average cost of employee
297
298
    public $salary; // Monthly salary       - Denormalized value from llx_user_employment
299
    public $salaryextra; // Monthly salary extra - Denormalized value from llx_user_employment
300
    public $weeklyhours; // Weekly hours         - Denormalized value from llx_user_employment
301
302
    /**
303
     * @var string Define background color for user in agenda
304
     */
305
    public $color;
306
307
    public $dateemployment; // Define date of employment by company
308
    public $dateemploymentend; // Define date of employment end by company
309
310
    public $default_c_exp_tax_cat;
311
312
    /**
313
     * @var string ref for employee
314
     */
315
    public $ref_employee;
316
317
    /**
318
     * @var string national registration number
319
     */
320
    public $national_registration_number;
321
322
    public $default_range;
323
324
    /**
325
     * @var int id of warehouse
326
     */
327
    public $fk_warehouse;
328
329
    /**
330
     * @var int egroupware id
331
     */
332
    public $egroupware_id;
333
334
    public $fields = [
335
        'rowid' => ['type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'index' => 1, 'position' => 1, 'comment' => 'Id'],
336
        'lastname' => ['type' => 'varchar(50)', 'label' => 'Lastname', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 20, 'searchall' => 1],
337
        'firstname' => ['type' => 'varchar(50)', 'label' => 'Firstname', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 10, 'searchall' => 1],
338
        'ref_employee' => ['type' => 'varchar(50)', 'label' => 'RefEmployee', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 30, 'searchall' => 1],
339
        'national_registration_number' => ['type' => 'varchar(50)', 'label' => 'NationalRegistrationNumber', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 40, 'searchall' => 1],
340
    ];
341
    /**
342
     * @var array Cache array of already loaded permissions
343
     */
344
    private $_tab_loaded = [];
345
    private $cache_childids;
346
    /**
347
     * Cache the SQL results of the function "findUserIdByEmail($email)"
348
     *
349
     * NOTE: findUserIdByEmailCache[...] === -1 means not found in database
350
     *
351
     * @var array
352
     */
353
    private $findUserIdByEmailCache;
354
355
    /**
356
     *    Constructor of the class
357
     *
358
     * @param DoliDB $db Database handler
359
     */
360
    public function __construct($db)
361
    {
362
        $this->db = $db;
363
364
        // User preference
365
        $this->clicktodial_loaded = 0;
366
367
        // For cache usage
368
        $this->all_permissions_are_loaded = 0;
369
        $this->nb_rights = 0;
370
371
        // Force some default values
372
        $this->admin = 0;
373
        $this->employee = 1;
374
375
        $this->conf = new stdClass();
376
        $this->rights = new stdClass();
377
        $this->rights->user = new stdClass();
378
        $this->rights->user->user = new stdClass();
379
        $this->rights->user->self = new stdClass();
380
        $this->rights->user->user_advance = new stdClass();
381
        $this->rights->user->self_advance = new stdClass();
382
        $this->rights->user->group_advance = new stdClass();
383
    }
384
385
    /**
386
     * Function used to replace a thirdparty id with another one.
387
     *
388
     * @param DoliDB $dbs Database handler, because function is static we name it $dbs not $db to avoid breaking
389
     *                          coding test
390
     * @param int $origin_id Old thirdparty id
391
     * @param int $dest_id New thirdparty id
392
     *
393
     * @return  bool
394
     */
395
    public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
396
    {
397
        $tables = [
398
            'user',
399
        ];
400
401
        return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
402
    }
403
404
    /**
405
     *  Add a right to the user
406
     *
407
     * @param int $rid Id of permission to add or 0 to add several permissions
408
     * @param string $allmodule Add all permissions of module $allmodule or 'allmodules' to include all modules.
409
     * @param string $allperms Add all permissions of module $allmodule, subperms $allperms only or '' to include all
410
     *                          permissions.
411
     * @param int $entity Entity to use
412
     * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
413
     *
414
     * @return int                     > 0 if OK, < 0 if KO
415
     * @see    clearrights(), delrights(), getrights(), hasRight()
416
     */
417
    public function addrights($rid, $allmodule = '', $allperms = '', $entity = 0, $notrigger = 0)
418
    {
419
        global $conf, $user, $langs;
420
        global $config;
421
        $dbPrefix = $config->db->prefix;
422
423
        $entity = (empty($entity) ? $conf->entity : $entity);
424
425
        dol_syslog(get_class($this) . "::addrights $rid, $allmodule, $allperms, $entity, $notrigger for user id=" . $this->id);
426
427
        if (empty($this->id)) {
428
            $this->error = 'Try to call addrights on an object user with an empty id';
429
            return -1;
430
        }
431
432
        $error = 0;
433
        $whereforadd = '';
434
435
        $this->db->begin();
436
437
        if (!empty($rid)) {
438
            $module = $perms = $subperms = '';
439
440
            // If we ask to add a given permission, we first load properties of this permission (module, perms and subperms).
441
            $sql = "SELECT module, perms, subperms";
442
            $sql .= " FROM " . $dbPrefix . "rights_def";
443
            $sql .= " WHERE id = " . ((int)$rid);
444
            $sql .= " AND entity = " . ((int)$entity);
445
446
            $result = $this->db->query($sql);
447
            if ($result) {
448
                $obj = $this->db->fetch_object($result);
449
450
                if ($obj) {
451
                    $module = $obj->module;
452
                    $perms = $obj->perms;
453
                    $subperms = $obj->subperms;
454
                }
455
            } else {
456
                $error++;
457
                dol_print_error($this->db);
458
            }
459
460
            // Define the where for the permission to add
461
            $whereforadd = "id=" . ((int)$rid);
462
            // Add also inherited permissions
463
            if (!empty($subperms)) {
464
                $whereforadd .= " OR (module='" . $this->db->escape($module) . "' AND perms='" . $this->db->escape($perms) . "' AND (subperms='lire' OR subperms='read'))";
465
            } elseif (!empty($perms)) {
466
                $whereforadd .= " OR (module='" . $this->db->escape($module) . "' AND (perms='lire' OR perms='read') AND subperms IS NULL)";
467
            }
468
        } else {
469
            // A list of permission was requested (not a single specific permission)
470
            // based on the name of a module of permissions
471
            // Used in the where clause to determine the list of permissions to add.
472
            if (!empty($allmodule)) {
473
                if ($allmodule == 'allmodules') {
474
                    $whereforadd = 'allmodules';
475
                } else {
476
                    $whereforadd = "module='" . $this->db->escape($allmodule) . "'";
477
                    if (!empty($allperms)) {
478
                        $whereforadd .= " AND perms='" . $this->db->escape($allperms) . "'";
479
                    }
480
                }
481
            }
482
        }
483
484
        // Add automatically other permission using the criteria whereforadd
485
        // $whereforadd can be a SQL filter or the string 'allmodules'
486
        if (!empty($whereforadd)) {
487
            //print "$module-$perms-$subperms";
488
            $sql = "SELECT id";
489
            $sql .= " FROM " . $dbPrefix . "rights_def";
490
            $sql .= " WHERE entity = " . ((int)$entity);
491
            if (!empty($whereforadd) && $whereforadd != 'allmodules') {
492
                $sql .= " AND (" . $whereforadd . ")";  // Note: parenthesis are important because whereforadd can contains OR. Also note that $whereforadd is already sanitized
493
            }
494
495
            $sqldelete = "DELETE FROM " . $dbPrefix . "user_rights";
496
            $sqldelete .= " WHERE fk_user = " . ((int)$this->id) . " AND fk_id IN (";
497
            $sqldelete .= $sql;
498
            $sqldelete .= ") AND entity = " . ((int)$entity);
499
            if (!$this->db->query($sqldelete)) {
500
                $error++;
501
            }
502
503
            if (!$error) {
504
                $resql = $this->db->query($sql);
505
                if ($resql) {
506
                    $num = $this->db->num_rows($resql);
507
                    $i = 0;
508
                    while ($i < $num) {
509
                        $obj = $this->db->fetch_object($resql);
510
511
                        if ($obj) {
512
                            $nid = $obj->id;
513
514
                            $sql = "INSERT INTO " . $dbPrefix . "user_rights (entity, fk_user, fk_id) VALUES (" . ((int)$entity) . ", " . ((int)$this->id) . ", " . ((int)$nid) . ")";
515
                            if (!$this->db->query($sql)) {
516
                                $error++;
517
                            }
518
                        }
519
520
                        $i++;
521
                    }
522
                } else {
523
                    $error++;
524
                    dol_print_error($this->db);
525
                }
526
            }
527
        }
528
529
        if (!$error && !$notrigger) {
530
            $langs->load("other");
531
            $this->context = ['audit' => $langs->trans("PermissionsAdd") . ($rid ? ' (id=' . $rid . ')' : '')];
532
533
            // Call trigger
534
            $result = $this->call_trigger('USER_MODIFY', $user);
535
            if ($result < 0) {
536
                $error++;
537
            }
538
            // End call triggers
539
        }
540
541
        if ($error) {
542
            $this->db->rollback();
543
            return -$error;
544
        } else {
545
            $this->db->commit();
546
            return 1;
547
        }
548
    }
549
550
    /**
551
     *  Remove a right to the user
552
     *
553
     * @param int $rid Id du droit a retirer
554
     * @param string $allmodule Retirer tous les droits du module allmodule
555
     * @param string $allperms Retirer tous les droits du module allmodule, perms allperms
556
     * @param int $entity Entity to use
557
     * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
558
     *
559
     * @return int                 > 0 if OK, < 0 if OK
560
     * @see    clearrights(), addrights(), getrights(), hasRight()
561
     */
562
    public function delrights($rid, $allmodule = '', $allperms = '', $entity = 0, $notrigger = 0)
563
    {
564
        global $conf, $user, $langs;
565
566
        $error = 0;
567
        $wherefordel = '';
568
        $entity = (!empty($entity) ? $entity : $conf->entity);
569
570
        $this->db->begin();
571
572
        if (!empty($rid)) {
573
            $module = $perms = $subperms = '';
574
575
            // When the request is to delete a specific permissions, this gets the
576
            // les charactis for the module, permissions and sub-permission of this permission.
577
            $sql = "SELECT module, perms, subperms";
578
            $sql .= " FROM " . $dbPrefix . "rights_def";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
579
            $sql .= " WHERE id = '" . $this->db->escape($rid) . "'";
580
            $sql .= " AND entity = " . ((int)$entity);
581
582
            $result = $this->db->query($sql);
583
            if ($result) {
584
                $obj = $this->db->fetch_object($result);
585
586
                if ($obj) {
587
                    $module = $obj->module;
588
                    $perms = $obj->perms;
589
                    $subperms = $obj->subperms;
590
                }
591
            } else {
592
                $error++;
593
                dol_print_error($this->db);
594
            }
595
596
            // Where clause for the list of permissions to delete
597
            $wherefordel = "id=" . ((int)$rid);
598
            // Suppression des droits induits
599
            if ($subperms == 'lire' || $subperms == 'read') {
600
                $wherefordel .= " OR (module='" . $this->db->escape($module) . "' AND perms='" . $this->db->escape($perms) . "' AND subperms IS NOT NULL)";
601
            }
602
            if ($perms == 'lire' || $perms == 'read') {
603
                $wherefordel .= " OR (module='" . $this->db->escape($module) . "')";
604
            }
605
        } else {
606
            // The deletion of the permissions concerns the name of a module or
607
            // list of permissions.
608
            // Used in the Where clause to determine the list of permission to delete
609
            if (!empty($allmodule)) {
610
                if ($allmodule == 'allmodules') {
611
                    $wherefordel = 'allmodules';
612
                } else {
613
                    $wherefordel = "module='" . $this->db->escape($allmodule) . "'";
614
                    if (!empty($allperms)) {
615
                        $wherefordel .= " AND perms='" . $this->db->escape($allperms) . "'";
616
                    }
617
                }
618
            }
619
        }
620
621
        // Suppression des droits selon critere defini dans wherefordel
622
        if (!empty($wherefordel)) {
623
            //print "$module-$perms-$subperms";
624
            $sql = "SELECT id";
625
            $sql .= " FROM " . $dbPrefix . "rights_def";
626
            $sql .= " WHERE entity = " . ((int)$entity);
627
            if (!empty($wherefordel) && $wherefordel != 'allmodules') {
628
                $sql .= " AND (" . $wherefordel . ")";  // Note: parenthesis are important because wherefordel can contains OR. Also note that $wherefordel is already sanitized
629
            }
630
631
            // avoid admin can remove his own important rights
632
            if ($this->admin == 1) {
633
                $sql .= " AND id NOT IN (251, 252, 253, 254, 255, 256)"; // other users rights
634
                $sql .= " AND id NOT IN (341, 342, 343, 344)"; // own rights
635
                $sql .= " AND id NOT IN (351, 352, 353, 354)"; // groups rights
636
                $sql .= " AND id NOT IN (358)"; // user export
637
            }
638
639
            $sqldelete = "DELETE FROM " . $dbPrefix . "user_rights";
640
            $sqldelete .= " WHERE fk_user = " . ((int)$this->id) . " AND fk_id IN (";
641
            $sqldelete .= $sql;
642
            $sqldelete .= ")";
643
            $sqldelete .= " AND entity = " . ((int)$entity);
644
645
            $resql = $this->db->query($sqldelete);
646
            if (!$resql) {
647
                $error++;
648
                dol_print_error($this->db);
649
            }
650
        }
651
652
        if (!$error && !$notrigger) {
653
            $langs->load("other");
654
            $this->context = ['audit' => $langs->trans("PermissionsDelete") . ($rid ? ' (id=' . $rid . ')' : '')];
655
656
            // Call trigger
657
            $result = $this->call_trigger('USER_MODIFY', $user);
658
            if ($result < 0) {
659
                $error++;
660
            }
661
            // End call triggers
662
        }
663
664
        if ($error) {
665
            $this->db->rollback();
666
            return -$error;
667
        } else {
668
            $this->db->commit();
669
            return 1;
670
        }
671
    }
672
673
    /**
674
     *  Clear all permissions array of user
675
     *
676
     * @return void
677
     * @see    getrights(), hasRight()
678
     */
679
    public function clearrights()
680
    {
681
        dol_syslog(get_class($this) . "::clearrights reset user->rights");
682
        $this->rights = new stdClass();
683
        $this->nb_rights = 0;
684
        $this->all_permissions_are_loaded = 0;
685
        $this->_tab_loaded = [];
686
    }
687
688
    /**
689
     *  Load permissions granted to a user->id into object user->rights
690
     *
691
     * @param string $moduletag Limit permission for a particular module ('' by default means load all permissions)
692
     * @param int $forcereload Force reload of permissions even if they were already loaded (ignore cache)
693
     *
694
     * @return void
695
     * @see    clearrights(), delrights(), addrights(), hasRight()
696
     */
697
    public function getrights($moduletag = '', $forcereload = 0)
698
    {
699
        global $conf;
700
        $dbPrefix = $conf->db->prefix;
701
702
        $alreadyloaded = false;
703
704
        if (empty($forcereload)) {
705
            if ($moduletag && isset($this->_tab_loaded[$moduletag]) && $this->_tab_loaded[$moduletag]) {
706
                // Rights for this module are already loaded, so we leave
707
                $alreadyloaded = true;
708
            }
709
710
            if (!empty($this->all_permissions_are_loaded)) {
711
                // We already loaded all rights for this user, so we leave
712
                $alreadyloaded = true;
713
            }
714
        }
715
716
        // For avoid error
717
        if (!isset($this->rights) || !is_object($this->rights)) {
718
            $this->rights = new stdClass(); // For avoid error
719
        }
720
        if (!isset($this->rights->user) || !is_object($this->rights->user)) {
721
            $this->rights->user = new stdClass(); // For avoid error
722
        }
723
724
        // Get permission of users + Get permissions of groups
725
        if ($alreadyloaded) {
726
            return;
727
        }
728
729
        // First user permissions
730
        $sql = "SELECT DISTINCT r.module, r.perms, r.subperms";
731
        $sql .= " FROM " . $dbPrefix . "user_rights as ur,";
732
        $sql .= " " . $dbPrefix . "rights_def as r";
733
        $sql .= " WHERE r.id = ur.fk_id";
734
        if (getDolGlobalString('MULTICOMPANY_BACKWARD_COMPATIBILITY')) {
735
            // on old version, we use entity defined into table r only
736
            $sql .= " AND r.entity IN (0," . (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE') ? "1," : "") . $conf->entity . ")";
737
        } else {
738
            // On table r=rights_def, the unique key is (id, entity) because id is hard coded into module descriptor and insert during module activation.
739
            // So we must include the filter on entity on both table r. and ur.
740
            $sql .= " AND r.entity = " . ((int)$conf->entity) . " AND ur.entity = " . ((int)$conf->entity);
741
        }
742
        $sql .= " AND ur.fk_user= " . ((int)$this->id);
743
        $sql .= " AND r.perms IS NOT NULL";
744
        if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
745
            $sql .= " AND r.perms NOT LIKE '%_advance'"; // Hide advanced perms if option is not enabled
746
        }
747
        if ($moduletag) {
748
            $sql .= " AND r.module = '" . $this->db->escape($moduletag) . "'";
749
        }
750
751
        $resql = $this->db->query($sql);
752
        if ($resql) {
753
            $num = $this->db->num_rows($resql);
754
            $i = 0;
755
            while ($i < $num) {
756
                $obj = $this->db->fetch_object($resql);
757
758
                if ($obj) {
759
                    $module = $obj->module;
760
                    $perms = $obj->perms;
761
                    $subperms = $obj->subperms;
762
763
                    if (!empty($perms)) {
764
                        if (!empty($module)) {
765
                            if (!isset($this->rights->$module) || !is_object($this->rights->$module)) {
766
                                $this->rights->$module = new stdClass();
767
                            }
768
                            if (!empty($subperms)) {
769
                                if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
770
                                    $this->rights->$module->$perms = new stdClass();
771
                                }
772
                                if (empty($this->rights->$module->$perms->$subperms)) {
773
                                    $this->nb_rights++;
774
                                }
775
                                $this->rights->$module->$perms->$subperms = 1;
776
                            } else {
777
                                if (empty($this->rights->$module->$perms)) {
778
                                    $this->nb_rights++;
779
                                }
780
                                $this->rights->$module->$perms = 1;
781
                            }
782
                        }
783
                    }
784
                }
785
                $i++;
786
            }
787
            $this->db->free($resql);
788
        }
789
790
        // Now permissions of groups
791
        $sql = "SELECT DISTINCT r.module, r.perms, r.subperms";
792
        $sql .= " FROM " . $dbPrefix . "usergroup_rights as gr,";
793
        $sql .= " " . $dbPrefix . "usergroup_user as gu,";
794
        $sql .= " " . $dbPrefix . "rights_def as r";
795
        $sql .= " WHERE r.id = gr.fk_id";
796
        // A very strange business rules. Must be same than into user->getrights() user/perms.php and user/group/perms.php
797
        if (getDolGlobalString('MULTICOMPANY_BACKWARD_COMPATIBILITY')) {
798
            if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
799
                $sql .= " AND gu.entity IN (0," . $conf->entity . ")";
800
            } else {
801
                $sql .= " AND r.entity = " . ((int)$conf->entity);
802
            }
803
        } else {
804
            $sql .= " AND gr.entity = " . ((int)$conf->entity);  // Only groups created in current entity
805
            // The entity on the table usergroup_user should be useless and should never be used because it is already into gr and r.
806
            // but when using MULTICOMPANY_TRANSVERSE_MODE, we may insert record that make rubbish result due to duplicate record of
807
            // other entities, so we are forced to add a filter here
808
            $sql .= " AND gu.entity IN (0," . $conf->entity . ")";
809
            $sql .= " AND r.entity = " . ((int)$conf->entity);   // Only permission of modules enabled in current entity
810
        }
811
        // End of strange business rule
812
        $sql .= " AND gr.fk_usergroup = gu.fk_usergroup";
813
        $sql .= " AND gu.fk_user = " . ((int)$this->id);
814
        $sql .= " AND r.perms IS NOT NULL";
815
        if ($moduletag) {
816
            $sql .= " AND r.module = '" . $this->db->escape($moduletag) . "'";
817
        }
818
819
        $resql = $this->db->query($sql);
820
        if ($resql) {
821
            $num = $this->db->num_rows($resql);
822
            $i = 0;
823
            while ($i < $num) {
824
                $obj = $this->db->fetch_object($resql);
825
826
                if ($obj) {
827
                    $module = $obj->module;
828
                    $perms = $obj->perms;
829
                    $subperms = $obj->subperms;
830
831
                    if (!empty($perms)) {
832
                        if (!empty($module)) {
833
                            if (!isset($this->rights->$module) || !is_object($this->rights->$module)) {
834
                                $this->rights->$module = new stdClass();
835
                            }
836
                            if (!empty($subperms)) {
837
                                if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
838
                                    $this->rights->$module->$perms = new stdClass();
839
                                }
840
                                if (empty($this->rights->$module->$perms->$subperms)) {
841
                                    $this->nb_rights++;
842
                                }
843
                                $this->rights->$module->$perms->$subperms = 1;
844
                            } else {
845
                                if (empty($this->rights->$module->$perms)) {
846
                                    $this->nb_rights++;
847
                                }
848
                                // if we have already define a subperm like this $this->rights->$module->level1->level2 with llx_user_rights, we don't want override level1 because the level2 can be not define on user group
849
                                if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
850
                                    $this->rights->$module->$perms = 1;
851
                                }
852
                            }
853
                        }
854
                    }
855
                }
856
                $i++;
857
            }
858
            $this->db->free($resql);
859
        }
860
861
        // Force permission on user for admin
862
        if (!empty($this->admin)) {
863
            if (empty($this->rights->user->user)) {
864
                $this->rights->user->user = new stdClass();
865
            }
866
            $listofpermtotest = ['lire', 'creer', 'password', 'supprimer', 'export'];
867
            foreach ($listofpermtotest as $permtotest) {
868
                if (empty($this->rights->user->user->$permtotest)) {
869
                    $this->rights->user->user->$permtotest = 1;
870
                    $this->nb_rights++;
871
                }
872
            }
873
            if (empty($this->rights->user->self)) {
874
                $this->rights->user->self = new stdClass();
875
            }
876
            $listofpermtotest = ['creer', 'password'];
877
            foreach ($listofpermtotest as $permtotest) {
878
                if (empty($this->rights->user->self->$permtotest)) {
879
                    $this->rights->user->self->$permtotest = 1;
880
                    $this->nb_rights++;
881
                }
882
            }
883
            // Add test on advanced permissions
884
            if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
885
                if (empty($this->rights->user->user_advance)) {
886
                    $this->rights->user->user_advance = new stdClass();
887
                }
888
                $listofpermtotest = ['readperms', 'write'];
889
                foreach ($listofpermtotest as $permtotest) {
890
                    if (empty($this->rights->user->user_advance->$permtotest)) {
891
                        $this->rights->user->user_advance->$permtotest = 1;
892
                        $this->nb_rights++;
893
                    }
894
                }
895
                if (empty($this->rights->user->self_advance)) {
896
                    $this->rights->user->self_advance = new stdClass();
897
                }
898
                $listofpermtotest = ['readperms', 'writeperms'];
899
                foreach ($listofpermtotest as $permtotest) {
900
                    if (empty($this->rights->user->self_advance->$permtotest)) {
901
                        $this->rights->user->self_advance->$permtotest = 1;
902
                        $this->nb_rights++;
903
                    }
904
                }
905
                if (empty($this->rights->user->group_advance)) {
906
                    $this->rights->user->group_advance = new stdClass();
907
                }
908
                $listofpermtotest = ['read', 'readperms', 'write', 'delete'];
909
                foreach ($listofpermtotest as $permtotest) {
910
                    if (empty($this->rights->user) || empty($this->rights->user->group_advance->$permtotest)) {
911
                        $this->rights->user->group_advance->$permtotest = 1;
912
                        $this->nb_rights++;
913
                    }
914
                }
915
            }
916
        }
917
918
        // For backward compatibility
919
        if (isset($this->rights->propale) && !isset($this->rights->propal)) {
920
            $this->rights->propal = $this->rights->propale;
921
        }
922
        if (isset($this->rights->propal) && !isset($this->rights->propale)) {
923
            $this->rights->propale = $this->rights->propal;
924
        }
925
926
        if (!$moduletag) {
927
            // If the module was not define, then everything is loaded.
928
            // Therefore, we can consider that the permissions are cached
929
            // because they were all loaded for this user instance.
930
            $this->all_permissions_are_loaded = 1;
931
        } else {
932
            // If the module is defined, we flag it as loaded into cache
933
            $this->_tab_loaded[$moduletag] = 1;
934
        }
935
    }
936
937
    /**
938
     * Return array of data to show into tooltips
939
     *
940
     * @param array $params Array with options, infologin
941
     *
942
     * @return array
943
     * @since v18
944
     */
945
    public function getTooltipContentArray($params)
946
    {
947
        global $conf, $langs, $menumanager;
948
        global $dolibarr_main_demo;
949
950
        $infologin = $params['infologin'] ?? 0;
951
        $option = $params['option'] ?? '';
952
953
        $data = [];
954
        if (!empty($this->photo)) {
955
            $photo = '<div class="photointooltip floatright">';
956
            $photo .= Form::showphoto('userphoto', $this, 0, 60, 0, 'photoref photowithmargin photologintooltip', 'small', 0, 1); // Force height to 60 so we total height of tooltip can be calculated and collision can be managed
957
            $photo .= '</div>';
958
            $data['photo'] = $photo;
959
            //$label .= '<div style="clear: both;"></div>';
960
        }
961
962
        // Info Login
963
        $data['opendiv'] = '<div class="centpercent divtooltip">';
964
        $data['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("User") . '</u> ' . $this->getLibStatut(4);
965
        $data['name'] = '<br><b>' . $langs->trans('Name') . ':</b> ' . dol_string_nohtmltag($this->getFullName($langs, ''));
966
        if (!empty($this->login)) {
967
            $data['login'] = '<br><b>' . $langs->trans('Login') . ':</b> ' . dol_string_nohtmltag($this->login);
968
        }
969
        if (!empty($this->job)) {
970
            $data['job'] = '<br><b>' . $langs->trans("Job") . ':</b> ' . dol_string_nohtmltag($this->job);
971
        }
972
        $data['email'] = '<br><b>' . $langs->trans("Email") . ':</b> ' . dol_string_nohtmltag($this->email);
973
        if (!empty($this->office_phone) || !empty($this->office_fax) || !empty($this->fax)) {
974
            $phonelist = [];
975
            if ($this->office_phone) {
976
                $phonelist[] = dol_print_phone($this->office_phone, $this->country_code, $this->id, 0, '', '&nbsp', 'phone');
977
            }
978
            if ($this->office_fax) {
979
                $phonelist[] = dol_print_phone($this->office_fax, $this->country_code, $this->id, 0, '', '&nbsp', 'fax');
980
            }
981
            if ($this->user_mobile) {
982
                $phonelist[] = dol_print_phone($this->user_mobile, $this->country_code, $this->id, 0, '', '&nbsp', 'mobile');
983
            }
984
            $data['phones'] = '<br><b>' . $langs->trans('Phone') . ':</b> ' . implode('&nbsp;', $phonelist);
985
        }
986
        if (!empty($this->admin)) {
987
            $data['administrator'] = '<br><b>' . $langs->trans("Administrator") . '</b>: ' . yn($this->admin);
988
        }
989
        if (!empty($this->accountancy_code) || $option == 'accountancy') {
990
            $langs->load("companies");
991
            $data['accountancycode'] = '<br><b>' . $langs->trans("AccountancyCode") . '</b>: ' . $this->accountancy_code;
992
        }
993
        $company = '';
994
        if (!empty($this->socid)) { // Add thirdparty for external users
995
            $thirdpartystatic = new Societe($this->db);
996
            $thirdpartystatic->fetch($this->socid);
997
            $companyimg = '';
998
            if (empty($params['hidethirdpartylogo'])) {
999
                $companyimg = ' ' . $thirdpartystatic->getNomUrl(2, (($option == 'nolink') ? 'nolink' : '')); // picto only of company
1000
            }
1001
            $company = ' (' . $langs->trans("Company") . ': ' . ($companyimg ? $companyimg : img_picto('', 'company')) . ' ' . dol_string_nohtmltag($thirdpartystatic->name) . ')';
1002
        }
1003
        $type = ($this->socid ? $langs->trans("ExternalUser") . $company : $langs->trans("InternalUser"));
1004
        $data['type'] = '<br><b>' . $langs->trans("Type") . ':</b> ' . $type;
1005
        $data['closediv'] = '</div>';
1006
1007
        if ($infologin > 0) {
1008
            $data['newlinelogin'] = '<br>';
1009
            $data['session'] = '<br><u>' . $langs->trans("Session") . '</u>';
1010
            $data['ip'] = '<br><b>' . $langs->trans("IPAddress") . '</b>: ' . dol_string_nohtmltag(getUserRemoteIP());
1011
            if (getDolGlobalString('MAIN_MODULE_MULTICOMPANY')) {
1012
                $data['multicompany'] = '<br><b>' . $langs->trans("ConnectedOnMultiCompany") . ':</b> ' . $conf->entity . ' (User entity ' . $this->entity . ')';
1013
            }
1014
            $data['authentication'] = '<br><b>' . $langs->trans("AuthenticationMode") . ':</b> ' . dol_string_nohtmltag($_SESSION["dol_authmode"] . (empty($dolibarr_main_demo) ? '' : ' (demo)'));
1015
            $data['connectedsince'] = '<br><b>' . $langs->trans("ConnectedSince") . ':</b> ' . dol_print_date($this->datelastlogin, "dayhour", 'tzuser');
1016
            $data['previousconnexion'] = '<br><b>' . $langs->trans("PreviousConnexion") . ':</b> ' . dol_print_date($this->datepreviouslogin, "dayhour", 'tzuser');
1017
            $data['currenttheme'] = '<br><b>' . $langs->trans("CurrentTheme") . ':</b> ' . dol_string_nohtmltag($conf->theme);
1018
            $data['currentmenumanager'] = '<br><b>' . $langs->trans("CurrentMenuManager") . ':</b> ' . dol_string_nohtmltag($menumanager->name);
1019
            $s = picto_from_langcode($langs->getDefaultLang());
1020
            $data['currentuserlang'] = '<br><b>' . $langs->trans("CurrentUserLanguage") . ':</b> ' . dol_string_nohtmltag(($s ? $s . ' ' : '') . $langs->getDefaultLang());
1021
            $data['browser'] = '<br><b>' . $langs->trans("Browser") . ':</b> ' . dol_string_nohtmltag($conf->browser->name . ($conf->browser->version ? ' ' . $conf->browser->version : '') . ' (' . $_SERVER['HTTP_USER_AGENT'] . ')');
1022
            $data['layout'] = '<br><b>' . $langs->trans("Layout") . ':</b> ' . dol_string_nohtmltag($conf->browser->layout);
1023
            $data['screen'] = '<br><b>' . $langs->trans("Screen") . ':</b> ' . dol_string_nohtmltag($_SESSION['dol_screenwidth'] . ' x ' . $_SESSION['dol_screenheight']);
1024
            if ($conf->browser->layout == 'phone') {
1025
                $data['phone'] = '<br><b>' . $langs->trans("Phone") . ':</b> ' . $langs->trans("Yes");
1026
            }
1027
            if (!empty($_SESSION["disablemodules"])) {
1028
                $data['disabledmodules'] = '<br><b>' . $langs->trans("DisabledModules") . ':</b> <br>' . dol_string_nohtmltag(implode(', ', explode(',', $_SESSION["disablemodules"])));
1029
            }
1030
        }
1031
1032
        return $data;
1033
    }
1034
1035
    /**
1036
     *  Return the label of the status of user (active, inactive)
1037
     *
1038
     * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short
1039
     *                  label + Picto, 6=Long label + Picto
1040
     *
1041
     * @return string                 Label of status
1042
     */
1043
    public function getLibStatut($mode = 0)
1044
    {
1045
        return $this->LibStatut(isset($this->statut) ? (int)$this->statut : (int)$this->status, $mode);
1046
    }
1047
1048
    /**
1049
     *  Return the label of a status of user (active, inactive)
1050
     *
1051
     * @param int $status Id status
1052
     * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short
1053
     *                    label + Picto, 6=Long label + Picto
1054
     *
1055
     * @return string                  Label of status
1056
     */
1057
    public function LibStatut($status, $mode = 0)
1058
    {
1059
        // phpcs:enable
1060
        global $langs;
1061
1062
        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
1063
            global $langs;
1064
            //$langs->load("mymodule");
1065
            $this->labelStatus[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
1066
            $this->labelStatus[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
1067
            $this->labelStatusShort[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
1068
            $this->labelStatusShort[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
1069
        }
1070
1071
        $statusType = 'status5';
1072
        if ($status == self::STATUS_ENABLED) {
1073
            $statusType = 'status4';
1074
        }
1075
1076
        $label = $this->labelStatus[$status];
1077
        $labelshort = $this->labelStatusShort[$status];
1078
1079
        $now = dol_now();
1080
        if (!empty($this->datestartvalidity) && $now < $this->datestartvalidity) {
1081
            $statusType = 'status3';
1082
            $label .= ' (' . $langs->trans("UserNotYetValid") . ')';
1083
        }
1084
        if (!empty($this->dateendvalidity) && $now > ($this->dateendvalidity + 24 * 3600 - 1)) {
1085
            $statusType = 'status2';
1086
            $label .= ' (' . $langs->trans("UserExpired") . ')';
1087
        }
1088
1089
        return dolGetStatus($label, $labelshort, '', $statusType, $mode);
1090
    }
1091
1092
    /**
1093
     *  Load a user from database with its id or ref (login).
1094
     *  This function does not load permissions, only user properties. Use getrights() for this just after the fetch.
1095
     *
1096
     * @param int $id If defined, id to used for search
1097
     * @param string $login If defined, login to used for search
1098
     * @param string $sid If defined, sid to used for search
1099
     * @param int $loadpersonalconf 1=also load personal conf of user (in $user->conf->xxx), 0=do not load personal
1100
     *                                 conf.
1101
     * @param int $entity If a value is >= 0, we force the search on a specific entity. If -1, means
1102
     *                                 search depens on default setup.
1103
     * @param string $email If defined, email to used for search
1104
     * @param int $fk_socpeople If defined, id of contact for search
1105
     * @param int $use_email_oauth2 1=Use also email_oauth2 to fetch on email
1106
     *
1107
     * @return int                         Return integer <0 if KO, 0 not found, >0 if OK
1108
     */
1109
    public function fetch($id = 0, $login = '', $sid = '', $loadpersonalconf = 0, $entity = -1, $email = '', $fk_socpeople = 0, $use_email_oauth2 = 0)
1110
    {
1111
        global $conf, $user;
1112
        $config = Load::getConfig();
1113
        $dbPrefix = $config->db->prefix;
1114
1115
        // Clean parameters
1116
        $login = trim($login);
1117
1118
        // Get user
1119
        $sql = "SELECT u.rowid, u.lastname, u.firstname, u.employee, u.gender, u.civility as civility_code, u.birth, u.job,";
1120
        $sql .= " u.email, u.email_oauth2, u.personal_email,";
1121
        $sql .= " u.socialnetworks,";
1122
        $sql .= " u.signature, u.office_phone, u.office_fax, u.user_mobile, u.personal_mobile,";
1123
        $sql .= " u.address, u.zip, u.town, u.fk_state as state_id, u.fk_country as country_id,";
1124
        $sql .= " u.admin, u.login, u.note_private, u.note_public,";
1125
        $sql .= " u.pass, u.pass_crypted, u.pass_temp, u.api_key,";
1126
        $sql .= " u.fk_soc, u.fk_socpeople, u.fk_member, u.fk_user, u.ldap_sid, u.fk_user_expense_validator, u.fk_user_holiday_validator,";
1127
        $sql .= " u.statut as status, u.lang, u.entity,";
1128
        $sql .= " u.datec as datec,";
1129
        $sql .= " u.tms as datem,";
1130
        $sql .= " u.datelastlogin as datel,";
1131
        $sql .= " u.datepreviouslogin as datep,";
1132
        $sql .= " u.flagdelsessionsbefore,";
1133
        $sql .= " u.iplastlogin,";
1134
        $sql .= " u.ippreviouslogin,";
1135
        $sql .= " u.datelastpassvalidation,";
1136
        $sql .= " u.datestartvalidity,";
1137
        $sql .= " u.dateendvalidity,";
1138
        $sql .= " u.photo as photo,";
1139
        $sql .= " u.openid as openid,";
1140
        $sql .= " u.accountancy_code,";
1141
        $sql .= " u.thm,";
1142
        $sql .= " u.tjm,";
1143
        $sql .= " u.salary,";
1144
        $sql .= " u.salaryextra,";
1145
        $sql .= " u.weeklyhours,";
1146
        $sql .= " u.color,";
1147
        $sql .= " u.dateemployment, u.dateemploymentend,";
1148
        $sql .= " u.fk_warehouse,";
1149
        $sql .= " u.ref_ext,";
1150
        $sql .= " u.default_range, u.default_c_exp_tax_cat,"; // Expense report default mode
1151
        $sql .= " u.national_registration_number,";
1152
        $sql .= " u.ref_employee,";
1153
        $sql .= " c.code as country_code, c.label as country,";
1154
        $sql .= " d.code_departement as state_code, d.nom as state";
1155
        $sql .= " FROM " . $dbPrefix . "user as u";
1156
        $sql .= " LEFT JOIN " . $dbPrefix . "c_country as c ON u.fk_country = c.rowid";
1157
        $sql .= " LEFT JOIN " . $dbPrefix . "c_departements as d ON u.fk_state = d.rowid";
1158
1159
        if ($entity < 0) {
1160
            if ((!isModEnabled('multicompany') || !getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) && (!empty($user->entity))) {
1161
                $sql .= " WHERE u.entity IN (0, " . ((int)$conf->entity) . ")";
1162
            } else {
1163
                $sql .= " WHERE u.entity IS NOT NULL"; // multicompany is on in transverse mode or user making fetch is on entity 0, so user is allowed to fetch anywhere into database
1164
            }
1165
        } else {
1166
            // The fetch was forced on an entity
1167
            if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
1168
                $sql .= " WHERE u.entity IS NOT NULL"; // multicompany is on in transverse mode or user making fetch is on entity 0, so user is allowed to fetch anywhere into database
1169
            } else {
1170
                $sql .= " WHERE u.entity IN (0, " . ((int)(($entity != '' && $entity >= 0) ? $entity : $conf->entity)) . ")"; // search in entity provided in parameter
1171
            }
1172
        }
1173
1174
        if ($sid) {
1175
            // permet une recherche du user par son SID ActiveDirectory ou Samba
1176
            $sql .= " AND (u.ldap_sid = '" . $this->db->escape($sid) . "' OR u.login = '" . $this->db->escape($login) . "')";
1177
        } elseif ($login) {
1178
            $sql .= " AND u.login = '" . $this->db->escape($login) . "'";
1179
        } elseif ($email) {
1180
            $sql .= " AND (u.email = '" . $this->db->escape($email) . "'";
1181
            if ($use_email_oauth2) {
1182
                $sql .= " OR u.email_oauth2 = '" . $this->db->escape($email) . "'";
1183
            }
1184
            $sql .= ")";
1185
        } elseif ($fk_socpeople > 0) {
1186
            $sql .= " AND u.fk_socpeople = " . ((int)$fk_socpeople);
1187
        } else {
1188
            $sql .= " AND u.rowid = " . ((int)$id);
1189
        }
1190
        $sql .= " ORDER BY u.entity ASC"; // Avoid random result when there is 2 login in 2 different entities
1191
1192
        if ($sid) {
1193
            // permet une recherche du user par son SID ActiveDirectory ou Samba
1194
            $sql .= ' ' . $this->db->plimit(1);
1195
        }
1196
1197
        $resql = $this->db->query($sql);
1198
        if ($resql) {
1199
            $num = $this->db->num_rows($resql);
1200
            if ($num > 1) {
1201
                $this->error = "USERDUPLICATEFOUND";
1202
                dol_syslog(get_class($this) . "::fetch more than 1 user found", LOG_WARNING);
1203
1204
                $this->db->free($resql);
1205
                return 0;
1206
            }
1207
1208
            $obj = $this->db->fetch_object($resql);
1209
            if ($obj) {
1210
                $this->id = $obj->rowid;
1211
                $this->ref = $obj->rowid;
1212
1213
                $this->ref_ext = $obj->ref_ext;
1214
1215
                $this->ldap_sid = $obj->ldap_sid;
1216
                $this->civility_code = $obj->civility_code;
1217
                $this->lastname = $obj->lastname;
1218
                $this->firstname = $obj->firstname;
1219
                $this->ref_employee = $obj->ref_employee;
1220
                $this->national_registration_number = $obj->national_registration_number;
1221
1222
                $this->employee = $obj->employee;
1223
1224
                $this->login = $obj->login;
1225
                $this->gender = $obj->gender;
1226
                $this->birth = $this->db->jdate($obj->birth);
1227
                $this->pass_indatabase = $obj->pass;
1228
                $this->pass_indatabase_crypted = $obj->pass_crypted;
1229
                $this->pass = $obj->pass;
1230
                $this->pass_temp = $obj->pass_temp;
1231
                $this->api_key = dolDecrypt($obj->api_key);
1232
1233
                $this->address = $obj->address;
1234
                $this->zip = $obj->zip;
1235
                $this->town = $obj->town;
1236
1237
                $this->country_id = $obj->country_id;
1238
                $this->country_code = $obj->country_id ? $obj->country_code : '';
1239
                //$this->country = $obj->country_id?($langs->trans('Country'.$obj->country_code)!='Country'.$obj->country_code?$langs->transnoentities('Country'.$obj->country_code):$obj->country):'';
1240
1241
                $this->state_id = $obj->state_id;
1242
                $this->state_code = $obj->state_code;
1243
                $this->state = ($obj->state != '-' ? $obj->state : '');
1244
1245
                $this->office_phone = $obj->office_phone;
1246
                $this->office_fax = $obj->office_fax;
1247
                $this->user_mobile = $obj->user_mobile;
1248
                $this->personal_mobile = $obj->personal_mobile;
1249
                $this->email = $obj->email;
1250
                $this->email_oauth2 = $obj->email_oauth2;
1251
                $this->personal_email = $obj->personal_email;
1252
                $this->socialnetworks = ($obj->socialnetworks ? (array)json_decode($obj->socialnetworks, true) : []);
1253
                $this->job = $obj->job;
1254
                $this->signature = $obj->signature;
1255
                $this->admin = $obj->admin;
1256
                $this->note_public = $obj->note_public;
1257
                $this->note_private = $obj->note_private;
1258
                $this->note = $obj->note_private;   // deprecated
1259
1260
                $this->statut = $obj->status;         // deprecated
1261
                $this->status = $obj->status;
1262
1263
                $this->photo = $obj->photo;
1264
                $this->openid = $obj->openid;
1265
                $this->lang = $obj->lang;
1266
                $this->entity = $obj->entity;
1267
                $this->accountancy_code = $obj->accountancy_code;
1268
                $this->thm = $obj->thm;
1269
                $this->tjm = $obj->tjm;
1270
                $this->salary = $obj->salary;
1271
                $this->salaryextra = $obj->salaryextra;
1272
                $this->weeklyhours = $obj->weeklyhours;
1273
                $this->color = $obj->color;
1274
                $this->dateemployment = $this->db->jdate($obj->dateemployment);
1275
                $this->dateemploymentend = $this->db->jdate($obj->dateemploymentend);
1276
1277
                $this->datec = $this->db->jdate($obj->datec);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->datec) can also be of type string. However, the property $datec is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1278
                $this->datem = $this->db->jdate($obj->datem);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->datem) can also be of type string. However, the property $datem is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1279
                $this->datelastlogin = $this->db->jdate($obj->datel);
1280
                $this->datepreviouslogin = $this->db->jdate($obj->datep);
1281
                $this->flagdelsessionsbefore = $this->db->jdate($obj->flagdelsessionsbefore, 'gmt');
1282
                $this->iplastlogin = $obj->iplastlogin;
1283
                $this->ippreviouslogin = $obj->ippreviouslogin;
1284
                $this->datestartvalidity = $this->db->jdate($obj->datestartvalidity);
1285
                $this->dateendvalidity = $this->db->jdate($obj->dateendvalidity);
1286
1287
                $this->socid = $obj->fk_soc;
1288
                $this->contact_id = $obj->fk_socpeople;
1289
                $this->fk_member = $obj->fk_member;
1290
                $this->fk_user = $obj->fk_user;
1291
                $this->fk_user_expense_validator = $obj->fk_user_expense_validator;
1292
                $this->fk_user_holiday_validator = $obj->fk_user_holiday_validator;
1293
1294
                $this->default_range = $obj->default_range;
1295
                $this->default_c_exp_tax_cat = $obj->default_c_exp_tax_cat;
1296
                $this->fk_warehouse = $obj->fk_warehouse;
1297
1298
                // Protection when module multicompany was set, admin was set to first entity and then, the module was disabled,
1299
                // in such case, this admin user must be admin for ALL entities.
1300
                if (!isModEnabled('multicompany') && $this->admin && $this->entity == 1) {
1301
                    $this->entity = 0;
1302
                }
1303
1304
                // Retrieve all extrafield
1305
                // fetch optionals attributes and labels
1306
                $this->fetch_optionals();
1307
1308
                $this->db->free($resql);
1309
            } else {
1310
                $this->error = "USERNOTFOUND";
1311
                dol_syslog(get_class($this) . "::fetch user not found", LOG_DEBUG);
1312
1313
                $this->db->free($resql);
1314
                return 0;
1315
            }
1316
        } else {
1317
            $this->error = $this->db->lasterror();
1318
            return -1;
1319
        }
1320
1321
        // To get back the global configuration unique to the user
1322
        if ($loadpersonalconf) {
1323
            $result = $this->loadPersonalConf();
1324
1325
            $result = $this->loadDefaultValues();
1326
1327
            if ($result < 0) {
1328
                $this->error = $this->db->lasterror();
1329
                return -3;
1330
            }
1331
        }
1332
1333
        return 1;
1334
    }
1335
1336
    /**
1337
     *  Load const values from database table user_param and set it into user->conf->XXX
1338
     *
1339
     * @return int                     >= 0 if OK, < 0 if KO
1340
     */
1341
    public function loadPersonalConf()
1342
    {
1343
        $conf = Load::getConfig();
1344
        $dbPrefix = $conf->db->prefix;
1345
1346
        // Load user->conf for user
1347
        $sql = "SELECT param, value FROM " . $dbPrefix . "user_param";
1348
        $sql .= " WHERE fk_user = " . ((int)$this->id);
1349
        $sql .= " AND entity = " . ((int)$conf->entity);
1350
        //dol_syslog(get_class($this).'::fetch load personalized conf', LOG_DEBUG);
1351
        $resql = $this->db->query($sql);
1352
        if ($resql) {
1353
            $num = $this->db->num_rows($resql);
1354
            $i = 0;
1355
            while ($i < $num) {
1356
                $obj = $this->db->fetch_object($resql);
1357
                $p = (!empty($obj->param) ? $obj->param : '');
1358
                if (!empty($p)) {
1359
                    $this->conf->$p = $obj->value;
1360
                }
1361
                $i++;
1362
            }
1363
            $this->db->free($resql);
1364
1365
            return $num;
1366
        } else {
1367
            $this->error = $this->db->lasterror();
1368
1369
            return -2;
1370
        }
1371
    }
1372
1373
    /**
1374
     *  Load default values from database table into property ->default_values
1375
     *
1376
     * @return int                     > 0 if OK, < 0 if KO
1377
     */
1378
    public function loadDefaultValues()
1379
    {
1380
        global $conf;
1381
1382
        if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
1383
            // Load user->default_values for user. TODO Save this in memcached ?
1384
1385
            $defaultValues = new DefaultValues($this->db);
1386
            $result = $defaultValues->fetchAll('', '', 0, 0, '(t.user_id:in:0,' . $this->id . ') AND (entity:in:' . (isset($this->entity) ? $this->entity : $conf->entity) . ',' . $conf->entity . ')');    // User 0 (all) + me (if defined)
1387
            //$result = $defaultValues->fetchAll('', '', 0, 0, array('t.user_id'=>array(0, $this->id), 'entity'=>array((isset($this->entity) ? $this->entity : $conf->entity), $conf->entity)));    // User 0 (all) + me (if defined)
1388
1389
            if (!is_array($result) && $result < 0) {
1390
                setEventMessages($defaultValues->error, $defaultValues->errors, 'errors');
1391
                dol_print_error($this->db);
1392
                return -1;
1393
            } elseif (count($result) > 0) {
1394
                foreach ($result as $defval) {
1395
                    if (!empty($defval->page) && !empty($defval->type) && !empty($defval->param)) {
1396
                        $pagewithoutquerystring = $defval->page;
1397
                        $pagequeries = '';
1398
                        $reg = [];
1399
                        if (preg_match('/^([^\?]+)\?(.*)$/', $pagewithoutquerystring, $reg)) {    // There is query param
1400
                            $pagewithoutquerystring = $reg[1];
1401
                            $pagequeries = $reg[2];
1402
                        }
1403
                        $this->default_values[$pagewithoutquerystring][$defval->type][$pagequeries ? $pagequeries : '_noquery_'][$defval->param] = $defval->value;
1404
                    }
1405
                }
1406
            }
1407
            if (!empty($this->default_values)) {
1408
                foreach ($this->default_values as $a => $b) {
1409
                    foreach ($b as $c => $d) {
1410
                        krsort($this->default_values[$a][$c]);
1411
                    }
1412
                }
1413
            }
1414
        }
1415
        return 1;
1416
    }
1417
1418
1419
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1420
1421
    /**
1422
     *  Load all objects into $this->users
1423
     *
1424
     * @param string $sortorder sort order
1425
     * @param string $sortfield sort field
1426
     * @param int $limit limit page
1427
     * @param int $offset page
1428
     * @param string $filter Filter as an Universal Search string.
1429
     *                                      Example: '((client:=:1) OR ((client:>=:2) AND (client:<=:3))) AND
1430
     *                                      (client:!=:8) AND (nom:like:'a%')'
1431
     * @param string $filtermode No more used
1432
     * @param bool $entityfilter Activate entity filter
1433
     *
1434
     * @return int                         Return integer <0 if KO, >0 if OK
1435
     */
1436
    public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $entityfilter = false)
1437
    {
1438
        global $conf, $user;
1439
1440
        $sql = "SELECT t.rowid";
1441
        $sql .= ' FROM ' . $dbPrefix . $this->table_element . ' as t ';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
1442
1443
        if ($entityfilter) {
1444
            if (getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
1445
                if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1446
                    $sql .= " WHERE t.entity IS NOT NULL"; // Show all users
1447
                } else {
1448
                    $sql .= "," . $dbPrefix . "usergroup_user as ug";
1449
                    $sql .= " WHERE ((ug.fk_user = t.rowid";
1450
                    $sql .= " AND ug.entity IN (" . getEntity('usergroup') . "))";
1451
                    $sql .= " OR t.entity = 0)"; // Show always superadmin
1452
                }
1453
            } else {
1454
                $sql .= " WHERE t.entity IN (" . getEntity('user') . ")";
1455
            }
1456
        } else {
1457
            $sql .= " WHERE 1 = 1";
1458
        }
1459
1460
        // Manage filter
1461
        $errormessage = '';
1462
        $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1463
        if ($errormessage) {
1464
            $this->errors[] = $errormessage;
1465
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1466
            return -1;
1467
        }
1468
1469
        $sql .= $this->db->order($sortfield, $sortorder);
1470
        if ($limit) {
1471
            $sql .= $this->db->plimit($limit + 1, $offset);
1472
        }
1473
1474
        dol_syslog(__METHOD__, LOG_DEBUG);
1475
1476
        $resql = $this->db->query($sql);
1477
        if ($resql) {
1478
            $this->users = [];
1479
            $num = $this->db->num_rows($resql);
1480
            if ($num) {
1481
                while ($obj = $this->db->fetch_object($resql)) {
1482
                    $line = new self($this->db);
1483
                    $result = $line->fetch($obj->rowid);
1484
                    if ($result > 0 && !empty($line->id)) {
1485
                        $this->users[$obj->rowid] = clone $line;
1486
                    }
1487
                }
1488
                $this->db->free($resql);
1489
            }
1490
            return $num;
1491
        } else {
1492
            $this->errors[] = $this->db->lasterror();
1493
            return -1;
1494
        }
1495
    }
1496
1497
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1498
1499
    /**
1500
     *  Return a HTML link to the user card (with optionally the picto)
1501
     *  Use this->id,this->lastname, this->firstname
1502
     *
1503
     * @param int $withpictoimg Include picto in link (0=No picto, 1=Include picto into link, 2=Only
1504
     *                                       picto, -1=Include photo into link, -2=Only picto photo, -3=Only photo very
1505
     *                                       small)
1506
     * @param string $option On what the link point to ('leave', 'accountancy', 'nolink', )
1507
     * @param integer $infologin 0=Add default info tooltip, 1=Add complete info tooltip, -1=No info
1508
     *                                       tooltip
1509
     * @param integer $notooltip 1=Disable tooltip on picto and name
1510
     * @param int $maxlen Max length of visible user name
1511
     * @param int $hidethirdpartylogo Hide logo of thirdparty if user is external user
1512
     * @param string $mode ''=Show firstname and lastname, 'firstname'=Show only firstname,
1513
     *                                       'firstelselast'=Show firstname or lastname if not defined, 'login'=Show
1514
     *                                       login
1515
     * @param string $morecss Add more css on link
1516
     * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save
1517
     *                                       lastsearch_values whenclicking
1518
     *
1519
     * @return string                              String with URL
1520
     */
1521
    public function getNomUrl($withpictoimg = 0, $option = '', $infologin = 0, $notooltip = 0, $maxlen = 24, $hidethirdpartylogo = 0, $mode = '', $morecss = '', $save_lastsearch_value = -1)
1522
    {
1523
        global $langs, $conf, $db, $hookmanager, $user;
1524
        global $dolibarr_main_authentication, $dolibarr_main_demo;
1525
1526
        if (!$user->hasRight('user', 'user', 'read') && $user->id != $this->id) {
1527
            $option = 'nolink';
1528
        }
1529
1530
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpictoimg) {
1531
            $withpictoimg = 0;
1532
        }
1533
1534
        $result = '';
1535
        $params = [
1536
            'id' => $this->id,
1537
            'objecttype' => $this->element,
1538
            'infologin' => $infologin,
1539
            'option' => $option,
1540
            'hidethirdpartylogo' => $hidethirdpartylogo,
1541
        ];
1542
        $classfortooltip = 'classfortooltip';
1543
        $dataparams = '';
1544
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1545
            $classfortooltip = 'classforajaxtooltip';
1546
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
1547
            $label = '';
1548
        } else {
1549
            $label = implode($this->getTooltipContentArray($params));
1550
        }
1551
1552
        $companylink = '';
1553
        if (!empty($this->socid)) { // Add thirdparty for external users
1554
            $thirdpartystatic = new Societe($this->db);
1555
            $thirdpartystatic->fetch($this->socid);
1556
            if (empty($hidethirdpartylogo)) {
1557
                $companylink = ' ' . $thirdpartystatic->getNomUrl(2, (($option == 'nolink') ? 'nolink' : '')); // picto only of company
1558
            }
1559
        }
1560
1561
        if ($infologin < 0) {
1562
            $label = '';
1563
        }
1564
1565
        $url = DOL_URL_ROOT . '/user/card.php?id=' . $this->id;
1566
        if ($option == 'leave') {
1567
            $url = DOL_URL_ROOT . '/holiday/list.php?id=' . $this->id;
1568
        }
1569
1570
        if ($option != 'nolink') {
1571
            // Add param to save lastsearch_values or not
1572
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1573
            if ($save_lastsearch_value == -1 && isset($_SERVER['PHP_SELF']) && preg_match('/list\.php/', $_SERVER['PHP_SELF'])) {
1574
                $add_save_lastsearch_values = 1;
1575
            }
1576
            if ($add_save_lastsearch_values) {
1577
                $url .= '&save_lastsearch_values=1';
1578
            }
1579
        }
1580
1581
        $linkstart = '<a href="' . $url . '"';
1582
        $linkclose = "";
1583
        if (empty($notooltip)) {
1584
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1585
                $langs->load("users");
1586
                $label = $langs->trans("ShowUser");
1587
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1588
            }
1589
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
1590
            $linkclose .= $dataparams . ' class="' . $classfortooltip . ($morecss ? ' ' . $morecss : '') . '"';
1591
        } else {
1592
            $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
1593
        }
1594
1595
        $linkstart .= $linkclose . '>';
1596
        $linkend = '</a>';
1597
1598
        //if ($withpictoimg == -1) $result.='<div class="nowrap">';
1599
        $result .= (($option == 'nolink') ? '' : $linkstart);
1600
        if ($withpictoimg) {
1601
            $paddafterimage = '';
1602
            if (abs((int)$withpictoimg) == 1) {
1603
                $paddafterimage = 'style="margin-' . ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right') . ': 3px;"';
1604
            }
1605
            // Only picto
1606
            if ($withpictoimg > 0) {
1607
                $picto = '<!-- picto user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '"><div class="valignmiddle userphoto inline-block center marginrightonlyshort"' . ($paddafterimage ? ' ' . $paddafterimage : '') . '>' . img_object('', 'user', 'class=""', 0, 0, $notooltip ? 0 : 1) . '</div></span>';
1608
            } else {
1609
                // Picto must be a photo
1610
                $picto = '<!-- picto photo user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '"' . ($paddafterimage ? ' ' . $paddafterimage : '') . '>' . Form::showphoto('userphoto', $this, 0, 0, 0, 'userphoto' . ($withpictoimg == -3 ? 'small' : ''), 'mini', 0, 1) . '</span>';
1611
            }
1612
            $result .= $picto;
1613
        }
1614
        if ($withpictoimg > -2 && $withpictoimg != 2) {
1615
            if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1616
                $result .= '<span class="nopadding usertext' . ((!isset($this->status) || $this->status) ? '' : ' strikefordisabled') . ($morecss ? ' ' . $morecss : '') . '">';
1617
            }
1618
            if ($mode == 'login') {
1619
                $result .= dol_string_nohtmltag(dol_trunc($this->login, $maxlen));
1620
            } else {
1621
                $result .= dol_string_nohtmltag($this->getFullName($langs, '', ($mode == 'firstelselast' ? 3 : ($mode == 'firstname' ? 2 : -1)), $maxlen));
1622
            }
1623
            if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1624
                $result .= '</span>';
1625
            }
1626
        }
1627
        $result .= (($option == 'nolink') ? '' : $linkend);
1628
        //if ($withpictoimg == -1) $result.='</div>';
1629
1630
        $result .= $companylink;
1631
1632
        global $action;
1633
        $hookmanager->initHooks(['userdao']);
1634
        $parameters = ['id' => $this->id, 'getnomurl' => &$result];
1635
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1636
        if ($reshook > 0) {
1637
            $result = $hookmanager->resPrint;
1638
        } else {
1639
            $result .= $hookmanager->resPrint;
1640
        }
1641
1642
        return $result;
1643
    }
1644
1645
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1646
1647
    /**
1648
     *  Return if a user has a permission.
1649
     *  You can use it like this: if ($user->hasRight('module', 'level11')).
1650
     *  It replaces old syntax: if ($user->rights->module->level1)
1651
     *
1652
     * @param string $module Module of permission to check
1653
     * @param string $permlevel1 Permission level1 (Example: 'read', 'write', 'delete')
1654
     * @param string $permlevel2 Permission level2
1655
     *
1656
     * @return int                     1 if user has permission, 0 if not.
1657
     * @see    clearrights(), delrights(), getrights(), hasRight()
1658
     */
1659
    public function hasRight($module, $permlevel1, $permlevel2 = '')
1660
    {
1661
        // For compatibility with bad naming permissions on module
1662
        $moduletomoduletouse = [
1663
            'compta' => 'comptabilite',
1664
            'contract' => 'contrat',
1665
            'member' => 'adherent',
1666
            'mo' => 'mrp',
1667
            'order' => 'commande',
1668
            'produit' => 'product',
1669
            'project' => 'projet',
1670
            'propale' => 'propal',
1671
            'shipping' => 'expedition',
1672
            'task' => 'task@projet',
1673
            'fichinter' => 'ficheinter',
1674
            'inventory' => 'stock',
1675
            'invoice' => 'facture',
1676
            'invoice_supplier' => 'fournisseur',
1677
            'order_supplier' => 'fournisseur',
1678
            'knowledgerecord' => 'knowledgerecord@knowledgemanagement',
1679
            'skill@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
1680
            'job@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
1681
            'position@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
1682
            'facturerec' => 'facture',
1683
            'margins' => 'margin',
1684
        ];
1685
1686
        if (!empty($moduletomoduletouse[$module])) {
1687
            $module = $moduletomoduletouse[$module];
1688
        }
1689
1690
        $moduleRightsMapping = [
1691
            'product' => 'produit',
1692
            'margin' => 'margins',
1693
            'comptabilite' => 'compta',
1694
        ];
1695
1696
        $rightsPath = $module;
1697
        if (!empty($moduleRightsMapping[$rightsPath])) {
1698
            $rightsPath = $moduleRightsMapping[$rightsPath];
1699
        }
1700
1701
        // If module is abc@module, we check permission user->hasRight(module, abc, permlevel1)
1702
        $tmp = explode('@', $rightsPath, 2);
1703
        if (!empty($tmp[1])) {
1704
            if (strpos($module, '@') !== false) {
1705
                $module = $tmp[1];
1706
            }
1707
            if ($tmp[0] != $tmp[1]) {
1708
                // If $module = 'myobject@mymodule'
1709
                $rightsPath = $tmp[1];
1710
                $permlevel2 = $permlevel1;
1711
                $permlevel1 = $tmp[0];
1712
            } else {
1713
                // If $module = 'abc@abc'
1714
                $rightsPath = $tmp[1];
1715
            }
1716
        }
1717
1718
        // In $conf->modules, we have 'accounting', 'product', 'facture', ...
1719
        // In $user->rights, we have 'accounting', 'produit', 'facture', ...
1720
        //var_dump($this->rights->$rightsPath);
1721
        //var_dump($conf->modules);
1722
        //var_dump($module.' '.isModEnabled($module).' '.$rightsPath.' '.$permlevel1.' '.$permlevel2);
1723
        if (!isModEnabled($module)) {
1724
            // dump("$module is not enabled!");
1725
            return 0;
1726
        }
1727
1728
        // Special case for external user
1729
        if (!empty($this->socid)) {
1730
            // dump('External user!');
1731
            if ($module = 'societe' && $permlevel1 == 'client' && $permlevel2 == 'voir') {
0 ignored issues
show
Comprehensibility introduced by
Consider adding parentheses for clarity. Current Interpretation: $module = ('societe' && ... $permlevel2 == 'voir'), Probably Intended Meaning: ($module = 'societe' && ...& $permlevel2 == 'voir'
Loading history...
1732
                return 0;   // An external user never has the permission ->societe->client->voir to see all thirdparties (always restricted to himself)
1733
            }
1734
        }
1735
1736
        // For compatibility with bad naming permissions on permlevel1
1737
        if ($permlevel1 == 'propale') {
1738
            $permlevel1 = 'propal';
1739
        }
1740
        if ($permlevel1 == 'member') {
1741
            $permlevel1 = 'adherent';
1742
        }
1743
        if ($permlevel1 == 'recruitmentcandidature') {
1744
            $permlevel1 = 'recruitmentjobposition';
1745
        }
1746
1747
        //var_dump($this->rights);
1748
        //var_dump($rightsPath.' '.$permlevel1.' '.$permlevel2);
1749
        if (empty($rightsPath) || empty($this->rights) || empty($this->rights->$rightsPath) || empty($permlevel1)) {
1750
            /*
1751
            dump([
1752
                'this->rights' => $this->rights,
1753
                'rightsPath' => $rightsPath,
1754
                '$this->rights->' . $rightsPath => $this->rights->$rightsPath??null,
1755
            ]);
1756
            */
1757
            return 0;
1758
        }
1759
1760
        if ($permlevel2) {
1761
            if (!empty($this->rights->$rightsPath->$permlevel1)) {
1762
                if (!empty($this->rights->$rightsPath->$permlevel1->$permlevel2)) {
1763
                    return $this->rights->$rightsPath->$permlevel1->$permlevel2;
1764
                }
1765
                // For backward compatibility with old permissions called "lire", "creer", "create", "supprimer"
1766
                // instead of "read", "write", "delete"
1767
                if ($permlevel2 == 'read' && !empty($this->rights->$rightsPath->$permlevel1->lire)) {
1768
                    return $this->rights->$rightsPath->$permlevel1->lire;
1769
                }
1770
                if ($permlevel2 == 'write' && !empty($this->rights->$rightsPath->$permlevel1->creer)) {
1771
                    return $this->rights->$rightsPath->$permlevel1->creer;
1772
                }
1773
                if ($permlevel2 == 'write' && !empty($this->rights->$rightsPath->$permlevel1->create)) {
1774
                    return $this->rights->$rightsPath->$permlevel1->create;
1775
                }
1776
                if ($permlevel2 == 'delete' && !empty($this->rights->$rightsPath->$permlevel1->supprimer)) {
1777
                    return $this->rights->$rightsPath->$permlevel1->supprimer;
1778
                }
1779
            }
1780
        } else {
1781
            if (!empty($this->rights->$rightsPath->$permlevel1)) {
1782
                return $this->rights->$rightsPath->$permlevel1;
1783
            }
1784
            // For backward compatibility with old permissions called "lire", "creer", "create", "supprimer"
1785
            // instead of "read", "write", "delete"
1786
            if ($permlevel1 == 'read' && !empty($this->rights->$rightsPath->lire)) {
1787
                return $this->rights->$rightsPath->lire;
1788
            }
1789
            if ($permlevel1 == 'write' && !empty($this->rights->$rightsPath->creer)) {
1790
                return $this->rights->$rightsPath->creer;
1791
            }
1792
            if ($permlevel1 == 'write' && !empty($this->rights->$rightsPath->create)) {
1793
                return $this->rights->$rightsPath->create;
1794
            }
1795
            if ($permlevel1 == 'delete' && !empty($this->rights->$rightsPath->supprimer)) {
1796
                return $this->rights->$rightsPath->supprimer;
1797
            }
1798
        }
1799
1800
        return 0;
1801
    }
1802
1803
    /**
1804
     *  Change status of a user
1805
     *
1806
     * @param int $status Status to set
1807
     *
1808
     * @return int                 Return integer <0 if KO, 0 if nothing is done, >0 if OK
1809
     */
1810
    public function setstatus($status)
1811
    {
1812
        global $conf, $langs, $user;
1813
1814
        $error = 0;
1815
1816
        // Check parameters
1817
        if (isset($this->statut)) {
1818
            if ($this->statut == $status) {
1819
                return 0;
1820
            }
1821
        } elseif (isset($this->status) && $this->status == $status) {
1822
            return 0;
1823
        }
1824
1825
        $this->db->begin();
1826
1827
        // Save in database
1828
        $sql = "UPDATE " . $dbPrefix . "user";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
1829
        $sql .= " SET statut = " . ((int)$status);
1830
        $sql .= " WHERE rowid = " . ((int)$this->id);
1831
        $result = $this->db->query($sql);
1832
1833
        dol_syslog(get_class($this) . "::setstatus", LOG_DEBUG);
1834
        if ($result) {
1835
            if ($status == 0) {
1836
                $this->context['actionmsg'] = 'User ' . $this->login . ' disabled';
1837
            } else {
1838
                $this->context['actionmsg'] = 'User ' . $this->login . ' enabled';
1839
            }
1840
            // Call trigger
1841
            $result = $this->call_trigger('USER_ENABLEDISABLE', $user);
1842
            if ($result < 0) {
1843
                $error++;
1844
            }
1845
            // End call triggers
1846
        }
1847
1848
        if ($error) {
1849
            $this->db->rollback();
1850
            return -$error;
1851
        } else {
1852
            $this->status = $status;
1853
            $this->statut = $status;
1854
            $this->db->commit();
1855
            return 1;
1856
        }
1857
    }
1858
1859
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1860
1861
    /**
1862
     * Sets object to supplied categories.
1863
     *
1864
     * Deletes object from existing categories not supplied.
1865
     * Adds it to non existing supplied categories.
1866
     * Existing categories are left untouch.
1867
     *
1868
     * @param int[]|int $categories Category or categories IDs
1869
     *
1870
     * @return  int                         Return integer <0 if KO, >0 if OK
1871
     */
1872
    public function setCategories($categories)
1873
    {
1874
        return parent::setCategoriesCommon($categories, Categorie::TYPE_USER);
1875
    }
1876
1877
    /**
1878
     *  Delete the user
1879
     *
1880
     * @param User $user User than delete
1881
     *
1882
     * @return     int             Return integer <0 if KO, >0 if OK
1883
     */
1884
    public function delete(User $user)
1885
    {
1886
        global $conf, $langs;
1887
1888
        $error = 0;
1889
1890
        $this->db->begin();
1891
1892
        $this->fetch($this->id);
1893
1894
        dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
1895
1896
        // Remove rights
1897
        $sql = "DELETE FROM " . $dbPrefix . "user_rights WHERE fk_user = " . ((int)$this->id);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
1898
1899
        if (!$error && !$this->db->query($sql)) {
1900
            $error++;
1901
            $this->error = $this->db->lasterror();
1902
        }
1903
1904
        // Remove group
1905
        $sql = "DELETE FROM " . $dbPrefix . "usergroup_user WHERE fk_user  = " . ((int)$this->id);
1906
        if (!$error && !$this->db->query($sql)) {
1907
            $error++;
1908
            $this->error = $this->db->lasterror();
1909
        }
1910
1911
        // Remove params
1912
        $sql = "DELETE FROM " . $dbPrefix . "user_param WHERE fk_user  = " . ((int)$this->id);
1913
        if (!$error && !$this->db->query($sql)) {
1914
            $error++;
1915
            $this->error = $this->db->lasterror();
1916
        }
1917
1918
        // If contact, remove link
1919
        if ($this->contact_id > 0) {
1920
            $sql = "UPDATE " . $dbPrefix . "socpeople SET fk_user_creat = null WHERE rowid = " . ((int)$this->contact_id);
1921
            if (!$error && !$this->db->query($sql)) {
1922
                $error++;
1923
                $this->error = $this->db->lasterror();
1924
            }
1925
        }
1926
1927
        // Remove extrafields
1928
        if (!$error) {
1929
            $result = $this->deleteExtraFields();
1930
            if ($result < 0) {
1931
                $error++;
1932
                dol_syslog(get_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
1933
            }
1934
        }
1935
1936
        // Remove user
1937
        if (!$error) {
1938
            $sql = "DELETE FROM " . $dbPrefix . "user WHERE rowid = " . ((int)$this->id);
1939
            dol_syslog(get_class($this) . "::delete", LOG_DEBUG);
1940
            if (!$this->db->query($sql)) {
1941
                $error++;
1942
                $this->error = $this->db->lasterror();
1943
            }
1944
        }
1945
1946
        if (!$error) {
1947
            // Call trigger
1948
            $result = $this->call_trigger('USER_DELETE', $user);
1949
            if ($result < 0) {
1950
                $error++;
1951
                $this->db->rollback();
1952
                return -1;
1953
            }
1954
            // End call triggers
1955
1956
            $this->db->commit();
1957
            return 1;
1958
        } else {
1959
            $this->db->rollback();
1960
            return -1;
1961
        }
1962
    }
1963
1964
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1965
1966
    /**
1967
     *  Create a user from a contact object. User will be internal but if contact is linked to a third party, user will
1968
     *  be external
1969
     *
1970
     * @param Contact $contact Object for source contact
1971
     * @param string $login Login to force
1972
     * @param string $password Password to force
1973
     *
1974
     * @return int                 Return integer <0 if error, if OK returns id of created user
1975
     */
1976
    public function create_from_contact($contact, $login = '', $password = '')
1977
    {
1978
        // phpcs:enable
1979
        global $conf, $user, $langs;
1980
1981
        $error = 0;
1982
1983
        // Define parameters
1984
        $this->admin = 0;
1985
        $this->civility_code = $contact->civility_code;
1986
        $this->lastname = $contact->lastname;
1987
        $this->firstname = $contact->firstname;
1988
        //$this->gender = $contact->gender;     // contact ha sno gender
1989
        $this->email = $contact->email;
1990
        $this->socialnetworks = $contact->socialnetworks;
1991
        $this->office_phone = $contact->phone_pro;
1992
        $this->office_fax = $contact->fax;
1993
        $this->user_mobile = $contact->phone_mobile;
1994
        $this->address = $contact->address;
1995
        $this->zip = $contact->zip;
1996
        $this->town = $contact->town;
1997
        $this->setUpperOrLowerCase();
1998
        $this->state_id = $contact->state_id;
1999
        $this->country_id = $contact->country_id;
2000
        $this->employee = 0;
2001
2002
        if (empty($login)) {
2003
            include_once BASE_PATH . '/../Dolibarr/Lib/Functions2.php';
2004
            $login = dol_buildlogin($contact->lastname, $contact->firstname);
2005
        }
2006
        $this->login = $login;
2007
2008
        $this->db->begin();
2009
2010
        // Create user and set $this->id. Trigger is disabled because executed later.
2011
        $result = $this->create($user, 1);
2012
        if ($result > 0) {
2013
            $sql = "UPDATE " . $dbPrefix . "user";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
2014
            $sql .= " SET fk_socpeople=" . ((int)$contact->id);
2015
            $sql .= ", civility='" . $this->db->escape($contact->civility_code) . "'";
2016
            if ($contact->socid > 0) {
2017
                $sql .= ", fk_soc=" . ((int)$contact->socid);
2018
            }
2019
            $sql .= " WHERE rowid=" . ((int)$this->id);
2020
2021
            $resql = $this->db->query($sql);
2022
2023
            dol_syslog(get_class($this) . "::create_from_contact", LOG_DEBUG);
2024
            if ($resql) {
2025
                $this->context['createfromcontact'] = 'createfromcontact';
2026
2027
                // Call trigger
2028
                $result = $this->call_trigger('USER_CREATE', $user);
2029
                if ($result < 0) {
2030
                    $error++;
2031
                    $this->db->rollback();
2032
                    return -1;
2033
                }
2034
                // End call triggers
2035
2036
                $this->db->commit();
2037
                return $this->id;
2038
            } else {
2039
                $this->error = $this->db->error();
2040
2041
                $this->db->rollback();
2042
                return -1;
2043
            }
2044
        } else {
2045
            // $this->error deja positionne
2046
            dol_syslog(get_class($this) . "::create_from_contact - 0");
2047
2048
            $this->db->rollback();
2049
            return $result;
2050
        }
2051
    }
2052
2053
    /**
2054
     *  Create a user into database
2055
     *
2056
     * @param User $user Object user doing creation
2057
     * @param int $notrigger 1=do not execute triggers, 0 otherwise
2058
     *
2059
     * @return int                     Return integer <0 if KO, id of created user if OK
2060
     */
2061
    public function create($user, $notrigger = 0)
2062
    {
2063
        global $conf, $langs;
2064
        global $mysoc;
2065
2066
        global $config;
2067
        $dbPrefix = $config->db->prefix;
2068
2069
        // Clean parameters
2070
        $this->setUpperOrLowerCase();
2071
2072
        $this->civility_code = trim((string)$this->civility_code);
2073
        $this->login = trim((string)$this->login);
2074
        if (!isset($this->entity)) {
2075
            $this->entity = $conf->entity; // If not defined, we use default value
2076
        }
2077
2078
        dol_syslog(get_class($this) . "::create login=" . $this->login . ", user=" . (is_object($user) ? $user->id : ''), LOG_DEBUG);
2079
2080
        $badCharUnauthorizedIntoLoginName = getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
2081
2082
        // Check parameters
2083
        if (getDolGlobalString('USER_MAIL_REQUIRED') && !isValidEmail($this->email)) {
2084
            $langs->load("errors");
2085
            $this->error = $langs->trans("ErrorBadEMail", $this->email);
2086
            return -1;
2087
        }
2088
        if (empty($this->login)) {
2089
            $langs->load("errors");
2090
            $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Login"));
2091
            return -1;
2092
        } elseif (preg_match('/[' . preg_quote($badCharUnauthorizedIntoLoginName, '/') . ']/', $this->login)) {
2093
            $langs->load("errors");
2094
            $this->error = $langs->trans("ErrorBadCharIntoLoginName", $langs->transnoentitiesnoconv("Login"));
2095
            return -1;
2096
        }
2097
2098
        $this->datec = dol_now();
2099
2100
        $error = 0;
2101
        $this->db->begin();
2102
2103
        // Check if login already exists in same entity or into entity 0.
2104
        if ($this->login) {
2105
            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
2106
            $resqltochecklogin = $this->db->query($sqltochecklogin);
2107
            if ($resqltochecklogin) {
2108
                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
2109
                if ($objtochecklogin && $objtochecklogin->nb > 0) {
2110
                    $langs->load("errors");
2111
                    $this->error = $langs->trans("ErrorLoginAlreadyExists", $this->login);
2112
                    dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
2113
                    $this->db->rollback();
2114
                    return -6;
2115
                }
2116
                $this->db->free($resqltochecklogin);
2117
            }
2118
        }
2119
        if (!empty($this->email)) {
2120
            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
2121
            $resqltochecklogin = $this->db->query($sqltochecklogin);
2122
            if ($resqltochecklogin) {
2123
                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
2124
                if ($objtochecklogin && $objtochecklogin->nb > 0) {
2125
                    $langs->load("errors");
2126
                    $this->error = $langs->trans("ErrorEmailAlreadyExists", $this->email);
2127
                    dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
2128
                    $this->db->rollback();
2129
                    return -6;
2130
                }
2131
                $this->db->free($resqltochecklogin);
2132
            }
2133
        }
2134
2135
        // Insert into database
2136
        $sql = "INSERT INTO " . $dbPrefix . "user (datec, login, ldap_sid, entity)";
2137
        $sql .= " VALUES('" . $this->db->idate($this->datec) . "', '" . $this->db->escape($this->login) . "', '" . $this->db->escape($this->ldap_sid) . "', " . ((int)$this->entity) . ")";
2138
        $result = $this->db->query($sql);
2139
2140
        dol_syslog(get_class($this) . "::create", LOG_DEBUG);
2141
        if ($result) {
2142
            $this->id = $this->db->last_insert_id($dbPrefix . "user");
2143
2144
            // Set default rights
2145
            if ($this->set_default_rights() < 0) {
2146
                $this->error = 'ErrorFailedToSetDefaultRightOfUser';
2147
                $this->db->rollback();
2148
                return -5;
2149
            }
2150
2151
            if (getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER') && getDolGlobalString('STOCK_USERSTOCK_AUTOCREATE')) {
2152
                require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
2153
                $langs->load("stocks");
2154
2155
                $entrepot = new Entrepot($this->db);
0 ignored issues
show
Bug introduced by
The type DoliModules\User\Model\Entrepot was not found. Did you mean Entrepot? If so, make sure to prefix the type with \.
Loading history...
2156
                $entrepot->label = $langs->trans("PersonalStock", $this->getFullName($langs));
2157
                $entrepot->libelle = $entrepot->label; // For backward compatibility
2158
                $entrepot->description = $langs->trans("ThisWarehouseIsPersonalStock", $this->getFullName($langs));
2159
                $entrepot->statut = 1;
2160
                $entrepot->country_id = $mysoc->country_id;
2161
2162
                $warehouseid = $entrepot->create($user);
2163
2164
                $this->fk_warehouse = $warehouseid;
2165
            }
2166
2167
            // Update minor fields
2168
            $result = $this->update($user, 1, 1);
2169
            if ($result < 0) {
2170
                $this->db->rollback();
2171
                return -4;
2172
            }
2173
2174
            if (!$notrigger) {
2175
                // Call trigger
2176
                $result = $this->call_trigger('USER_CREATE', $user);
2177
                if ($result < 0) {
2178
                    $error++;
2179
                }
2180
                // End call triggers
2181
            }
2182
2183
            if (!$error) {
2184
                $this->db->commit();
2185
                return $this->id;
2186
            } else {
2187
                //$this->error=$interface->error;
2188
                dol_syslog(get_class($this) . "::create " . $this->error, LOG_ERR);
2189
                $this->db->rollback();
2190
                return -3;
2191
            }
2192
        } else {
2193
            $this->error = $this->db->lasterror();
2194
            $this->db->rollback();
2195
            return -2;
2196
        }
2197
    }
2198
2199
2200
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2201
2202
    /**
2203
     *    Assign rights by default
2204
     *
2205
     * @return     integer erreur <0, si ok renvoi le nbre de droits par default positions
2206
     */
2207
    public function set_default_rights()
2208
    {
2209
        // phpcs:enable
2210
        global $conf;
2211
2212
        $rd = [];
2213
        $num = 0;
2214
        $sql = "SELECT id FROM " . $dbPrefix . "rights_def";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
2215
        $sql .= " WHERE bydefault = 1";
2216
        $sql .= " AND entity = " . ((int)$conf->entity);
2217
2218
        $resql = $this->db->query($sql);
2219
        if ($resql) {
2220
            $num = $this->db->num_rows($resql);
2221
            $i = 0;
2222
            while ($i < $num) {
2223
                $row = $this->db->fetch_row($resql);
2224
                $rd[$i] = $row[0];
2225
                $i++;
2226
            }
2227
            $this->db->free($resql);
2228
        }
2229
        $i = 0;
2230
        while ($i < $num) {
2231
            $sql = "DELETE FROM " . $dbPrefix . "user_rights WHERE fk_user = $this->id AND fk_id=$rd[$i]";
2232
            $result = $this->db->query($sql);
2233
2234
            $sql = "INSERT INTO " . $dbPrefix . "user_rights (fk_user, fk_id) VALUES ($this->id, $rd[$i])";
2235
            $result = $this->db->query($sql);
2236
            if (!$result) {
2237
                return -1;
2238
            }
2239
            $i++;
2240
        }
2241
2242
        return $i;
2243
    }
2244
2245
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2246
2247
    /**
2248
     *      Update a user into database (and also password if this->pass is defined)
2249
     *
2250
     * @param User $user User making update
2251
     * @param int $notrigger 1=do not execute triggers, 0 by default
2252
     * @param int $nosyncmember 0=Synchronize linked member (standard info), 1=Do not synchronize linked member
2253
     * @param int $nosyncmemberpass 0=Synchronize linked member (password), 1=Do not synchronize linked member
2254
     * @param int $nosynccontact 0=Synchronize linked contact, 1=Do not synchronize linked contact
2255
     *
2256
     * @return int                         Return integer <0 if KO, >=0 if OK
2257
     */
2258
    public function update($user, $notrigger = 0, $nosyncmember = 0, $nosyncmemberpass = 0, $nosynccontact = 0)
2259
    {
2260
        global $conf, $langs;
2261
2262
        global $config;
2263
        $dbPrefix = $config->db->prefix;
2264
2265
        $nbrowsaffected = 0;
2266
        $error = 0;
2267
2268
        dol_syslog(get_class($this) . "::update notrigger=" . $notrigger . ", nosyncmember=" . $nosyncmember . ", nosyncmemberpass=" . $nosyncmemberpass);
2269
2270
        // Clean parameters
2271
        $this->civility_code = trim((string)$this->civility_code);
2272
        $this->lastname = trim((string)$this->lastname);
2273
        $this->firstname = trim((string)$this->firstname);
2274
        $this->ref_employee = trim((string)$this->ref_employee);
2275
        $this->national_registration_number = trim((string)$this->national_registration_number);
2276
        $this->employee = ($this->employee > 0 ? $this->employee : 0);
2277
        $this->login = trim((string)$this->login);
2278
        $this->gender = trim((string)$this->gender);
2279
2280
        $this->pass = trim((string)$this->pass);
2281
        $this->api_key = trim((string)$this->api_key);
2282
        $this->datestartvalidity = empty($this->datestartvalidity) ? '' : $this->datestartvalidity;
2283
        $this->dateendvalidity = empty($this->dateendvalidity) ? '' : $this->dateendvalidity;
2284
2285
        $this->address = trim((string)$this->address);
2286
        $this->zip = trim((string)$this->zip);
2287
        $this->town = trim((string)$this->town);
2288
2289
        $this->state_id = ($this->state_id > 0 ? $this->state_id : 0);
2290
        $this->country_id = ($this->country_id > 0 ? $this->country_id : 0);
2291
        $this->office_phone = trim((string)$this->office_phone);
2292
        $this->office_fax = trim((string)$this->office_fax);
2293
        $this->user_mobile = trim((string)$this->user_mobile);
2294
        $this->personal_mobile = trim((string)$this->personal_mobile);
2295
        $this->email = trim((string)$this->email);
2296
        $this->personal_email = trim((string)$this->personal_email);
2297
2298
        $this->job = trim((string)$this->job);
2299
        $this->signature = trim((string)$this->signature);
2300
        $this->note_public = trim((string)$this->note_public);
2301
        $this->note_private = trim((string)$this->note_private);
2302
        $this->openid = trim((string)$this->openid);
2303
        $this->admin = ($this->admin > 0 ? $this->admin : 0);
2304
2305
        $this->accountancy_code = trim((string)$this->accountancy_code);
2306
        $this->color = trim((string)$this->color);
2307
        $this->dateemployment = empty($this->dateemployment) ? '' : $this->dateemployment;
2308
        $this->dateemploymentend = empty($this->dateemploymentend) ? '' : $this->dateemploymentend;
2309
2310
        $this->birth = empty($this->birth) ? '' : $this->birth;
2311
        $this->fk_warehouse = (int)$this->fk_warehouse;
2312
2313
        $this->setUpperOrLowerCase();
2314
2315
        // Check parameters
2316
        $badCharUnauthorizedIntoLoginName = getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
2317
2318
        if (getDolGlobalString('USER_MAIL_REQUIRED') && !isValidEmail($this->email)) {
2319
            $langs->load("errors");
2320
            $this->error = $langs->trans("ErrorBadEMail", $this->email);
2321
            return -1;
2322
        }
2323
        if (empty($this->login)) {
2324
            $langs->load("errors");
2325
            $this->error = $langs->trans("ErrorFieldRequired", 'Login');
2326
            return -1;
2327
        } elseif (preg_match('/[' . preg_quote($badCharUnauthorizedIntoLoginName, '/') . ']/', $this->login)) {
2328
            $langs->load("errors");
2329
            $this->error = $langs->trans("ErrorBadCharIntoLoginName", $langs->transnoentitiesnoconv("Login"));
2330
            return -1;
2331
        }
2332
2333
        $this->db->begin();
2334
2335
        // Check if login already exists in same entity or into entity 0.
2336
        if (!empty($this->oldcopy) && $this->oldcopy->login != $this->login) {
2337
            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
2338
            $resqltochecklogin = $this->db->query($sqltochecklogin);
2339
            if ($resqltochecklogin) {
2340
                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
2341
                if ($objtochecklogin && $objtochecklogin->nb > 0) {
2342
                    $langs->load("errors");
2343
                    $this->error = $langs->trans("ErrorLoginAlreadyExists", $this->login);
2344
                    dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
2345
                    $this->db->rollback();
2346
                    return -1;
2347
                }
2348
            }
2349
        }
2350
        if (!empty($this->oldcopy) && !empty($this->email) && $this->oldcopy->email != $this->email) {
2351
            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $dbPrefix . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
2352
            $resqltochecklogin = $this->db->query($sqltochecklogin);
2353
            if ($resqltochecklogin) {
2354
                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
2355
                if ($objtochecklogin && $objtochecklogin->nb > 0) {
2356
                    $langs->load("errors");
2357
                    $this->error = $langs->trans("ErrorEmailAlreadyExists", $this->email);
2358
                    dol_syslog(get_class($this) . "::create " . $this->error, LOG_DEBUG);
2359
                    $this->db->rollback();
2360
                    return -1;
2361
                }
2362
            }
2363
        }
2364
2365
        // Update data
2366
        $sql = "UPDATE " . $dbPrefix . "user SET";
2367
        $sql .= " civility = '" . $this->db->escape($this->civility_code) . "'";
2368
        $sql .= ", lastname = '" . $this->db->escape($this->lastname) . "'";
2369
        $sql .= ", firstname = '" . $this->db->escape($this->firstname) . "'";
2370
        $sql .= ", ref_employee = '" . $this->db->escape($this->ref_employee) . "'";
2371
        $sql .= ", national_registration_number = '" . $this->db->escape($this->national_registration_number) . "'";
2372
        $sql .= ", employee = " . (int)$this->employee;
2373
        $sql .= ", login = '" . $this->db->escape($this->login) . "'";
2374
        $sql .= ", api_key = " . ($this->api_key ? "'" . $this->db->escape(dolEncrypt($this->api_key, '', '', 'dolibarr')) . "'" : "null");
2375
        $sql .= ", gender = " . ($this->gender != -1 ? "'" . $this->db->escape($this->gender) . "'" : "null"); // 'man' or 'woman'
2376
        $sql .= ", birth=" . (strval($this->birth) != '' ? "'" . $this->db->idate($this->birth, 'tzserver') . "'" : 'null');
2377
        if (!empty($user->admin)) {
2378
            $sql .= ", admin = " . (int)$this->admin; // admin flag can be set/unset only by an admin user
2379
        }
2380
        $sql .= ", address = '" . $this->db->escape($this->address) . "'";
2381
        $sql .= ", zip = '" . $this->db->escape($this->zip) . "'";
2382
        $sql .= ", town = '" . $this->db->escape($this->town) . "'";
2383
        $sql .= ", fk_state = " . ((!empty($this->state_id) && $this->state_id > 0) ? "'" . $this->db->escape($this->state_id) . "'" : "null");
2384
        $sql .= ", fk_country = " . ((!empty($this->country_id) && $this->country_id > 0) ? "'" . $this->db->escape($this->country_id) . "'" : "null");
2385
        $sql .= ", office_phone = '" . $this->db->escape($this->office_phone) . "'";
2386
        $sql .= ", office_fax = '" . $this->db->escape($this->office_fax) . "'";
2387
        $sql .= ", user_mobile = '" . $this->db->escape($this->user_mobile) . "'";
2388
        $sql .= ", personal_mobile = '" . $this->db->escape($this->personal_mobile) . "'";
2389
        $sql .= ", email = '" . $this->db->escape($this->email) . "'";
2390
        $sql .= ", personal_email = '" . $this->db->escape($this->personal_email) . "'";
2391
        $sql .= ", socialnetworks = '" . $this->db->escape(json_encode($this->socialnetworks)) . "'";
2392
        $sql .= ", job = '" . $this->db->escape($this->job) . "'";
2393
        $sql .= ", signature = '" . $this->db->escape($this->signature) . "'";
2394
        $sql .= ", accountancy_code = '" . $this->db->escape($this->accountancy_code) . "'";
2395
        $sql .= ", color = '" . $this->db->escape($this->color) . "'";
2396
        $sql .= ", dateemployment=" . (strval($this->dateemployment) != '' ? "'" . $this->db->idate($this->dateemployment) . "'" : 'null');
2397
        $sql .= ", dateemploymentend=" . (strval($this->dateemploymentend) != '' ? "'" . $this->db->idate($this->dateemploymentend) . "'" : 'null');
2398
        $sql .= ", datestartvalidity=" . (strval($this->datestartvalidity) != '' ? "'" . $this->db->idate($this->datestartvalidity) . "'" : 'null');
2399
        $sql .= ", dateendvalidity=" . (strval($this->dateendvalidity) != '' ? "'" . $this->db->idate($this->dateendvalidity) . "'" : 'null');
2400
        $sql .= ", note_private = '" . $this->db->escape($this->note_private) . "'";
2401
        $sql .= ", note_public = '" . $this->db->escape($this->note_public) . "'";
2402
        $sql .= ", photo = " . ($this->photo ? "'" . $this->db->escape($this->photo) . "'" : "null");
2403
        $sql .= ", openid = " . ($this->openid ? "'" . $this->db->escape($this->openid) . "'" : "null");
2404
        $sql .= ", fk_user = " . ($this->fk_user > 0 ? "'" . $this->db->escape($this->fk_user) . "'" : "null");
2405
        $sql .= ", fk_user_expense_validator = " . ($this->fk_user_expense_validator > 0 ? "'" . $this->db->escape($this->fk_user_expense_validator) . "'" : "null");
2406
        $sql .= ", fk_user_holiday_validator = " . ($this->fk_user_holiday_validator > 0 ? "'" . $this->db->escape($this->fk_user_holiday_validator) . "'" : "null");
2407
        if (isset($this->thm) || $this->thm != '') {
2408
            $sql .= ", thm= " . ($this->thm != '' ? "'" . $this->db->escape($this->thm) . "'" : "null");
2409
        }
2410
        if (isset($this->tjm) || $this->tjm != '') {
2411
            $sql .= ", tjm= " . ($this->tjm != '' ? "'" . $this->db->escape($this->tjm) . "'" : "null");
2412
        }
2413
        if (isset($this->salary) || $this->salary != '') {
2414
            $sql .= ", salary= " . ($this->salary != '' ? "'" . $this->db->escape($this->salary) . "'" : "null");
2415
        }
2416
        if (isset($this->salaryextra) || $this->salaryextra != '') {
2417
            $sql .= ", salaryextra= " . ($this->salaryextra != '' ? "'" . $this->db->escape($this->salaryextra) . "'" : "null");
2418
        }
2419
        $sql .= ", weeklyhours= " . ($this->weeklyhours != '' ? "'" . $this->db->escape($this->weeklyhours) . "'" : "null");
2420
        if (!empty($user->admin) && empty($user->entity) && $user->id != $this->id) {
2421
            $sql .= ", entity = " . ((int)$this->entity); // entity flag can be set/unset only by an another superadmin user
2422
        }
2423
        $sql .= ", default_range = " . ($this->default_range > 0 ? $this->default_range : 'null');
2424
        $sql .= ", default_c_exp_tax_cat = " . ($this->default_c_exp_tax_cat > 0 ? $this->default_c_exp_tax_cat : 'null');
2425
        $sql .= ", fk_warehouse = " . ($this->fk_warehouse > 0 ? $this->fk_warehouse : "null");
2426
        $sql .= ", lang = " . ($this->lang ? "'" . $this->db->escape($this->lang) . "'" : "null");
2427
        $sql .= " WHERE rowid = " . ((int)$this->id);
2428
2429
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
2430
        $resql = $this->db->query($sql);
2431
        if ($resql) {
2432
            $nbrowsaffected += $this->db->affected_rows($resql);
2433
2434
            // Update password
2435
            if (!empty($this->pass)) {
2436
                if ($this->pass != $this->pass_indatabase && !dol_verifyHash($this->pass, $this->pass_indatabase_crypted)) {
2437
                    // If a new value for password is set and different than the one encrypted into database
2438
                    $result = $this->setPassword($user, $this->pass, 0, $notrigger, $nosyncmemberpass, 0, 1);
2439
                    if (is_int($result) && $result < 0) {
2440
                        return -5;
2441
                    }
2442
                }
2443
            }
2444
2445
            // If user is linked to a member, remove old link to this member
2446
            if ($this->fk_member > 0) {
2447
                dol_syslog(get_class($this) . "::update remove link with member. We will recreate it later", LOG_DEBUG);
2448
                $sql = "UPDATE " . $dbPrefix . "user SET fk_member = NULL where fk_member = " . ((int)$this->fk_member);
2449
                $resql = $this->db->query($sql);
2450
                if (!$resql) {
2451
                    $this->error = $this->db->error();
2452
                    $this->db->rollback();
2453
                    return -5;
2454
                }
2455
            }
2456
            // Set link to user
2457
            dol_syslog(get_class($this) . "::update set link with member", LOG_DEBUG);
2458
            $sql = "UPDATE " . $dbPrefix . "user SET fk_member =" . ($this->fk_member > 0 ? ((int)$this->fk_member) : 'null') . " where rowid = " . ((int)$this->id);
2459
            $resql = $this->db->query($sql);
2460
            if (!$resql) {
2461
                $this->error = $this->db->error();
2462
                $this->db->rollback();
2463
                return -5;
2464
            }
2465
2466
            if ($nbrowsaffected) {  // If something has changed in data
2467
                if ($this->fk_member > 0 && !$nosyncmember) {
2468
                    dol_syslog(get_class($this) . "::update user is linked with a member. We try to update member too.", LOG_DEBUG);
2469
2470
2471
                    // This user is linked with a member, so we also update member information
2472
                    // if this is an update.
2473
                    $adh = new Adherent($this->db);
2474
                    $result = $adh->fetch($this->fk_member);
2475
2476
                    if ($result > 0) {
2477
                        $adh->civility_code = $this->civility_code;
2478
                        $adh->firstname = $this->firstname;
2479
                        $adh->lastname = $this->lastname;
2480
                        $adh->login = $this->login;
2481
                        $adh->gender = $this->gender;
2482
                        $adh->birth = $this->birth;
2483
2484
                        $adh->pass = $this->pass;
2485
2486
                        $adh->address = $this->address;
2487
                        $adh->town = $this->town;
2488
                        $adh->zip = $this->zip;
2489
                        $adh->state_id = $this->state_id;
2490
                        $adh->country_id = $this->country_id;
2491
2492
                        $adh->email = $this->email;
2493
2494
                        $adh->socialnetworks = $this->socialnetworks;
2495
2496
                        $adh->phone = $this->office_phone;
2497
                        $adh->phone_mobile = $this->user_mobile;
2498
2499
                        $adh->default_lang = $this->lang;
2500
2501
                        $adh->user_id = $this->id;
2502
                        $adh->user_login = $this->login;
2503
2504
                        $result = $adh->update($user, 0, 1, 0);
2505
                        if ($result < 0) {
2506
                            $this->error = $adh->error;
2507
                            $this->errors = $adh->errors;
2508
                            dol_syslog(get_class($this) . "::update error after calling adh->update to sync it with user: " . $this->error, LOG_ERR);
2509
                            $error++;
2510
                        }
2511
                    } elseif ($result < 0) {
2512
                        $this->error = $adh->error;
2513
                        $this->errors = $adh->errors;
2514
                        $error++;
2515
                    }
2516
                }
2517
2518
                if ($this->contact_id > 0 && !$nosynccontact) {
2519
                    dol_syslog(get_class($this) . "::update user is linked with a contact. We try to update contact too.", LOG_DEBUG);
2520
2521
2522
                    // This user is linked with a contact, so we also update contact information if this is an update.
2523
                    $tmpobj = new Contact($this->db);
2524
                    $result = $tmpobj->fetch($this->contact_id);
2525
2526
                    if ($result >= 0) {
2527
                        $tmpobj->civility_code = $this->civility_code;
2528
                        $tmpobj->firstname = $this->firstname;
2529
                        $tmpobj->lastname = $this->lastname;
2530
                        $tmpobj->login = $this->login;
2531
                        $tmpobj->gender = $this->gender;
2532
                        $tmpobj->birth = $this->birth;
2533
2534
                        //$tmpobj->pass=$this->pass;
2535
2536
                        $tmpobj->email = $this->email;
2537
2538
                        $tmpobj->socialnetworks = $this->socialnetworks;
2539
2540
                        $tmpobj->phone_pro = $this->office_phone;
2541
                        $tmpobj->phone_mobile = $this->user_mobile;
2542
                        $tmpobj->fax = $this->office_fax;
2543
2544
                        $tmpobj->default_lang = $this->lang;
2545
2546
                        $tmpobj->address = $this->address;
2547
                        $tmpobj->town = $this->town;
2548
                        $tmpobj->zip = $this->zip;
2549
                        $tmpobj->state_id = $this->state_id;
2550
                        $tmpobj->country_id = $this->country_id;
2551
2552
                        $tmpobj->user_id = $this->id;
2553
                        $tmpobj->user_login = $this->login;
2554
2555
                        $result = $tmpobj->update($tmpobj->id, $user, 0, 'update', 1);
2556
                        if ($result < 0) {
2557
                            $this->error = $tmpobj->error;
2558
                            $this->errors = $tmpobj->errors;
2559
                            dol_syslog(get_class($this) . "::update error after calling adh->update to sync it with user: " . $this->error, LOG_ERR);
2560
                            $error++;
2561
                        }
2562
                    } else {
2563
                        $this->error = $tmpobj->error;
2564
                        $this->errors = $tmpobj->errors;
2565
                        $error++;
2566
                    }
2567
                }
2568
            }
2569
2570
            $action = 'update';
2571
2572
            // Actions on extra fields
2573
            if (!$error) {
2574
                $result = $this->insertExtraFields();
2575
                if ($result < 0) {
2576
                    $error++;
2577
                }
2578
            }
2579
2580
            if (!$error && !$notrigger) {
2581
                // Call trigger
2582
                $result = $this->call_trigger('USER_MODIFY', $user);
2583
                if ($result < 0) {
2584
                    $error++;
2585
                }
2586
                // End call triggers
2587
            }
2588
2589
            if (!$error) {
2590
                $this->db->commit();
2591
                return $nbrowsaffected;
2592
            } else {
2593
                dol_syslog(get_class($this) . "::update error=" . $this->error, LOG_ERR);
2594
                $this->db->rollback();
2595
                return -1;
2596
            }
2597
        } else {
2598
            $this->error = $this->db->lasterror();
2599
            $this->db->rollback();
2600
            return -2;
2601
        }
2602
    }
2603
2604
2605
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2606
2607
    /**
2608
     *  Change password of a user
2609
     *
2610
     * @param User $user Object user of user requesting the change (not the user for who we change
2611
     *                                       the password). May be unknown.
2612
     * @param string $password New password, in clear text or already encrypted (to generate if not
2613
     *                                       provided)
2614
     * @param int $changelater 0=Default, 1=Save password into pass_temp to change password only after
2615
     *                                       clicking on confirm email
2616
     * @param int $notrigger 1=Does not launch triggers
2617
     * @param int $nosyncmember Do not synchronize linked member
2618
     * @param int $passwordalreadycrypted 0=Value is cleartext password, 1=Value is encrypted value.
2619
     * @param int $flagdelsessionsbefore 1=Save also the current date to ask to invalidate all other session before
2620
     *                                       this date.
2621
     *
2622
     * @return int|string                      If OK return clear password, 0 if no change (warning, you may retrieve 1
2623
     *                                         instead of 0 even if password was same), < 0 if error
2624
     */
2625
    public function setPassword($user, $password = '', $changelater = 0, $notrigger = 0, $nosyncmember = 0, $passwordalreadycrypted = 0, $flagdelsessionsbefore = 1)
2626
    {
2627
        global $conf, $langs;
2628
        global $config;
2629
        $dbPrefix = $config->db->prefix;
2630
2631
        require_once BASE_PATH . '/../Dolibarr/Lib/Security2.php';
2632
2633
        $error = 0;
2634
2635
        dol_syslog(get_class($this) . "::setPassword user=" . $user->id . " password=" . preg_replace('/./i', '*', $password) . " changelater=" . $changelater . " notrigger=" . $notrigger . " nosyncmember=" . $nosyncmember, LOG_DEBUG);
2636
2637
        // If new password not provided, we generate one
2638
        if (!$password) {
2639
            $password = getRandomPassword(false);
2640
        }
2641
2642
        // Check and encrypt the password
2643
        if (empty($passwordalreadycrypted)) {
2644
            if (getDolGlobalString('USER_PASSWORD_GENERATED')) {
2645
                // Add a check on rules for password syntax using the setup of the password generator
2646
                $modGeneratePassClass = 'modGeneratePass' . ucfirst($conf->global->USER_PASSWORD_GENERATED);
2647
2648
                include_once DOL_DOCUMENT_ROOT . '/core/modules/security/generate/' . $modGeneratePassClass . '.class.php';
2649
                if (class_exists($modGeneratePassClass)) {
2650
                    $modGeneratePass = new $modGeneratePassClass($this->db, $conf, $langs, $user);
2651
2652
                    // To check an input user password, we disable the cleaning on ambiguous characters (this is used only for auto-generated password)
2653
                    $modGeneratePass->WithoutAmbi = 0;
2654
2655
                    // Call to validatePassword($password) to check pass match rules
2656
                    $testpassword = $modGeneratePass->validatePassword($password);
2657
                    if (!$testpassword) {
2658
                        $this->error = $modGeneratePass->error;
2659
                        return -1;
2660
                    }
2661
                }
2662
            }
2663
2664
2665
            // Now, we encrypt the new password
2666
            $password_crypted = dol_hash($password);
2667
        }
2668
2669
        // Update password
2670
        if (!$changelater) {
2671
            if (!is_object($this->oldcopy)) {
2672
                $this->oldcopy = clone $this;
2673
            }
2674
2675
            $this->db->begin();
2676
2677
            $sql = "UPDATE " . $dbPrefix . "user";
2678
            $sql .= " SET pass_crypted = '" . $this->db->escape($password_crypted) . "',";
2679
            $sql .= " pass_temp = null";
2680
            if (!empty($flagdelsessionsbefore)) {
2681
                $sql .= ", flagdelsessionsbefore = '" . $this->db->idate(dol_now() - 5, 'gmt') . "'";
2682
            }
2683
            if (getDolGlobalString('DATABASE_PWD_ENCRYPTED')) {
2684
                $sql .= ", pass = null";
2685
            } else {
2686
                $sql .= ", pass = '" . $this->db->escape($password) . "'";
2687
            }
2688
            $sql .= " WHERE rowid = " . ((int)$this->id);
2689
2690
            dol_syslog(get_class($this) . "::setPassword", LOG_DEBUG);
2691
            $result = $this->db->query($sql);
2692
            if ($result) {
2693
                if ($this->db->affected_rows($result)) {
2694
                    $this->pass = $password;
2695
                    $this->pass_indatabase = $password;
2696
                    $this->pass_indatabase_crypted = $password_crypted;
2697
2698
                    if ($this->fk_member && !$nosyncmember) {
2699
                        // This user is linked with a member, so we also update members information
2700
                        // if this is an update.
2701
                        $adh = new Adherent($this->db);
2702
                        $result = $adh->fetch($this->fk_member);
2703
2704
                        if ($result >= 0) {
2705
                            $result = $adh->setPassword($user, $this->pass, (!getDolGlobalString('DATABASE_PWD_ENCRYPTED') ? 0 : 1), 1); // The encryption is not managed in the 'adherent' module
2706
                            if (is_int($result) && $result < 0) {
2707
                                $this->error = $adh->error;
2708
                                dol_syslog(get_class($this) . "::setPassword " . $this->error, LOG_ERR);
2709
                                $error++;
2710
                            }
2711
                        } else {
2712
                            $this->error = $adh->error;
2713
                            $error++;
2714
                        }
2715
                    }
2716
2717
                    dol_syslog(get_class($this) . "::setPassword notrigger=" . $notrigger . " error=" . $error, LOG_DEBUG);
2718
2719
                    if (!$error && !$notrigger) {
2720
                        // Call trigger
2721
                        $result = $this->call_trigger('USER_NEW_PASSWORD', $user);
2722
                        if ($result < 0) {
2723
                            $error++;
2724
                            $this->db->rollback();
2725
                            return -1;
2726
                        }
2727
                        // End call triggers
2728
                    }
2729
2730
                    $this->db->commit();
2731
                    return $this->pass;
2732
                } else {
2733
                    $this->db->rollback();
2734
                    return 0;
2735
                }
2736
            } else {
2737
                $this->db->rollback();
2738
                dol_print_error($this->db);
2739
                return -1;
2740
            }
2741
        } else {
2742
            // We store password in password temporary field.
2743
            // After receiving confirmation link, we will erase and store it in pass_crypted
2744
            $sql = "UPDATE " . $dbPrefix . "user";
2745
            $sql .= " SET pass_temp = '" . $this->db->escape($password) . "'";
2746
            $sql .= " WHERE rowid = " . ((int)$this->id);
2747
2748
            dol_syslog(get_class($this) . "::setPassword", LOG_DEBUG); // No log
2749
            $result = $this->db->query($sql);
2750
            if ($result) {
2751
                return $password;
2752
            } else {
2753
                dol_print_error($this->db);
2754
                return -3;
2755
            }
2756
        }
2757
    }
2758
2759
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2760
2761
    /**
2762
     *      Renvoie la derniere erreur fonctionnelle de manipulation de l'objet
2763
     *
2764
     * @return    string      chaine erreur
2765
     */
2766
    public function error()
2767
    {
2768
        return $this->error;
2769
    }
2770
2771
    /**
2772
     *  Create a user into database from a member object.
2773
     *  If $member->fk_soc is set, it will be an external user.
2774
     *
2775
     * @param Adherent $member Object member source
2776
     * @param string $login Login to force
2777
     *
2778
     * @return int                         Return integer <0 if KO, if OK, return id of created account
2779
     */
2780
    public function create_from_member($member, $login = '')
2781
    {
2782
        // phpcs:enable
2783
        global $conf, $user, $langs;
2784
2785
        // Set properties on new user
2786
        $this->admin = 0;
2787
        $this->civility_code = $member->civility_id;
2788
        $this->lastname = $member->lastname;
2789
        $this->firstname = $member->firstname;
2790
        $this->gender = $member->gender;
2791
        $this->email = $member->email;
2792
        $this->fk_member = $member->id;
2793
        $this->address = $member->address;
2794
        $this->zip = $member->zip;
2795
        $this->town = $member->town;
2796
        $this->setUpperOrLowerCase();
2797
        $this->state_id = $member->state_id;
2798
        $this->country_id = $member->country_id;
2799
        $this->socialnetworks = $member->socialnetworks;
2800
2801
        $this->pass = $member->pass;
2802
        $this->pass_crypted = $member->pass_indatabase_crypted;
2803
2804
        if (empty($login)) {
2805
            include_once BASE_PATH . '/../Dolibarr/Lib/Functions2.php';
2806
            $login = dol_buildlogin($member->lastname, $member->firstname);
2807
        }
2808
        $this->login = $login;
2809
2810
        $this->db->begin();
2811
2812
        // Create and set $this->id
2813
        $result = $this->create($user);
2814
        if ($result > 0) {
2815
            if (!empty($this->pass)) {  // If a clear password was received (this situation should not happen anymore now), we use it to save it into database
2816
                $newpass = $this->setPassword($user, $this->pass);
2817
                if (is_int($newpass) && $newpass < 0) {
2818
                    $result = -2;
2819
                }
2820
            } elseif (!empty($this->pass_crypted)) {    // If an encrypted password is already known, we save it directly into database because the previous create did not save it.
2821
                $sql = "UPDATE " . $dbPrefix . "user";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
2822
                $sql .= " SET pass_crypted = '" . $this->db->escape($this->pass_crypted) . "'";
2823
                $sql .= " WHERE rowid=" . ((int)$this->id);
2824
2825
                $resql = $this->db->query($sql);
2826
                if (!$resql) {
2827
                    $result = -1;
2828
                }
2829
            }
2830
2831
            if ($result > 0 && $member->socid) {    // If member is linked to a thirdparty
2832
                $sql = "UPDATE " . $dbPrefix . "user";
2833
                $sql .= " SET fk_soc=" . ((int)$member->socid);
2834
                $sql .= " WHERE rowid=" . ((int)$this->id);
2835
2836
                dol_syslog(get_class($this) . "::create_from_member", LOG_DEBUG);
2837
                $resql = $this->db->query($sql);
2838
                if ($resql) {
2839
                    $this->db->commit();
2840
                    return $this->id;
2841
                } else {
2842
                    $this->error = $this->db->lasterror();
2843
2844
                    $this->db->rollback();
2845
                    return -1;
2846
                }
2847
            }
2848
        }
2849
2850
        if ($result > 0) {
2851
            $this->db->commit();
2852
            return $this->id;
2853
        } else {
2854
            // $this->error deja positionne
2855
            $this->db->rollback();
2856
            return -2;
2857
        }
2858
    }
2859
2860
    /**
2861
     *  Update the user's last login date in the database.
2862
     *  Function called when a new connection is made by the user
2863
     *
2864
     * @return int     Return integer <0 si echec, >=0 si ok
2865
     */
2866
    public function update_last_login_date()
2867
    {
2868
        global $config;
2869
        $dbPrefix = $config->db->prefix;
2870
2871
        // phpcs:enable
2872
        $now = dol_now();
2873
2874
        $userremoteip = getUserRemoteIP();
2875
2876
        $sql = "UPDATE " . $dbPrefix . "user SET";
2877
        $sql .= " datepreviouslogin = datelastlogin,";
2878
        $sql .= " ippreviouslogin = iplastlogin,";
2879
        $sql .= " datelastlogin = '" . $this->db->idate($now) . "',";
2880
        $sql .= " iplastlogin = '" . $this->db->escape($userremoteip) . "',";
2881
        $sql .= " tms = tms"; // The last update date must change because the last login date is updated
2882
        $sql .= " WHERE rowid = " . ((int)$this->id);
2883
2884
        dol_syslog(get_class($this) . "::update_last_login_date user->id=" . $this->id . " " . $sql, LOG_DEBUG);
2885
        $resql = $this->db->query($sql);
2886
        if ($resql) {
2887
            $this->datepreviouslogin = $this->datelastlogin;
2888
            $this->datelastlogin = $now;
2889
            $this->ippreviouslogin = $this->iplastlogin;
2890
            $this->iplastlogin = $userremoteip;
2891
            return 1;
2892
        } else {
2893
            $this->error = $this->db->lasterror() . ' sql=' . $sql;
2894
            return -1;
2895
        }
2896
    }
2897
2898
    /**
2899
     *  Send a new password (or instructions to reset it) by email
2900
     *
2901
     * @param User $user Object user that send the email (not the user we send to) @todo object $user is not
2902
     *                            used !
2903
     * @param string $password New password
2904
     * @param int $changelater 0=Send clear passwod into email, 1=Change password only after clicking on confirm
2905
     *                            email. @return int                     Return integer < 0 si erreur, > 0 si ok
2906
     * @todo Add method 2 = Send link to reset password
2907
     *
2908
     */
2909
    public function send_password($user, $password = '', $changelater = 0)
2910
    {
2911
        // phpcs:enable
2912
        global $conf, $langs, $mysoc;
2913
        global $dolibarr_main_url_root;
2914
2915
        require_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php';
2916
2917
        $msgishtml = 0;
2918
2919
        // Define $msg
2920
        $mesg = '';
2921
2922
        $outputlangs = new Translate("", $conf);
0 ignored issues
show
Bug introduced by
The type DoliModules\User\Model\Translate was not found. Did you mean Translate? If so, make sure to prefix the type with \.
Loading history...
2923
2924
        if (
2925
            isset($this->conf->MAIN_LANG_DEFAULT)
2926
            && $this->conf->MAIN_LANG_DEFAULT != 'auto'
2927
        ) {  // If user has defined its own language (rare because in most cases, auto is used)
2928
            $outputlangs->getDefaultLang($this->conf->MAIN_LANG_DEFAULT);
2929
        }
2930
2931
        if ($this->conf->MAIN_LANG_DEFAULT) {
2932
            $outputlangs->setDefaultLang($this->conf->MAIN_LANG_DEFAULT);
2933
        } else {    // If user has not defined its own language, we used current language
2934
            $outputlangs = $langs;
2935
        }
2936
2937
        // Load translation files required by the page
2938
        $outputlangs->loadLangs(["main", "errors", "users", "other"]);
2939
2940
        $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', constant('DOL_APPLICATION_TITLE'));
2941
2942
        $subject = '[' . $appli . '] ' . $outputlangs->transnoentitiesnoconv("SubjectNewPassword", $appli);
2943
2944
        if (!isset($dolibarr_main_url_root)) {
2945
            $dolibarr_main_url_root = '';
2946
        }
2947
2948
        // Define $urlwithroot
2949
        $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($dolibarr_main_url_root));
2950
        $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
2951
2952
        if (!$changelater) {
2953
            $url = $urlwithroot . '/';
2954
            if (getDolGlobalString('URL_REDIRECTION_AFTER_CHANGEPASSWORD')) {
2955
                $url = getDolGlobalString('URL_REDIRECTION_AFTER_CHANGEPASSWORD');
2956
            }
2957
2958
            dol_syslog(get_class($this) . "::send_password changelater is off, url=" . $url);
2959
2960
            $mesg .= $outputlangs->transnoentitiesnoconv("RequestToResetPasswordReceived") . ".\n";
2961
            $mesg .= $outputlangs->transnoentitiesnoconv("NewKeyIs") . " :\n\n";
2962
            $mesg .= $outputlangs->transnoentitiesnoconv("Login") . " = " . $this->login . "\n";
2963
            $mesg .= $outputlangs->transnoentitiesnoconv("Password") . " = " . $password . "\n\n";
2964
            $mesg .= "\n";
2965
2966
            $mesg .= $outputlangs->transnoentitiesnoconv("ClickHereToGoTo", $appli) . ': ' . $url . "\n\n";
2967
            $mesg .= "--\n";
2968
            $mesg .= $user->getFullName($outputlangs); // Username that send the email (not the user for who we want to reset password)
2969
        } else {
2970
            //print $password.'-'.$this->id.'-'.$conf->file->instance_unique_id;
2971
            $url = $urlwithroot . '/user/passwordforgotten.php?action=validatenewpassword';
2972
            $url .= '&username=' . urlencode($this->login) . "&passworduidhash=" . urlencode(dol_hash($password . '-' . $this->id . '-' . $conf->file->instance_unique_id));
2973
            if (isModEnabled('multicompany')) {
2974
                $url .= '&entity=' . (!empty($this->entity) ? $this->entity : 1);
2975
            }
2976
2977
            dol_syslog(get_class($this) . "::send_password changelater is on, url=" . $url);
2978
2979
            $msgishtml = 1;
2980
2981
            $mesg .= $outputlangs->transnoentitiesnoconv("RequestToResetPasswordReceived") . "<br>\n";
2982
            $mesg .= $outputlangs->transnoentitiesnoconv("NewKeyWillBe") . " :<br>\n<br>\n";
2983
            $mesg .= '<strong>' . $outputlangs->transnoentitiesnoconv("Login") . "</strong> = " . $this->login . "<br>\n";
2984
            $mesg .= '<strong>' . $outputlangs->transnoentitiesnoconv("Password") . "</strong> = " . $password . "<br>\n<br>\n";
2985
            $mesg .= "<br>\n";
2986
            $mesg .= $outputlangs->transnoentitiesnoconv("YouMustClickToChange") . " :<br>\n";
2987
            $mesg .= '<a href="' . $url . '" rel="noopener">' . $outputlangs->transnoentitiesnoconv("ConfirmPasswordChange") . '</a>' . "<br>\n<br>\n";
2988
            $mesg .= $outputlangs->transnoentitiesnoconv("ForgetIfNothing") . "<br>\n<br>\n";
2989
        }
2990
2991
        $trackid = 'use' . $this->id;
2992
        $sendcontext = 'password';
2993
2994
        $mailfile = new CMailFile(
0 ignored issues
show
Bug introduced by
The type DoliModules\User\Model\CMailFile was not found. Did you mean CMailFile? If so, make sure to prefix the type with \.
Loading history...
2995
            $subject,
2996
            $this->email,
2997
            $conf->global->MAIN_MAIL_EMAIL_FROM,
2998
            $mesg,
2999
            [],
3000
            [],
3001
            [],
3002
            '',
3003
            '',
3004
            0,
3005
            $msgishtml,
3006
            '',
3007
            '',
3008
            $trackid,
3009
            '',
3010
            $sendcontext
3011
        );
3012
3013
        if ($mailfile->sendfile()) {
3014
            return 1;
3015
        } else {
3016
            $langs->trans("errors");
3017
            $this->error = $langs->trans("ErrorFailedToSendPassword") . ' ' . $mailfile->error;
3018
            return -1;
3019
        }
3020
    }
3021
3022
    /**
3023
     *  Read clicktodial information for user
3024
     *
3025
     * @return int Return integer <0 if KO, >0 if OK
3026
     */
3027
    public function fetch_clicktodial()
3028
    {
3029
        // phpcs:enable
3030
        $sql = "SELECT url, login, pass, poste ";
3031
        $sql .= " FROM " . $dbPrefix . "user_clicktodial as u";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
3032
        $sql .= " WHERE u.fk_user = " . ((int)$this->id);
3033
3034
        $resql = $this->db->query($sql);
3035
        if ($resql) {
3036
            if ($this->db->num_rows($resql)) {
3037
                $obj = $this->db->fetch_object($resql);
3038
3039
                $this->clicktodial_url = $obj->url;
3040
                $this->clicktodial_login = $obj->login;
3041
                $this->clicktodial_password = $obj->pass;
3042
                $this->clicktodial_poste = $obj->poste;
3043
            }
3044
3045
            $this->clicktodial_loaded = 1; // Data loaded (found or not)
3046
3047
            $this->db->free($resql);
3048
            return 1;
3049
        } else {
3050
            $this->error = $this->db->error();
3051
            return -1;
3052
        }
3053
    }
3054
3055
    /**
3056
     *  Update clicktodial info
3057
     *
3058
     * @return int  Return integer <0 if KO, >0 if OK
3059
     */
3060
    public function update_clicktodial()
3061
    {
3062
        global $config;
3063
        $dbPrefix = $config->db->prefix;
3064
3065
        // phpcs:enable
3066
        $this->db->begin();
3067
3068
        $sql = "DELETE FROM " . $dbPrefix . "user_clicktodial";
3069
        $sql .= " WHERE fk_user = " . ((int)$this->id);
3070
3071
        dol_syslog(get_class($this) . '::update_clicktodial', LOG_DEBUG);
3072
        $result = $this->db->query($sql);
3073
3074
        $sql = "INSERT INTO " . $dbPrefix . "user_clicktodial";
3075
        $sql .= " (fk_user,url,login,pass,poste)";
3076
        $sql .= " VALUES (" . $this->id;
3077
        $sql .= ", '" . $this->db->escape($this->clicktodial_url) . "'";
3078
        $sql .= ", '" . $this->db->escape($this->clicktodial_login) . "'";
3079
        $sql .= ", '" . $this->db->escape($this->clicktodial_password) . "'";
3080
        $sql .= ", '" . $this->db->escape($this->clicktodial_poste) . "')";
3081
3082
        dol_syslog(get_class($this) . '::update_clicktodial', LOG_DEBUG);
3083
        $result = $this->db->query($sql);
3084
        if ($result) {
3085
            $this->db->commit();
3086
            return 1;
3087
        } else {
3088
            $this->db->rollback();
3089
            $this->error = $this->db->lasterror();
3090
            return -1;
3091
        }
3092
    }
3093
3094
    /**
3095
     *  Add user into a group
3096
     *
3097
     * @param int $group Id of group
3098
     * @param int $entity Entity
3099
     * @param int $notrigger Disable triggers
3100
     *
3101
     * @return int                 Return integer <0 if KO, >0 if OK
3102
     */
3103
    public function SetInGroup($group, $entity, $notrigger = 0)
3104
    {
3105
        // phpcs:enable
3106
        global $conf, $langs, $user;
3107
        global $config;
3108
        $dbPrefix = $config->db->prefix;
3109
3110
        $error = 0;
3111
3112
        $this->db->begin();
3113
3114
        $sql = "DELETE FROM " . $dbPrefix . "usergroup_user";
3115
        $sql .= " WHERE fk_user  = " . ((int)$this->id);
3116
        $sql .= " AND fk_usergroup = " . ((int)$group);
3117
        $sql .= " AND entity = " . ((int)$entity);
3118
3119
        $result = $this->db->query($sql);
3120
3121
        $sql = "INSERT INTO " . $dbPrefix . "usergroup_user (entity, fk_user, fk_usergroup)";
3122
        $sql .= " VALUES (" . ((int)$entity) . "," . ((int)$this->id) . "," . ((int)$group) . ")";
3123
3124
        $result = $this->db->query($sql);
3125
        if ($result) {
3126
            if (!$error && !$notrigger) {
3127
                $this->context = ['audit' => $langs->trans("UserSetInGroup"), 'newgroupid' => $group];
3128
3129
                // Call trigger
3130
                $result = $this->call_trigger('USER_MODIFY', $user);
3131
                if ($result < 0) {
3132
                    $error++;
3133
                }
3134
                // End call triggers
3135
            }
3136
3137
            if (!$error) {
3138
                $this->db->commit();
3139
                return 1;
3140
            } else {
3141
                dol_syslog(get_class($this) . "::SetInGroup " . $this->error, LOG_ERR);
3142
                $this->db->rollback();
3143
                return -2;
3144
            }
3145
        } else {
3146
            $this->error = $this->db->lasterror();
3147
            $this->db->rollback();
3148
            return -1;
3149
        }
3150
    }
3151
3152
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3153
3154
    /**
3155
     *  Remove a user from a group
3156
     *
3157
     * @param int $group Id of group
3158
     * @param int $entity Entity
3159
     * @param int $notrigger Disable triggers
3160
     *
3161
     * @return int                  Return integer <0 if KO, >0 if OK
3162
     */
3163
    public function RemoveFromGroup($group, $entity, $notrigger = 0)
3164
    {
3165
        // phpcs:enable
3166
        global $conf, $langs, $user;
3167
3168
        $error = 0;
3169
3170
        $this->db->begin();
3171
3172
        $sql = "DELETE FROM " . $dbPrefix . "usergroup_user";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
3173
        $sql .= " WHERE fk_user  = " . ((int)$this->id);
3174
        $sql .= " AND fk_usergroup = " . ((int)$group);
3175
        if (empty($entity)) {
3176
            $sql .= " AND entity IN (0, 1)";    // group may be in entity 0 (so $entity=0) and link with user into entity 1.
3177
        } else {
3178
            $sql .= " AND entity = " . ((int)$entity);
3179
        }
3180
3181
        $result = $this->db->query($sql);
3182
        if ($result) {
3183
            if (!$error && !$notrigger) {
3184
                $this->context = ['audit' => $langs->trans("UserRemovedFromGroup"), 'oldgroupid' => $group];
3185
3186
                // Call trigger
3187
                $result = $this->call_trigger('USER_MODIFY', $user);
3188
                if ($result < 0) {
3189
                    $error++;
3190
                }
3191
                // End call triggers
3192
            }
3193
3194
            if (!$error) {
3195
                $this->db->commit();
3196
                return 1;
3197
            } else {
3198
                dol_syslog(get_class($this) . "::RemoveFromGroup " . $this->error, LOG_ERR);
3199
                $this->db->rollback();
3200
                return -2;
3201
            }
3202
        } else {
3203
            $this->error = $this->db->lasterror();
3204
            $this->db->rollback();
3205
            return -1;
3206
        }
3207
    }
3208
3209
    /**
3210
     *  Return a link with photo
3211
     *  Use this->id,this->photo
3212
     *
3213
     * @return int     0=Valid, >0 if not valid
3214
     */
3215
    public function isNotIntoValidityDateRange()
3216
    {
3217
        include_once BASE_PATH . '/../Dolibarr/Lib/Date.php';
3218
3219
        $now = dol_now();
3220
3221
        //dol_syslog("isNotIntoValidityDateRange ".$this->datestartvalidity);
3222
3223
        // Check date start validity
3224
        if ($this->datestartvalidity && $this->datestartvalidity > dol_get_last_hour($now)) {
3225
            return 1;
3226
        }
3227
        // Check date end validity
3228
        if ($this->dateendvalidity && $this->dateendvalidity < dol_get_first_hour($now)) {
3229
            return 1;
3230
        }
3231
3232
        return 0;
3233
    }
3234
3235
3236
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
3237
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3238
3239
    /**
3240
     *  Return a link with photo
3241
     *  Use this->id,this->photo
3242
     *
3243
     * @param int $width Width of image
3244
     * @param int $height Height of image
3245
     * @param string $cssclass Force a css class
3246
     * @param string $imagesize 'mini', 'small' or '' (original)
3247
     *
3248
     * @return string                  String with URL link
3249
     * @see getImagePublicURLOfObject()
3250
     */
3251
    public function getPhotoUrl($width, $height, $cssclass = '', $imagesize = '')
3252
    {
3253
        $result = '<a href="' . DOL_URL_ROOT . '/user/card.php?id=' . $this->id . '">';
3254
        $result .= Form::showphoto('userphoto', $this, $width, $height, 0, $cssclass, $imagesize);
3255
        $result .= '</a>';
3256
3257
        return $result;
3258
    }
3259
3260
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
3261
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3262
3263
    /**
3264
     *  Return clickable link of login (optionally with picto)
3265
     *
3266
     * @param int $withpictoimg Include picto into link (1=picto, -1=photo)
3267
     * @param string $option On what the link point to ('leave', 'accountancy', 'nolink', )
3268
     * @param integer $notooltip 1=Disable tooltip on picto and name
3269
     * @param string $morecss Add more css on link
3270
     *
3271
     * @return string                      String with URL
3272
     */
3273
    public function getLoginUrl($withpictoimg = 0, $option = '', $notooltip = 0, $morecss = '')
3274
    {
3275
        global $langs, $user;
3276
3277
        $result = '';
3278
3279
        $linkstart = '<a href="' . DOL_URL_ROOT . '/user/card.php?id=' . $this->id . '">';
3280
        $linkend = '</a>';
3281
3282
        //Check user's rights to see an other user
3283
        if ((!$user->hasRight('user', 'user', 'lire') && $this->id != $user->id)) {
3284
            $option = 'nolink';
3285
        }
3286
3287
        if ($option == 'xxx') {
3288
            $linkstart = '<a href="' . DOL_URL_ROOT . '/user/card.php?id=' . $this->id . '">';
3289
            $linkend = '</a>';  // @phan-suppress-current-line PhanPluginRedundantAssignment
3290
        }
3291
3292
        if ($option == 'nolink') {
3293
            $linkstart = '';
3294
            $linkend = '';
3295
        }
3296
3297
        $result .= $linkstart;
3298
        if ($withpictoimg) {
3299
            $paddafterimage = '';
3300
            if (abs($withpictoimg) == 1) {
3301
                $paddafterimage = 'style="margin-' . ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right') . ': 3px;"';
3302
            }
3303
            // Only picto
3304
            if ($withpictoimg > 0) {
3305
                $picto = '<!-- picto user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '">' . img_object('', 'user', $paddafterimage . ' ' . ($notooltip ? '' : 'class="paddingright classfortooltip"'), 0, 0, $notooltip ? 0 : 1) . '</span>';
3306
            } else {
3307
                // Picto must be a photo
3308
                $picto = '<!-- picto photo user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '"' . ($paddafterimage ? ' ' . $paddafterimage : '') . '>' . Form::showphoto('userphoto', $this, 0, 0, 0, 'userphoto' . ($withpictoimg == -3 ? 'small' : ''), 'mini', 0, 1) . '</span>';
3309
            }
3310
            $result .= $picto;
3311
        }
3312
        $result .= $this->login;
3313
        $result .= $linkend;
3314
3315
        return $result;
3316
    }
3317
3318
    /**
3319
     *  Return clicable link of object (optionally with picto)
3320
     *
3321
     * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
3322
     * @param array $arraydata Array of data
3323
     *
3324
     * @return     string                              HTML Code for Kanban thumb.
3325
     */
3326
    public function getKanbanView($option = '', $arraydata = null)
3327
    {
3328
        global $langs;
3329
3330
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3331
3332
        $return = '<div class="box-flex-item box-flex-grow-zero">';
3333
        $return .= '<div class="info-box info-box-sm">';
3334
        $return .= '<span class="info-box-icon bg-infobox-action">';
3335
3336
        $label = '';
3337
        if (!empty($this->photo)) {
3338
            //$label .= '<div class="photointooltip floatright">';
3339
            $label .= Form::showphoto('userphoto', $this, 0, 60, 0, 'photokanban photoref photowithmargin photologintooltip', 'small', 0, 1); // Force height to 60 so we total height of tooltip can be calculated and collision can be managed
3340
            //$label .= '</div>';
3341
            //$label .= '<div style="clear: both;"></div>';
3342
            $return .= $label;
3343
        } else {
3344
            $return .= img_picto('', $this->picto);
3345
        }
3346
3347
        //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
3348
        $return .= '</span>';
3349
        $return .= '<div class="info-box-content">';
3350
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl(0, '', 0, 0, 24, 0, '', 'valignmiddle') : $this->ref);
3351
        if (isModEnabled('multicompany') && $this->admin && !$this->entity) {
3352
            $return .= img_picto($langs->trans("SuperAdministratorDesc"), 'redstar', 'class="valignmiddle paddingright paddingleft"');
3353
        } elseif ($this->admin) {
3354
            $return .= img_picto($langs->trans("AdministratorDesc"), 'star', 'class="valignmiddle paddingright paddingleft"');
3355
        }
3356
        $return .= '</span>';
3357
        if ($selected >= 0) {
3358
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
3359
        }
3360
        if (property_exists($this, 'label')) {
3361
            $return .= '<br><span class="info-box-label opacitymedium">' . $this->label . '</span>';
3362
        }
3363
        if ($this->email) {
3364
            $return .= '<br><span class="info-box-label opacitymedium small">' . img_picto('', 'email') . ' ' . $this->email . '</span>';
3365
        }
3366
        if (method_exists($this, 'getLibStatut')) {
3367
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
3368
        }
3369
        $return .= '</div>';
3370
        $return .= '</div>';
3371
        $return .= '</div>';
3372
3373
        return $return;
3374
    }
3375
3376
    /**
3377
     *  Retourne chaine DN complete dans l'annuaire LDAP pour l'objet
3378
     *
3379
     * @param array $info Info array loaded by _load_ldap_info
3380
     * @param int $mode 0=Return full DN (uid=qqq,ou=xxx,dc=aaa,dc=bbb)
3381
     *                              1=Return parent (ou=xxx,dc=aaa,dc=bbb)
3382
     *                              2=Return key only (RDN) (uid=qqq)
3383
     *
3384
     * @return string              DN
3385
     */
3386
    public function _load_ldap_dn($info, $mode = 0)
3387
    {
3388
        // phpcs:enable
3389
        global $conf;
3390
        $dn = '';
3391
        if ($mode == 0) {
3392
            $dn = getDolGlobalString('LDAP_KEY_USERS') . "=" . $info[getDolGlobalString('LDAP_KEY_USERS')] . "," . getDolGlobalString('LDAP_USER_DN');
3393
        } elseif ($mode == 1) {
3394
            $dn = getDolGlobalString('LDAP_USER_DN');
3395
        } elseif ($mode == 2) {
3396
            $dn = getDolGlobalString('LDAP_KEY_USERS') . "=" . $info[getDolGlobalString('LDAP_KEY_USERS')];
3397
        }
3398
        return $dn;
3399
    }
3400
3401
    /**
3402
     *  Initialize the info array (array of LDAP values) that will be used to call LDAP functions
3403
     *
3404
     * @return     array       Table with attribute information
3405
     */
3406
    public function _load_ldap_info()
3407
    {
3408
        // phpcs:enable
3409
        global $conf, $langs;
3410
3411
        $info = [];
3412
3413
        $socialnetworks = getArrayOfSocialNetworks();
3414
3415
        $keymodified = false;
3416
3417
        // Object classes
3418
        $info["objectclass"] = explode(',', getDolGlobalString('LDAP_USER_OBJECT_CLASS'));
3419
3420
        $this->fullname = $this->getFullName($langs);
3421
3422
        // Possible LDAP KEY (constname => varname)
3423
        $ldapkey = [
3424
            'LDAP_FIELD_FULLNAME' => 'fullname',
3425
            'LDAP_FIELD_NAME' => 'lastname',
3426
            'LDAP_FIELD_FIRSTNAME' => 'firstname',
3427
            'LDAP_FIELD_LOGIN' => 'login',
3428
            'LDAP_FIELD_LOGIN_SAMBA' => 'login',
3429
            'LDAP_FIELD_PHONE' => 'office_phone',
3430
            'LDAP_FIELD_MOBILE' => 'user_mobile',
3431
            'LDAP_FIELD_FAX' => 'office_fax',
3432
            'LDAP_FIELD_MAIL' => 'email',
3433
            'LDAP_FIELD_SID' => 'ldap_sid',
3434
        ];
3435
3436
        // Champs
3437
        foreach ($ldapkey as $constname => $varname) {
3438
            if (!empty($this->$varname) && getDolGlobalString($constname)) {
3439
                $info[getDolGlobalString($constname)] = $this->$varname;
3440
3441
                // Check if it is the LDAP key and if its value has been changed
3442
                if (getDolGlobalString('LDAP_KEY_USERS') && $conf->global->LDAP_KEY_USERS == getDolGlobalString($constname)) {
3443
                    if (!empty($this->oldcopy) && $this->$varname != $this->oldcopy->$varname) {
3444
                        $keymodified = true; // For check if LDAP key has been modified
3445
                    }
3446
                }
3447
            }
3448
        }
3449
        foreach ($socialnetworks as $key => $value) {
3450
            if (!empty($this->socialnetworks[$value['label']]) && getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']))) {
3451
                $info[getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']))] = $this->socialnetworks[$value['label']];
3452
            }
3453
        }
3454
        if ($this->address && getDolGlobalString('LDAP_FIELD_ADDRESS')) {
3455
            $info[getDolGlobalString('LDAP_FIELD_ADDRESS')] = $this->address;
3456
        }
3457
        if ($this->zip && getDolGlobalString('LDAP_FIELD_ZIP')) {
3458
            $info[getDolGlobalString('LDAP_FIELD_ZIP')] = $this->zip;
3459
        }
3460
        if ($this->town && getDolGlobalString('LDAP_FIELD_TOWN')) {
3461
            $info[getDolGlobalString('LDAP_FIELD_TOWN')] = $this->town;
3462
        }
3463
        if ($this->note_public && getDolGlobalString('LDAP_FIELD_DESCRIPTION')) {
3464
            $info[getDolGlobalString('LDAP_FIELD_DESCRIPTION')] = dol_string_nohtmltag($this->note_public, 2);
3465
        }
3466
        if ($this->socid > 0) {
3467
            $soc = new Societe($this->db);
3468
            $soc->fetch($this->socid);
3469
3470
            $info[getDolGlobalString('LDAP_FIELD_COMPANY')] = $soc->name;
3471
            if ($soc->client == 1) {
3472
                $info["businessCategory"] = "Customers";
3473
            }
3474
            if ($soc->client == 2) {
3475
                $info["businessCategory"] = "Prospects";
3476
            }
3477
            if ($soc->fournisseur == 1) {
3478
                $info["businessCategory"] = "Suppliers";
3479
            }
3480
        }
3481
3482
        // When password is modified
3483
        if (!empty($this->pass)) {
3484
            if (getDolGlobalString('LDAP_FIELD_PASSWORD')) {
3485
                $info[getDolGlobalString('LDAP_FIELD_PASSWORD')] = $this->pass; // this->pass = unencrypted password
3486
            }
3487
            if (getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3488
                $info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dol_hash($this->pass, 'openldap'); // Create OpenLDAP password (see LDAP_PASSWORD_HASH_TYPE)
3489
            }
3490
        } elseif (getDolGlobalString('LDAP_SERVER_PROTOCOLVERSION') !== '3') {
3491
            // Set LDAP password if possible
3492
            // If ldap key is modified and LDAPv3 we use ldap_rename function for avoid lose encrypt password
3493
            if (getDolGlobalString('DATABASE_PWD_ENCRYPTED')) {
3494
                // Just for the default MD5 !
3495
                if (!getDolGlobalString('MAIN_SECURITY_HASH_ALGO')) {
3496
                    if ($this->pass_indatabase_crypted && getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3497
                        $info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dolGetLdapPasswordHash($this->pass_indatabase_crypted, 'md5frommd5'); // Create OpenLDAP MD5 password from Dolibarr MD5 password
3498
                    }
3499
                }
3500
            } elseif (!empty($this->pass_indatabase)) {
3501
                // Use $this->pass_indatabase value if exists
3502
                if (getDolGlobalString('LDAP_FIELD_PASSWORD')) {
3503
                    $info[getDolGlobalString('LDAP_FIELD_PASSWORD')] = $this->pass_indatabase; // $this->pass_indatabase = unencrypted password
3504
                }
3505
                if (getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3506
                    $info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dol_hash($this->pass_indatabase, 'openldap'); // Create OpenLDAP password (see LDAP_PASSWORD_HASH_TYPE)
3507
                }
3508
            }
3509
        }
3510
3511
        if (getDolGlobalString('LDAP_SERVER_TYPE') == 'egroupware') {
3512
            $info["objectclass"][4] = "phpgwContact"; // compatibilite egroupware
3513
3514
            $info['uidnumber'] = $this->id;
3515
3516
            $info['phpgwTz'] = 0;
3517
            $info['phpgwMailType'] = 'INTERNET';
3518
            $info['phpgwMailHomeType'] = 'INTERNET';
3519
3520
            $info["phpgwContactTypeId"] = 'n';
3521
            $info["phpgwContactCatId"] = 0;
3522
            $info["phpgwContactAccess"] = "public";
3523
3524
            if (dol_strlen($this->egroupware_id) == 0) {
3525
                $this->egroupware_id = 1;
3526
            }
3527
3528
            $info["phpgwContactOwner"] = $this->egroupware_id;
3529
3530
            if ($this->email) {
3531
                $info["rfc822Mailbox"] = $this->email;
3532
            }
3533
            if ($this->user_mobile) {
3534
                $info["phpgwCellTelephoneNumber"] = $this->user_mobile;
3535
            }
3536
        }
3537
3538
        if (getDolGlobalString('LDAP_FIELD_USERID')) {
3539
            $info[getDolGlobalString('LDAP_FIELD_USERID')] = $this->id;
3540
        }
3541
        if (getDolGlobalString('LDAP_FIELD_GROUPID')) {
3542
            $usergroup = new UserGroup($this->db);
3543
            $groupslist = $usergroup->listGroupsForUser($this->id);
3544
            $info[getDolGlobalString('LDAP_FIELD_GROUPID')] = '65534';
3545
            if (!empty($groupslist)) {
3546
                foreach ($groupslist as $groupforuser) {
3547
                    $info[getDolGlobalString('LDAP_FIELD_GROUPID')] = $groupforuser->id; //Select first group in list
3548
                    break;
3549
                }
3550
            }
3551
        }
3552
        if (getDolGlobalString('LDAP_FIELD_HOMEDIRECTORY') && getDolGlobalString('LDAP_FIELD_HOMEDIRECTORYPREFIX')) {
3553
            $info[getDolGlobalString('LDAP_FIELD_HOMEDIRECTORY')] = "{$conf->global->LDAP_FIELD_HOMEDIRECTORYPREFIX}/$this->login";
3554
        }
3555
3556
        return $info;
3557
    }
3558
3559
    /**
3560
     *  Initialise an instance with random values.
3561
     *  Used to build previews or test instances.
3562
     *  id must be 0 if object instance is a specimen.
3563
     *
3564
     * @return int
3565
     */
3566
    public function initAsSpecimen()
3567
    {
3568
        global $user, $langs;
3569
3570
        $now = dol_now();
3571
3572
        // Initialise parameters
3573
        $this->id = 0;
3574
        $this->ref = 'SPECIMEN';
3575
        $this->specimen = 1;
3576
3577
        $this->lastname = 'DOLIBARR';
3578
        $this->firstname = 'SPECIMEN';
3579
        $this->gender = 'man';
3580
        $this->note_public = 'This is a note public';
3581
        $this->note_private = 'This is a note private';
3582
        $this->email = '[email protected]';
3583
        $this->personal_email = '[email protected]';
3584
        $this->socialnetworks = [
3585
            'skype' => 'skypepseudo',
3586
            'twitter' => 'twitterpseudo',
3587
            'facebook' => 'facebookpseudo',
3588
            'linkedin' => 'linkedinpseudo',
3589
        ];
3590
        $this->office_phone = '0999999999';
3591
        $this->office_fax = '0999999998';
3592
        $this->user_mobile = '0999999997';
3593
        $this->personal_mobile = '0999999996';
3594
        $this->admin = 0;
3595
        $this->login = 'dolibspec';
3596
        $this->pass = 'dolibSpec+@123';
3597
        //$this->pass_indatabase='dolibspec';                                   Set after a fetch
3598
        //$this->pass_indatabase_crypted='e80ca5a88c892b0aaaf7e154853bccab';    Set after a fetch
3599
        $this->datec = $now;
3600
        $this->datem = $now;
3601
3602
        $this->datelastlogin = $now;
3603
        $this->iplastlogin = '127.0.0.1';
3604
        $this->datepreviouslogin = $now;
3605
        $this->ippreviouslogin = '127.0.0.1';
3606
        $this->statut = 1;      // deprecated
3607
        $this->status = 1;
3608
3609
        $this->entity = 1;
3610
3611
        return 1;
3612
    }
3613
3614
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3615
3616
    /**
3617
     *  Load info of user object
3618
     *
3619
     * @param int $id Id of user to load
3620
     *
3621
     * @return void
3622
     */
3623
    public function info($id)
3624
    {
3625
        $sql = "SELECT u.rowid, u.login as ref, u.datec,";
3626
        $sql .= " u.tms as date_modification, u.entity";
3627
        $sql .= " FROM " . $dbPrefix . "user as u";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
3628
        $sql .= " WHERE u.rowid = " . ((int)$id);
3629
3630
        $result = $this->db->query($sql);
3631
        if ($result) {
3632
            if ($this->db->num_rows($result)) {
3633
                $obj = $this->db->fetch_object($result);
3634
3635
                $this->id = $obj->rowid;
3636
3637
                $this->ref = (!$obj->ref) ? $obj->rowid : $obj->ref;
3638
                $this->date_creation = $this->db->jdate($obj->datec);
3639
                $this->date_modification = $this->db->jdate($obj->date_modification);
3640
                $this->entity = $obj->entity;
3641
            }
3642
3643
            $this->db->free($result);
3644
        } else {
3645
            dol_print_error($this->db);
3646
        }
3647
    }
3648
3649
3650
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3651
3652
    /**
3653
     *    Return number of mass Emailing received by this contacts with its email
3654
     *
3655
     * @return       int     Number of EMailings
3656
     */
3657
    public function getNbOfEMailings()
3658
    {
3659
        $sql = "SELECT count(mc.email) as nb";
3660
        $sql .= " FROM " . $dbPrefix . "mailing_cibles as mc";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
3661
        $sql .= " WHERE mc.email = '" . $this->db->escape($this->email) . "'";
3662
        $sql .= " AND mc.statut NOT IN (-1,0)"; // -1 error, 0 not sent, 1 sent with success
3663
3664
        $resql = $this->db->query($sql);
3665
        if ($resql) {
3666
            $obj = $this->db->fetch_object($resql);
3667
            $nb = $obj->nb;
3668
3669
            $this->db->free($resql);
3670
            return $nb;
3671
        } else {
3672
            $this->error = $this->db->error();
3673
            return -1;
3674
        }
3675
    }
3676
3677
    /**
3678
     *  Return number of existing users
3679
     *
3680
     * @param string $limitTo Limit to '' or 'active'
3681
     * @param string $option 'superadmin' = return for entity 0 only
3682
     * @param int $admin Filter on admin tag
3683
     *
3684
     * @return int                 Number of users
3685
     */
3686
    public function getNbOfUsers($limitTo, $option = '', $admin = -1)
3687
    {
3688
        global $conf;
3689
3690
        $sql = "SELECT count(rowid) as nb";
3691
        $sql .= " FROM " . $dbPrefix . "user";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
3692
        if ($option == 'superadmin') {
3693
            $sql .= " WHERE entity = 0";
3694
        } else {
3695
            $sql .= " WHERE entity IN (" . getEntity('user', 0) . ")";
3696
            if ($limitTo == 'active') {
3697
                $sql .= " AND statut = 1";
3698
            }
3699
        }
3700
        if ($admin >= 0) {
3701
            $sql .= " AND admin = " . (int)$admin;
3702
        }
3703
3704
        $resql = $this->db->query($sql);
3705
        if ($resql) {
3706
            $obj = $this->db->fetch_object($resql);
3707
            $nb = (int)$obj->nb;
3708
3709
            $this->db->free($resql);
3710
            return $nb;
3711
        } else {
3712
            $this->error = $this->db->lasterror();
3713
            return -1;
3714
        }
3715
    }
3716
3717
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3718
3719
    /**
3720
     *  Update user using data from the LDAP
3721
     *
3722
     * @param Object $ldapuser Ladp User
3723
     *
3724
     * @return int                 Return integer <0 if KO, >0 if OK
3725
     */
3726
    public function update_ldap2dolibarr(&$ldapuser)
3727
    {
3728
        // phpcs:enable
3729
        // TODO: Voir pourquoi le update met à jour avec toutes les valeurs vide (global $user écrase ?)
3730
        global $user, $conf;
3731
3732
        $socialnetworks = getArrayOfSocialNetworks();
3733
3734
        $tmpvar = getDolGlobalString('LDAP_FIELD_FIRSTNAME');
3735
        $this->firstname = $ldapuser->$tmpvar;
3736
        $tmpvar = getDolGlobalString('LDAP_FIELD_NAME');
3737
        $this->lastname = $ldapuser->$tmpvar;
3738
        $tmpvar = getDolGlobalString('LDAP_FIELD_LOGIN');
3739
        $this->login = $ldapuser->$tmpvar;
3740
        $tmpvar = getDolGlobalString('LDAP_FIELD_PASSWORD');
3741
        $this->pass = $ldapuser->$tmpvar;
3742
        $tmpvar = getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED');
3743
        $this->pass_indatabase_crypted = $ldapuser->$tmpvar;
3744
3745
        $tmpvar = getDolGlobalString('LDAP_FIELD_PHONE');
3746
        $this->office_phone = $ldapuser->$tmpvar;
3747
        $tmpvar = getDolGlobalString('LDAP_FIELD_MOBILE');
3748
        $this->user_mobile = $ldapuser->$tmpvar;
3749
        $tmpvar = getDolGlobalString('LDAP_FIELD_FAX');
3750
        $this->office_fax = $ldapuser->$tmpvar;
3751
        $tmpvar = getDolGlobalString('LDAP_FIELD_MAIL');
3752
        $this->email = $ldapuser->$tmpvar;
3753
        foreach ($socialnetworks as $key => $value) {
3754
            $tmpvar = getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']));
3755
            $this->socialnetworks[$value['label']] = $ldapuser->$tmpvar;
3756
        }
3757
        $tmpvar = getDolGlobalString('LDAP_FIELD_SID');
3758
        $this->ldap_sid = $ldapuser->$tmpvar;
3759
3760
        $tmpvar = getDolGlobalString('LDAP_FIELD_TITLE');
3761
        $this->job = $ldapuser->$tmpvar;
3762
        $tmpvar = getDolGlobalString('LDAP_FIELD_DESCRIPTION');
3763
        $this->note_public = $ldapuser->$tmpvar;
3764
3765
        $result = $this->update($user);
3766
3767
        dol_syslog(get_class($this) . "::update_ldap2dolibarr result=" . $result, LOG_DEBUG);
3768
3769
        return $result;
3770
    }
3771
3772
    /**
3773
     * Return and array with all instantiated first level children users of current user
3774
     *
3775
     * @return  User[]|int<-1,-1>
3776
     * @see getAllChildIds()
3777
     */
3778
    public function get_children()
3779
    {
3780
        // phpcs:enable
3781
        $sql = "SELECT rowid FROM " . $dbPrefix . "user";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
3782
        $sql .= " WHERE fk_user = " . ((int)$this->id);
3783
3784
        dol_syslog(get_class($this) . "::get_children", LOG_DEBUG);
3785
        $res = $this->db->query($sql);
3786
        if ($res) {
3787
            $users = [];
3788
            while ($rec = $this->db->fetch_array($res)) {
3789
                $user = new User($this->db);
3790
                $user->fetch($rec['rowid']);
3791
                $users[] = $user;
3792
            }
3793
            return $users;
3794
        } else {
3795
            dol_print_error($this->db);
3796
            return -1;
3797
        }
3798
    }
3799
3800
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3801
3802
    /**
3803
     *  Return list of all child user ids in hierarchy (all sublevels).
3804
     *  Note: Calling this function also reset full list of users into $this->users.
3805
     *
3806
     * @param int $addcurrentuser 1=Add also current user id to the list.
3807
     *
3808
     * @return     array                       Array of user id lower than user (all levels under user). This overwrite
3809
     *                                         this->users.
3810
     * @see get_children()
3811
     */
3812
    public function getAllChildIds($addcurrentuser = 0)
3813
    {
3814
        $childids = [];
3815
3816
        if (isset($this->cache_childids[$this->id])) {
3817
            $childids = $this->cache_childids[$this->id];
3818
        } else {
3819
            // Init this->users
3820
            $this->get_full_tree();
3821
3822
            $idtoscan = $this->id;
3823
3824
            dol_syslog("Build childid for id = " . $idtoscan);
3825
            foreach ($this->users as $id => $val) {
3826
                //var_dump($val['fullpath']);
3827
                if (preg_match('/_' . $idtoscan . '_/', $val['fullpath'])) {
3828
                    $childids[$val['id']] = $val['id'];
3829
                }
3830
            }
3831
        }
3832
        $this->cache_childids[$this->id] = $childids;
3833
3834
        if ($addcurrentuser) {
3835
            $childids[$this->id] = $this->id;
3836
        }
3837
3838
        return $childids;
3839
    }
3840
3841
    /**
3842
     *  Build the hierarchy/tree of users into an array.
3843
     *  Set and return this->users that is an array sorted according to tree with arrays of:
3844
     *              id = id user
3845
     *              lastname
3846
     *              firstname
3847
     *              fullname = Name with full path to user
3848
     *              fullpath = Full path composed of the ids: "_grandparentid_parentid_id"
3849
     *
3850
     * @param int $deleteafterid Removed all users including the leaf $deleteafterid (and all its child) in user
3851
     *                              tree.
3852
     * @param string $filter SQL filter on users. This parameter must not come from user input.
3853
     *
3854
     * @return     array|int                   Array of users $this->users. Note: $this->parentof is also set.
3855
     */
3856
    public function get_full_tree($deleteafterid = 0, $filter = '')
3857
    {
3858
        // phpcs:enable
3859
        global $conf, $user;
3860
        global $hookmanager;
3861
3862
        global $config;
3863
        $dbPrefix = $config->db->prefix;
3864
3865
        // Actions hooked (by external module)
3866
        $hookmanager->initHooks(['userdao']);
3867
3868
        $this->users = [];
3869
3870
        // Init this->parentof that is array(id_son=>id_parent, ...)
3871
        $this->loadParentOf();
3872
3873
        // Init $this->users array
3874
        $sql = "SELECT DISTINCT u.rowid, u.firstname, u.lastname, u.fk_user, u.fk_soc, u.login, u.email, u.gender, u.admin, u.statut, u.photo, u.entity"; // Distinct reduce pb with old tables with duplicates
3875
        $sql .= " FROM " . $dbPrefix . "user as u";
3876
        // Add fields from hooks
3877
        $parameters = [];
3878
        $reshook = $hookmanager->executeHooks('printUserListWhere', $parameters); // Note that $action and $object may have been modified by hook
3879
        if ($reshook > 0) {
3880
            $sql .= $hookmanager->resPrint;
3881
        } else {
3882
            $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
3883
        }
3884
        if ($filter) {
3885
            $sql .= " AND " . $filter;
3886
        }
3887
3888
        dol_syslog(get_class($this) . "::get_full_tree get user list", LOG_DEBUG);
3889
        $resql = $this->db->query($sql);
3890
        if ($resql) {
3891
            $i = 0;
3892
            while ($obj = $this->db->fetch_object($resql)) {
3893
                $this->users[$obj->rowid]['rowid'] = $obj->rowid;
3894
                $this->users[$obj->rowid]['id'] = $obj->rowid;
3895
                $this->users[$obj->rowid]['fk_user'] = $obj->fk_user;
3896
                $this->users[$obj->rowid]['fk_soc'] = $obj->fk_soc;
3897
                $this->users[$obj->rowid]['firstname'] = $obj->firstname;
3898
                $this->users[$obj->rowid]['lastname'] = $obj->lastname;
3899
                $this->users[$obj->rowid]['login'] = $obj->login;
3900
                $this->users[$obj->rowid]['statut'] = $obj->statut;
3901
                $this->users[$obj->rowid]['entity'] = $obj->entity;
3902
                $this->users[$obj->rowid]['email'] = $obj->email;
3903
                $this->users[$obj->rowid]['gender'] = $obj->gender;
3904
                $this->users[$obj->rowid]['admin'] = $obj->admin;
3905
                $this->users[$obj->rowid]['photo'] = $obj->photo;
3906
                $i++;
3907
            }
3908
        } else {
3909
            dol_print_error($this->db);
3910
            return -1;
3911
        }
3912
3913
        // We add the fullpath property to each element of the first level (no parent exists)
3914
        dol_syslog(get_class($this) . "::get_full_tree call to build_path_from_id_user", LOG_DEBUG);
3915
        foreach ($this->users as $key => $val) {
3916
            $result = $this->build_path_from_id_user($key, 0); // Process a branch from the root user key (this user has no parent)
3917
            if ($result < 0) {
3918
                $this->error = 'ErrorLoopInHierarchy';
3919
                return -1;
3920
            }
3921
        }
3922
3923
        // Exclude leaf including $deleteafterid from tree
3924
        if ($deleteafterid) {
3925
            //print "Look to discard user ".$deleteafterid."\n";
3926
            $keyfilter1 = '^' . $deleteafterid . '$';
3927
            $keyfilter2 = '_' . $deleteafterid . '$';
3928
            $keyfilter3 = '^' . $deleteafterid . '_';
3929
            $keyfilter4 = '_' . $deleteafterid . '_';
3930
            foreach (array_keys($this->users) as $key) {
3931
                $fullpath = $this->users[$key]['fullpath'];
3932
                if (
3933
                    preg_match('/' . $keyfilter1 . '/', $fullpath) || preg_match('/' . $keyfilter2 . '/', $fullpath)
3934
                    || preg_match('/' . $keyfilter3 . '/', $fullpath) || preg_match('/' . $keyfilter4 . '/', $fullpath)
3935
                ) {
3936
                    unset($this->users[$key]);
3937
                }
3938
            }
3939
        }
3940
3941
        dol_syslog(get_class($this) . "::get_full_tree dol_sort_array", LOG_DEBUG);
3942
        $this->users = dol_sort_array($this->users, 'fullname', 'asc', true, false);
3943
3944
        //var_dump($this->users);
3945
3946
        return $this->users;
3947
    }
3948
3949
    /**
3950
     *  Load this->parentof that is array(id_son=>id_parent, ...)
3951
     *
3952
     * @return     int<-1,1>     Return integer <0 if KO, >0 if OK
3953
     */
3954
    private function loadParentOf()
3955
    {
3956
        global $conf;
3957
3958
        global $config;
3959
        $dbPrefix = $config->db->prefix;
3960
3961
        $this->parentof = [];
3962
3963
        // Load array[child]=parent
3964
        $sql = "SELECT fk_user as id_parent, rowid as id_son";
3965
        $sql .= " FROM " . $dbPrefix . "user";
3966
        $sql .= " WHERE fk_user <> 0";
3967
        $sql .= " AND entity IN (" . getEntity('user') . ")";
3968
3969
        dol_syslog(get_class($this) . "::loadParentOf", LOG_DEBUG);
3970
        $resql = $this->db->query($sql);
3971
        if ($resql) {
3972
            while ($obj = $this->db->fetch_object($resql)) {
3973
                $this->parentof[$obj->id_son] = $obj->id_parent;
3974
            }
3975
            return 1;
3976
        } else {
3977
            dol_print_error($this->db);
3978
            return -1;
3979
        }
3980
    }
3981
3982
    /**
3983
     *  For user id_user and its children available in this->users, define property fullpath and fullname.
3984
     *  Function called by get_full_tree().
3985
     *
3986
     * @param int $id_user id_user entry to update
3987
     * @param int $protection Deep counter to avoid infinite loop (no more required, a protection is added with array
3988
     *                        useridfound)
3989
     *
3990
     * @return     int<-1,1>               Return integer < 0 if KO (infinite loop), >= 0 if OK
3991
     */
3992
    public function build_path_from_id_user($id_user, $protection = 0)
3993
    {
3994
        // phpcs:enable
3995
        //dol_syslog(get_class($this)."::build_path_from_id_user id_user=".$id_user." protection=".$protection, LOG_DEBUG);
3996
3997
        if (!empty($this->users[$id_user]['fullpath'])) {
3998
            // Already defined
3999
            dol_syslog(get_class($this) . "::build_path_from_id_user fullpath and fullname already defined", LOG_WARNING);
4000
            return 0;
4001
        }
4002
4003
        // Define fullpath and fullname
4004
        $this->users[$id_user]['fullpath'] = '_' . $id_user;
4005
        $this->users[$id_user]['fullname'] = $this->users[$id_user]['lastname'];
4006
        $i = 0;
4007
        $cursor_user = $id_user;
4008
4009
        $useridfound = [$id_user];
4010
        while (!empty($this->parentof[$cursor_user]) && !empty($this->users[$this->parentof[$cursor_user]])) {
4011
            if (in_array($this->parentof[$cursor_user], $useridfound)) {
4012
                dol_syslog("The hierarchy of user has a recursive loop", LOG_WARNING);
4013
                return -1; // Should not happen. Protection against looping hierarchy
4014
            }
4015
            $useridfound[] = $this->parentof[$cursor_user];
4016
            $this->users[$id_user]['fullpath'] = '_' . $this->parentof[$cursor_user] . $this->users[$id_user]['fullpath'];
4017
            $this->users[$id_user]['fullname'] = $this->users[$this->parentof[$cursor_user]]['lastname'] . ' >> ' . $this->users[$id_user]['fullname'];
4018
            $i++;
4019
            $cursor_user = $this->parentof[$cursor_user];
4020
        }
4021
4022
        // We count number of _ to have level
4023
        $this->users[$id_user]['level'] = dol_strlen(preg_replace('/[^_]/i', '', $this->users[$id_user]['fullpath']));
4024
4025
        return 1;
4026
    }
4027
4028
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4029
4030
    /**
4031
     *      Load metrics this->nb for dashboard
4032
     *
4033
     * @return     int         Return integer <0 if KO, >0 if OK
4034
     */
4035
    public function loadStateBoard()
4036
    {
4037
        global $conf;
4038
        $dbPrefix = $conf->db->prefix;
4039
4040
        $this->nb = [];
4041
4042
        $sql = "SELECT COUNT(DISTINCT u.rowid) as nb";
4043
        $sql .= " FROM " . $dbPrefix . "user as u";
4044
        if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
4045
            $sql .= ", " . $dbPrefix . "usergroup_user as ug";
4046
            $sql .= " WHERE ug.entity IN (" . getEntity('usergroup') . ")";
4047
            $sql .= " AND ug.fk_user = u.rowid";
4048
        } else {
4049
            $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
4050
        }
4051
        $sql .= " AND u.statut > 0";
4052
        //$sql.= " AND employee != 0";
4053
4054
        $resql = $this->db->query($sql);
4055
        if ($resql) {
4056
            while ($obj = $this->db->fetch_object($resql)) {
4057
                $this->nb["users"] = $obj->nb;
4058
            }
4059
            $this->db->free($resql);
4060
            return 1;
4061
        } else {
4062
            dol_print_error($this->db);
4063
            $this->error = $this->db->error();
4064
            return -1;
4065
        }
4066
    }
4067
4068
    /**
4069
     *  Create a document onto disk according to template module.
4070
     *
4071
     * @param string $modele Force model to use ('' to not force)
4072
     * @param Translate $outputlangs Object langs to use for output
4073
     * @param int $hidedetails Hide details of lines
4074
     * @param int $hidedesc Hide description
4075
     * @param int $hideref Hide ref
4076
     * @param null|array $moreparams Array to provide more information
4077
     *
4078
     * @return     int                         0 if KO, 1 if OK
4079
     */
4080
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4081
    {
4082
        global $conf, $user, $langs;
4083
4084
        $langs->load("user");
4085
4086
        // Set the '$modele' to the name of the document template (model) to use
4087
        if (!dol_strlen($modele)) {
4088
            if (getDolGlobalString('USER_ADDON_PDF')) {
4089
                $modele = getDolGlobalString('USER_ADDON_PDF');
4090
            } else {
4091
                $modele = 'bluesky';
4092
            }
4093
        }
4094
4095
        $modelpath = "core/modules/user/doc/";
4096
4097
        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4098
    }
4099
4100
    /**
4101
     *  Return property of user from its id
4102
     *
4103
     * @param int $rowid id of contact
4104
     * @param string $mode 'email', 'mobile', or 'name'
4105
     *
4106
     * @return string              Email of user with format: "Full name <email>"
4107
     */
4108
    public function user_get_property($rowid, $mode)
4109
    {
4110
        // phpcs:enable
4111
        $user_property = '';
4112
4113
        if (empty($rowid)) {
4114
            return '';
4115
        }
4116
4117
        $sql = "SELECT rowid, email, user_mobile, civility, lastname, firstname";
4118
        $sql .= " FROM " . $dbPrefix . "user";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
4119
        $sql .= " WHERE rowid = " . ((int)$rowid);
4120
4121
        $resql = $this->db->query($sql);
4122
        if ($resql) {
4123
            $nump = $this->db->num_rows($resql);
4124
4125
            if ($nump) {
4126
                $obj = $this->db->fetch_object($resql);
4127
4128
                if ($mode == 'email') {
4129
                    $user_property = dolGetFirstLastname($obj->firstname, $obj->lastname) . " <" . $obj->email . ">";
4130
                } elseif ($mode == 'mobile') {
4131
                    $user_property = $obj->user_mobile;
4132
                } elseif ($mode == 'name') {
4133
                    $user_property = dolGetFirstLastname($obj->firstname, $obj->lastname);
4134
                }
4135
            }
4136
            return $user_property;
4137
        } else {
4138
            dol_print_error($this->db);
4139
        }
4140
4141
        return '';
4142
    }
4143
4144
    /**
4145
     * Return string with full Url to virtual card
4146
     *
4147
     * @param string $mode Mode for link
4148
     * @param string $typeofurl 'external' or 'internal'
4149
     *
4150
     * @return  string                  Url string link
4151
     */
4152
    public function getOnlineVirtualCardUrl($mode = '', $typeofurl = 'external')
4153
    {
4154
        global $dolibarr_main_url_root;
4155
        global $conf;
4156
4157
        $encodedsecurekey = dol_hash($conf->file->instance_unique_id . 'uservirtualcard' . $this->id . '-' . $this->login, 'md5');
4158
        if (isModEnabled('multicompany')) {
4159
            $entity_qr = '&entity=' . ((int)$conf->entity);
4160
        } else {
4161
            $entity_qr = '';
4162
        }
4163
4164
        if (!isset($dolibarr_main_url_root)) {
4165
            $dolibarr_main_url_root = '';
4166
        }
4167
4168
        // Define $urlwithroot
4169
        $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($dolibarr_main_url_root));
4170
        $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use external domain name found into config file
4171
        //$urlwithroot=DOL_MAIN_URL_ROOT;                   // This is to use same domain name than current
4172
4173
        if ($typeofurl == 'internal') {
4174
            $urlwithroot = DOL_URL_ROOT;
4175
        }
4176
4177
        return $urlwithroot . '/public/users/view.php?id=' . $this->id . '&securekey=' . $encodedsecurekey . $entity_qr . ($mode ? '&mode=' . urlencode($mode) : '');
4178
    }
4179
4180
    /**
4181
     * Find a user by the given e-mail and return it's user id when found
4182
     *
4183
     * NOTE:
4184
     * Use AGENDA_DISABLE_EXACT_USER_EMAIL_COMPARE_FOR_EXTERNAL_CALENDAR
4185
     * to disable exact e-mail search
4186
     *
4187
     * @param string $email The full e-mail (or a part of a e-mail)
4188
     *
4189
     * @return int              Return integer <0 = user was not found, >0 = The id of the user
4190
     */
4191
    public function findUserIdByEmail($email)
4192
    {
4193
        if (isset($this->findUserIdByEmailCache[$email])) {
4194
            return $this->findUserIdByEmailCache[$email];
4195
        }
4196
4197
        $this->findUserIdByEmailCache[$email] = -1;
4198
4199
        $sql = 'SELECT rowid';
4200
        $sql .= ' FROM ' . $dbPrefix . 'user';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $dbPrefix seems to be never defined.
Loading history...
4201
        if (getDolGlobalString('AGENDA_DISABLE_EXACT_USER_EMAIL_COMPARE_FOR_EXTERNAL_CALENDAR')) {
4202
            $sql .= " WHERE email LIKE '%" . $this->db->escape($this->db->escapeforlike($email)) . "%'";
4203
        } else {
4204
            $sql .= " WHERE email = '" . $this->db->escape($email) . "'";
4205
        }
4206
        $sql .= ' LIMIT 1';
4207
4208
        $resql = $this->db->query($sql);
4209
        if (!$resql) {
4210
            return -1;
4211
        }
4212
4213
        $obj = $this->db->fetch_object($resql);
4214
        if (!$obj) {
4215
            return -1;
4216
        }
4217
4218
        $this->findUserIdByEmailCache[$email] = (int)$obj->rowid;
4219
4220
        return $this->findUserIdByEmailCache[$email];
4221
    }
4222
}
4223