User::getTooltipContentArray()   F
last analyzed

Complexity

Conditions 25
Paths > 20000

Size

Total Lines 88
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 25
eloc 64
nc 25920
nop 1
dl 0
loc 88
rs 0
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-2024	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		Lenin Rivas				    <[email protected]>
20
 * Copyright (C) 2024       Rafael San José             <[email protected]>
21
 *
22
 * This program is free software; you can redistribute it and/or modify
23
 * it under the terms of the GNU General Public License as published by
24
 * the Free Software Foundation; either version 3 of the License, or
25
 * (at your option) any later version.
26
 *
27
 * This program is distributed in the hope that it will be useful,
28
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30
 * GNU General Public License for more details.
31
 *
32
 * You should have received a copy of the GNU General Public License
33
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
34
 */
35
36
namespace Dolibarr\Code\User\Classes;
37
38
use Dolibarr\Code\Adherents\Classes\Adherent;
39
use Dolibarr\Code\Categories\Classes\Categorie;
40
use Dolibarr\Code\Contact\Classes\Contact;
41
use Dolibarr\Code\Core\Classes\DefaultValues;
42
use Dolibarr\Code\Core\Classes\Form;
43
use Dolibarr\Code\Core\Traits\CommonPeople;
44
use Dolibarr\Core\Base\CommonObject;
45
use DoliDB;
46
use stdClass;
47
48
/**
49
 *  \file       htdocs/user/class/user.class.php
50
 *  \brief      File of class to manage users
51
 *  \ingroup    core
52
 */
53
54
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/security.lib.php';
55
56
/**
57
 *  Class to manage Dolibarr users
58
 */
59
class User extends CommonObject
60
{
61
    use CommonPeople;
0 ignored issues
show
introduced by
The trait Dolibarr\Code\Core\Traits\CommonPeople requires some properties which are not provided by Dolibarr\Code\User\Classes\User: $MAIN_SHOW_REGION_IN_STATE_SELECT, $phone_perso, $attributes, $company, $global, $nom, $use_javascript_ajax, $societe
Loading history...
62
63
    const STATUS_DISABLED = 0;
64
    const STATUS_ENABLED = 1;
65
    /**
66
     * @var string ID to identify managed object
67
     */
68
    public $element = 'user';
69
    /**
70
     * @var string Name of table without prefix where object is stored
71
     */
72
    public $table_element = 'user';
73
    /**
74
     * @var string Field with ID of parent key if this field has a parent
75
     */
76
    public $fk_element = 'fk_user';
77
    /**
78
     * @var string picto
79
     */
80
    public $picto = 'user';
81
    public $id = 0;
82
    /**
83
     * @var static old copy of User
84
     */
85
    public $oldcopy;
86
    /**
87
     * @var int
88
     * @deprecated
89
     * @see $status
90
     */
91
    public $statut;
92
    public $status;
93
    /**
94
     * @var string      Open ID
95
     */
96
    public $openid;
97
    public $ldap_sid;
98
    public $search_sid;
99
    public $employee;
100
    public $civility_code;
101
    /**
102
     * @var string fullname
103
     */
104
    public $fullname;
105
    /**
106
     * @var string|int<-1,-1> gender (man|woman|other)
107
     */
108
    public $gender;
109
    public $birth;
110
    /**
111
     * @var string email
112
     */
113
    public $email;
114
    /**
115
     * @var string email
116
     */
117
    public $email_oauth2;
118
    /**
119
     * @var string personal email
120
     */
121
    public $personal_email;
122
    /**
123
     * @var array array of socialnetwo18dprks
124
     */
125
    public $socialnetworks;
126
    /**
127
     * @var string job position
128
     */
129
    public $job;
130
    /**
131
     * @var string user signature
132
     */
133
    public $signature;
134
    /**
135
     * @var string office phone
136
     */
137
    public $office_phone;
138
    /**
139
     * @var string office fax
140
     */
141
    public $office_fax;
142
    /**
143
     * @var string phone mobile
144
     */
145
    public $user_mobile;
146
    /**
147
     * @var string personal phone mobile
148
     */
149
    public $personal_mobile;
150
    /**
151
     * @var int 1 if admin 0 if standard user
152
     */
153
    public $admin;
154
    /**
155
     * @var string user login
156
     */
157
    public $login;
158
    /**
159
     * @var string user apikey
160
     */
161
    public $api_key;
162
    /**
163
     * @var int Entity
164
     */
165
    public $entity;
166
    /**
167
     * @var string Clear password in memory
168
     */
169
    public $pass;
170
    /**
171
     * @var string Encrypted password in memory
172
     */
173
    public $pass_crypted;
174
    /**
175
     * @var string Clear password in database (defined if DATABASE_PWD_ENCRYPTED=0)
176
     */
177
    public $pass_indatabase;
178
    /**
179
     * @var string Encrypted password in database (always defined)
180
     */
181
    public $pass_indatabase_crypted;
182
    /**
183
     * @var string Temporary password
184
     */
185
    public $pass_temp;
186
    /**
187
     * Date creation record (datec)
188
     *
189
     * @var integer
190
     */
191
    public $datec;
192
    /**
193
     * Date modification record (tms)
194
     *
195
     * @var integer
196
     */
197
    public $datem;
198
    /**
199
     * @var int If this is defined, it is an external user
200
     */
201
    public $socid;
202
    /**
203
     * @var int If this is defined, it is a user created from a contact
204
     */
205
    public $contact_id;
206
    /**
207
     * @var int ID
208
     */
209
    public $fk_member;
210
    /**
211
     * @var int User ID of supervisor
212
     */
213
    public $fk_user;
214
    /**
215
     * @var int User ID of expense validator
216
     */
217
    public $fk_user_expense_validator;
218
    /**
219
     * @var int User ID of holidays validator
220
     */
221
    public $fk_user_holiday_validator;
222
    /**
223
     * @string clicktodial url
224
     */
225
    public $clicktodial_url;
226
    /**
227
     * @var string clicktodial login
228
     */
229
    public $clicktodial_login;
230
    /**
231
     * @var string clicktodial password
232
     */
233
    public $clicktodial_password;
234
    /**
235
     * @var string clicktodial poste
236
     */
237
    public $clicktodial_poste;
238
    /**
239
     * @var int     0 by default, 1 if click to dial data were already loaded for this user
240
     */
241
    public $clicktodial_loaded;
242
    public $datelastlogin;
243
    public $datepreviouslogin;
244
    public $flagdelsessionsbefore;
245
    public $iplastlogin;
246
    public $ippreviouslogin;
247
    public $datestartvalidity;
248
    public $dateendvalidity;
249
    /**
250
     * @var string photo filename
251
     */
252
    public $photo;
253
    /**
254
     * @var string default language
255
     */
256
    public $lang;
257
    /**
258
     * @var stdClass Class of permissions user->rights->permx
259
     */
260
    public $rights;
261
    /**
262
     * @var int  All permissions are loaded
263
     */
264
    public $all_permissions_are_loaded;
265
    /**
266
     * @var int Number of rights granted to the user. Value loaded after a getrights().
267
     */
268
    public $nb_rights;
269
    /**
270
     * @var array   To store list of groups of user (used by API /info for example)
271
     */
272
    public $user_group_list;
273
    /**
274
     * @var stdClass To store personal config
275
     */
276
    public $conf; // To store default values for user. Loaded by loadDefaultValues().
277
public $default_values; // To store current search criteria for user
278
    public $lastsearch_values_tmp; // To store last saved search criteria for user
279
public $lastsearch_values;
280
        /**
281
     * @var array<int,User>|array<int,array{rowid:int,id:int,fk_user:int,fk_soc:int,firstname:string,lastname:string,login:string,statut:int,entity:int,email:string,gender:string|int<-1,-1>,admin:int<0,1>,photo:string,fullpath:string,fullname:string,level:int}>  Array of User (filled from fetchAll) or Array with hierarchy of user information (filled with get_full_tree()
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<int,User>|array<in...name:string,level:int}> at position 57 could not be parsed: Expected '}' at position 57, but found 'int'.
Loading history...
282
     */
283
    public $users = array(); // To store an array of all parents for all ids.
284
    public $parentof; // Cache array of already loaded children
285
public $accountancy_code; // Accountancy code in prevision of the complete accountancy module
286
287
    public $thm; // Average cost of employee - Used for valuation of time spent
288
    public $tjm; // Average cost of employee
289
290
    public $salary; // Monthly salary       - Denormalized value from llx_user_employment
291
    public $salaryextra; // Monthly salary extra - Denormalized value from llx_user_employment
292
    public $weeklyhours; // Weekly hours         - Denormalized value from llx_user_employment
293
294
    /**
295
     * @var string Define background color for user in agenda
296
     */
297
    public $color;
298
299
    public $dateemployment; // Define date of employment by company
300
    public $dateemploymentend; // Define date of employment end by company
301
302
    public $default_c_exp_tax_cat;
303
304
    /**
305
     * @var string ref for employee
306
     */
307
    public $ref_employee;
308
309
    /**
310
     * @var string national registration number
311
     */
312
    public $national_registration_number;
313
314
    public $default_range;
315
316
    /**
317
     * @var int id of warehouse
318
     */
319
    public $fk_warehouse;
320
321
    /**
322
     * @var int id of establishment
323
     */
324
    public $fk_establishment;
325
326
    /**
327
     * @var string label of establishment
328
     */
329
    public $label_establishment;
330
331
    /**
332
     * @var int egroupware id
333
     */
334
    //private $egroupware_id;
335
336
    /**
337
     * @var array<int>      Entity in table llx_user_group
338
     * @deprecated          Seems not used.
339
     */
340
    public $usergroup_entity;
341
342
    public $fields = array(
343
        'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'index' => 1, 'position' => 1, 'comment' => 'Id'),
344
        'lastname' => array('type' => 'varchar(50)', 'label' => 'Lastname', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 20, 'searchall' => 1),
345
        'firstname' => array('type' => 'varchar(50)', 'label' => 'Firstname', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 10, 'searchall' => 1),
346
        'ref_employee' => array('type' => 'varchar(50)', 'label' => 'RefEmployee', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 30, 'searchall' => 1),
347
        'national_registration_number' => array('type' => 'varchar(50)', 'label' => 'NationalRegistrationNumber', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'showoncombobox' => 1, 'index' => 1, 'position' => 40, 'searchall' => 1)
348
    );
349
    /**
350
     * @var array Cache array of already loaded permissions
351
     */
352
    private $_tab_loaded = array();
353
private $cache_childids;
354
    /**
355
     * Cache the SQL results of the function "findUserIdByEmail($email)"
356
     *
357
     * NOTE: findUserIdByEmailCache[...] === -1 means not found in database
358
     *
359
     * @var array
360
     */
361
    private $findUserIdByEmailCache;
362
363
    /**
364
     *    Constructor of the class
365
     *
366
     * @param DoliDB $db Database handler
367
     */
368
    public function __construct($db)
369
    {
370
        $this->db = $db;
371
372
        $this->ismultientitymanaged = 1;
373
        $this->isextrafieldmanaged = 1;
374
        // User preference
375
        $this->clicktodial_loaded = 0;
376
377
        // For cache usage
378
        $this->all_permissions_are_loaded = 0;
379
        $this->nb_rights = 0;
380
381
        // Force some default values
382
        $this->admin = 0;
383
        $this->employee = 1;
384
385
        $this->conf = new stdClass();
386
        $this->rights = new stdClass();
387
        $this->rights->user = new stdClass();
388
        $this->rights->user->user = new stdClass();
389
        $this->rights->user->self = new stdClass();
390
        $this->rights->user->user_advance = new stdClass();
391
        $this->rights->user->self_advance = new stdClass();
392
        $this->rights->user->group_advance = new stdClass();
393
    }
394
395
    /**
396
     * Function used to replace a thirdparty id with another one.
397
     *
398
     * @param DoliDB $dbs Database handler, because function is static we name it $dbs not $db to avoid breaking coding test
399
     * @param int $origin_id Old thirdparty id
400
     * @param int $dest_id New thirdparty id
401
     * @return  bool
402
     */
403
    public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id)
404
    {
405
        $tables = array(
406
            'user',
407
        );
408
409
        return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables);
410
    }
411
412
    /**
413
     *  Add a right to the user
414
     *
415
     * @param int $rid Id of permission to add or 0 to add several permissions
416
     * @param string $allmodule Add all permissions of module $allmodule or 'allmodules' to include all modules.
417
     * @param string $allperms Add all permissions of module $allmodule, subperms $allperms only or '' to include all permissions.
418
     * @param int $entity Entity to use
419
     * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
420
     * @return int                     > 0 if OK, < 0 if KO
421
     * @see    clearrights(), delrights(), getrights(), hasRight()
422
     */
423
    public function addrights($rid, $allmodule = '', $allperms = '', $entity = 0, $notrigger = 0)
424
    {
425
        global $conf, $user, $langs;
426
427
        $entity = (empty($entity) ? $conf->entity : $entity);
428
429
        dol_syslog(get_only_class($this) . "::addrights $rid, $allmodule, $allperms, $entity, $notrigger for user id=" . $this->id);
430
431
        if (empty($this->id)) {
432
            $this->error = 'Try to call addrights on an object user with an empty id';
433
            return -1;
434
        }
435
436
        $error = 0;
437
        $whereforadd = '';
438
439
        $this->db->begin();
440
441
        if (!empty($rid)) {
442
            $module = $perms = $subperms = '';
443
444
            // If we ask to add a given permission, we first load properties of this permission (module, perms and subperms).
445
            $sql = "SELECT module, perms, subperms";
446
            $sql .= " FROM " . $this->db->prefix() . "rights_def";
447
            $sql .= " WHERE id = " . ((int)$rid);
448
            $sql .= " AND entity = " . ((int)$entity);
449
450
            $result = $this->db->query($sql);
451
            if ($result) {
452
                $obj = $this->db->fetch_object($result);
453
454
                if ($obj) {
455
                    $module = $obj->module;
456
                    $perms = $obj->perms;
457
                    $subperms = $obj->subperms;
458
                }
459
            } else {
460
                $error++;
461
                dol_print_error($this->db);
462
            }
463
464
            // Define the where for the permission to add
465
            $whereforadd = "id=" . ((int)$rid);
466
            // Add also inherited permissions
467
            if (!empty($subperms)) {
468
                $whereforadd .= " OR (module='" . $this->db->escape($module) . "' AND perms='" . $this->db->escape($perms) . "' AND (subperms='lire' OR subperms='read'))";
469
            } elseif (!empty($perms)) {
470
                $whereforadd .= " OR (module='" . $this->db->escape($module) . "' AND (perms='lire' OR perms='read') AND subperms IS NULL)";
471
            }
472
        } else {
473
            // A list of permission was requested (not a single specific permission)
474
            // based on the name of a module of permissions
475
            // Used in the where clause to determine the list of permissions to add.
476
            if (!empty($allmodule)) {
477
                if ($allmodule == 'allmodules') {
478
                    $whereforadd = 'allmodules';
479
                } else {
480
                    $whereforadd = "module='" . $this->db->escape($allmodule) . "'";
481
                    if (!empty($allperms)) {
482
                        $whereforadd .= " AND perms='" . $this->db->escape($allperms) . "'";
483
                    }
484
                }
485
            }
486
        }
487
488
        // Add automatically other permission using the criteria whereforadd
489
        // $whereforadd can be a SQL filter or the string 'allmodules'
490
        if (!empty($whereforadd)) {
491
            //print "$module-$perms-$subperms";
492
            $sql = "SELECT id";
493
            $sql .= " FROM " . $this->db->prefix() . "rights_def";
494
            $sql .= " WHERE entity = " . ((int)$entity);
495
            if (!empty($whereforadd) && $whereforadd != 'allmodules') {
496
                $sql .= " AND (" . $whereforadd . ")";  // Note: parenthesis are important because whereforadd can contains OR. Also note that $whereforadd is already sanitized
497
            }
498
499
            $sqldelete = "DELETE FROM " . $this->db->prefix() . "user_rights";
500
            $sqldelete .= " WHERE fk_user = " . ((int)$this->id) . " AND fk_id IN (";
501
            $sqldelete .= $sql;
502
            $sqldelete .= ") AND entity = " . ((int)$entity);
503
            if (!$this->db->query($sqldelete)) {
504
                $error++;
505
            }
506
507
            if (!$error) {
508
                $resql = $this->db->query($sql);
509
                if ($resql) {
510
                    $num = $this->db->num_rows($resql);
511
                    $i = 0;
512
                    while ($i < $num) {
513
                        $obj = $this->db->fetch_object($resql);
514
515
                        if ($obj) {
516
                            $nid = $obj->id;
517
518
                            $sql = "INSERT INTO " . $this->db->prefix() . "user_rights (entity, fk_user, fk_id) VALUES (" . ((int)$entity) . ", " . ((int)$this->id) . ", " . ((int)$nid) . ")";
519
                            if (!$this->db->query($sql)) {
520
                                $error++;
521
                            }
522
                        }
523
524
                        $i++;
525
                    }
526
                } else {
527
                    $error++;
528
                    dol_print_error($this->db);
529
                }
530
            }
531
        }
532
533
        if (!$error && !$notrigger) {
534
            $langs->load("other");
535
            $this->context = array('audit' => $langs->trans("PermissionsAdd") . ($rid ? ' (id=' . $rid . ')' : ''));
536
537
            // Call trigger
538
            $result = $this->call_trigger('USER_MODIFY', $user);
539
            if ($result < 0) {
540
                $error++;
541
            }
542
            // End call triggers
543
        }
544
545
        if ($error) {
546
            $this->db->rollback();
547
            return -$error;
548
        } else {
549
            $this->db->commit();
550
            return 1;
551
        }
552
    }
553
554
    /**
555
     *  Remove a right to the user
556
     *
557
     * @param int $rid Id of permission to remove (or 0 when using the other filters)
558
     * @param string $allmodule Retirer tous les droits du module allmodule
559
     * @param string $allperms Retirer tous les droits du module allmodule, perms allperms
560
     * @param int|string $entity Entity to use. Example: '1', or '0,1', or '2,3'
561
     * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers
562
     * @return int                     > 0 if OK, < 0 if OK
563
     * @see    clearrights(), addrights(), getrights(), hasRight()
564
     */
565
    public function delrights($rid, $allmodule = '', $allperms = '', $entity = 0, $notrigger = 0)
566
    {
567
        global $conf, $user, $langs;
568
569
        $error = 0;
570
        $wherefordel = '';
571
        $entity = (!empty($entity) ? $entity : $conf->entity);
572
573
        $this->db->begin();
574
575
        if (!empty($rid)) {
576
            $module = $perms = $subperms = '';
577
578
            // When the request is to delete a specific permissions, this gets the
579
            // les charactis for the module, permissions and sub-permission of this permission.
580
            $sql = "SELECT module, perms, subperms";
581
            $sql .= " FROM " . $this->db->prefix() . "rights_def";
582
            $sql .= " WHERE id = '" . $this->db->escape($rid) . "'";
583
            $sql .= " AND entity IN (" . $this->db->sanitize($entity, 0, 0, 0, 0) . ")";
584
585
            $result = $this->db->query($sql);
586
            if ($result) {
587
                $obj = $this->db->fetch_object($result);
588
589
                if ($obj) {
590
                    $module = $obj->module;
591
                    $perms = $obj->perms;
592
                    $subperms = $obj->subperms;
593
                }
594
            } else {
595
                $error++;
596
                dol_print_error($this->db);
597
            }
598
599
            // Where clause for the list of permissions to delete
600
            $wherefordel = "id=" . ((int)$rid);
601
            // Suppression des droits induits
602
            if ($subperms == 'lire' || $subperms == 'read') {
603
                $wherefordel .= " OR (module='" . $this->db->escape($module) . "' AND perms='" . $this->db->escape($perms) . "' AND subperms IS NOT NULL)";
604
            }
605
            if ($perms == 'lire' || $perms == 'read') {
606
                $wherefordel .= " OR (module='" . $this->db->escape($module) . "')";
607
            }
608
        } else {
609
            // The deletion of the permissions concerns the name of a module or
610
            // list of permissions.
611
            // Used in the Where clause to determine the list of permission to delete
612
            if (!empty($allmodule)) {
613
                if ($allmodule == 'allmodules') {
614
                    $wherefordel = 'allmodules';
615
                } else {
616
                    $wherefordel = "module='" . $this->db->escape($allmodule) . "'";
617
                    if (!empty($allperms)) {
618
                        $wherefordel .= " AND perms='" . $this->db->escape($allperms) . "'";
619
                    }
620
                }
621
            }
622
        }
623
624
        // Delete permission according to a criteria set into $wherefordel
625
        if (!empty($wherefordel)) {
626
            //print "$module-$perms-$subperms";
627
            $sql = "SELECT id";
628
            $sql .= " FROM " . $this->db->prefix() . "rights_def";
629
            $sql .= " WHERE entity IN (" . $this->db->sanitize($entity, 0, 0, 0, 0) . ")";
630
            if (!empty($wherefordel) && $wherefordel != 'allmodules') {
631
                $sql .= " AND (" . $wherefordel . ")";  // Note: parenthesis are important because wherefordel can contains OR. Also note that $wherefordel is already sanitized
632
            }
633
634
            // avoid admin to remove his own important rights
635
            if ($this->admin == 1) {
636
                $sql .= " AND id NOT IN (251, 252, 253, 254, 255, 256)"; // other users rights
637
                $sql .= " AND id NOT IN (341, 342, 343, 344)"; // own rights
638
                $sql .= " AND id NOT IN (351, 352, 353, 354)"; // groups rights
639
                $sql .= " AND id NOT IN (358)"; // user export
640
            }
641
642
            $sqldelete = "DELETE FROM " . $this->db->prefix() . "user_rights";
643
            $sqldelete .= " WHERE fk_user = " . ((int)$this->id) . " AND fk_id IN (";
644
            $sqldelete .= $sql;
645
            $sqldelete .= ")";
646
            $sqldelete .= " AND entity IN (" . $this->db->sanitize($entity, 0, 0, 0, 0) . ")";
647
648
            $resql = $this->db->query($sqldelete);
649
            if (!$resql) {
650
                $error++;
651
                dol_print_error($this->db);
652
            }
653
        }
654
655
        if (!$error && !$notrigger) {
656
            $langs->load("other");
657
            $this->context = array('audit' => $langs->trans("PermissionsDelete") . ($rid ? ' (id=' . $rid . ')' : ''));
658
659
            // Call trigger
660
            $result = $this->call_trigger('USER_MODIFY', $user);
661
            if ($result < 0) {
662
                $error++;
663
            }
664
            // End call triggers
665
        }
666
667
        if ($error) {
668
            $this->db->rollback();
669
            return -$error;
670
        } else {
671
            $this->db->commit();
672
            return 1;
673
        }
674
    }
