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

User::getTooltipContentArray()   F

Complexity

Conditions 24
Paths 15552

Size

Total Lines 84
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 24
eloc 61
nc 15552
nop 1
dl 0
loc 84
rs 0
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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