Passed
Branch develop (07a336)
by
unknown
39:19
created

User::setCategories()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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