675
676
    /**
677
     *  Clear all permissions array of user
678
     *
679
     * @return void
680
     * @see    getrights(), hasRight()
681
     */
682
    public function clearrights()
683
    {
684
        dol_syslog(get_only_class($this) . "::clearrights reset user->rights");
685
        $this->rights = new stdClass();
686
        $this->nb_rights = 0;
687
        $this->all_permissions_are_loaded = 0;
688
        $this->_tab_loaded = array();
689
    }
690
691
    /**
692
     *  Load permissions granted to a user->id into object user->rights
693
     *  TODO Remove this method. It has a name conflict with getRights() in CommonObject.
694
     *
695
     * @param string $moduletag Limit permission for a particular module ('' by default means load all permissions)
696
     * @param int $forcereload Force reload of permissions even if they were already loaded (ignore cache)
697
     * @return void
698
     *
699
     * @see    clearrights(), delrights(), addrights(), hasRight()
700
     * @phpstan-ignore-next-line
701
     */
702
    public function getrights($moduletag = '', $forcereload = 0)
703
    {
704
        $this->loadRights($moduletag, $forcereload);
705
    }
706
707
    /**
708
     *  Load permissions granted to a user->id into object user->rights
709
     *
710
     * @param string $moduletag Limit permission for a particular module ('' by default means load all permissions)
711
     * @param int $forcereload Force reload of permissions even if they were already loaded (ignore cache)
712
     * @return void
713
     * @see    clearrights(), delrights(), addrights(), hasRight()
714
     */
715
    public function loadRights($moduletag = '', $forcereload = 0)
716
    {
717
        global $conf;
718
719
        $alreadyloaded = false;
720
721
        if (empty($forcereload)) {
722
            if ($moduletag && isset($this->_tab_loaded[$moduletag]) && $this->_tab_loaded[$moduletag]) {
723
                // Rights for this module are already loaded, so we leave
724
                $alreadyloaded = true;
725
            }
726
727
            if (!empty($this->all_permissions_are_loaded)) {
728
                // We already loaded all rights for this user, so we leave
729
                $alreadyloaded = true;
730
            }
731
        }
732
733
        // More init to avoid warnings/errors
734
        if (!isset($this->rights) || !is_object($this->rights)) {
735
            $this->rights = new stdClass();
736
        }
737
        if (!isset($this->rights->user) || !is_object($this->rights->user)) {
738
            $this->rights->user = new stdClass();
739
        }
740
741
        // Get permission of users + Get permissions of groups
742
743
        if (!$alreadyloaded) {
744
            // First user permissions
745
            $sql = "SELECT DISTINCT r.module, r.perms, r.subperms";
746
            $sql .= " FROM " . $this->db->prefix() . "user_rights as ur,";
747
            $sql .= " " . $this->db->prefix() . "rights_def as r";
748
            $sql .= " WHERE r.id = ur.fk_id";
749
            if (getDolGlobalString('MULTICOMPANY_BACKWARD_COMPATIBILITY')) {
750
                // on old version, we use entity defined into table r only
751
                $sql .= " AND r.entity IN (0," . (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE') ? "1," : "") . $conf->entity . ")";
752
            } else {
753
                // On table r=rights_def, the unique key is (id, entity) because id is hard coded into module descriptor and insert during module activation.
754
                // So we must include the filter on entity on both table r. and ur.
755
                $sql .= " AND r.entity = " . ((int)$conf->entity) . " AND ur.entity = " . ((int)$conf->entity);
756
            }
757
            $sql .= " AND ur.fk_user= " . ((int)$this->id);
758
            $sql .= " AND r.perms IS NOT NULL";
759
            if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
760
                $sql .= " AND r.perms NOT LIKE '%_advance'"; // Hide advanced perms if option is not enabled
761
            }
762
            if ($moduletag) {
763
                $sql .= " AND r.module = '" . $this->db->escape($moduletag) . "'";
764
            }
765
766
            $resql = $this->db->query($sql);
767
            if ($resql) {
768
                $num = $this->db->num_rows($resql);
769
                $i = 0;
770
                while ($i < $num) {
771
                    $obj = $this->db->fetch_object($resql);
772
773
                    if ($obj) {
774
                        $module = $obj->module;
775
                        $perms = $obj->perms;
776
                        $subperms = $obj->subperms;
777
778
                        if (!empty($perms)) {
779
                            if (!empty($module)) {
780
                                if (!isset($this->rights->$module) || !is_object($this->rights->$module)) {
781
                                    $this->rights->$module = new stdClass();
782
                                }
783
                                if (!empty($subperms)) {
784
                                    if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
785
                                        $this->rights->$module->$perms = new stdClass();
786
                                    }
787
                                    if (empty($this->rights->$module->$perms->$subperms)) {
788
                                        $this->nb_rights++;
789
                                    }
790
                                    $this->rights->$module->$perms->$subperms = 1;
791
                                } else {
792
                                    if (empty($this->rights->$module->$perms)) {
793
                                        $this->nb_rights++;
794
                                    }
795
                                    $this->rights->$module->$perms = 1;
796
                                }
797
                            }
798
                        }
799
                    }
800
                    $i++;
801
                }
802
                $this->db->free($resql);
803
            }
804
805
            // Now permissions of groups
806
            $sql = "SELECT DISTINCT r.module, r.perms, r.subperms, r.entity";
807
            $sql .= " FROM " . $this->db->prefix() . "usergroup_rights as gr,";
808
            $sql .= " " . $this->db->prefix() . "usergroup_user as gu,";
809
            $sql .= " " . $this->db->prefix() . "rights_def as r";
810
            $sql .= " WHERE r.id = gr.fk_id";
811
            // @FIXME Very strange business rules. Must be alays the same than into user->getrights() user/perms.php and user/group/perms.php
812
            if (getDolGlobalString('MULTICOMPANY_BACKWARD_COMPATIBILITY')) {
813
                if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
814
                    $sql .= " AND gu.entity IN (0," . $conf->entity . ")";
815
                } else {
816
                    $sql .= " AND r.entity = " . ((int)$conf->entity);
817
                }
818
            } else {
819
                $sql .= " AND gr.entity = " . ((int)$conf->entity);  // Only groups created in current entity
820
                // The entity on the table gu=usergroup_user should be useless and should never be used because it is already into gr and r.
821
                // but when using MULTICOMPANY_TRANSVERSE_MODE, we may have inserted record that make rubbish result here due to the duplicate record of
822
                // other entities, so we are forced to add a filter on gu here
823
                $sql .= " AND gu.entity IN (0," . $conf->entity . ")";
824
                $sql .= " AND r.entity = " . ((int)$conf->entity);   // Only permission of modules enabled in current entity
825
            }
826
            // End of strange business rule
827
            $sql .= " AND gr.fk_usergroup = gu.fk_usergroup";
828
            $sql .= " AND gu.fk_user = " . ((int)$this->id);
829
            $sql .= " AND r.perms IS NOT NULL";
830
            if ($moduletag) {
831
                $sql .= " AND r.module = '" . $this->db->escape($moduletag) . "'";
832
            }
833
834
            $resql = $this->db->query($sql);
835
            if ($resql) {
836
                $num = $this->db->num_rows($resql);
837
                $i = 0;
838
                while ($i < $num) {
839
                    $obj = $this->db->fetch_object($resql);
840
841
                    if ($obj) {
842
                        $module = $obj->module;
843
                        $perms = $obj->perms;
844
                        $subperms = $obj->subperms;
845
846
                        if (!empty($perms)) {
847
                            if (!empty($module)) {
848
                                if (!isset($this->rights->$module) || !is_object($this->rights->$module)) {
849
                                    $this->rights->$module = new stdClass();
850
                                }
851
                                if (!empty($subperms)) {
852
                                    if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
853
                                        $this->rights->$module->$perms = new stdClass();
854
                                    }
855
                                    if (empty($this->rights->$module->$perms->$subperms)) { // already counted
856
                                        $this->nb_rights++;
857
                                    }
858
                                    $this->rights->$module->$perms->$subperms = 1;
859
                                } else {
860
                                    if (empty($this->rights->$module->$perms)) {            // already counted
861
                                        $this->nb_rights++;
862
                                    }
863
                                    // 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
864
                                    if (!isset($this->rights->$module->$perms) || !is_object($this->rights->$module->$perms)) {
865
                                        $this->rights->$module->$perms = 1;
866
                                    }
867
                                }
868
                            }
869
                        }
870
                    }
871
                    $i++;
872
                }
873
                $this->db->free($resql);
874
            }
875
876
            // Force permission on user for admin
877
            if (!empty($this->admin)) {
878
                if (empty($this->rights->user->user)) {
879
                    $this->rights->user->user = new stdClass();
880
                }
881
                $listofpermtotest = array('lire', 'creer', 'password', 'supprimer', 'export');
882
                foreach ($listofpermtotest as $permtotest) {
883
                    if (empty($this->rights->user->user->$permtotest)) {
884
                        $this->rights->user->user->$permtotest = 1;
885
                        $this->nb_rights++;
886
                    }
887
                }
888
                if (empty($this->rights->user->self)) {
889
                    $this->rights->user->self = new stdClass();
890
                }
891
                $listofpermtotest = array('creer', 'password');
892
                foreach ($listofpermtotest as $permtotest) {
893
                    if (empty($this->rights->user->self->$permtotest)) {
894
                        $this->rights->user->self->$permtotest = 1;
895
                        $this->nb_rights++;
896
                    }
897
                }
898
                // Add test on advanced permissions
899
                if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS')) {
900
                    if (empty($this->rights->user->user_advance)) {
901
                        $this->rights->user->user_advance = new stdClass();
902
                    }
903
                    $listofpermtotest = array('readperms', 'write');
904
                    foreach ($listofpermtotest as $permtotest) {
905
                        if (empty($this->rights->user->user_advance->$permtotest)) {
906
                            $this->rights->user->user_advance->$permtotest = 1;
907
                            $this->nb_rights++;
908
                        }
909
                    }
910
                    if (empty($this->rights->user->self_advance)) {
911
                        $this->rights->user->self_advance = new stdClass();
912
                    }
913
                    $listofpermtotest = array('readperms', 'writeperms');
914
                    foreach ($listofpermtotest as $permtotest) {
915
                        if (empty($this->rights->user->self_advance->$permtotest)) {
916
                            $this->rights->user->self_advance->$permtotest = 1;
917
                            $this->nb_rights++;
918
                        }
919
                    }
920
                    if (empty($this->rights->user->group_advance)) {
921
                        $this->rights->user->group_advance = new stdClass();
922
                    }
923
                    $listofpermtotest = array('read', 'readperms', 'write', 'delete');
924
                    foreach ($listofpermtotest as $permtotest) {
925
                        if (empty($this->rights->user) || empty($this->rights->user->group_advance->$permtotest)) {
926
                            $this->rights->user->group_advance->$permtotest = 1;
927
                            $this->nb_rights++;
928
                        }
929
                    }
930
                }
931
            }
932
933
            // For backward compatibility
934
            if (isset($this->rights->propale) && !isset($this->rights->propal)) {
935
                $this->rights->propal = $this->rights->propale;
936
            }
937
            if (isset($this->rights->propal) && !isset($this->rights->propale)) {
938
                $this->rights->propale = $this->rights->propal;
939
            }
940
941
            if (!$moduletag) {
942
                // If the module was not define, then everything is loaded.
943
                // Therefore, we can consider that the permissions are cached
944
                // because they were all loaded for this user instance.
945
                $this->all_permissions_are_loaded = 1;
946
            } else {
947
                // If the module is defined, we flag it as loaded into cache
948
                $this->_tab_loaded[$moduletag] = 1;
949
            }
950
        }
951
    }
952
953
    /**
954
     * Return array of data to show into tooltips
955
     *
956
     * @param array $params Array with options, infologin
957
     * @return array
958
     * @since v18
959
     */
960
    public function getTooltipContentArray($params)
961
    {
962
        global $conf, $langs, $menumanager;
963
        global $dolibarr_main_demo;
964
965
        $infologin = $params['infologin'] ?? 0;
966
        $option = $params['option'] ?? '';
967
968
        $data = [];
969
        if (!empty($this->photo)) {
970
            $photo = '<div class="photointooltip floatright">';
971
            $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
972
            $photo .= '</div>';
973
            $data['photo'] = $photo;
974
            //$label .= '<div style="clear: both;"></div>';
975
        }
976
977
        // Info Login
978
        $data['opendiv'] = '<div class="centpercent divtooltip">';
979
        $data['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("User") . '</u> ' . $this->getLibStatut(4);
980
        $data['name'] = '<br><b>' . $langs->trans('Name') . ':</b> ' . dol_string_nohtmltag($this->getFullName($langs, ''));
981
        if (!empty($this->login)) {
982
            $data['login'] = '<br><b>' . $langs->trans('Login') . ':</b> ' . dol_string_nohtmltag($this->login);
983
        }
984
        if (!empty($this->job)) {
985
            $data['job'] = '<br><b>' . $langs->trans("Job") . ':</b> ' . dol_string_nohtmltag($this->job);
986
        }
987
        $data['email'] = '<br><b>' . $langs->trans("Email") . ':</b> ' . dol_string_nohtmltag($this->email);
988
        if (!empty($this->office_phone) || !empty($this->office_fax) || !empty($this->fax)) {
989
            $phonelist = array();
990
            if ($this->office_phone) {
991
                $phonelist[] = dol_print_phone($this->office_phone, $this->country_code, $this->id, 0, '', '&nbsp', 'phone');
992
            }
993
            if ($this->office_fax) {
994
                $phonelist[] = dol_print_phone($this->office_fax, $this->country_code, $this->id, 0, '', '&nbsp', 'fax');
995
            }
996
            if ($this->user_mobile) {
997
                $phonelist[] = dol_print_phone($this->user_mobile, $this->country_code, $this->id, 0, '', '&nbsp', 'mobile');
998
            }
999
            $data['phones'] = '<br><b>' . $langs->trans('Phone') . ':</b> ' . implode('&nbsp;', $phonelist);
1000
        }
1001
        if (!empty($this->admin)) {
1002
            $data['administrator'] = '<br><b>' . $langs->trans("Administrator") . '</b>: ' . yn($this->admin);
1003
        }
1004
        if (!empty($this->accountancy_code) || $option == 'accountancy') {
1005
            $langs->load("companies");
1006
            $data['accountancycode'] = '<br><b>' . $langs->trans("AccountancyCode") . '</b>: ' . $this->accountancy_code;
1007
        }
1008
        $company = '';
1009
        if (!empty($this->socid)) { // Add thirdparty for external users
1010
            $thirdpartystatic = new Societe($this->db);
1011
            $thirdpartystatic->fetch($this->socid);
1012
            $companyimg = '';
1013
            if (empty($params['hidethirdpartylogo'])) {
1014
                $companyimg = ' ' . $thirdpartystatic->getNomUrl(2, (($option == 'nolink') ? 'nolink' : '')); // picto only of company
1015
            }
1016
            $company = ' (' . $langs->trans("Company") . ': ' . ($companyimg ? $companyimg : img_picto('', 'company')) . ' ' . dol_string_nohtmltag($thirdpartystatic->name) . ')';
1017
        }
1018
        $type = ($this->socid ? $langs->trans("ExternalUser") . $company : $langs->trans("InternalUser"));
1019
        $data['type'] = '<br><b>' . $langs->trans("Type") . ':</b> ' . $type;
1020
        $data['closediv'] = '</div>';
1021
1022
        if ($infologin > 0) {
1023
            $data['newlinelogin'] = '<br>';
1024
            $data['session'] = '<br><u>' . $langs->trans("Session") . '</u>';
1025
            $data['ip'] = '<br><b>' . $langs->trans("IPAddress") . '</b>: ' . dol_string_nohtmltag(getUserRemoteIP());
1026
            if (getDolGlobalString('MAIN_MODULE_MULTICOMPANY')) {
1027
                $data['multicompany'] = '<br><b>' . $langs->trans("ConnectedOnMultiCompany") . ':</b> ' . $conf->entity . ' (User entity ' . $this->entity . ')';
1028
            }
1029
            $data['authentication'] = '<br><b>' . $langs->trans("AuthenticationMode") . ':</b> ' . dol_string_nohtmltag($_SESSION["dol_authmode"] . (empty($dolibarr_main_demo) ? '' : ' (demo)'));
1030
            $data['connectedsince'] = '<br><b>' . $langs->trans("ConnectedSince") . ':</b> ' . dol_print_date($this->datelastlogin, "dayhour", 'tzuser');
1031
            $data['previousconnexion'] = '<br><b>' . $langs->trans("PreviousConnexion") . ':</b> ' . dol_print_date($this->datepreviouslogin, "dayhour", 'tzuser');
1032
            $data['currenttheme'] = '<br><b>' . $langs->trans("CurrentTheme") . ':</b> ' . dol_string_nohtmltag($conf->theme);
1033
            $data['currentmenumanager'] = '<br><b>' . $langs->trans("CurrentMenuManager") . ':</b> ' . dol_string_nohtmltag($menumanager->name);
1034
            $s = picto_from_langcode($langs->getDefaultLang());
1035
            $data['currentuserlang'] = '<br><b>' . $langs->trans("CurrentUserLanguage") . ':</b> ' . dol_string_nohtmltag(($s ? $s . ' ' : '') . $langs->getDefaultLang());
1036
            $data['browser'] = '<br><b>' . $langs->trans("Browser") . ':</b> ' . dol_string_nohtmltag($conf->browser->name . ($conf->browser->version ? ' ' . $conf->browser->version : '') . ' (' . $_SERVER['HTTP_USER_AGENT'] . ')');
1037
            $data['layout'] = '<br><b>' . $langs->trans("Layout") . ':</b> ' . dol_string_nohtmltag($conf->browser->layout);
1038
            $data['screen'] = '<br><b>' . $langs->trans("Screen") . ':</b> ' . dol_string_nohtmltag($_SESSION['dol_screenwidth'] . ' x ' . $_SESSION['dol_screenheight']);
1039
            if ($conf->browser->layout == 'phone') {
1040
                $data['phone'] = '<br><b>' . $langs->trans("Phone") . ':</b> ' . $langs->trans("Yes");
1041
            }
1042
            if (!empty($_SESSION["disablemodules"])) {
1043
                $data['disabledmodules'] = '<br><b>' . $langs->trans("DisabledModules") . ':</b> <br>' . dol_string_nohtmltag(implode(', ', explode(',', $_SESSION["disablemodules"])));
1044
            }
1045
        }
1046
1047
        return $data;
1048
    }
1049
1050
    /**
1051
     *  Return the label of the status of user (active, inactive)
1052
     *
1053
     * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
1054
     * @return string                 Label of status
1055
     */
1056
    public function getLibStatut($mode = 0)
1057
    {
1058
        return $this->LibStatut(isset($this->statut) ? (int)$this->statut : (int)$this->status, $mode);
1059
    }
1060
1061
    /**
1062
     *  Return the label of a status of user (active, inactive)
1063
     *
1064
     * @param int $status Id status
1065
     * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
1066
     * @return string                  Label of status
1067
     */
1068
    public function LibStatut($status, $mode = 0)
1069
    {
1070
        // phpcs:enable
1071
        global $langs;
1072
1073
        if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
1074
            global $langs;
1075
            //$langs->load("mymodule");
1076
            $this->labelStatus[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
1077
            $this->labelStatus[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
1078
            $this->labelStatusShort[self::STATUS_ENABLED] = $langs->transnoentitiesnoconv('Enabled');
1079
            $this->labelStatusShort[self::STATUS_DISABLED] = $langs->transnoentitiesnoconv('Disabled');
1080
        }
1081
1082
        $statusType = 'status5';
1083
        if ($status == self::STATUS_ENABLED) {
1084
            $statusType = 'status4';
1085
        }
1086
1087
        $label = $this->labelStatus[$status];
1088
        $labelshort = $this->labelStatusShort[$status];
1089
1090
        $now = dol_now();
1091
        if (!empty($this->datestartvalidity) && $now < $this->datestartvalidity) {
1092
            $statusType = 'status3';
1093
            $label .= ' (' . $langs->trans("UserNotYetValid") . ')';
1094
        }
1095
        if (!empty($this->dateendvalidity) && $now > ($this->dateendvalidity + 24 * 3600 - 1)) {
1096
            $statusType = 'status2';
1097
            $label .= ' (' . $langs->trans("UserExpired") . ')';
1098
        }
1099
1100
        return dolGetStatus($label, $labelshort, '', $statusType, $mode);
1101
    }
1102
1103
    /**
1104
     *  Return a HTML link to the user card (with optionally the picto)
1105
     *  Use this->id,this->lastname, this->firstname
1106
     *
1107
     * @param int $withpictoimg Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto, -1=Include photo into link, -2=Only picto photo, -3=Only photo very small)
1108
     * @param string $option On what the link point to ('leave', 'accountancy', 'nolink', )
1109
     * @param integer $infologin 0=Add default info tooltip, 1=Add complete info tooltip, -1=No info tooltip
1110
     * @param integer $notooltip 1=Disable tooltip on picto and name
1111
     * @param int $maxlen Max length of visible user name
1112
     * @param int $hidethirdpartylogo Hide logo of thirdparty if user is external user
1113
     * @param string $mode ''=Show firstname and lastname, 'firstname'=Show only firstname, 'firstelselast'=Show firstname or lastname if not defined, 'login'=Show login
1114
     * @param string $morecss Add more css on link
1115
     * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1116
     * @return string                              String with URL
1117
     */
1118
    public function getNomUrl($withpictoimg = 0, $option = '', $infologin = 0, $notooltip = 0, $maxlen = 24, $hidethirdpartylogo = 0, $mode = '', $morecss = '', $save_lastsearch_value = -1)
1119
    {
1120
        global $langs, $conf, $db, $hookmanager, $user;
1121
        global $dolibarr_main_authentication, $dolibarr_main_demo;
1122
1123
        if (!$user->hasRight('user', 'user', 'read') && $user->id != $this->id) {
1124
            $option = 'nolink';
1125
        }
1126
1127
        if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpictoimg) {
1128
            $withpictoimg = 0;
1129
        }
1130
1131
        $result = '';
1132
        $params = [
1133
            'id' => $this->id,
1134
            'objecttype' => $this->element,
1135
            'infologin' => $infologin,
1136
            'option' => $option,
1137
            'hidethirdpartylogo' => $hidethirdpartylogo,
1138
        ];
1139
        $classfortooltip = 'classfortooltip';
1140
        $dataparams = '';
1141
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1142
            $classfortooltip = 'classforajaxtooltip';
1143
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
1144
            $label = '';
1145
        } else {
1146
            $label = implode($this->getTooltipContentArray($params));
1147
        }
1148
1149
        $companylink = '';
1150
        if (!empty($this->socid)) { // Add thirdparty for external users
1151
            $thirdpartystatic = new Societe($this->db);
1152
            $thirdpartystatic->fetch($this->socid);
1153
            if (empty($hidethirdpartylogo)) {
1154
                $companylink = ' ' . $thirdpartystatic->getNomUrl(2, (($option == 'nolink') ? 'nolink' : '')); // picto only of company
1155
            }
1156
        }
1157
1158
        if ($infologin < 0) {
1159
            $label = '';
1160
        }
1161
1162
        $url = constant('BASE_URL') . '/user/card.php?id=' . $this->id;
1163
        if ($option == 'leave') {
1164
            $url = constant('BASE_URL') . '/holiday/list.php?id=' . $this->id;
1165
        }
1166
1167
        if ($option != 'nolink') {
1168
            // Add param to save lastsearch_values or not
1169
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1170
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1171
                $add_save_lastsearch_values = 1;
1172
            }
1173
            if ($add_save_lastsearch_values) {
1174
                $url .= '&save_lastsearch_values=1';
1175
            }
1176
        }
