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

User::getNomUrl()   F

Complexity

Conditions 61
Paths 0

Size

Total Lines 170
Code Lines 116

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

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