Passed
Branch develop (356b3a)
by
unknown
98:06
created

User::getNomUrl()   F

Complexity

Conditions 42
Paths > 20000

Size

Total Lines 118
Code Lines 77

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 42
eloc 77
nc 24295680
nop 9
dl 0
loc 118
rs 0
c 1
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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