1177
1178
        $linkstart = '<a href="' . $url . '"';
1179
        $linkclose = "";
1180
        if (empty($notooltip)) {
1181
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1182
                $langs->load("users");
1183
                $label = $langs->trans("ShowUser");
1184
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1185
            }
1186
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
1187
            $linkclose .= $dataparams . ' class="' . $classfortooltip . ($morecss ? ' ' . $morecss : '') . '"';
1188
        } else {
1189
            $linkclose = ($morecss ? ' class="' . $morecss . '"' : '');
1190
        }
1191
1192
        $linkstart .= $linkclose . '>';
1193
        $linkend = '</a>';
1194
1195
        //if ($withpictoimg == -1) $result.='<div class="nowrap">';
1196
        $result .= (($option == 'nolink') ? '' : $linkstart);
1197
        if ($withpictoimg) {
1198
            $paddafterimage = '';
1199
            if (abs((int)$withpictoimg) == 1) {
1200
                $paddafterimage = 'style="margin-' . ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right') . ': 3px;"';
1201
            }
1202
            // Only picto
1203
            if ($withpictoimg > 0) {
1204
                $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>';
1205
            } else {
1206
                // Picto must be a photo
1207
                $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>';
1208
            }
1209
            $result .= $picto;
1210
        }
1211
        if ($withpictoimg > -2 && $withpictoimg != 2) {
1212
            if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1213
                $result .= '<span class="nopadding usertext' . ((!isset($this->status) || $this->status) ? '' : ' strikefordisabled') . ($morecss ? ' ' . $morecss : '') . '">';
1214
            }
1215
            if ($mode == 'login') {
1216
                $result .= dol_string_nohtmltag(dol_trunc($this->login, $maxlen));
1217
            } else {
1218
                $result .= dol_string_nohtmltag($this->getFullName($langs, '', ($mode == 'firstelselast' ? 3 : ($mode == 'firstname' ? 2 : -1)), $maxlen));
1219
            }
1220
            if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1221
                $result .= '</span>';
1222
            }
1223
        }
1224
        $result .= (($option == 'nolink') ? '' : $linkend);
1225
        //if ($withpictoimg == -1) $result.='</div>';
1226
1227
        $result .= $companylink;
1228
1229
        global $action;
1230
        $hookmanager->initHooks(array('userdao'));
1231
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1232
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1233
        if ($reshook > 0) {
1234
            $result = $hookmanager->resPrint;
1235
        } else {
1236
            $result .= $hookmanager->resPrint;
1237
        }
1238
1239
        return $result;
1240
    }
1241
1242
    /**
1243
     *  Return if a user has a permission.
1244
     *  You can use it like this: if ($user->hasRight('module', 'level11')).
1245
     *  It replaces old syntax: if ($user->rights->module->level1)
1246
     *
1247
     * @param string $module Module of permission to check
1248
     * @param string $permlevel1 Permission level1 (Example: 'read', 'write', 'delete')
1249
     * @param string $permlevel2 Permission level2
1250
     * @return int                     1 if user has permission, 0 if not.
1251
     * @see    clearrights(), delrights(), getrights(), hasRight()
1252
     */
1253
    public function hasRight($module, $permlevel1, $permlevel2 = '')
1254
    {
1255
        // For compatibility with bad naming permissions on module
1256
        $moduletomoduletouse = array(
1257
            'compta' => 'comptabilite',
1258
            'contract' => 'contrat',
1259
            'member' => 'adherent',
1260
            'mo' => 'mrp',
1261
            'order' => 'commande',
1262
            'produit' => 'product',
1263
            'project' => 'projet',
1264
            'propale' => 'propal',
1265
            'shipping' => 'expedition',
1266
            'task' => 'task@projet',
1267
            'fichinter' => 'ficheinter',
1268
            'inventory' => 'stock',
1269
            'invoice' => 'facture',
1270
            'invoice_supplier' => 'fournisseur',
1271
            'order_supplier' => 'fournisseur',
1272
            'knowledgerecord' => 'knowledgerecord@knowledgemanagement',
1273
            'skill@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
1274
            'job@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
1275
            'position@hrm' => 'all@hrm', // skill / job / position objects rights are for the moment grouped into right level "all"
1276
            'facturerec' => 'facture',
1277
            'margins' => 'margin',
1278
        );
1279
1280
        if (!empty($moduletomoduletouse[$module])) {
1281
            $module = $moduletomoduletouse[$module];
1282
        }
1283
1284
        $moduleRightsMapping = array(
1285
            'product' => 'produit',
1286
            'margin' => 'margins',
1287
            'comptabilite' => 'compta'
1288
        );
1289
1290
        $rightsPath = $module;
1291
        if (!empty($moduleRightsMapping[$rightsPath])) {
1292
            $rightsPath = $moduleRightsMapping[$rightsPath];
1293
        }
1294
1295
        // If module is abc@module, we check permission user->hasRight(module, abc, permlevel1)
1296
        $tmp = explode('@', $rightsPath, 2);
1297
        if (!empty($tmp[1])) {
1298
            if (strpos($module, '@') !== false) {
1299
                $module = $tmp[1];
1300
            }
1301
            if ($tmp[0] != $tmp[1]) {
1302
                // If $module = 'myobject@mymodule'
1303
                $rightsPath = $tmp[1];
1304
                $permlevel2 = $permlevel1;
1305
                $permlevel1 = $tmp[0];
1306
            } else {
1307
                // If $module = 'abc@abc'
1308
                $rightsPath = $tmp[1];
1309
            }
1310
        }
1311
1312
        // In $conf->modules, we have 'accounting', 'product', 'facture', ...
1313
        // In $user->rights, we have 'accounting', 'produit', 'facture', ...
1314
        //var_dump($this->rights->$rightsPath);
1315
        //var_dump($conf->modules);
1316
        //var_dump($module.' '.isModEnabled($module).' '.$rightsPath.' '.$permlevel1.' '.$permlevel2);
1317
        if (!isModEnabled($module)) {
1318
            return 0;
1319
        }
1320
1321
        // Special case for external user
1322
        if (!empty($this->socid)) {
1323
            if ($module == 'societe' && ($permlevel1 == 'creer' || $permlevel1 == 'write')) {
1324
                return 0;   // An external user never has the permission ->societe->write to see all thirdparties (always restricted to himself)
1325
            }
1326
            if ($module == 'societe' && $permlevel1 == 'client' && $permlevel2 == 'voir') {
1327
                return 0;   // An external user never has the permission ->societe->client->voir to see all thirdparties (always restricted to himself)
1328
            }
1329
            if ($module == 'societe' && $permlevel1 == 'export') {
1330
                return 0;   // An external user never has the permission ->societe->export to see all thirdparties (always restricted to himself)
1331
            }
1332
            if ($module == 'societe' && ($permlevel1 == 'supprimer' || $permlevel1 == 'delete')) {
1333
                return 0;   // An external user never has the permission ->societe->delete to see all thirdparties (always restricted to himself)
1334
            }
1335
        }
1336
1337
        // For compatibility with bad naming permissions on permlevel1
1338
        if ($permlevel1 == 'propale') {
1339
            $permlevel1 = 'propal';
1340
        }
1341
        if ($permlevel1 == 'member') {
1342
            $permlevel1 = 'adherent';
1343
        }
1344
        if ($permlevel1 == 'recruitmentcandidature') {
1345
            $permlevel1 = 'recruitmentjobposition';
1346
        }
1347
1348
        //var_dump($this->rights);
1349
        //var_dump($rightsPath.' '.$permlevel1.' '.$permlevel2);
1350
        if (empty($rightsPath) || empty($this->rights) || empty($this->rights->$rightsPath) || empty($permlevel1)) {
1351
            return 0;
1352
        }
1353
1354
        if ($permlevel2) {
1355
            if (!empty($this->rights->$rightsPath->$permlevel1)) {
1356
                if (!empty($this->rights->$rightsPath->$permlevel1->$permlevel2)) {
1357
                    return $this->rights->$rightsPath->$permlevel1->$permlevel2;
1358
                }
1359
                // For backward compatibility with old permissions called "lire", "creer", "create", "supprimer"
1360
                // instead of "read", "write", "delete"
1361
                if ($permlevel2 == 'read' && !empty($this->rights->$rightsPath->$permlevel1->lire)) {
1362
                    return $this->rights->$rightsPath->$permlevel1->lire;
1363
                }
1364
                if ($permlevel2 == 'write' && !empty($this->rights->$rightsPath->$permlevel1->creer)) {
1365
                    return $this->rights->$rightsPath->$permlevel1->creer;
1366
                }
1367
                if ($permlevel2 == 'write' && !empty($this->rights->$rightsPath->$permlevel1->create)) {
1368
                    return $this->rights->$rightsPath->$permlevel1->create;
1369
                }
1370
                if ($permlevel2 == 'delete' && !empty($this->rights->$rightsPath->$permlevel1->supprimer)) {
1371
                    return $this->rights->$rightsPath->$permlevel1->supprimer;
1372
                }
1373
            }
1374
        } else {
1375
            if (!empty($this->rights->$rightsPath->$permlevel1)) {
1376
                return $this->rights->$rightsPath->$permlevel1;
1377
            }
1378
            // For backward compatibility with old permissions called "lire", "creer", "create", "supprimer"
1379
            // instead of "read", "write", "delete"
1380
            if ($permlevel1 == 'read' && !empty($this->rights->$rightsPath->lire)) {
1381
                return $this->rights->$rightsPath->lire;
1382
            }
1383
            if ($permlevel1 == 'write' && !empty($this->rights->$rightsPath->creer)) {
1384
                return $this->rights->$rightsPath->creer;
1385
            }
1386
            if ($permlevel1 == 'write' && !empty($this->rights->$rightsPath->create)) {
1387
                return $this->rights->$rightsPath->create;
1388
            }
1389
            if ($permlevel1 == 'delete' && !empty($this->rights->$rightsPath->supprimer)) {
1390
                return $this->rights->$rightsPath->supprimer;
1391
            }
1392
        }
1393
1394
        return 0;
1395
    }
1396
1397
    /**
1398
     *  Change status of a user
1399
     *
1400
     * @param int $status Status to set
1401
     * @return int                 Return integer <0 if KO, 0 if nothing is done, >0 if OK
1402
     */
1403
    public function setstatus($status)
1404
    {
1405
        global $conf, $langs, $user;
1406
1407
        $error = 0;
1408
1409
        // Check parameters
1410
        if (isset($this->statut)) {
1411
            if ($this->statut == $status) {
1412
                return 0;
1413
            }
1414
        } elseif (isset($this->status) && $this->status == $status) {
1415
            return 0;
1416
        }
1417
1418
        $this->db->begin();
1419
1420
        // Save in database
1421
        $sql = "UPDATE " . $this->db->prefix() . "user";
1422
        $sql .= " SET statut = " . ((int)$status);
1423
        $sql .= " WHERE rowid = " . ((int)$this->id);
1424
        $result = $this->db->query($sql);
1425
1426
        dol_syslog(get_only_class($this) . "::setstatus", LOG_DEBUG);
1427
        if ($result) {
1428
            if ($status == 0) {
1429
                $this->context['actionmsg'] = 'User ' . $this->login . ' disabled';
1430
            } else {
1431
                $this->context['actionmsg'] = 'User ' . $this->login . ' enabled';
1432
            }
1433
            // Call trigger
1434
            $result = $this->call_trigger('USER_ENABLEDISABLE', $user);
1435
            if ($result < 0) {
1436
                $error++;
1437
            }
1438
            // End call triggers
1439
        }
1440
1441
        if ($error) {
1442
            $this->db->rollback();
1443
            return -$error;
1444
        } else {
1445
            $this->status = $status;
1446
            $this->statut = $status;
1447
            $this->db->commit();
1448
            return 1;
1449
        }
1450
    }
1451
1452
1453
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1454
1455
    /**
1456
     * Sets object to supplied categories.
1457
     *
1458
     * Deletes object from existing categories not supplied.
1459
     * Adds it to non existing supplied categories.
1460
     * Existing categories are left untouch.
1461
     *
1462
     * @param int[]|int $categories Category or categories IDs
1463
     * @return  int                         Return integer <0 if KO, >0 if OK
1464
     */
1465
    public function setCategories($categories)
1466
    {
1467
        return parent::setCategoriesCommon($categories, Categorie::TYPE_USER);
1468
    }
1469
1470
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1471
1472
    /**
1473
     *  Delete the user
1474
     *
1475
     * @param User $user User than delete
1476
     * @return     int             Return integer <0 if KO, >0 if OK
1477
     */
1478
    public function delete(User $user)
1479
    {
1480
        global $conf, $langs;
1481
1482
        $error = 0;
1483
1484
        $this->db->begin();
1485
1486
        $this->fetch($this->id);
1487
1488
        dol_syslog(get_only_class($this) . "::delete", LOG_DEBUG);
1489
1490
        // Remove rights
1491
        $sql = "DELETE FROM " . $this->db->prefix() . "user_rights WHERE fk_user = " . ((int)$this->id);
1492
1493
        if (!$error && !$this->db->query($sql)) {
1494
            $error++;
1495
            $this->error = $this->db->lasterror();
1496
        }
1497
1498
        // Remove group
1499
        $sql = "DELETE FROM " . $this->db->prefix() . "usergroup_user WHERE fk_user  = " . ((int)$this->id);
1500
        if (!$error && !$this->db->query($sql)) {
1501
            $error++;
1502
            $this->error = $this->db->lasterror();
1503
        }
1504
1505
        // Remove params
1506
        $sql = "DELETE FROM " . $this->db->prefix() . "user_param WHERE fk_user  = " . ((int)$this->id);
1507
        if (!$error && !$this->db->query($sql)) {
1508
            $error++;
1509
            $this->error = $this->db->lasterror();
1510
        }
1511
1512
        // If contact, remove link
1513
        if ($this->contact_id > 0) {
1514
            $sql = "UPDATE " . $this->db->prefix() . "socpeople SET fk_user_creat = null WHERE rowid = " . ((int)$this->contact_id);
1515
            if (!$error && !$this->db->query($sql)) {
1516
                $error++;
1517
                $this->error = $this->db->lasterror();
1518
            }
1519
        }
1520
1521
        // Remove extrafields
1522
        if (!$error) {
1523
            $result = $this->deleteExtraFields();
1524
            if ($result < 0) {
1525
                $error++;
1526
                dol_syslog(get_only_class($this) . "::delete error -4 " . $this->error, LOG_ERR);
1527
            }
1528
        }
1529
1530
        // Remove user
1531
        if (!$error) {
1532
            $sql = "DELETE FROM " . $this->db->prefix() . "user WHERE rowid = " . ((int)$this->id);
1533
            dol_syslog(get_only_class($this) . "::delete", LOG_DEBUG);
1534
            if (!$this->db->query($sql)) {
1535
                $error++;
1536
                $this->error = $this->db->lasterror();
1537
            }
1538
        }
1539
1540
        if (!$error) {
1541
            // Call trigger
1542
            $result = $this->call_trigger('USER_DELETE', $user);
1543
            if ($result < 0) {
1544
                $error++;
1545
                $this->db->rollback();
1546
                return -1;
1547
            }
1548
            // End call triggers
1549
1550
            $this->db->commit();
1551
            return 1;
1552
        } else {
1553
            $this->db->rollback();
1554
            return -1;
1555
        }
1556
    }
1557
1558
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1559
1560
    /**
1561
     *  Load a user from database with its id or ref (login).
1562
     *  This function does not load permissions, only user properties. Use getrights() for this just after the fetch.
1563
     *
1564
     * @param int $id If defined, id to used for search
1565
     * @param string $login If defined, login to used for search
1566
     * @param string $sid If defined, sid to used for search
1567
     * @param int $loadpersonalconf 1=also load personal conf of user (in $user->conf->xxx), 0=do not load personal conf.
1568
     * @param int $entity If a value is >= 0, we force the search on a specific entity. If -1, means search depens on default setup.
1569
     * @param string $email If defined, email to used for search
1570
     * @param int $fk_socpeople If defined, id of contact for search
1571
     * @param int $use_email_oauth2 1=Use also email_oauth2 to fetch on email
1572
     * @return int                         Return integer <0 if KO, 0 not found, >0 if OK
1573
     */
1574
    public function fetch($id = 0, $login = '', $sid = '', $loadpersonalconf = 0, $entity = -1, $email = '', $fk_socpeople = 0, $use_email_oauth2 = 0)
1575
    {
1576
        global $conf, $user;
1577
1578
        // Clean parameters
1579
        $login = trim($login);
1580
1581
        // Get user
1582
        $sql = "SELECT u.rowid, u.lastname, u.firstname, u.employee, u.gender, u.civility as civility_code, u.birth, u.job,";
1583
        $sql .= " u.email, u.email_oauth2, u.personal_email,";
1584
        $sql .= " u.socialnetworks,";
1585
        $sql .= " u.signature, u.office_phone, u.office_fax, u.user_mobile, u.personal_mobile,";
1586
        $sql .= " u.address, u.zip, u.town, u.fk_state as state_id, u.fk_country as country_id,";
1587
        $sql .= " u.admin, u.login, u.note_private, u.note_public,";
1588
        $sql .= " u.pass, u.pass_crypted, u.pass_temp, u.api_key,";
1589
        $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,";
1590
        $sql .= " u.statut as status, u.lang, u.entity,";
1591
        $sql .= " u.datec as datec,";
1592
        $sql .= " u.tms as datem,";
1593
        $sql .= " u.datelastlogin as datel,";
1594
        $sql .= " u.datepreviouslogin as datep,";
1595
        $sql .= " u.flagdelsessionsbefore,";
1596
        $sql .= " u.iplastlogin,";
1597
        $sql .= " u.ippreviouslogin,";
1598
        $sql .= " u.datelastpassvalidation,";
1599
        $sql .= " u.datestartvalidity,";
1600
        $sql .= " u.dateendvalidity,";
1601
        $sql .= " u.photo as photo,";
1602
        $sql .= " u.openid as openid,";
1603
        $sql .= " u.accountancy_code,";
1604
        $sql .= " u.thm,";
1605
        $sql .= " u.tjm,";
1606
        $sql .= " u.salary,";
1607
        $sql .= " u.salaryextra,";
1608
        $sql .= " u.weeklyhours,";
1609
        $sql .= " u.color,";
1610
        $sql .= " u.dateemployment, u.dateemploymentend,";
1611
        $sql .= " u.fk_warehouse,";
1612
        $sql .= " u.ref_ext,";
1613
        $sql .= " u.default_range, u.default_c_exp_tax_cat,"; // Expense report default mode
1614
        $sql .= " u.national_registration_number,";
1615
        $sql .= " u.ref_employee,";
1616
        $sql .= " c.code as country_code, c.label as country,";
1617
        $sql .= " d.code_departement as state_code, d.nom as state,";
1618
        $sql .= " s.label as label_establishment, u.fk_establishment";
1619
        $sql .= " FROM " . $this->db->prefix() . "user as u";
1620
        $sql .= " LEFT JOIN " . $this->db->prefix() . "c_country as c ON u.fk_country = c.rowid";
1621
        $sql .= " LEFT JOIN " . $this->db->prefix() . "c_departements as d ON u.fk_state = d.rowid";
1622
        $sql .= " LEFT JOIN " . $this->db->prefix() . "establishment as s ON u.fk_establishment = s.rowid";
1623
1624
        if ($entity < 0) {
1625
            if ((!isModEnabled('multicompany') || !getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) && (!empty($user->entity))) {
1626
                $sql .= " WHERE u.entity IN (0, " . ((int)$conf->entity) . ")";
1627
            } else {
1628
                $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
1629
            }
1630
        } else {
1631
            // The fetch was forced on an entity
1632
            if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
1633
                $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
1634
            } else {
1635
                $sql .= " WHERE u.entity IN (0, " . ((int)(($entity != '' && $entity >= 0) ? $entity : $conf->entity)) . ")"; // search in entity provided in parameter
1636
            }
1637
        }
