Passed
Branch develop (4336b1)
by
unknown
85:39
created

User::getOnlineVirtualCardUrl()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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