Passed
Branch develop (96ac0e)
by
unknown
23:19
created

User::getNomUrl()   F

Complexity

Conditions 61
Paths 0

Size

Total Lines 177
Code Lines 117

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 61
eloc 117
c 0
b 0
f 0
nc 0
nop 9
dl 0
loc 177
rs 3.3333

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

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