1638
1639
        if ($sid) {
1640
            // permet une recherche du user par son SID ActiveDirectory ou Samba
1641
            $sql .= " AND (u.ldap_sid = '" . $this->db->escape($sid) . "' OR u.login = '" . $this->db->escape($login) . "')";
1642
        } elseif ($login) {
1643
            $sql .= " AND u.login = '" . $this->db->escape($login) . "'";
1644
        } elseif ($email) {
1645
            $sql .= " AND (u.email = '" . $this->db->escape($email) . "'";
1646
            if ($use_email_oauth2) {
1647
                $sql .= " OR u.email_oauth2 = '" . $this->db->escape($email) . "'";
1648
            }
1649
            $sql .= ")";
1650
        } elseif ($fk_socpeople > 0) {
1651
            $sql .= " AND u.fk_socpeople = " . ((int)$fk_socpeople);
1652
        } else {
1653
            $sql .= " AND u.rowid = " . ((int)$id);
1654
        }
1655
        $sql .= " ORDER BY u.entity ASC"; // Avoid random result when there is 2 login in 2 different entities
1656
1657
        if ($sid) {
1658
            // permet une recherche du user par son SID ActiveDirectory ou Samba
1659
            $sql .= ' ' . $this->db->plimit(1);
1660
        }
1661
1662
        $resql = $this->db->query($sql);
1663
        if ($resql) {
1664
            $num = $this->db->num_rows($resql);
1665
            if ($num > 1) {
1666
                $this->error = "USERDUPLICATEFOUND";
1667
                dol_syslog(get_only_class($this) . "::fetch more than 1 user found", LOG_WARNING);
1668
1669
                $this->db->free($resql);
1670
                return 0;
1671
            }
1672
1673
            $obj = $this->db->fetch_object($resql);
1674
            if ($obj) {
1675
                $this->id = $obj->rowid;
1676
                $this->ref = $obj->rowid;
1677
1678
                $this->ref_ext = $obj->ref_ext;
1679
1680
                $this->ldap_sid = $obj->ldap_sid;
1681
                $this->civility_code = $obj->civility_code;
1682
                $this->lastname = $obj->lastname;
1683
                $this->firstname = $obj->firstname;
1684
                $this->ref_employee = $obj->ref_employee;
1685
                $this->national_registration_number = $obj->national_registration_number;
1686
1687
                $this->employee = $obj->employee;
1688
1689
                $this->login = $obj->login;
1690
                $this->gender = $obj->gender;
1691
                $this->birth = $this->db->jdate($obj->birth);
1692
                $this->pass_indatabase = $obj->pass;
1693
                $this->pass_indatabase_crypted = $obj->pass_crypted;
1694
                $this->pass = $obj->pass;
1695
                $this->pass_temp = $obj->pass_temp;
1696
                $this->api_key = dolDecrypt($obj->api_key);
1697
1698
                $this->address = $obj->address;
1699
                $this->zip = $obj->zip;
1700
                $this->town = $obj->town;
1701
1702
                $this->country_id = $obj->country_id;
1703
                $this->country_code = $obj->country_id ? $obj->country_code : '';
1704
                //$this->country = $obj->country_id?($langs->trans('Country'.$obj->country_code)!='Country'.$obj->country_code?$langs->transnoentities('Country'.$obj->country_code):$obj->country):'';
1705
1706
                $this->state_id = $obj->state_id;
1707
                $this->state_code = $obj->state_code;
1708
                $this->state = ($obj->state != '-' ? $obj->state : '');
1709
1710
                $this->office_phone = $obj->office_phone;
1711
                $this->office_fax = $obj->office_fax;
1712
                $this->user_mobile = $obj->user_mobile;
1713
                $this->personal_mobile = $obj->personal_mobile;
1714
                $this->email = $obj->email;
1715
                $this->email_oauth2 = $obj->email_oauth2;
1716
                $this->personal_email = $obj->personal_email;
1717
                $this->socialnetworks = ($obj->socialnetworks ? (array)json_decode($obj->socialnetworks, true) : array());
1718
                $this->job = $obj->job;
1719
                $this->signature = $obj->signature;
1720
                $this->admin = $obj->admin;
1721
                $this->note_public = $obj->note_public;
1722
                $this->note_private = $obj->note_private;
1723
1724
                $this->statut = $obj->status;         // deprecated
1725
                $this->status = $obj->status;
1726
1727
                $this->photo = $obj->photo;
1728
                $this->openid = $obj->openid;
1729
                $this->lang = $obj->lang;
1730
                $this->entity = $obj->entity;
1731
                $this->accountancy_code = $obj->accountancy_code;
1732
                $this->thm = $obj->thm;
1733
                $this->tjm = $obj->tjm;
1734
                $this->salary = $obj->salary;
1735
                $this->salaryextra = $obj->salaryextra;
1736
                $this->weeklyhours = $obj->weeklyhours;
1737
                $this->color = $obj->color;
1738
                $this->dateemployment = $this->db->jdate($obj->dateemployment);
1739
                $this->dateemploymentend = $this->db->jdate($obj->dateemploymentend);
1740
1741
                $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...
1742
                $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...
1743
                $this->datelastlogin = $this->db->jdate($obj->datel);
1744
                $this->datepreviouslogin = $this->db->jdate($obj->datep);
1745
                $this->flagdelsessionsbefore = $this->db->jdate($obj->flagdelsessionsbefore, 'gmt');
1746
                $this->iplastlogin = $obj->iplastlogin;
1747
                $this->ippreviouslogin = $obj->ippreviouslogin;
1748
                $this->datestartvalidity = $this->db->jdate($obj->datestartvalidity);
1749
                $this->dateendvalidity = $this->db->jdate($obj->dateendvalidity);
1750
1751
                $this->socid = $obj->fk_soc;
1752
                $this->contact_id = $obj->fk_socpeople;
1753
                $this->fk_member = $obj->fk_member;
1754
                $this->fk_user = $obj->fk_user;
1755
                $this->fk_user_expense_validator = $obj->fk_user_expense_validator;
1756
                $this->fk_user_holiday_validator = $obj->fk_user_holiday_validator;
1757
1758
                $this->default_range = $obj->default_range;
1759
                $this->default_c_exp_tax_cat = $obj->default_c_exp_tax_cat;
1760
                $this->fk_warehouse = $obj->fk_warehouse;
1761
                $this->fk_establishment = $obj->fk_establishment;
1762
                $this->label_establishment = $obj->label_establishment;
1763
1764
                // Protection when module multicompany was set, admin was set to first entity and then, the module was disabled,
1765
                // in such case, this admin user must be admin for ALL entities.
1766
                if (!isModEnabled('multicompany') && $this->admin && $this->entity == 1) {
1767
                    $this->entity = 0;
1768
                }
1769
1770
                // Retrieve all extrafield
1771
                // fetch optionals attributes and labels
1772
                $this->fetch_optionals();
1773
1774
                $this->db->free($resql);
1775
            } else {
1776
                $this->error = "USERNOTFOUND";
1777
                dol_syslog(get_only_class($this) . "::fetch user not found", LOG_DEBUG);
1778
1779
                $this->db->free($resql);
1780
                return 0;
1781
            }
1782
        } else {
1783
            $this->error = $this->db->lasterror();
1784
            return -1;
1785
        }
1786
1787
        // To get back the global configuration unique to the user
1788
        if ($loadpersonalconf) {
1789
            $result = $this->loadPersonalConf();
1790
1791
            $result = $this->loadDefaultValues();
1792
1793
            if ($result < 0) {
1794
                $this->error = $this->db->lasterror();
1795
                return -3;
1796
            }
1797
        }
1798
1799
        return 1;
1800
    }
1801
1802
    /**
1803
     *  Load const values from database table user_param and set it into user->conf->XXX
1804
     *
1805
     * @return int                     >= 0 if OK, < 0 if KO
1806
     */
1807
    public function loadPersonalConf()
1808
    {
1809
        global $conf;
1810
1811
        // Load user->conf for user
1812
        $sql = "SELECT param, value FROM " . $this->db->prefix() . "user_param";
1813
        $sql .= " WHERE fk_user = " . ((int)$this->id);
1814
        $sql .= " AND entity = " . ((int)$conf->entity);
1815
        //dol_syslog(get_only_class($this).'::fetch load personalized conf', LOG_DEBUG);
1816
        $resql = $this->db->query($sql);
1817
        if ($resql) {
1818
            $num = $this->db->num_rows($resql);
1819
            $i = 0;
1820
            while ($i < $num) {
1821
                $obj = $this->db->fetch_object($resql);
1822
                $p = (!empty($obj->param) ? $obj->param : '');
1823
                if (!empty($p)) {
1824
                    $this->conf->$p = $obj->value;
1825
                }
1826
                $i++;
1827
            }
1828
            $this->db->free($resql);
1829
1830
            return $num;
1831
        } else {
1832
            $this->error = $this->db->lasterror();
1833
1834
            return -2;
1835
        }
1836
    }
1837
1838
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1839
1840
    /**
1841
     *  Load default values from database table into property ->default_values
1842
     *
1843
     * @return int                     > 0 if OK, < 0 if KO
1844
     */
1845
    public function loadDefaultValues()
1846
    {
1847
        global $conf;
1848
1849
        if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
1850
            // Load user->default_values for user. TODO Save this in memcached ?
1851
            $defaultValues = new DefaultValues($this->db);
1852
            $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)
1853
            //$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)
1854
1855
            if (!is_array($result) && $result < 0) {
1856
                setEventMessages($defaultValues->error, $defaultValues->errors, 'errors');
1857
                dol_print_error($this->db);
1858
                return -1;
1859
            } elseif (count($result) > 0) {
1860
                foreach ($result as $defval) {
1861
                    if (!empty($defval->page) && !empty($defval->type) && !empty($defval->param)) {
1862
                        $pagewithoutquerystring = $defval->page;
1863
                        $pagequeries = '';
1864
                        $reg = array();
1865
                        if (preg_match('/^([^\?]+)\?(.*)$/', $pagewithoutquerystring, $reg)) {    // There is query param
1866
                            $pagewithoutquerystring = $reg[1];
1867
                            $pagequeries = $reg[2];
1868
                        }
1869
                        $this->default_values[$pagewithoutquerystring][$defval->type][$pagequeries ? $pagequeries : '_noquery_'][$defval->param] = $defval->value;
1870
                    }
1871
                }
1872
            }
1873
            if (!empty($this->default_values)) {
1874
                foreach ($this->default_values as $a => $b) {
1875
                    foreach ($b as $c => $d) {
1876
                        krsort($this->default_values[$a][$c]);
1877
                    }
1878
                }
1879
            }
1880
        }
1881
        return 1;
1882
    }
1883
1884
    /**
1885
     *  Load all objects into $this->users
1886
     *
1887
     * @param string $sortorder sort order
1888
     * @param string $sortfield sort field
1889
     * @param int $limit limit page
1890
     * @param int $offset page
1891
     * @param string $filter Filter as an Universal Search string.
1892
     *                                      Example: '((client:=:1) OR ((client:>=:2) AND (client:<=:3))) AND (client:!=:8) AND (nom:like:'a%')'
1893
     * @param string $filtermode No more used
1894
     * @param bool $entityfilter Activate entity filter
1895
     * @return int                         Return integer <0 if KO, >0 if OK
1896
     */
1897
    public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, $filter = '', $filtermode = 'AND', $entityfilter = false)
1898
    {
1899
        global $conf, $user;
1900
1901
        $sql = "SELECT t.rowid";
1902
        $sql .= ' FROM ' . $this->db->prefix() . $this->table_element . ' as t ';
1903
1904
        if ($entityfilter) {
1905
            if (getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
1906
                if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1907
                    $sql .= " WHERE t.entity IS NOT NULL"; // Show all users
1908
                } else {
1909
                    $sql .= "," . $this->db->prefix() . "usergroup_user as ug";
1910
                    $sql .= " WHERE ((ug.fk_user = t.rowid";
1911
                    $sql .= " AND ug.entity IN (" . getEntity('usergroup') . "))";
1912
                    $sql .= " OR t.entity = 0)"; // Show always superadmin
1913
                }
1914
            } else {
1915
                $sql .= " WHERE t.entity IN (" . getEntity('user') . ")";
1916
            }
1917
        } else {
1918
            $sql .= " WHERE 1 = 1";
1919
        }
1920
1921
        // Manage filter
1922
        $errormessage = '';
1923
        $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
1924
        if ($errormessage) {
1925
            $this->errors[] = $errormessage;
1926
            dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
1927
            return -1;
1928
        }
1929
1930
        $sql .= $this->db->order($sortfield, $sortorder);
1931
        if ($limit) {
1932
            $sql .= $this->db->plimit($limit + 1, $offset);
1933
        }
1934
1935
        dol_syslog(__METHOD__, LOG_DEBUG);
1936
1937
        $resql = $this->db->query($sql);
1938
        if ($resql) {
1939
            $this->users = array();
1940
            $num = $this->db->num_rows($resql);
1941
            if ($num) {
1942
                while ($obj = $this->db->fetch_object($resql)) {
1943
                    $line = new self($this->db);
1944
                    $result = $line->fetch($obj->rowid);
1945
                    if ($result > 0 && !empty($line->id)) {
1946
                        $this->users[$obj->rowid] = clone $line;
1947
                    }
1948
                }
1949
                $this->db->free($resql);
1950
            }
1951
            return $num;
1952
        } else {
1953
            $this->errors[] = $this->db->lasterror();
1954
            return -1;
1955
        }
1956
    }
1957
1958
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1959
1960
    /**
1961
     *  Create a user from a contact object. User will be internal but if contact is linked to a third party, user will be external
1962
     *
1963
     * @param Contact $contact Object for source contact
1964
     * @param string $login Login to force
1965
     * @param string $password Password to force
1966
     * @return int                 Return integer <0 if error, if OK returns id of created user
1967
     */
1968
    public function create_from_contact($contact, $login = '', $password = '')
1969
    {
1970
        // phpcs:enable
1971
        global $conf, $user, $langs;
1972
1973
        $error = 0;
1974
1975
        // Define parameters
1976
        $this->admin = 0;
1977
        $this->civility_code = $contact->civility_code;
1978
        $this->lastname = $contact->lastname;
1979
        $this->firstname = $contact->firstname;
1980
        //$this->gender = $contact->gender;     // contact has no gender
1981
        $this->email = $contact->email;
1982
        $this->socialnetworks = $contact->socialnetworks;
1983
        $this->office_phone = $contact->phone_pro;
1984
        $this->office_fax = $contact->fax;
1985
        $this->user_mobile = $contact->phone_mobile;
1986
        $this->address = $contact->address;
1987
        $this->zip = $contact->zip;
1988
        $this->town = $contact->town;
1989
        $this->setUpperOrLowerCase();
1990
        $this->state_id = $contact->state_id;
1991
        $this->country_id = $contact->country_id;
1992
        $this->employee = 0;
1993
1994
        if (empty($login)) {
1995
            include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
1996
            $login = dol_buildlogin($contact->lastname, $contact->firstname);
1997
        }
1998
        $this->login = $login;
1999
2000
        $this->db->begin();
2001
2002
        // Create user and set $this->id. Trigger is disabled because executed later.
2003
        $result = $this->create($user, 1);
2004
        if ($result > 0) {
2005
            $sql = "UPDATE " . $this->db->prefix() . "user";
2006
            $sql .= " SET fk_socpeople=" . ((int)$contact->id);
2007
            $sql .= ", civility='" . $this->db->escape($contact->civility_code) . "'";
2008
            if ($contact->socid > 0) {
2009
                $sql .= ", fk_soc=" . ((int)$contact->socid);
2010
            }
2011
            $sql .= " WHERE rowid=" . ((int)$this->id);
2012
2013
            $resql = $this->db->query($sql);
2014
2015
            dol_syslog(get_only_class($this) . "::create_from_contact", LOG_DEBUG);
2016
            if ($resql) {
2017
                $this->context['createfromcontact'] = 'createfromcontact';
2018
2019
                // Call trigger
2020
                $result = $this->call_trigger('USER_CREATE', $user);
2021
                if ($result < 0) {
2022
                    $error++;
2023
                    $this->db->rollback();
2024
                    return -1;
2025
                }
2026
                // End call triggers
2027
2028
                $this->db->commit();
2029
                return $this->id;
2030
            } else {
2031
                $this->error = $this->db->error();
2032
2033
                $this->db->rollback();
2034
                return -1;
2035
            }
2036
        } else {
2037
            // $this->error deja positionne
2038
            dol_syslog(get_only_class($this) . "::create_from_contact - 0");
2039
2040
            $this->db->rollback();
2041
            return $result;
2042
        }
2043
    }
2044
2045
    /**
2046
     *  Create a user into database
2047
     *
2048
     * @param User $user Object user doing creation
2049
     * @param int $notrigger 1=do not execute triggers, 0 otherwise
2050
     * @return int                     Return integer <0 if KO, id of created user if OK
2051
     */
2052
    public function create($user, $notrigger = 0)
2053
    {
2054
        global $conf, $langs;
2055
        global $mysoc;
2056
2057
        // Clean parameters
2058
        $this->setUpperOrLowerCase();
2059
2060
        $this->civility_code = trim((string)$this->civility_code);
2061
        $this->login = trim((string)$this->login);
2062
        if (!isset($this->entity)) {
2063
            $this->entity = $conf->entity; // If not defined, we use default value
2064
        }
2065
2066
        dol_syslog(get_only_class($this) . "::create login=" . $this->login . ", user=" . (is_object($user) ? $user->id : ''), LOG_DEBUG);
2067
2068
        $badCharUnauthorizedIntoLoginName = getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
2069
2070
        // Check parameters
2071
        if (getDolGlobalString('USER_MAIL_REQUIRED') && !isValidEmail($this->email)) {
2072
            $langs->load("errors");
2073
            $this->error = $langs->trans("ErrorBadEMail", $this->email);
2074
            return -1;
2075
        }
2076
        if (empty($this->login)) {
2077
            $langs->load("errors");
2078
            $this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Login"));
2079
            return -1;
2080
        } elseif (preg_match('/[' . preg_quote($badCharUnauthorizedIntoLoginName, '/') . ']/', $this->login)) {
2081
            $langs->load("errors");
2082
            $this->error = $langs->trans("ErrorBadCharIntoLoginName", $langs->transnoentitiesnoconv("Login"));
2083
            return -1;
2084
        }
2085
2086
        $this->datec = dol_now();
2087
2088
        $error = 0;
2089
        $this->db->begin();
2090
2091
        // Check if login already exists in same entity or into entity 0.
2092
        if ($this->login) {
2093
            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $this->db->prefix() . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
2094
            $resqltochecklogin = $this->db->query($sqltochecklogin);
2095
            if ($resqltochecklogin) {
2096
                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
2097
                if ($objtochecklogin && $objtochecklogin->nb > 0) {
2098
                    $langs->load("errors");
2099
                    $this->error = $langs->trans("ErrorLoginAlreadyExists", $this->login);
2100
                    dol_syslog(get_only_class($this) . "::create " . $this->error, LOG_DEBUG);
2101
                    $this->db->rollback();
2102
                    return -6;
2103
                }
2104
                $this->db->free($resqltochecklogin);
2105
            }
2106
        }
2107
        if (!empty($this->email)) {
2108
            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $this->db->prefix() . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
2109
            $resqltochecklogin = $this->db->query($sqltochecklogin);
2110
            if ($resqltochecklogin) {
2111
                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
2112
                if ($objtochecklogin && $objtochecklogin->nb > 0) {
2113
                    $langs->load("errors");
2114
                    $this->error = $langs->trans("ErrorEmailAlreadyExists", $this->email);
2115
                    dol_syslog(get_only_class($this) . "::create " . $this->error, LOG_DEBUG);
2116
                    $this->db->rollback();
2117
                    return -6;
2118
                }
2119
                $this->db->free($resqltochecklogin);
2120
            }
2121
        }
2122
2123
        // Insert into database
2124
        $sql = "INSERT INTO " . $this->db->prefix() . "user (datec, login, ldap_sid, entity)";
2125
        $sql .= " VALUES('" . $this->db->idate($this->datec) . "', '" . $this->db->escape($this->login) . "', '" . $this->db->escape($this->ldap_sid) . "', " . ((int)$this->entity) . ")";
2126
        $result = $this->db->query($sql);
2127
2128
        dol_syslog(get_only_class($this) . "::create", LOG_DEBUG);
2129
        if ($result) {
2130
            $this->id = $this->db->last_insert_id($this->db->prefix() . "user");
2131
2132
            // Set default rights
2133
            if ($this->set_default_rights() < 0) {
2134
                $this->error = 'ErrorFailedToSetDefaultRightOfUser';
2135
                $this->db->rollback();
2136
                return -5;
2137
            }
2138
2139
            if (getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER') && getDolGlobalString('STOCK_USERSTOCK_AUTOCREATE')) {
2140
                $langs->load("stocks");
2141
2142
                $entrepot = new Entrepot($this->db);
2143
                $entrepot->label = $langs->trans("PersonalStock", $this->getFullName($langs));
2144
                $entrepot->libelle = $entrepot->label; // For backward compatibility
2145
                $entrepot->description = $langs->trans("ThisWarehouseIsPersonalStock", $this->getFullName($langs));
2146
                $entrepot->statut = 1;
2147
                $entrepot->country_id = $mysoc->country_id;
2148
2149
                $warehouseid = $entrepot->create($user);
2150
2151
                $this->fk_warehouse = $warehouseid;
2152
            }
2153
2154
            // Update minor fields
2155
            $result = $this->update($user, 1, 1);
2156
            if ($result < 0) {
2157
                $this->db->rollback();
2158
                return -4;
2159
            }
2160
2161
            if (!$notrigger) {
2162
                // Call trigger
2163
                $result = $this->call_trigger('USER_CREATE', $user);
2164
                if ($result < 0) {
2165
                    $error++;
2166
                }
2167
                // End call triggers
2168
            }
2169
2170
            if (!$error) {
2171
                $this->db->commit();
2172
                return $this->id;
2173
            } else {
2174
                //$this->error=$interface->error;
2175
                dol_syslog(get_only_class($this) . "::create " . $this->error, LOG_ERR);
2176
                $this->db->rollback();
2177
                return -3;
2178
            }
2179
        } else {
2180
            $this->error = $this->db->lasterror();
2181
            $this->db->rollback();
2182
            return -2;
2183
        }
2184
    }
2185
2186
2187
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2188
2189
    /**
2190
     *    Assign rights by default
2191
     *
2192
     * @return     integer erreur <0, si ok renvoi le nbre de droits par default positions
2193
     */
2194
    public function set_default_rights()
