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

User::update_ldap2dolibarr()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 20
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 32
rs 9.6
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