Passed
Pull Request — dev (#8)
by Rafael
58:47
created

User   F

Complexity

Total Complexity 669

Size/Duplication

Total Lines 4149
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 2194
dl 0
loc 4149
rs 0.8
c 0
b 0
f 0
wmc 669

53 Methods

Rating   Name   Duplication   Size   Complexity  
A generateDocument() 0 18 3
A update_clicktodial() 0 28 2
F fetch() 0 226 26
A loadStateBoard() 0 29 5
C getLoginUrl() 0 43 15
A isNotIntoValidityDateRange() 0 18 5
B LibStatut() 0 33 8
F getKanbanView() 0 48 13
A loadPersonalConf() 0 28 5
F getTooltipContentArray() 0 88 25
A findUserIdByEmail() 0 30 5
C fetchAll() 0 58 13
A getrights() 0 3 1
A set_default_rights() 0 36 5
F _load_ldap_info() 0 153 44
C get_full_tree() 0 92 13
A loadParentOf() 0 22 3
A update_last_login_date() 0 26 2
A build_path_from_id_user() 0 34 5
C loadDefaultValues() 0 37 15
B RemoveFromGroup() 0 43 7
F update() 0 340 91
B user_get_property() 0 34 7
A initAsSpecimen() 0 46 1
F addrights() 0 128 25
A error() 0 3 1
A getNbOfUsers() 0 28 5
B create_from_contact() 0 74 6
A get_children() 0 19 3
A _load_ldap_dn() 0 13 4
A replaceThirdparty() 0 7 1
A getPhotoUrl() 0 7 1
F setPassword() 0 127 22
A getNbOfEMailings() 0 17 2
C create_from_member() 0 77 12
A update_ldap2dolibarr() 0 44 2
F hasRight() 0 142 46
A setCategories() 0 3 1
A info() 0 23 4
F getNomUrl() 0 122 45
A getOnlineVirtualCardUrl() 0 21 4
F delrights() 0 108 22
A fetch_clicktodial() 0 25 3
F loadRights() 0 234 69
A getAllChildIds() 0 27 5
B setstatus() 0 46 9
B send_password() 0 105 9
A __construct() 0 25 1
F create() 0 131 23
A getLibStatut() 0 3 2
F delete() 0 77 16
B SetInGroup() 0 44 6
A clearrights() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like User often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use User, and based on these observations, apply Extract Interface, too.

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