2195
    {
2196
        // phpcs:enable
2197
        global $conf;
2198
2199
        $rd = array();
2200
        $num = 0;
2201
        $sql = "SELECT id FROM " . $this->db->prefix() . "rights_def";
2202
        $sql .= " WHERE bydefault = 1";
2203
        $sql .= " AND entity = " . ((int)$conf->entity);
2204
2205
        $resql = $this->db->query($sql);
2206
        if ($resql) {
2207
            $num = $this->db->num_rows($resql);
2208
            $i = 0;
2209
            while ($i < $num) {
2210
                $row = $this->db->fetch_row($resql);
2211
                $rd[$i] = $row[0];
2212
                $i++;
2213
            }
2214
            $this->db->free($resql);
2215
        }
2216
        $i = 0;
2217
        while ($i < $num) {
2218
            $sql = "DELETE FROM " . $this->db->prefix() . "user_rights WHERE fk_user = $this->id AND fk_id=$rd[$i]";
2219
            $result = $this->db->query($sql);
2220
2221
            $sql = "INSERT INTO " . $this->db->prefix() . "user_rights (fk_user, fk_id) VALUES ($this->id, $rd[$i])";
2222
            $result = $this->db->query($sql);
2223
            if (!$result) {
2224
                return -1;
2225
            }
2226
            $i++;
2227
        }
2228
2229
        return $i;
2230
    }
2231
2232
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2233
2234
    /**
2235
     *      Update a user into database (and also password if this->pass is defined)
2236
     *
2237
     * @param User $user User making update
2238
     * @param int $notrigger 1=do not execute triggers, 0 by default
2239
     * @param int $nosyncmember 0=Synchronize linked member (standard info), 1=Do not synchronize linked member
2240
     * @param int $nosyncmemberpass 0=Synchronize linked member (password), 1=Do not synchronize linked member
2241
     * @param int $nosynccontact 0=Synchronize linked contact, 1=Do not synchronize linked contact
2242
     * @return int                         Return integer <0 if KO, >=0 if OK
2243
     */
2244
    public function update($user, $notrigger = 0, $nosyncmember = 0, $nosyncmemberpass = 0, $nosynccontact = 0)
2245
    {
2246
        global $conf, $langs;
2247
2248
        $nbrowsaffected = 0;
2249
        $error = 0;
2250
2251
        dol_syslog(get_only_class($this) . "::update notrigger=" . $notrigger . ", nosyncmember=" . $nosyncmember . ", nosyncmemberpass=" . $nosyncmemberpass);
2252
2253
        // Clean parameters
2254
        $this->civility_code = trim((string)$this->civility_code);
2255
        $this->lastname = trim((string)$this->lastname);
2256
        $this->firstname = trim((string)$this->firstname);
2257
        $this->ref_employee = trim((string)$this->ref_employee);
2258
        $this->national_registration_number = trim((string)$this->national_registration_number);
2259
        $this->employee = ($this->employee > 0 ? $this->employee : 0);
2260
        $this->login = trim((string)$this->login);
2261
        $this->gender = trim((string)$this->gender);
2262
2263
        $this->pass = trim((string)$this->pass);
2264
        $this->api_key = trim((string)$this->api_key);
2265
        $this->datestartvalidity = empty($this->datestartvalidity) ? '' : $this->datestartvalidity;
2266
        $this->dateendvalidity = empty($this->dateendvalidity) ? '' : $this->dateendvalidity;
2267
2268
        $this->address = trim((string)$this->address);
2269
        $this->zip = trim((string)$this->zip);
2270
        $this->town = trim((string)$this->town);
2271
2272
        $this->state_id = ($this->state_id > 0 ? $this->state_id : 0);
2273
        $this->country_id = ($this->country_id > 0 ? $this->country_id : 0);
2274
        $this->office_phone = trim((string)$this->office_phone);
2275
        $this->office_fax = trim((string)$this->office_fax);
2276
        $this->user_mobile = trim((string)$this->user_mobile);
2277
        $this->personal_mobile = trim((string)$this->personal_mobile);
2278
        $this->email = trim((string)$this->email);
2279
        $this->personal_email = trim((string)$this->personal_email);
2280
2281
        $this->job = trim((string)$this->job);
2282
        $this->signature = trim((string)$this->signature);
2283
        $this->note_public = trim((string)$this->note_public);
2284
        $this->note_private = trim((string)$this->note_private);
2285
        $this->openid = trim((string)$this->openid);
2286
        $this->admin = ($this->admin > 0 ? $this->admin : 0);
2287
2288
        $this->accountancy_code = trim((string)$this->accountancy_code);
2289
        $this->color = trim((string)$this->color);
2290
        $this->dateemployment = empty($this->dateemployment) ? '' : $this->dateemployment;
2291
        $this->dateemploymentend = empty($this->dateemploymentend) ? '' : $this->dateemploymentend;
2292
2293
        $this->birth = empty($this->birth) ? '' : $this->birth;
2294
        $this->fk_warehouse = (int)$this->fk_warehouse;
2295
        $this->fk_establishment = (int)$this->fk_establishment;
2296
2297
        $this->setUpperOrLowerCase();
2298
2299
        // Check parameters
2300
        $badCharUnauthorizedIntoLoginName = getDolGlobalString('MAIN_LOGIN_BADCHARUNAUTHORIZED', ',@<>"\'');
2301
2302
        if (getDolGlobalString('USER_MAIL_REQUIRED') && !isValidEmail($this->email)) {
2303
            $langs->load("errors");
2304
            $this->error = $langs->trans("ErrorBadEMail", $this->email);
2305
            return -1;
2306
        }
2307
        if (empty($this->login)) {
2308
            $langs->load("errors");
2309
            $this->error = $langs->trans("ErrorFieldRequired", 'Login');
2310
            return -1;
2311
        } elseif (preg_match('/[' . preg_quote($badCharUnauthorizedIntoLoginName, '/') . ']/', $this->login)) {
2312
            $langs->load("errors");
2313
            $this->error = $langs->trans("ErrorBadCharIntoLoginName", $langs->transnoentitiesnoconv("Login"));
2314
            return -1;
2315
        }
2316
2317
        $this->db->begin();
2318
2319
        // Check if login already exists in same entity or into entity 0.
2320
        if (is_object($this->oldcopy) && !$this->oldcopy->isEmpty() && $this->oldcopy->login != $this->login) {
2321
            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $this->db->prefix() . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND login = '" . $this->db->escape($this->login) . "'";
2322
            $resqltochecklogin = $this->db->query($sqltochecklogin);
2323
            if ($resqltochecklogin) {
2324
                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
2325
                if ($objtochecklogin && $objtochecklogin->nb > 0) {
2326
                    $langs->load("errors");
2327
                    $this->error = $langs->trans("ErrorLoginAlreadyExists", $this->login);
2328
                    dol_syslog(get_only_class($this) . "::create " . $this->error, LOG_DEBUG);
2329
                    $this->db->rollback();
2330
                    return -1;
2331
                }
2332
            }
2333
        }
2334
        if (is_object($this->oldcopy) && !$this->oldcopy->isEmpty() && !empty($this->email) && $this->oldcopy->email != $this->email) {
2335
            $sqltochecklogin = "SELECT COUNT(*) as nb FROM " . $this->db->prefix() . "user WHERE entity IN (" . $this->db->sanitize((int)$this->entity) . ", 0) AND email = '" . $this->db->escape($this->email) . "'";
2336
            $resqltochecklogin = $this->db->query($sqltochecklogin);
2337
            if ($resqltochecklogin) {
2338
                $objtochecklogin = $this->db->fetch_object($resqltochecklogin);
2339
                if ($objtochecklogin && $objtochecklogin->nb > 0) {
2340
                    $langs->load("errors");
2341
                    $this->error = $langs->trans("ErrorEmailAlreadyExists", $this->email);
2342
                    dol_syslog(get_only_class($this) . "::create " . $this->error, LOG_DEBUG);
2343
                    $this->db->rollback();
2344
                    return -1;
2345
                }
2346
            }
2347
        }
2348
2349
        // Update data
2350
        $sql = "UPDATE " . $this->db->prefix() . "user SET";
2351
        $sql .= " civility = '" . $this->db->escape($this->civility_code) . "'";
2352
        $sql .= ", lastname = '" . $this->db->escape($this->lastname) . "'";
2353
        $sql .= ", firstname = '" . $this->db->escape($this->firstname) . "'";
2354
        $sql .= ", ref_employee = '" . $this->db->escape($this->ref_employee) . "'";
2355
        $sql .= ", national_registration_number = '" . $this->db->escape($this->national_registration_number) . "'";
2356
        $sql .= ", employee = " . (int)$this->employee;
2357
        $sql .= ", login = '" . $this->db->escape($this->login) . "'";
2358
        $sql .= ", api_key = " . ($this->api_key ? "'" . $this->db->escape(dolEncrypt($this->api_key, '', '', 'dolibarr')) . "'" : "null");
2359
        $sql .= ", gender = " . ($this->gender != -1 ? "'" . $this->db->escape($this->gender) . "'" : "null"); // 'man' or 'woman' or 'other'
2360
        $sql .= ", birth=" . (strval($this->birth) != '' ? "'" . $this->db->idate($this->birth, 'tzserver') . "'" : 'null');
2361
        if (!empty($user->admin)) {
2362
            $sql .= ", admin = " . (int)$this->admin; // admin flag can be set/unset only by an admin user
2363
        }
2364
        $sql .= ", address = '" . $this->db->escape($this->address) . "'";
2365
        $sql .= ", zip = '" . $this->db->escape($this->zip) . "'";
2366
        $sql .= ", town = '" . $this->db->escape($this->town) . "'";
2367
        $sql .= ", fk_state = " . ((!empty($this->state_id) && $this->state_id > 0) ? "'" . $this->db->escape($this->state_id) . "'" : "null");
2368
        $sql .= ", fk_country = " . ((!empty($this->country_id) && $this->country_id > 0) ? "'" . $this->db->escape($this->country_id) . "'" : "null");
2369
        $sql .= ", office_phone = '" . $this->db->escape($this->office_phone) . "'";
2370
        $sql .= ", office_fax = '" . $this->db->escape($this->office_fax) . "'";
2371
        $sql .= ", user_mobile = '" . $this->db->escape($this->user_mobile) . "'";
2372
        $sql .= ", personal_mobile = '" . $this->db->escape($this->personal_mobile) . "'";
2373
        $sql .= ", email = '" . $this->db->escape($this->email) . "'";
2374
        $sql .= ", personal_email = '" . $this->db->escape($this->personal_email) . "'";
2375
        $sql .= ", socialnetworks = '" . $this->db->escape(json_encode($this->socialnetworks)) . "'";
2376
        $sql .= ", job = '" . $this->db->escape($this->job) . "'";
2377
        $sql .= ", signature = '" . $this->db->escape($this->signature) . "'";
2378
        $sql .= ", accountancy_code = '" . $this->db->escape($this->accountancy_code) . "'";
2379
        $sql .= ", color = '" . $this->db->escape($this->color) . "'";
2380
        $sql .= ", dateemployment=" . (strval($this->dateemployment) != '' ? "'" . $this->db->idate($this->dateemployment) . "'" : 'null');
2381
        $sql .= ", dateemploymentend=" . (strval($this->dateemploymentend) != '' ? "'" . $this->db->idate($this->dateemploymentend) . "'" : 'null');
2382
        $sql .= ", datestartvalidity=" . (strval($this->datestartvalidity) != '' ? "'" . $this->db->idate($this->datestartvalidity) . "'" : 'null');
2383
        $sql .= ", dateendvalidity=" . (strval($this->dateendvalidity) != '' ? "'" . $this->db->idate($this->dateendvalidity) . "'" : 'null');
2384
        $sql .= ", note_private = '" . $this->db->escape($this->note_private) . "'";
2385
        $sql .= ", note_public = '" . $this->db->escape($this->note_public) . "'";
2386
        $sql .= ", photo = " . ($this->photo ? "'" . $this->db->escape($this->photo) . "'" : "null");
2387
        $sql .= ", openid = " . ($this->openid ? "'" . $this->db->escape($this->openid) . "'" : "null");
2388
        $sql .= ", fk_user = " . ($this->fk_user > 0 ? "'" . $this->db->escape($this->fk_user) . "'" : "null");
2389
        $sql .= ", fk_user_expense_validator = " . ($this->fk_user_expense_validator > 0 ? "'" . $this->db->escape($this->fk_user_expense_validator) . "'" : "null");
2390
        $sql .= ", fk_user_holiday_validator = " . ($this->fk_user_holiday_validator > 0 ? "'" . $this->db->escape($this->fk_user_holiday_validator) . "'" : "null");
2391
        if (isset($this->thm) || $this->thm != '') {
2392
            $sql .= ", thm= " . ($this->thm != '' ? "'" . $this->db->escape($this->thm) . "'" : "null");
2393
        }
2394
        if (isset($this->tjm) || $this->tjm != '') {
2395
            $sql .= ", tjm= " . ($this->tjm != '' ? "'" . $this->db->escape($this->tjm) . "'" : "null");
2396
        }
2397
        if (isset($this->salary) || $this->salary != '') {
2398
            $sql .= ", salary= " . ($this->salary != '' ? "'" . $this->db->escape($this->salary) . "'" : "null");
2399
        }
2400
        if (isset($this->salaryextra) || $this->salaryextra != '') {
2401
            $sql .= ", salaryextra= " . ($this->salaryextra != '' ? "'" . $this->db->escape($this->salaryextra) . "'" : "null");
2402
        }
2403
        $sql .= ", weeklyhours= " . ($this->weeklyhours != '' ? "'" . $this->db->escape($this->weeklyhours) . "'" : "null");
2404
        if (!empty($user->admin) && empty($user->entity) && $user->id != $this->id) {
2405
            $sql .= ", entity = " . ((int)$this->entity); // entity flag can be set/unset only by an another superadmin user
2406
        }
2407
        $sql .= ", default_range = " . ($this->default_range > 0 ? $this->default_range : 'null');
2408
        $sql .= ", default_c_exp_tax_cat = " . ($this->default_c_exp_tax_cat > 0 ? $this->default_c_exp_tax_cat : 'null');
2409
        $sql .= ", fk_warehouse = " . ($this->fk_warehouse > 0 ? $this->fk_warehouse : "null");
2410
        $sql .= ", fk_establishment = " . ($this->fk_establishment > 0 ? $this->fk_establishment : "null");
2411
        $sql .= ", lang = " . ($this->lang ? "'" . $this->db->escape($this->lang) . "'" : "null");
2412
        $sql .= " WHERE rowid = " . ((int)$this->id);
2413
2414
        dol_syslog(get_only_class($this) . "::update", LOG_DEBUG);
2415
        $resql = $this->db->query($sql);
2416
        if ($resql) {
2417
            $nbrowsaffected += $this->db->affected_rows($resql);
2418
2419
            // Update password
2420
            if (!empty($this->pass)) {
2421
                if ($this->pass != $this->pass_indatabase && !dol_verifyHash($this->pass, $this->pass_indatabase_crypted)) {
2422
                    // If a new value for password is set and different than the one encrypted into database
2423
                    $result = $this->setPassword($user, $this->pass, 0, $notrigger, $nosyncmemberpass, 0, 1);
2424
                    if (is_int($result) && $result < 0) {
2425
                        return -5;
2426
                    }
2427
                }
2428
            }
2429
2430
            // If user is linked to a member, remove old link to this member
2431
            if ($this->fk_member > 0) {
2432
                dol_syslog(get_only_class($this) . "::update remove link with member. We will recreate it later", LOG_DEBUG);
2433
                $sql = "UPDATE " . $this->db->prefix() . "user SET fk_member = NULL where fk_member = " . ((int)$this->fk_member);
2434
                $resql = $this->db->query($sql);
2435
                if (!$resql) {
2436
                    $this->error = $this->db->error();
2437
                    $this->db->rollback();
2438
                    return -5;
2439
                }
2440
            }
2441
            // Set link to user
2442
            dol_syslog(get_only_class($this) . "::update set link with member", LOG_DEBUG);
2443
            $sql = "UPDATE " . $this->db->prefix() . "user SET fk_member =" . ($this->fk_member > 0 ? ((int)$this->fk_member) : 'null') . " where rowid = " . ((int)$this->id);
2444
            $resql = $this->db->query($sql);
2445
            if (!$resql) {
2446
                $this->error = $this->db->error();
2447
                $this->db->rollback();
2448
                return -5;
2449
            }
2450
2451
            if ($nbrowsaffected) {  // If something has changed in data
2452
                if ($this->fk_member > 0 && !$nosyncmember) {
2453
                    dol_syslog(get_only_class($this) . "::update user is linked with a member. We try to update member too.", LOG_DEBUG);
2454
2455
                    // This user is linked with a member, so we also update member information
2456
                    // if this is an update.
2457
                    $adh = new Adherent($this->db);
2458
                    $result = $adh->fetch($this->fk_member);
2459
2460
                    if ($result > 0) {
2461
                        $adh->civility_code = $this->civility_code;
2462
                        $adh->firstname = $this->firstname;
2463
                        $adh->lastname = $this->lastname;
2464
                        $adh->login = $this->login;
2465
                        $adh->gender = $this->gender;
2466
                        $adh->birth = $this->birth;
2467
2468
                        $adh->pass = $this->pass;
2469
2470
                        $adh->address = $this->address;
2471
                        $adh->town = $this->town;
2472
                        $adh->zip = $this->zip;
2473
                        $adh->state_id = $this->state_id;
2474
                        $adh->country_id = $this->country_id;
2475
2476
                        $adh->email = $this->email;
2477
2478
                        $adh->socialnetworks = $this->socialnetworks;
2479
2480
                        $adh->phone = $this->office_phone;
2481
                        $adh->phone_mobile = $this->user_mobile;
2482
2483
                        $adh->default_lang = $this->lang;
2484
2485
                        $adh->user_id = $this->id;
2486
                        $adh->user_login = $this->login;
2487
2488
                        $result = $adh->update($user, 0, 1, 0);
2489
                        if ($result < 0) {
2490
                            $this->error = $adh->error;
2491
                            $this->errors = $adh->errors;
2492
                            dol_syslog(get_only_class($this) . "::update error after calling adh->update to sync it with user: " . $this->error, LOG_ERR);
2493
                            $error++;
2494
                        }
2495
                    } elseif ($result < 0) {
2496
                        $this->error = $adh->error;
2497
                        $this->errors = $adh->errors;
2498
                        $error++;
2499
                    }
2500
                }
2501
2502
                if ($this->contact_id > 0 && !$nosynccontact) {
2503
                    dol_syslog(get_only_class($this) . "::update user is linked with a contact. We try to update contact too.", LOG_DEBUG);
2504
2505
                    // This user is linked with a contact, so we also update contact information if this is an update.
2506
                    $tmpobj = new Contact($this->db);
2507
                    $result = $tmpobj->fetch($this->contact_id);
2508
2509
                    if ($result >= 0) {
2510
                        $tmpobj->civility_code = $this->civility_code;
2511
                        $tmpobj->firstname = $this->firstname;
2512
                        $tmpobj->lastname = $this->lastname;
2513
                        $tmpobj->login = $this->login;
0 ignored issues
show
Bug Best Practice introduced by
The property login does not exist on Dolibarr\Code\Contact\Classes\Contact. Since you implemented __set, consider adding a @property annotation.
Loading history...
2514
                        $tmpobj->gender = $this->gender;
2515
                        $tmpobj->birth = $this->birth;
0 ignored issues
show
Bug Best Practice introduced by
The property birth does not exist on Dolibarr\Code\Contact\Classes\Contact. Since you implemented __set, consider adding a @property annotation.
Loading history...
2516
2517
                        //$tmpobj->pass=$this->pass;
2518
2519
                        $tmpobj->email = $this->email;
2520
2521
                        $tmpobj->socialnetworks = $this->socialnetworks;
2522
2523
                        $tmpobj->phone_pro = $this->office_phone;
2524
                        $tmpobj->phone_mobile = $this->user_mobile;
2525
                        $tmpobj->fax = $this->office_fax;
2526
2527
                        $tmpobj->default_lang = $this->lang;
2528
2529
                        $tmpobj->address = $this->address;
2530
                        $tmpobj->town = $this->town;
2531
                        $tmpobj->zip = $this->zip;
2532
                        $tmpobj->state_id = $this->state_id;
2533
                        $tmpobj->country_id = $this->country_id;
2534
2535
                        $tmpobj->user_id = $this->id;
2536
                        $tmpobj->user_login = $this->login;
2537
2538
                        $result = $tmpobj->update($tmpobj->id, $user, 0, 'update', 1);
2539
                        if ($result < 0) {
2540
                            $this->error = $tmpobj->error;
2541
                            $this->errors = $tmpobj->errors;
2542
                            dol_syslog(get_only_class($this) . "::update error after calling adh->update to sync it with user: " . $this->error, LOG_ERR);
2543
                            $error++;
2544
                        }
2545
                    } else {
2546
                        $this->error = $tmpobj->error;
2547
                        $this->errors = $tmpobj->errors;
2548
                        $error++;
2549
                    }
2550
                }
2551
            }
2552
2553
            $action = 'update';
2554
2555
            // Actions on extra fields
2556
            if (!$error) {
2557
                $result = $this->insertExtraFields();
2558
                if ($result < 0) {
2559
                    $error++;
2560
                }
2561
            }
2562
2563
            if (!$error && !$notrigger) {
2564
                // Call trigger
2565
                $result = $this->call_trigger('USER_MODIFY', $user);
2566
                if ($result < 0) {
2567
                    $error++;
2568
                }
2569
                // End call triggers
2570
            }
2571
2572
            if (!$error) {
2573
                $this->db->commit();
2574
                return $nbrowsaffected;
2575
            } else {
2576
                dol_syslog(get_only_class($this) . "::update error=" . $this->error, LOG_ERR);
2577
                $this->db->rollback();
2578
                return -1;
2579
            }
2580
        } else {
2581
            $this->error = $this->db->lasterror();
2582
            $this->db->rollback();
2583
            return -2;
2584
        }
2585
    }
2586
2587
2588
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2589
2590
    /**
2591
     *  Change password of a user
2592
     *
2593
     * @param User $user Object user of user requesting the change (not the user for who we change the password). May be unknown.
2594
     * @param string $password New password, in clear text or already encrypted (to generate if not provided)
2595
     * @param int $changelater 0=Default, 1=Save password into pass_temp to change password only after clicking on confirm email
2596
     * @param int $notrigger 1=Does not launch triggers
2597
     * @param int $nosyncmember Do not synchronize linked member
2598
     * @param int $passwordalreadycrypted 0=Value is cleartext password, 1=Value is encrypted value.
2599
     * @param int $flagdelsessionsbefore 1=Save also the current date to ask to invalidate all other session before this date.
2600
     * @return int|string                      If OK return clear password, 0 if no change (warning, you may retrieve 1 instead of 0 even if password was same), < 0 if error
2601
     */
2602
    public function setPassword($user, $password = '', $changelater = 0, $notrigger = 0, $nosyncmember = 0, $passwordalreadycrypted = 0, $flagdelsessionsbefore = 1)
2603
    {
2604
        global $conf, $langs;
2605
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/security2.lib.php';
2606
2607
        $error = 0;
2608
2609
        dol_syslog(get_only_class($this) . "::setPassword user=" . $user->id . " password=" . preg_replace('/./i', '*', $password) . " changelater=" . $changelater . " notrigger=" . $notrigger . " nosyncmember=" . $nosyncmember, LOG_DEBUG);
2610
2611
        // If new password not provided, we generate one
2612
        if (!$password) {
2613
            $password = getRandomPassword(false);
2614
        }
2615
2616
        // Check and encrypt the password
2617
        if (empty($passwordalreadycrypted)) {
2618
            if (getDolGlobalString('USER_PASSWORD_GENERATED')) {
2619
                // Add a check on rules for password syntax using the setup of the password generator
2620
                $modGeneratePassClass = 'modGeneratePass' . ucfirst($conf->global->USER_PASSWORD_GENERATED);
2621
2622
                include_once DOL_DOCUMENT_ROOT . '/core/modules/security/generate/' . $modGeneratePassClass . '.class.php';
2623
                if (class_exists($modGeneratePassClass)) {
2624
                    $modGeneratePass = new $modGeneratePassClass($this->db, $conf, $langs, $user);
2625
2626
                    // To check an input user password, we disable the cleaning on ambiguous characters (this is used only for auto-generated password)
2627
                    $modGeneratePass->WithoutAmbi = 0;
2628
2629
                    // Call to validatePassword($password) to check pass match rules
2630
                    $testpassword = $modGeneratePass->validatePassword($password);
2631
                    if (!$testpassword) {
2632
                        $this->error = $modGeneratePass->error;
2633
                        return -1;
2634
                    }
2635
                }
2636
            }
2637
2638
2639
            // Now, we encrypt the new password
2640
            $password_crypted = dol_hash($password);
2641
        }
2642
2643
        // Update password
2644
        if (!$changelater) {
2645
            if (!is_object($this->oldcopy)) {
2646
                $this->oldcopy = clone $this;
2647
            }
2648
2649
            $this->db->begin();
2650
2651
            $sql = "UPDATE " . $this->db->prefix() . "user";
2652
            $sql .= " SET pass_crypted = '" . $this->db->escape($password_crypted) . "',";
2653
            $sql .= " pass_temp = null";
2654
            if (!empty($flagdelsessionsbefore)) {
2655
                $sql .= ", flagdelsessionsbefore = '" . $this->db->idate(dol_now() - 5, 'gmt') . "'";
2656
            }
2657
            if (getDolGlobalString('DATABASE_PWD_ENCRYPTED')) {
2658
                $sql .= ", pass = null";
2659
            } else {
2660
                $sql .= ", pass = '" . $this->db->escape($password) . "'";
2661
            }
2662
            $sql .= " WHERE rowid = " . ((int)$this->id);
2663
2664
            dol_syslog(get_only_class($this) . "::setPassword", LOG_DEBUG);
2665
            $result = $this->db->query($sql);
2666
            if ($result) {
2667
                if ($this->db->affected_rows($result)) {
2668
                    $this->pass = $password;
2669
                    $this->pass_indatabase = $password;
2670
                    $this->pass_indatabase_crypted = $password_crypted;
2671
2672
                    if ($this->fk_member && !$nosyncmember) {
2673
                        // This user is linked with a member, so we also update members information
2674
                        // if this is an update.
2675
                        $adh = new Adherent($this->db);
2676
                        $result = $adh->fetch($this->fk_member);
2677
2678
                        if ($result >= 0) {
2679
                            $result = $adh->setPassword($user, $this->pass, (!getDolGlobalString('DATABASE_PWD_ENCRYPTED') ? 0 : 1), 1); // The encryption is not managed in the 'adherent' module
2680
                            if (is_int($result) && $result < 0) {
2681
                                $this->error = $adh->error;
2682
                                dol_syslog(get_only_class($this) . "::setPassword " . $this->error, LOG_ERR);
2683
                                $error++;
2684
                            }
2685
                        } else {
2686
                            $this->error = $adh->error;
2687
                            $error++;
2688
                        }
2689
                    }
2690
2691
                    dol_syslog(get_only_class($this) . "::setPassword notrigger=" . $notrigger . " error=" . $error, LOG_DEBUG);
2692
2693
                    if (!$error && !$notrigger) {
2694
                        // Call trigger
2695
                        $result = $this->call_trigger('USER_NEW_PASSWORD', $user);
2696
                        if ($result < 0) {
2697
                            $error++;
2698
                            $this->db->rollback();
2699
                            return -1;
2700
                        }
2701
                        // End call triggers
2702
                    }
2703
2704
                    $this->db->commit();
2705
                    return $this->pass;
2706
                } else {
2707
                    $this->db->rollback();
2708
                    return 0;
2709
                }
2710
            } else {
2711
                $this->db->rollback();
2712
                dol_print_error($this->db);
2713
                return -1;
2714
            }
2715
        } else {
2716
            // We store password in password temporary field.
2717
            // After receiving confirmation link, we will erase and store it in pass_crypted
2718
            $sql = "UPDATE " . $this->db->prefix() . "user";
2719
            $sql .= " SET pass_temp = '" . $this->db->escape($password) . "'";
2720
            $sql .= " WHERE rowid = " . ((int)$this->id);
2721
2722
            dol_syslog(get_only_class($this) . "::setPassword", LOG_DEBUG); // No log
2723
            $result = $this->db->query($sql);
2724
            if ($result) {
2725
                return $password;
2726
            } else {
2727
                dol_print_error($this->db);
2728
                return -3;
2729
            }
2730
        }
2731
    }
2732
2733
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2734
2735
    /**
2736
     *      Renvoie la derniere erreur fonctionnelle de manipulation de l'objet
2737
     *
2738
     * @return    string      chaine erreur
2739
     */
2740
    public function error()
2741
    {
2742
        return $this->error;
2743
    }
2744
2745
    /**
2746
     *  Create a user into database from a member object.
2747
     *  If $member->fk_soc is set, it will be an external user.
2748
     *
2749
     * @param Adherent $member Object member source
2750
     * @param string $login Login to force
2751
     * @return int                         Return integer <0 if KO, if OK, return id of created account
2752
     */
2753
    public function create_from_member($member, $login = '')
2754
    {
2755
        // phpcs:enable
2756
        global $user;
2757
2758
        // Set properties on new user
2759
        $this->admin = 0;
2760
        $this->civility_code = $member->civility_code;
2761
        $this->lastname = $member->lastname;
2762
        $this->firstname = $member->firstname;
2763
        $this->gender = $member->gender;
2764
        $this->email = $member->email;
2765
        $this->fk_member = $member->id;
2766
        $this->address = $member->address;
2767
        $this->zip = $member->zip;
2768
        $this->town = $member->town;
2769
        $this->setUpperOrLowerCase();
2770
        $this->state_id = $member->state_id;
2771
        $this->country_id = $member->country_id;
2772
        $this->socialnetworks = $member->socialnetworks;
2773
2774
        $this->pass = $member->pass;
2775
        $this->pass_crypted = $member->pass_indatabase_crypted;
2776
2777
        if (empty($login)) {
2778
            include_once DOL_DOCUMENT_ROOT . '/core/lib/functions2.lib.php';
2779
            $login = dol_buildlogin($member->lastname, $member->firstname);
2780
        }
2781
        $this->login = $login;
2782
2783
        $this->db->begin();
2784
2785
        // Create and set $this->id
2786
        $result = $this->create($user);
2787
        if ($result > 0) {
2788
            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
2789
                $newpass = $this->setPassword($user, $this->pass);
2790
                if (is_int($newpass) && $newpass < 0) {
2791
                    $result = -2;
2792
                }
2793
            } 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.
2794
                $sql = "UPDATE " . $this->db->prefix() . "user";
2795
                $sql .= " SET pass_crypted = '" . $this->db->escape($this->pass_crypted) . "'";
2796
                $sql .= " WHERE rowid=" . ((int)$this->id);
2797
2798
                $resql = $this->db->query($sql);
2799
                if (!$resql) {
2800
                    $result = -1;
2801
                }
2802
            }
2803
2804
            if ($result > 0 && $member->socid) {    // If member is linked to a thirdparty
2805
                $sql = "UPDATE " . $this->db->prefix() . "user";
2806
                $sql .= " SET fk_soc=" . ((int)$member->socid);
2807
                $sql .= " WHERE rowid=" . ((int)$this->id);
2808
2809
                dol_syslog(get_only_class($this) . "::create_from_member", LOG_DEBUG);
2810
                $resql = $this->db->query($sql);
2811
                if ($resql) {
2812
                    $this->db->commit();
2813
                    return $this->id;
2814
                } else {
2815
                    $this->error = $this->db->lasterror();
2816
2817
                    $this->db->rollback();
2818
                    return -1;
2819
                }
2820
            }
2821
        }
2822
2823
        if ($result > 0) {
2824
            $this->db->commit();
2825
            return $this->id;
2826
        } else {
2827
            // $this->error deja positionne
2828
            $this->db->rollback();
2829
            return -2;
2830
        }
2831
    }
2832
2833
    /**
2834
     *  Update the user's last login date in the database.
2835
     *  Function called when a new connection is made by the user
2836
     *
2837
     * @return int     Return integer <0 si echec, >=0 si ok
2838
     */
2839
    public function update_last_login_date()
2840
    {
2841
        // phpcs:enable
2842
        $now = dol_now();
2843
2844
        $userremoteip = getUserRemoteIP();
2845
2846
        $sql = "UPDATE " . $this->db->prefix() . "user SET";
2847
        $sql .= " datepreviouslogin = datelastlogin,";
2848
        $sql .= " ippreviouslogin = iplastlogin,";
2849
        $sql .= " datelastlogin = '" . $this->db->idate($now) . "',";
2850
        $sql .= " iplastlogin = '" . $this->db->escape($userremoteip) . "',";
2851
        $sql .= " tms = tms"; // The last update date must change because the last login date is updated
2852
        $sql .= " WHERE rowid = " . ((int)$this->id);
2853
2854
        dol_syslog(get_only_class($this) . "::update_last_login_date user->id=" . $this->id . " " . $sql, LOG_DEBUG);
2855
        $resql = $this->db->query($sql);
2856
        if ($resql) {
2857
            $this->datepreviouslogin = $this->datelastlogin;
2858
            $this->datelastlogin = $now;
2859
            $this->ippreviouslogin = $this->iplastlogin;
2860
            $this->iplastlogin = $userremoteip;
2861
            return 1;
2862
        } else {
2863
            $this->error = $this->db->lasterror() . ' sql=' . $sql;
2864
            return -1;
2865
        }
2866
    }
2867
2868
    /**
2869
     *  Send a new password (or instructions to reset it) by email
2870
     *
2871
     * @param User $user Object user that send the email (not the user we send to) @todo object $user is not used !
2872
     * @param string $password New password
2873
     * @param int $changelater 0=Send clear passwod into email, 1=Change password only after clicking on confirm email. @todo Add method 2 = Send link to reset password
2874
     * @return int                     Return integer < 0 si erreur, > 0 si ok
2875
     */
2876
    public function send_password($user, $password = '', $changelater = 0)
2877
    {
2878
        // phpcs:enable
2879
        global $conf, $langs, $mysoc;
2880
        global $dolibarr_main_url_root;
2881
2882
2883
        $msgishtml = 0;
2884
2885
        // Define $msg
2886
        $mesg = '';
2887
2888
        $outputlangs = new Translate("", $conf);
2889
2890
        if (isset($this->conf->MAIN_LANG_DEFAULT) && $this->conf->MAIN_LANG_DEFAULT != 'auto') {  // If user has defined its own language (rare because in most cases, auto is used)
2891
            $outputlangs->getDefaultLang($this->conf->MAIN_LANG_DEFAULT);
2892
        }
2893
2894
        if ($this->conf->MAIN_LANG_DEFAULT) {
2895
            $outputlangs->setDefaultLang($this->conf->MAIN_LANG_DEFAULT);
2896
        } else {    // If user has not defined its own language, we used current language
2897
            $outputlangs = $langs;
2898
        }
2899
2900
        // Load translation files required by the page
2901
        $outputlangs->loadLangs(array("main", "errors", "users", "other"));
2902
2903
        $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', constant('DOL_APPLICATION_TITLE'));
2904
2905
        $subject = '[' . $appli . '] ' . $outputlangs->transnoentitiesnoconv("SubjectNewPassword", $appli);
2906
2907
        // Define $urlwithroot
2908
        $urlwithouturlroot = preg_replace('/' . preg_quote(constant('BASE_URL'), '/') . '$/i', '', trim($dolibarr_main_url_root));
2909
        $urlwithroot = $urlwithouturlroot . constant('BASE_URL'); // This is to use external domain name found into config file
2910
2911
        if (!$changelater) {
2912
            $url = $urlwithroot . '/';
2913
            if (getDolGlobalString('URL_REDIRECTION_AFTER_CHANGEPASSWORD')) {
2914
                $url = getDolGlobalString('URL_REDIRECTION_AFTER_CHANGEPASSWORD');
2915
            }
2916
2917
            dol_syslog(get_only_class($this) . "::send_password changelater is off, url=" . $url);
2918
2919
            $mesg .= $outputlangs->transnoentitiesnoconv("RequestToResetPasswordReceived") . ".\n";
2920
            $mesg .= $outputlangs->transnoentitiesnoconv("NewKeyIs") . " :\n\n";
2921
            $mesg .= $outputlangs->transnoentitiesnoconv("Login") . " = " . $this->login . "\n";
2922
            $mesg .= $outputlangs->transnoentitiesnoconv("Password") . " = " . $password . "\n\n";
2923
            $mesg .= "\n";
2924
2925
            $mesg .= $outputlangs->transnoentitiesnoconv("ClickHereToGoTo", $appli) . ': ' . $url . "\n\n";
2926
            $mesg .= "--\n";
2927
            $mesg .= $user->getFullName($outputlangs); // Username that send the email (not the user for who we want to reset password)
2928
        } else {
2929
            //print $password.'-'.$this->id.'-'.$conf->file->instance_unique_id;
2930
            $url = $urlwithroot . '/user/passwordforgotten.php?action=validatenewpassword';
2931
            $url .= '&username=' . urlencode($this->login) . "&passworduidhash=" . urlencode(dol_hash($password . '-' . $this->id . '-' . $conf->file->instance_unique_id));
2932
            if (isModEnabled('multicompany')) {
2933
                $url .= '&entity=' . (!empty($this->entity) ? $this->entity : 1);
2934
            }
2935
2936
            dol_syslog(get_only_class($this) . "::send_password changelater is on, url=" . $url);
2937
2938
            $msgishtml = 1;
2939
2940
            $mesg .= $outputlangs->transnoentitiesnoconv("RequestToResetPasswordReceived") . "<br>\n";
2941
            $mesg .= $outputlangs->transnoentitiesnoconv("NewKeyWillBe") . " :<br>\n<br>\n";
2942
            $mesg .= '<strong>' . $outputlangs->transnoentitiesnoconv("Login") . "</strong> = " . $this->login . "<br>\n";
2943
            $mesg .= '<strong>' . $outputlangs->transnoentitiesnoconv("Password") . "</strong> = " . $password . "<br>\n<br>\n";
2944
            $mesg .= "<br>\n";
2945
            $mesg .= $outputlangs->transnoentitiesnoconv("YouMustClickToChange") . " :<br>\n";
2946
            $mesg .= '<a href="' . $url . '" rel="noopener">' . $outputlangs->transnoentitiesnoconv("ConfirmPasswordChange") . '</a>' . "<br>\n<br>\n";
2947
            $mesg .= $outputlangs->transnoentitiesnoconv("ForgetIfNothing") . "<br>\n<br>\n";
2948
        }
2949
2950
        $trackid = 'use' . $this->id;
2951
        $sendcontext = 'password';
2952
2953
        $mailfile = new CMailFile(
2954
            $subject,
2955
            $this->email,
2956
            $conf->global->MAIN_MAIL_EMAIL_FROM,
2957
            $mesg,
2958
            array(),
2959
            array(),
2960
            array(),
2961
            '',
2962
            '',
2963
            0,
2964
            $msgishtml,
2965
            '',
2966
            '',
2967
            $trackid,
2968
            '',
2969
            $sendcontext
2970
        );
2971
2972
        if ($mailfile->sendfile()) {
2973
            return 1;
2974
        } else {
2975
            $langs->trans("errors");
2976
            $this->error = $langs->trans("ErrorFailedToSendPassword") . ' ' . $mailfile->error;
2977
            return -1;
2978
        }
2979
    }
2980
2981
    /**
2982
     *  Read clicktodial information for user
2983
     *
2984
     * @return int Return integer <0 if KO, >0 if OK
2985
     */
2986
    public function fetch_clicktodial()
2987
    {
2988
        // phpcs:enable
2989
        $sql = "SELECT url, login, pass, poste ";
2990
        $sql .= " FROM " . $this->db->prefix() . "user_clicktodial as u";
2991
        $sql .= " WHERE u.fk_user = " . ((int)$this->id);
2992
2993
        $resql = $this->db->query($sql);
2994
        if ($resql) {
2995
            if ($this->db->num_rows($resql)) {
2996
                $obj = $this->db->fetch_object($resql);
2997
2998
                $this->clicktodial_url = $obj->url;
2999
                $this->clicktodial_login = $obj->login;
3000
                $this->clicktodial_password = $obj->pass;
3001
                $this->clicktodial_poste = $obj->poste;
3002
            }
3003
3004
            $this->clicktodial_loaded = 1; // Data loaded (found or not)
3005
3006
            $this->db->free($resql);
3007
            return 1;
3008
        } else {
3009
            $this->error = $this->db->error();
3010
            return -1;
3011
        }
3012
    }
3013
3014
    /**
3015
     *  Update clicktodial info
3016
     *
3017
     * @return int  Return integer <0 if KO, >0 if OK
3018
     */
3019
    public function update_clicktodial()
3020
    {
3021
        // phpcs:enable
3022
        $this->db->begin();
3023
3024
        $sql = "DELETE FROM " . $this->db->prefix() . "user_clicktodial";
3025
        $sql .= " WHERE fk_user = " . ((int)$this->id);
3026
3027
        dol_syslog(get_only_class($this) . '::update_clicktodial', LOG_DEBUG);
3028
        $result = $this->db->query($sql);
3029
3030
        $sql = "INSERT INTO " . $this->db->prefix() . "user_clicktodial";
3031
        $sql .= " (fk_user,url,login,pass,poste)";
3032
        $sql .= " VALUES (" . $this->id;
3033
        $sql .= ", '" . $this->db->escape($this->clicktodial_url) . "'";
3034
        $sql .= ", '" . $this->db->escape($this->clicktodial_login) . "'";
3035
        $sql .= ", '" . $this->db->escape($this->clicktodial_password) . "'";
3036
        $sql .= ", '" . $this->db->escape($this->clicktodial_poste) . "')";
3037
3038
        dol_syslog(get_only_class($this) . '::update_clicktodial', LOG_DEBUG);
3039
        $result = $this->db->query($sql);
3040
        if ($result) {
3041
            $this->db->commit();
3042
            return 1;
3043
        } else {
3044
            $this->db->rollback();
3045
            $this->error = $this->db->lasterror();
3046
            return -1;
3047
        }
3048
    }
3049
3050
    /**
3051
     *  Add user into a group
3052
     *
3053
     * @param int $group Id of group
3054
     * @param int $entity Entity
3055
     * @param int $notrigger Disable triggers
3056
     * @return int                 Return integer <0 if KO, >0 if OK
3057
     */
3058
    public function SetInGroup($group, $entity, $notrigger = 0)
3059
    {
3060
        // phpcs:enable
3061
        global $conf, $langs, $user;
3062
3063
        $error = 0;
3064
3065
        $this->db->begin();
3066
3067
        $sql = "DELETE FROM " . $this->db->prefix() . "usergroup_user";
3068
        $sql .= " WHERE fk_user  = " . ((int)$this->id);
3069
        $sql .= " AND fk_usergroup = " . ((int)$group);
3070
        $sql .= " AND entity = " . ((int)$entity);
3071
3072
        $result = $this->db->query($sql);
3073
3074
        $sql = "INSERT INTO " . $this->db->prefix() . "usergroup_user (entity, fk_user, fk_usergroup)";
3075
        $sql .= " VALUES (" . ((int)$entity) . "," . ((int)$this->id) . "," . ((int)$group) . ")";
3076
3077
        $result = $this->db->query($sql);
3078
        if ($result) {
3079
            if (!$error && !$notrigger) {
3080
                $this->context = array('audit' => $langs->trans("UserSetInGroup"), 'newgroupid' => $group);
3081
3082
                // Call trigger
3083
                $result = $this->call_trigger('USER_MODIFY', $user);
3084
                if ($result < 0) {
3085
                    $error++;
3086
                }
3087
                // End call triggers
3088
            }
3089
3090
            if (!$error) {
3091
                $this->db->commit();
3092
                return 1;
3093
            } else {
3094
                dol_syslog(get_only_class($this) . "::SetInGroup " . $this->error, LOG_ERR);
3095
                $this->db->rollback();
3096
                return -2;
3097
            }
3098
        } else {
3099
            $this->error = $this->db->lasterror();
3100
            $this->db->rollback();
3101
            return -1;
3102
        }
3103
    }
3104
3105
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3106
3107
    /**
3108
     *  Remove a user from a group
3109
     *
3110
     * @param int $group Id of group
3111
     * @param int $entity Entity
3112
     * @param int $notrigger Disable triggers
3113
     * @return int                  Return integer <0 if KO, >0 if OK
3114
     */
3115
    public function RemoveFromGroup($group, $entity, $notrigger = 0)
3116
    {
3117
        // phpcs:enable
3118
        global $conf, $langs, $user;
3119
3120
        $error = 0;
3121
3122
        $this->db->begin();
3123
3124
        $sql = "DELETE FROM " . $this->db->prefix() . "usergroup_user";
3125
        $sql .= " WHERE fk_user  = " . ((int)$this->id);
3126
        $sql .= " AND fk_usergroup = " . ((int)$group);
3127
        if (empty($entity)) {
3128
            $sql .= " AND entity IN (0, 1)";    // group may be in entity 0 (so $entity=0) and link with user into entity 1.
3129
        } else {
3130
            $sql .= " AND entity = " . ((int)$entity);
3131
        }
3132
3133
        $result = $this->db->query($sql);
3134
        if ($result) {
3135
            if (!$error && !$notrigger) {
3136
                $this->context = array('audit' => $langs->trans("UserRemovedFromGroup"), 'oldgroupid' => $group);
3137
3138
                // Call trigger
3139
                $result = $this->call_trigger('USER_MODIFY', $user);
3140
                if ($result < 0) {
3141
                    $error++;
3142
                }
3143
                // End call triggers
3144
            }
3145
3146
            if (!$error) {
3147
                $this->db->commit();
3148
                return 1;
3149
            } else {
3150
                dol_syslog(get_only_class($this) . "::RemoveFromGroup " . $this->error, LOG_ERR);
3151
                $this->db->rollback();
3152
                return -2;
3153
            }
3154
        } else {
3155
            $this->error = $this->db->lasterror();
3156
            $this->db->rollback();
3157
            return -1;
3158
        }
3159
    }
3160
3161
    /**
3162
     *  Return a link with photo
3163
     *  Use this->id,this->photo
3164
     *
3165
     * @return int     0=Valid, >0 if not valid
3166
     */
3167
    public function isNotIntoValidityDateRange()
3168
    {
3169
        include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
3170
3171
        $now = dol_now();
3172
3173
        //dol_syslog("isNotIntoValidityDateRange ".$this->datestartvalidity);
3174
3175
        // Check date start validity
3176
        if ($this->datestartvalidity && $this->datestartvalidity > dol_get_last_hour($now)) {
3177
            return 1;
3178
        }
3179
        // Check date end validity
3180
        if ($this->dateendvalidity && $this->dateendvalidity < dol_get_first_hour($now)) {
3181
            return 1;
3182
        }
3183
3184
        return 0;
3185
    }
3186
3187
3188
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
3189
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3190
3191
    /**
3192
     *  Return a link with photo
3193
     *  Use this->id,this->photo
3194
     *
3195
     * @param int $width Width of image
3196
     * @param int $height Height of image
3197
     * @param string $cssclass Force a css class
3198
     * @param string $imagesize 'mini', 'small' or '' (original)
3199
     * @return string                  String with URL link
3200
     * @see getImagePublicURLOfObject()
3201
     */
3202
    public function getPhotoUrl($width, $height, $cssclass = '', $imagesize = '')
3203
    {
3204
        $result = '<a href="' . constant('BASE_URL') . '/user/card.php?id=' . $this->id . '">';
3205
        $result .= Form::showphoto('userphoto', $this, $width, $height, 0, $cssclass, $imagesize);
3206
        $result .= '</a>';
3207
3208
        return $result;
3209
    }
3210
3211
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
3212
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3213
3214
    /**
3215
     *  Return clickable link of login (optionally with picto)
3216
     *
3217
     * @param int $withpictoimg Include picto into link (1=picto, -1=photo)
3218
     * @param string $option On what the link point to ('leave', 'accountancy', 'nolink', )
3219
     * @param integer $notooltip 1=Disable tooltip on picto and name
3220
     * @param string $morecss Add more css on link
3221
     * @return string                      String with URL
3222
     */
3223
    public function getLoginUrl($withpictoimg = 0, $option = '', $notooltip = 0, $morecss = '')
3224
    {
3225
        global $langs, $user;
3226
3227
        $result = '';
3228
3229
        $linkstart = '<a href="' . constant('BASE_URL') . '/user/card.php?id=' . $this->id . '">';
3230
        $linkend = '</a>';
3231
3232
        //Check user's rights to see an other user
3233
        if ((!$user->hasRight('user', 'user', 'lire') && $this->id != $user->id)) {
3234
            $option = 'nolink';
3235
        }
3236
3237
        if ($option == 'xxx') {
3238
            $linkstart = '<a href="' . constant('BASE_URL') . '/user/card.php?id=' . $this->id . '">';
3239
            $linkend = '</a>';
3240
        }
3241
3242
        if ($option == 'nolink') {
3243
            $linkstart = '';
3244
            $linkend = '';
3245
        }
3246
3247
        $result .= $linkstart;
3248
        if ($withpictoimg) {
3249
            $paddafterimage = '';
3250
            if (abs($withpictoimg) == 1) {
3251
                $paddafterimage = 'style="margin-' . ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right') . ': 3px;"';
3252
            }
3253
            // Only picto
3254
            if ($withpictoimg > 0) {
3255
                $picto = '<!-- picto user --><span class="nopadding userimg' . ($morecss ? ' ' . $morecss : '') . '">' . img_object('', 'user', $paddafterimage . ' ' . ($notooltip ? '' : 'class="paddingright classfortooltip"'), 0, 0, $notooltip ? 0 : 1) . '</span>';
3256
            } else {
3257
                // Picto must be a photo
3258
                $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>';
3259
            }
3260
            $result .= $picto;
3261
        }
3262
        $result .= $this->login;
3263
        $result .= $linkend;
3264
3265
        return $result;
3266
    }
3267
3268
    /**
3269
     *  Return clicable link of object (optionally with picto)
3270
     *
3271
     * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
3272
     * @param array $arraydata Array of data
3273
     * @return     string                              HTML Code for Kanban thumb.
3274
     */
3275
    public function getKanbanView($option = '', $arraydata = null)
3276
    {
3277
        global $langs;
3278
3279
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3280
3281
        $return = '<div class="box-flex-item box-flex-grow-zero">';
3282
        $return .= '<div class="info-box info-box-sm">';
3283
        $return .= '<span class="info-box-icon bg-infobox-action">';
3284
3285
        $label = '';
3286
        if (!empty($this->photo)) {
3287
            //$label .= '<div class="photointooltip floatright">';
3288
            $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
3289
            //$label .= '</div>';
3290
            //$label .= '<div style="clear: both;"></div>';
3291
            $return .= $label;
3292
        } else {
3293
            $return .= img_picto('', $this->picto);
3294
        }
3295
3296
        //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
3297
        $return .= '</span>';
3298
        $return .= '<div class="info-box-content">';
3299
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl(0, '', 0, 0, 24, 0, '', 'valignmiddle') : $this->ref);
3300
        if (isModEnabled('multicompany') && $this->admin && !$this->entity) {
3301
            $return .= img_picto($langs->trans("SuperAdministratorDesc"), 'redstar', 'class="valignmiddle paddingright paddingleft"');
3302
        } elseif ($this->admin) {
3303
            $return .= img_picto($langs->trans("AdministratorDesc"), 'star', 'class="valignmiddle paddingright paddingleft"');
3304
        }
3305
        $return .= '</span>';
3306
        if ($selected >= 0) {
3307
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
3308
        }
3309
        if (property_exists($this, 'label')) {
3310
            $return .= '<br><span class="info-box-label opacitymedium">' . $this->label . '</span>';
3311
        }
3312
        if ($this->email) {
3313
            $return .= '<br><span class="info-box-label opacitymedium small">' . img_picto('', 'email') . ' ' . $this->email . '</span>';
3314
        }
3315
        if (method_exists($this, 'getLibStatut')) {
3316
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
3317
        }
3318
        $return .= '</div>';
3319
        $return .= '</div>';
3320
        $return .= '</div>';
3321
3322
        return $return;
3323
    }
3324
3325
    /**
3326
     *  Retourne chaine DN complete dans l'annuaire LDAP pour l'objet
3327
     *
3328
     * @param array $info Info array loaded by _load_ldap_info
3329
     * @param int $mode 0=Return full DN (uid=qqq,ou=xxx,dc=aaa,dc=bbb)
3330
     *                              1=Return parent (ou=xxx,dc=aaa,dc=bbb)
3331
     *                              2=Return key only (RDN) (uid=qqq)
3332
     * @return string              DN
3333
     */
3334
    public function _load_ldap_dn($info, $mode = 0)
3335
    {
3336
        // phpcs:enable
3337
        global $conf;
3338
        $dn = '';
3339
        if ($mode == 0) {
3340
            $dn = getDolGlobalString('LDAP_KEY_USERS') . "=" . $info[getDolGlobalString('LDAP_KEY_USERS')] . "," . getDolGlobalString('LDAP_USER_DN');
3341
        } elseif ($mode == 1) {
3342
            $dn = getDolGlobalString('LDAP_USER_DN');
3343
        } elseif ($mode == 2) {
3344
            $dn = getDolGlobalString('LDAP_KEY_USERS') . "=" . $info[getDolGlobalString('LDAP_KEY_USERS')];
3345
        }
3346
        return $dn;
3347
    }
3348
3349
    /**
3350
     *  Initialize the info array (array of LDAP values) that will be used to call LDAP functions
3351
     *
3352
     * @return     array       Table with attribute information
3353
     */
3354
    public function _load_ldap_info()
3355
    {
3356
        // phpcs:enable
3357
        global $conf, $langs;
3358
3359
        $info = array();
3360
3361
        $socialnetworks = getArrayOfSocialNetworks();
3362
3363
        $keymodified = false;
3364
3365
        // Object classes
3366
        $info["objectclass"] = explode(',', getDolGlobalString('LDAP_USER_OBJECT_CLASS'));
3367
3368
        $this->fullname = $this->getFullName($langs);
3369
3370
        // Possible LDAP KEY (constname => varname)
3371
        $ldapkey = array(
3372
            'LDAP_FIELD_FULLNAME' => 'fullname',
3373
            'LDAP_FIELD_NAME' => 'lastname',
3374
            'LDAP_FIELD_FIRSTNAME' => 'firstname',
3375
            'LDAP_FIELD_LOGIN' => 'login',
3376
            'LDAP_FIELD_LOGIN_SAMBA' => 'login',
3377
            'LDAP_FIELD_PHONE' => 'office_phone',
3378
            'LDAP_FIELD_MOBILE' => 'user_mobile',
3379
            'LDAP_FIELD_FAX' => 'office_fax',
3380
            'LDAP_FIELD_MAIL' => 'email',
3381
            'LDAP_FIELD_SID' => 'ldap_sid',
3382
        );
3383
3384
        // Champs
3385
        foreach ($ldapkey as $constname => $varname) {
3386
            if (!empty($this->$varname) && getDolGlobalString($constname)) {
3387
                $info[getDolGlobalString($constname)] = $this->$varname;
3388
3389
                // Check if it is the LDAP key and if its value has been changed
3390
                if (getDolGlobalString('LDAP_KEY_USERS') && $conf->global->LDAP_KEY_USERS == getDolGlobalString($constname)) {
3391
                    if (is_object($this->oldcopy) && !$this->oldcopy->isEmpty() && $this->$varname != $this->oldcopy->$varname) {
3392
                        $keymodified = true; // For check if LDAP key has been modified
3393
                    }
3394
                }
3395
            }
3396
        }
3397
        foreach ($socialnetworks as $key => $value) {
3398
            if (!empty($this->socialnetworks[$value['label']]) && getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']))) {
3399
                $info[getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']))] = $this->socialnetworks[$value['label']];
3400
            }
3401
        }
3402
        if ($this->address && getDolGlobalString('LDAP_FIELD_ADDRESS')) {
3403
            $info[getDolGlobalString('LDAP_FIELD_ADDRESS')] = $this->address;
3404
        }
3405
        if ($this->zip && getDolGlobalString('LDAP_FIELD_ZIP')) {
3406
            $info[getDolGlobalString('LDAP_FIELD_ZIP')] = $this->zip;
3407
        }
3408
        if ($this->town && getDolGlobalString('LDAP_FIELD_TOWN')) {
3409
            $info[getDolGlobalString('LDAP_FIELD_TOWN')] = $this->town;
3410
        }
3411
        if ($this->note_public && getDolGlobalString('LDAP_FIELD_DESCRIPTION')) {
3412
            $info[getDolGlobalString('LDAP_FIELD_DESCRIPTION')] = dol_string_nohtmltag($this->note_public, 2);
3413
        }
3414
        if ($this->socid > 0) {
3415
            $soc = new Societe($this->db);
3416
            $soc->fetch($this->socid);
3417
3418
            $info[getDolGlobalString('LDAP_FIELD_COMPANY')] = $soc->name;
3419
            if ($soc->client == 1) {
3420
                $info["businessCategory"] = "Customers";
3421
            }
3422
            if ($soc->client == 2) {
3423
                $info["businessCategory"] = "Prospects";
3424
            }
3425
            if ($soc->fournisseur == 1) {
3426
                $info["businessCategory"] = "Suppliers";
3427
            }
3428
        }
3429
3430
        // When password is modified
3431
        if (!empty($this->pass)) {
3432
            if (getDolGlobalString('LDAP_FIELD_PASSWORD')) {
3433
                $info[getDolGlobalString('LDAP_FIELD_PASSWORD')] = $this->pass; // this->pass = unencrypted password
3434
            }
3435
            if (getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3436
                $info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dol_hash($this->pass, 'openldap'); // Create OpenLDAP password (see LDAP_PASSWORD_HASH_TYPE)
3437
            }
3438
        } elseif (getDolGlobalString('LDAP_SERVER_PROTOCOLVERSION') !== '3') {
3439
            // Set LDAP password if possible
3440
            // If ldap key is modified and LDAPv3 we use ldap_rename function for avoid lose encrypt password
3441
            if (getDolGlobalString('DATABASE_PWD_ENCRYPTED')) {
3442
                // Just for the default MD5 !
3443
                if (!getDolGlobalString('MAIN_SECURITY_HASH_ALGO')) {
3444
                    if ($this->pass_indatabase_crypted && getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3445
                        $info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dolGetLdapPasswordHash($this->pass_indatabase_crypted, 'md5frommd5'); // Create OpenLDAP MD5 password from Dolibarr MD5 password
3446
                    }
3447
                }
3448
            } elseif (!empty($this->pass_indatabase)) {
3449
                // Use $this->pass_indatabase value if exists
3450
                if (getDolGlobalString('LDAP_FIELD_PASSWORD')) {
3451
                    $info[getDolGlobalString('LDAP_FIELD_PASSWORD')] = $this->pass_indatabase; // $this->pass_indatabase = unencrypted password
3452
                }
3453
                if (getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')) {
3454
                    $info[getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED')] = dol_hash($this->pass_indatabase, 'openldap'); // Create OpenLDAP password (see LDAP_PASSWORD_HASH_TYPE)
3455
                }
3456
            }
3457
        }
3458
3459
        if (getDolGlobalString('LDAP_SERVER_TYPE') == 'egroupware') {
3460
            $info["objectclass"][4] = "phpgwContact"; // compatibilite egroupware
3461
3462
            $info['uidnumber'] = $this->id;
3463
3464
            $info['phpgwTz'] = 0;
3465
            $info['phpgwMailType'] = 'INTERNET';
3466
            $info['phpgwMailHomeType'] = 'INTERNET';
3467
3468
            $info["phpgwContactTypeId"] = 'n';
3469
            $info["phpgwContactCatId"] = 0;
3470
            $info["phpgwContactAccess"] = "public";
3471
3472
            /*
3473
            if (dol_strlen($this->egroupware_id) == 0) {
3474
                $this->egroupware_id = 1;
3475
            }
3476
            $info["phpgwContactOwner"] = $this->egroupware_id;
3477
            */
3478
            $info["phpgwContactOwner"] = 1;
3479
3480
            if ($this->email) {
3481
                $info["rfc822Mailbox"] = $this->email;
3482
            }
3483
            if ($this->user_mobile) {
3484
                $info["phpgwCellTelephoneNumber"] = $this->user_mobile;
3485
            }
3486
        }
3487
3488
        if (getDolGlobalString('LDAP_FIELD_USERID')) {
3489
            $info[getDolGlobalString('LDAP_FIELD_USERID')] = $this->id;
3490
        }
3491
        if (getDolGlobalString('LDAP_FIELD_GROUPID')) {
3492
            $usergroup = new UserGroup($this->db);
3493
            $groupslist = $usergroup->listGroupsForUser($this->id);
3494
            $info[getDolGlobalString('LDAP_FIELD_GROUPID')] = '65534';
3495
            if (!empty($groupslist)) {
3496
                foreach ($groupslist as $groupforuser) {
3497
                    $info[getDolGlobalString('LDAP_FIELD_GROUPID')] = $groupforuser->id; //Select first group in list
3498
                    break;
3499
                }
3500
            }
3501
        }
3502
        if (getDolGlobalString('LDAP_FIELD_HOMEDIRECTORY') && getDolGlobalString('LDAP_FIELD_HOMEDIRECTORYPREFIX')) {
3503
            $info[getDolGlobalString('LDAP_FIELD_HOMEDIRECTORY')] = "{$conf->global->LDAP_FIELD_HOMEDIRECTORYPREFIX}/$this->login";
3504
        }
3505
3506
        return $info;
3507
    }
3508
3509
    /**
3510
     *  Initialise an instance with random values.
3511
     *  Used to build previews or test instances.
3512
     *  id must be 0 if object instance is a specimen.
3513
     *
3514
     * @return int
3515
     */
3516
    public function initAsSpecimen()
3517
    {
3518
        global $user, $langs;
3519
3520
        $now = dol_now();
3521
3522
        // Initialise parameters
3523
        $this->id = 0;
3524
        $this->ref = 'SPECIMEN';
3525
        $this->specimen = 1;
3526
3527
        $this->lastname = 'DOLIBARR';
3528
        $this->firstname = 'SPECIMEN';
3529
        $this->gender = 'man';
3530
        $this->note_public = 'This is a note public';
3531
        $this->note_private = 'This is a note private';
3532
        $this->email = '[email protected]';
3533
        $this->personal_email = '[email protected]';
3534
        $this->socialnetworks = array(
3535
            'skype' => 'skypepseudo',
3536
            'twitter' => 'twitterpseudo',
3537
            'facebook' => 'facebookpseudo',
3538
            'linkedin' => 'linkedinpseudo',
3539
        );
3540
        $this->office_phone = '0999999999';
3541
        $this->office_fax = '0999999998';
3542
        $this->user_mobile = '0999999997';
3543
        $this->personal_mobile = '0999999996';
3544
        $this->admin = 0;
3545
        $this->login = 'dolibspec';
3546
        $this->pass = 'dolibSpec+@123';
3547
        //$this->pass_indatabase='dolibspec';                                   Set after a fetch
3548
        //$this->pass_indatabase_crypted='e80ca5a88c892b0aaaf7e154853bccab';    Set after a fetch
3549
        $this->datec = $now;
3550
        $this->datem = $now;
3551
3552
        $this->datelastlogin = $now;
3553
        $this->iplastlogin = '127.0.0.1';
3554
        $this->datepreviouslogin = $now;
3555
        $this->ippreviouslogin = '127.0.0.1';
3556
        $this->statut = 1;      // deprecated
3557
        $this->status = 1;
3558
3559
        $this->entity = 1;
3560
3561
        return 1;
3562
    }
3563
3564
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3565
3566
    /**
3567
     *  Load info of user object
3568
     *
3569
     * @param int $id Id of user to load
3570
     * @return void
3571
     */
3572
    public function info($id)
3573
    {
3574
        $sql = "SELECT u.rowid, u.login as ref, u.datec,";
3575
        $sql .= " u.tms as date_modification, u.entity";
3576
        $sql .= " FROM " . $this->db->prefix() . "user as u";
3577
        $sql .= " WHERE u.rowid = " . ((int)$id);
3578
3579
        $result = $this->db->query($sql);
3580
        if ($result) {
3581
            if ($this->db->num_rows($result)) {
3582
                $obj = $this->db->fetch_object($result);
3583
3584
                $this->id = $obj->rowid;
3585
3586
                $this->ref = (!$obj->ref) ? $obj->rowid : $obj->ref;
3587
                $this->date_creation = $this->db->jdate($obj->datec);
3588
                $this->date_modification = $this->db->jdate($obj->date_modification);
3589
                $this->entity = $obj->entity;
3590
            }
3591
3592
            $this->db->free($result);
3593
        } else {
3594
            dol_print_error($this->db);
3595
        }
3596
    }
3597
3598
3599
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3600
3601
    /**
3602
     *    Return number of mass Emailing received by this contacts with its email
3603
     *
3604
     * @return       int     Number of EMailings
3605
     */
3606
    public function getNbOfEMailings()
3607
    {
3608
        $sql = "SELECT count(mc.email) as nb";
3609
        $sql .= " FROM " . $this->db->prefix() . "mailing_cibles as mc";
3610
        $sql .= " WHERE mc.email = '" . $this->db->escape($this->email) . "'";
3611
        $sql .= " AND mc.statut NOT IN (-1,0)"; // -1 error, 0 not sent, 1 sent with success
3612
3613
        $resql = $this->db->query($sql);
3614
        if ($resql) {
3615
            $obj = $this->db->fetch_object($resql);
3616
            $nb = $obj->nb;
3617
3618
            $this->db->free($resql);
3619
            return $nb;
3620
        } else {
3621
            $this->error = $this->db->error();
3622
            return -1;
3623
        }
3624
    }
3625
3626
    /**
3627
     *  Return number of existing users
3628
     *
3629
     * @param string $limitTo Limit to '' or 'active'
3630
     * @param string $option 'superadmin' = return for entity 0 only
3631
     * @param int $admin Filter on admin tag
3632
     * @return int                 Number of users
3633
     */
3634
    public function getNbOfUsers($limitTo, $option = '', $admin = -1)
3635
    {
3636
        global $conf;
3637
3638
        $sql = "SELECT count(rowid) as nb";
3639
        $sql .= " FROM " . $this->db->prefix() . "user";
3640
        if ($option == 'superadmin') {
3641
            $sql .= " WHERE entity = 0";
3642
        } else {
3643
            $sql .= " WHERE entity IN (" . getEntity('user', 0) . ")";
3644
            if ($limitTo == 'active') {
3645
                $sql .= " AND statut = 1";
3646
            }
3647
        }
3648
        if ($admin >= 0) {
3649
            $sql .= " AND admin = " . (int)$admin;
3650
        }
3651
3652
        $resql = $this->db->query($sql);
3653
        if ($resql) {
3654
            $obj = $this->db->fetch_object($resql);
3655
            $nb = (int)$obj->nb;
3656
3657
            $this->db->free($resql);
3658
            return $nb;
3659
        } else {
3660
            $this->error = $this->db->lasterror();
3661
            return -1;
3662
        }
3663
    }
3664
3665
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3666
3667
    /**
3668
     *  Update user using data from the LDAP
3669
     *
3670
     * @param Object $ldapuser Ladp User
3671
     * @return int                 Return integer <0 if KO, >0 if OK
3672
     */
3673
    public function update_ldap2dolibarr(&$ldapuser)
3674
    {
3675
        // phpcs:enable
3676
        // TODO: Voir pourquoi le update met à jour avec toutes les valeurs vide (global $user écrase ?)
3677
        global $user, $conf;
3678
3679
        $socialnetworks = getArrayOfSocialNetworks();
3680
3681
        $tmpvar = getDolGlobalString('LDAP_FIELD_FIRSTNAME');
3682
        $this->firstname = $ldapuser->$tmpvar;
3683
        $tmpvar = getDolGlobalString('LDAP_FIELD_NAME');
3684
        $this->lastname = $ldapuser->$tmpvar;
3685
        $tmpvar = getDolGlobalString('LDAP_FIELD_LOGIN');
3686
        $this->login = $ldapuser->$tmpvar;
3687
        $tmpvar = getDolGlobalString('LDAP_FIELD_PASSWORD');
3688
        $this->pass = $ldapuser->$tmpvar;
3689
        $tmpvar = getDolGlobalString('LDAP_FIELD_PASSWORD_CRYPTED');
3690
        $this->pass_indatabase_crypted = $ldapuser->$tmpvar;
3691
3692
        $tmpvar = getDolGlobalString('LDAP_FIELD_PHONE');
3693
        $this->office_phone = $ldapuser->$tmpvar;
3694
        $tmpvar = getDolGlobalString('LDAP_FIELD_MOBILE');
3695
        $this->user_mobile = $ldapuser->$tmpvar;
3696
        $tmpvar = getDolGlobalString('LDAP_FIELD_FAX');
3697
        $this->office_fax = $ldapuser->$tmpvar;
3698
        $tmpvar = getDolGlobalString('LDAP_FIELD_MAIL');
3699
        $this->email = $ldapuser->$tmpvar;
3700
        foreach ($socialnetworks as $key => $value) {
3701
            $tmpvar = getDolGlobalString('LDAP_FIELD_' . strtoupper($value['label']));
3702
            $this->socialnetworks[$value['label']] = $ldapuser->$tmpvar;
3703
        }
3704
        $tmpvar = getDolGlobalString('LDAP_FIELD_SID');
3705
        $this->ldap_sid = $ldapuser->$tmpvar;
3706
3707
        $tmpvar = getDolGlobalString('LDAP_FIELD_TITLE');
3708
        $this->job = $ldapuser->$tmpvar;
3709
        $tmpvar = getDolGlobalString('LDAP_FIELD_DESCRIPTION');
3710
        $this->note_public = $ldapuser->$tmpvar;
3711
3712
        $result = $this->update($user);
3713
3714
        dol_syslog(get_only_class($this) . "::update_ldap2dolibarr result=" . $result, LOG_DEBUG);
3715
3716
        return $result;
3717
    }
3718
3719
    /**
3720
     * Return and array with all instantiated first level children users of current user
3721
     *
3722
     * @return  User[]|int<-1,-1>
3723
     * @see getAllChildIds()
3724
     */
3725
    public function get_children()
3726
    {
3727
        // phpcs:enable
3728
        $sql = "SELECT rowid FROM " . $this->db->prefix() . "user";
3729
        $sql .= " WHERE fk_user = " . ((int)$this->id);
3730
3731
        dol_syslog(get_only_class($this) . "::get_children", LOG_DEBUG);
3732
        $res = $this->db->query($sql);
3733
        if ($res) {
3734
            $users = array();
3735
            while ($rec = $this->db->fetch_array($res)) {
3736
                $user = new User($this->db);
3737
                $user->fetch($rec['rowid']);
3738
                $users[] = $user;
3739
            }
3740
            return $users;
3741
        } else {
3742
            dol_print_error($this->db);
3743
            return -1;
3744
        }
3745
    }
3746
3747
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3748
3749
    /**
3750
     *  Return list of all child user ids in hierarchy (all sublevels).
3751
     *  Note: Calling this function also reset full list of users into $this->users.
3752
     *
3753
     * @param int $addcurrentuser 1=Add also current user id to the list.
3754
     * @return     array                       Array of user id lower than user (all levels under user). This overwrite this->users.
3755
     * @see get_children()
3756
     */
3757
    public function getAllChildIds($addcurrentuser = 0)
3758
    {
3759
        $childids = array();
3760
3761
        if (isset($this->cache_childids[$this->id])) {
3762
            $childids = $this->cache_childids[$this->id];
3763
        } else {
3764
            // Init this->users
3765
            $this->get_full_tree();
3766
3767
            $idtoscan = $this->id;
3768
3769
            dol_syslog("Build childid for id = " . $idtoscan);
3770
            foreach ($this->users as $id => $val) {
3771
                //var_dump($val['fullpath']);
3772
                if (preg_match('/_' . $idtoscan . '_/', $val['fullpath'])) {
3773
                    $childids[$val['id']] = $val['id'];
3774
                }
3775
            }
3776
        }
3777
        $this->cache_childids[$this->id] = $childids;
3778
3779
        if ($addcurrentuser) {
3780
            $childids[$this->id] = $this->id;
3781
        }
3782
3783
        return $childids;
3784
    }
3785
3786
    /**
3787
     *  Build the hierarchy/tree of users into an array.
3788
     *  Set and return this->users that is an array sorted according to tree with arrays of:
3789
     *              id = id user
3790
     *              lastname
3791
     *              firstname
3792
     *              fullname = Name with full path to user
3793
     *              fullpath = Full path composed of the ids: "_grandparentid_parentid_id"
3794
     *
3795
     * @param int $deleteafterid Removed all users including the leaf $deleteafterid (and all its child) in user tree.
3796
     * @param string $filter SQL filter on users. This parameter must not come from user input.
3797
     * @return     int<-1,-1>|array<int,array{rowid:int,id:int,fk_user:int,fk_soc:int,firstname:string,lastname:string,login:string,statut:int,entity:int,email:string,gender:string|int<-1,-1>,admin:int<0,1>,photo:string,fullpath:string,fullname:string,level:int}>  Array of user information (also: $this->users). Note: $this->parentof is also set.
3798
     */
3799
    public function get_full_tree($deleteafterid = 0, $filter = '')
3800
    {
3801
        // phpcs:enable
3802
        global $conf, $user;
3803
        global $hookmanager;
3804
3805
        // Actions hooked (by external module)
3806
        $hookmanager->initHooks(array('userdao'));
3807
3808
        $this->users = array();
3809
3810
        // Init this->parentof that is array(id_son=>id_parent, ...)
3811
        $this->loadParentOf();
3812
3813
        // Init $this->users array
3814
        $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
3815
        $sql .= " FROM " . $this->db->prefix() . "user as u";
3816
        // Add fields from hooks
3817
        $parameters = array();
3818
        $reshook = $hookmanager->executeHooks('printUserListWhere', $parameters); // Note that $action and $object may have been modified by hook
3819
        if ($reshook > 0) {
3820
            $sql .= $hookmanager->resPrint;
3821
        } else {
3822
            $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
3823
        }
3824
        if ($filter) {
3825
            $sql .= " AND " . $filter;
3826
        }
3827
3828
        dol_syslog(get_only_class($this) . "::get_full_tree get user list", LOG_DEBUG);
3829
        $resql = $this->db->query($sql);
3830
        if ($resql) {
3831
            $i = 0;
3832
            while ($obj = $this->db->fetch_object($resql)) {
3833
                $this->users[$obj->rowid]['rowid'] = $obj->rowid;
3834
                $this->users[$obj->rowid]['id'] = $obj->rowid;
3835
                $this->users[$obj->rowid]['fk_user'] = $obj->fk_user;
3836
                $this->users[$obj->rowid]['fk_soc'] = $obj->fk_soc;
3837
                $this->users[$obj->rowid]['firstname'] = $obj->firstname;
3838
                $this->users[$obj->rowid]['lastname'] = $obj->lastname;
3839
                $this->users[$obj->rowid]['login'] = $obj->login;
3840
                $this->users[$obj->rowid]['statut'] = $obj->statut;
3841
                $this->users[$obj->rowid]['entity'] = $obj->entity;
3842
                $this->users[$obj->rowid]['email'] = $obj->email;
3843
                $this->users[$obj->rowid]['gender'] = $obj->gender;
3844
                $this->users[$obj->rowid]['admin'] = $obj->admin;
3845
                $this->users[$obj->rowid]['photo'] = $obj->photo;
3846
                // fields are filled with build_path_from_id_user
3847
                $this->users[$obj->rowid]['fullpath'] = '';
3848
                $this->users[$obj->rowid]['fullname'] = '';
3849
                $this->users[$obj->rowid]['level'] = 0;
3850
                $i++;
3851
            }
3852
        } else {
3853
            dol_print_error($this->db);
3854
            return -1;
3855
        }
3856
3857
        // We add the fullpath property to each element of the first level (no parent exists)
3858
        dol_syslog(get_only_class($this) . "::get_full_tree call to build_path_from_id_user", LOG_DEBUG);
3859
        foreach ($this->users as $key => $val) {
3860
            $result = $this->build_path_from_id_user($key, 0); // Process a branch from the root user key (this user has no parent)
3861
            if ($result < 0) {
3862
                $this->error = 'ErrorLoopInHierarchy';
3863
                return -1;
3864
            }
3865
        }
3866
3867
        // Exclude leaf including $deleteafterid from tree
3868
        if ($deleteafterid) {
3869
            //print "Look to discard user ".$deleteafterid."\n";
3870
            $keyfilter1 = '^' . $deleteafterid . '$';
3871
            $keyfilter2 = '_' . $deleteafterid . '$';
3872
            $keyfilter3 = '^' . $deleteafterid . '_';
3873
            $keyfilter4 = '_' . $deleteafterid . '_';
3874
            foreach (array_keys($this->users) as $key) {
3875
                $fullpath = (string)$this->users[$key]['fullpath'];
3876
                if (
3877
                    preg_match('/' . $keyfilter1 . '/', $fullpath) || preg_match('/' . $keyfilter2 . '/', $fullpath)
3878
                    || preg_match('/' . $keyfilter3 . '/', $fullpath) || preg_match('/' . $keyfilter4 . '/', $fullpath)
3879
                ) {
3880
                    unset($this->users[$key]);
3881
                }
3882
            }
3883
        }
3884
3885
        dol_syslog(get_only_class($this) . "::get_full_tree dol_sort_array", LOG_DEBUG);
3886
        $this->users = dol_sort_array($this->users, 'fullname', 'asc', true, false, 1);
3887
3888
        //var_dump($this->users);
3889
3890
        return $this->users;
3891
    }
3892
3893
    /**
3894
     *  Load this->parentof that is array(id_son=>id_parent, ...)
3895
     *
3896
     * @return     int<-1,1>     Return integer <0 if KO, >0 if OK
3897
     */
3898
    private function loadParentOf()
3899
    {
3900
        global $conf;
3901
3902
        $this->parentof = array();
3903
3904
        // Load array[child]=parent
3905
        $sql = "SELECT fk_user as id_parent, rowid as id_son";
3906
        $sql .= " FROM " . $this->db->prefix() . "user";
3907
        $sql .= " WHERE fk_user <> 0";
3908
        $sql .= " AND entity IN (" . getEntity('user') . ")";
3909
3910
        dol_syslog(get_only_class($this) . "::loadParentOf", LOG_DEBUG);
3911
        $resql = $this->db->query($sql);
3912
        if ($resql) {
3913
            while ($obj = $this->db->fetch_object($resql)) {
3914
                $this->parentof[$obj->id_son] = $obj->id_parent;
3915
            }
3916
            return 1;
3917
        } else {
3918
            dol_print_error($this->db);
3919
            return -1;
3920
        }
3921
    }
3922
3923
    /**
3924
     *  For user id_user and its children available in this->users, define property fullpath and fullname.
3925
     *  Function called by get_full_tree().
3926
     *
3927
     * @param int $id_user id_user entry to update
3928
     * @param int $protection Deep counter to avoid infinite loop (no more required, a protection is added with array useridfound)
3929
     * @return     int<-1,1>               Return integer < 0 if KO (infinite loop), >= 0 if OK
3930
     */
3931
    public function build_path_from_id_user($id_user, $protection = 0)
3932
    {
3933
        // phpcs:enable
3934
        //dol_syslog(get_only_class($this)."::build_path_from_id_user id_user=".$id_user." protection=".$protection, LOG_DEBUG);
3935
3936
        if (!empty($this->users[$id_user]['fullpath'])) {
3937
            // Already defined
3938
            dol_syslog(get_only_class($this) . "::build_path_from_id_user fullpath and fullname already defined", LOG_WARNING);
3939
            return 0;
3940
        }
3941
3942
        // Define fullpath and fullname
3943
        $this->users[$id_user]['fullpath'] = '_' . $id_user;
3944
        $this->users[$id_user]['fullname'] = $this->users[$id_user]['lastname'];
3945
        $i = 0;
3946
        $cursor_user = $id_user;
3947
3948
        $useridfound = array($id_user);
3949
        while (!empty($this->parentof[$cursor_user]) && !empty($this->users[$this->parentof[$cursor_user]])) {
3950
            if (in_array($this->parentof[$cursor_user], $useridfound)) {
3951
                dol_syslog("The hierarchy of user has a recursive loop", LOG_WARNING);
3952
                return -1; // Should not happen. Protection against looping hierarchy
3953
            }
3954
            $useridfound[] = $this->parentof[$cursor_user];
3955
            $this->users[$id_user]['fullpath'] = '_' . $this->parentof[$cursor_user] . $this->users[$id_user]['fullpath'];
3956
            $this->users[$id_user]['fullname'] = $this->users[$this->parentof[$cursor_user]]['lastname'] . ' >> ' . $this->users[$id_user]['fullname'];
3957
            $i++;
3958
            $cursor_user = $this->parentof[$cursor_user];
3959
        }
3960
3961
        // We count number of _ to have level
3962
        $this->users[$id_user]['level'] = dol_strlen(preg_replace('/[^_]/i', '', $this->users[$id_user]['fullpath']));
3963
3964
        return 1;
3965
    }
3966
3967
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3968
3969
    /**
3970
     *      Load metrics this->nb for dashboard
3971
     *
3972
     * @return     int         Return integer <0 if KO, >0 if OK
3973
     */
3974
    public function loadStateBoard()
3975
    {
3976
        global $conf;
3977
3978
        $this->nb = array();
3979
3980
        $sql = "SELECT COUNT(DISTINCT u.rowid) as nb";
3981
        $sql .= " FROM " . $this->db->prefix() . "user as u";
3982
        if (isModEnabled('multicompany') && getDolGlobalString('MULTICOMPANY_TRANSVERSE_MODE')) {
3983
            $sql .= ", " . $this->db->prefix() . "usergroup_user as ug";
3984
            $sql .= " WHERE ug.entity IN (" . getEntity('usergroup') . ")";
3985
            $sql .= " AND ug.fk_user = u.rowid";
3986
        } else {
3987
            $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
3988
        }
3989
        $sql .= " AND u.statut > 0";
3990
        //$sql.= " AND employee != 0";
3991
3992
        $resql = $this->db->query($sql);
3993
        if ($resql) {
3994
            while ($obj = $this->db->fetch_object($resql)) {
3995
                $this->nb["users"] = $obj->nb;
3996
            }
3997
            $this->db->free($resql);
3998
            return 1;
3999
        } else {
4000
            dol_print_error($this->db);
4001
            $this->error = $this->db->error();
4002
            return -1;
4003
        }
4004
    }
4005
4006
    /**
4007
     *  Create a document onto disk according to template module.
4008
     *
4009
     * @param string $modele Force model to use ('' to not force)
4010
     * @param Translate $outputlangs Object langs to use for output
4011
     * @param int $hidedetails Hide details of lines
4012
     * @param int $hidedesc Hide description
4013
     * @param int $hideref Hide ref
4014
     * @param null|array $moreparams Array to provide more information
4015
     * @return     int                         0 if KO, 1 if OK
4016
     */
4017
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
4018
    {
4019
        global $conf, $user, $langs;
4020
4021
        $langs->load("user");
4022
4023
        // Set the '$modele' to the name of the document template (model) to use
4024
        if (!dol_strlen($modele)) {
4025
            if (getDolGlobalString('USER_ADDON_PDF')) {
4026
                $modele = getDolGlobalString('USER_ADDON_PDF');
4027
            } else {
4028
                $modele = 'bluesky';
4029
            }
4030
        }
4031
4032
        $modelpath = "core/modules/user/doc/";
4033
4034
        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
4035
    }
4036
4037
    /**
4038
     *  Return property of user from its id
4039
     *
4040
     * @param int $rowid id of contact
4041
     * @param string $mode 'email', 'mobile', or 'name'
4042
     * @return string              Email of user with format: "Full name <email>"
4043
     */
4044
    public function user_get_property($rowid, $mode)
4045
    {
4046
        // phpcs:enable
4047
        $user_property = '';
4048
4049
        if (empty($rowid)) {
4050
            return '';
4051
        }
4052
4053
        $sql = "SELECT rowid, email, user_mobile, civility, lastname, firstname";
4054
        $sql .= " FROM " . $this->db->prefix() . "user";
4055
        $sql .= " WHERE rowid = " . ((int)$rowid);
4056
4057
        $resql = $this->db->query($sql);
4058
        if ($resql) {
4059
            $nump = $this->db->num_rows($resql);
4060
4061
            if ($nump) {
4062
                $obj = $this->db->fetch_object($resql);
4063
4064
                if ($mode == 'email') {
4065
                    $user_property = dolGetFirstLastname($obj->firstname, $obj->lastname) . " <" . $obj->email . ">";
4066
                } elseif ($mode == 'mobile') {
4067
                    $user_property = $obj->user_mobile;
4068
                } elseif ($mode == 'name') {
4069
                    $user_property = dolGetFirstLastname($obj->firstname, $obj->lastname);
4070
                }
4071
            }
4072
            return $user_property;
4073
        } else {
4074
            dol_print_error($this->db);
4075
        }
4076
4077
        return '';
4078
    }
4079
4080
    /**
4081
     * Return string with full Url to virtual card
4082
     *
4083
     * @param string $mode Mode for link
4084
     * @param string $typeofurl 'external' or 'internal'
4085
     * @return  string                  Url string link
4086
     */
4087
    public function getOnlineVirtualCardUrl($mode = '', $typeofurl = 'external')
4088
    {
4089
        global $dolibarr_main_url_root;
4090
        global $conf;
4091
4092
        $encodedsecurekey = dol_hash($conf->file->instance_unique_id . 'uservirtualcard' . $this->id . '-' . $this->login, 'md5');
4093
        if (isModEnabled('multicompany')) {
4094
            $entity_qr = '&entity=' . ((int)$conf->entity);
4095
        } else {
4096
            $entity_qr = '';
4097
        }
4098
        // Define $urlwithroot
4099
        $urlwithouturlroot = preg_replace('/' . preg_quote(constant('BASE_URL'), '/') . '$/i', '', trim($dolibarr_main_url_root));
4100
        $urlwithroot = $urlwithouturlroot . constant('BASE_URL'); // This is to use external domain name found into config file
4101
        //$urlwithroot=DOL_MAIN_URL_ROOT;                   // This is to use same domain name than current
4102
4103
        if ($typeofurl == 'internal') {
4104
            $urlwithroot = constant('BASE_URL');
4105
        }
4106
4107
        return $urlwithroot . '/public/users/view.php?id=' . $this->id . '&securekey=' . $encodedsecurekey . $entity_qr . ($mode ? '&mode=' . urlencode($mode) : '');
4108
    }
4109
4110
    /**
4111
     * Find a user by the given e-mail and return it's user id when found
4112
     *
4113
     * NOTE:
4114
     * Use AGENDA_DISABLE_EXACT_USER_EMAIL_COMPARE_FOR_EXTERNAL_CALENDAR
4115
     * to disable exact e-mail search
4116
     *
4117
     * @param string $email The full e-mail (or a part of a e-mail)
4118
     * @return int              Return integer <0 = user was not found, >0 = The id of the user
4119
     */
4120
    public function findUserIdByEmail($email)
4121
    {
4122
        if (isset($this->findUserIdByEmailCache[$email])) {
4123
            return $this->findUserIdByEmailCache[$email];
4124
        }
4125
4126
        $this->findUserIdByEmailCache[$email] = -1;
4127
4128
        $sql = 'SELECT rowid';
4129
        $sql .= ' FROM ' . $this->db->prefix() . 'user';
4130
        if (getDolGlobalString('AGENDA_DISABLE_EXACT_USER_EMAIL_COMPARE_FOR_EXTERNAL_CALENDAR')) {
4131
            $sql .= " WHERE email LIKE '%" . $this->db->escape($this->db->escapeforlike($email)) . "%'";
4132
        } else {
4133
            $sql .= " WHERE email = '" . $this->db->escape($email) . "'";
4134
        }
4135
        $sql .= ' LIMIT 1';
4136
4137
        $resql = $this->db->query($sql);
4138
        if (!$resql) {
4139
            return -1;
4140
        }
4141
4142
        $obj = $this->db->fetch_object($resql);
4143
        if (!$obj) {
4144
            return -1;
4145
        }
4146
4147
        $this->findUserIdByEmailCache[$email] = (int)$obj->rowid;
4148
4149
        return $this->findUserIdByEmailCache[$email];
4150
    }
4151
}
4152