Completed
Branch develop (5b8006)
by
unknown
43:45
created

functions.lib.php ➔ getEntity()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 3
nop 3
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2000-2007	Rodolphe Quiedeville			<[email protected]>
3
 * Copyright (C) 2003		Jean-Louis Bergamo			<[email protected]>
4
 * Copyright (C) 2004-2013	Laurent Destailleur			<[email protected]>
5
 * Copyright (C) 2004		Sebastien Di Cintio			<[email protected]>
6
 * Copyright (C) 2004		Benoit Mortier				<[email protected]>
7
 * Copyright (C) 2004		Christophe Combelles			<[email protected]>
8
 * Copyright (C) 2005-2017	Regis Houssin				<[email protected]>
9
 * Copyright (C) 2008		Raphael Bertrand (Resultic)	<[email protected]>
10
 * Copyright (C) 2010-2016	Juanjo Menent				<[email protected]>
11
 * Copyright (C) 2013		Cédric Salvador				<[email protected]>
12
 * Copyright (C) 2013-2017	Alexandre Spangaro			<[email protected]>
13
 * Copyright (C) 2014		Cédric GROSS					<[email protected]>
14
 * Copyright (C) 2014-2015	Marcos García				<[email protected]>
15
 * Copyright (C) 2015		Jean-François Ferry			<[email protected]>
16
 *
17
 * This program is free software; you can redistribute it and/or modify
18
 * it under the terms of the GNU General Public License as published by
19
 * the Free Software Foundation; either version 3 of the License, or
20
 * (at your option) any later version.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
 * GNU General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU General Public License
28
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29
 * or see http://www.gnu.org/
30
 */
31
32
/**
33
 *	\file			htdocs/core/lib/functions.lib.php
34
 *	\brief			A set of functions for Dolibarr
35
 *					This file contains all frequently used functions.
36
 */
37
38
include_once DOL_DOCUMENT_ROOT .'/core/lib/json.lib.php';
39
40
41
/**
42
 * Function to return value of a static property when class
43
 * name is dynamically defined (not hard coded).
44
 * This is because $myclass::$myvar works from PHP 5.3.0+ only
45
 *
46
 * @param	string 	$class		Class name
47
 * @param 	string 	$member		Name of property
48
 * @return 	mixed				Return value of static property
49
 * @deprecated Dolibarr now requires 5.3.0+, use $class::$property syntax
50
 * @see https://php.net/manual/language.oop5.static.php
51
 */
52
function getStaticMember($class, $member)
53
{
54
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
55
56
	// This part is deprecated. Uncomment if for php 5.2.*, and comment next isset class::member
57
	/*if (version_compare(phpversion(), '5.3.0', '<'))
58
	{
59
		if (is_object($class)) $class = get_class($class);
60
		$classObj = new ReflectionClass($class);
61
		$result = null;
62
63
		$found=0;
64
		foreach($classObj->getStaticProperties() as $prop => $value)
65
		{
66
			if ($prop == $member)
67
			{
68
				$result = $value;
69
				$found++;
70
				break;
71
			}
72
		}
73
74
		if ($found) return $result;
75
	}*/
76
77
	if (isset($class::$member)) return $class::$member;
78
	dol_print_error('','Try to get a static member "'.$member.'" in class "'.$class.'" that does not exists or is not static.');
79
	return null;
80
}
81
82
83
/**
84
 * Return a DoliDB instance (database handler).
85
 *
86
 * @param   string	$type		Type of database (mysql, pgsql...)
87
 * @param	string	$host		Address of database server
88
 * @param	string	$user		Nom de l'utilisateur autorise
89
 * @param	string	$pass		Mot de passe
90
 * @param	string	$name		Nom de la database
91
 * @param	int		$port		Port of database server
92
 * @return	DoliDB				A DoliDB instance
93
 */
94
function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
95
{
96
	require_once DOL_DOCUMENT_ROOT ."/core/db/".$type.'.class.php';
97
98
	$class='DoliDB'.ucfirst($type);
99
	$dolidb=new $class($type, $host, $user, $pass, $name, $port);
100
	return $dolidb;
101
}
102
103
/**
104
 * 	Get list of entity id to use.
105
 *
106
 * 	@param	string	$element		Current element
107
 *									'societe', 'socpeople', 'actioncomm', 'agenda', 'resource',
108
 *									'product', 'productprice', 'stock',
109
 *									'propal', 'supplier_proposal', 'facture', 'facture_fourn', 'payment_various',
110
 *									'categorie', 'bank_account', 'bank_account', 'adherent', 'user',
111
 *									'commande', 'commande_fournisseur', 'expedition', 'intervention', 'survey',
112
 *									'contract', 'tax', 'expensereport', 'holiday', 'multicurrency', 'project',
113
 *									'email_template', 'event', 'donation'
114
 *									'c_paiement', 'c_payment_term', ...
115
 * 	@param	int		$shared			0=Return id of current entity only,
116
 * 									1=Return id of current entity + shared entities (default)
117
 *  @param	int		$forceentity	Entity id
118
 * 	@return	mixed				Entity id(s) to use
119
 */
120
function getEntity($element, $shared=1, $forceentity=null)
121
{
122
	global $conf, $mc;
123
124
	if (is_object($mc))
125
	{
126
		return $mc->getEntity($element, $shared, $forceentity);
127
	}
128
	else
129
	{
130
		$out='';
131
		$addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values');
132
		if (in_array($element, $addzero)) $out.= '0,';
133
		$out.= $conf->entity;
134
		return $out;
135
	}
136
}
137
138
/**
139
 * Return information about user browser
140
 *
141
 * Returns array with the following format:
142
 * array(
143
 *  'browsername' => Browser name (firefox|chrome|iceweasel|epiphany|safari|opera|ie|unknown)
144
 *  'browserversion' => Browser version. Empty if unknown
145
 *  'browseros' => Set with mobile OS (android|blackberry|ios|palm|symbian|webos|maemo|windows|unknown)
146
 *  'layout' => (tablet|phone|classic)
147
 *  'phone' => empty if not mobile, (android|blackberry|ios|palm|unknown) if mobile
148
 *  'tablet' => true/false
149
 * )
150
 *
151
 * @param string $user_agent Content of $_SERVER["HTTP_USER_AGENT"] variable
152
 * @return	array Check function documentation
153
 */
154
function getBrowserInfo($user_agent)
155
{
156
	include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
157
158
	$name='unknown';
159
	$version='';
160
	$os='unknown';
161
	$phone = '';
162
163
	$detectmobile = new Mobile_Detect(null, $user_agent);
164
	$tablet = $detectmobile->isTablet();
165
166
	if ($detectmobile->isMobile()) {
167
168
		$phone = 'unknown';
169
170
		// If phone/smartphone, we set phone os name.
171
		if ($detectmobile->is('AndroidOS')) {
172
			$os = $phone = 'android';
173
		} elseif ($detectmobile->is('BlackBerryOS')) {
174
			$os = $phone = 'blackberry';
175
		} elseif ($detectmobile->is('iOS')) {
176
			$os = 'ios';
177
			$phone = 'iphone';
178
		} elseif ($detectmobile->is('PalmOS')) {
179
			$os = $phone = 'palm';
180
		} elseif ($detectmobile->is('SymbianOS')) {
181
			$os = 'symbian';
182
		} elseif ($detectmobile->is('webOS')) {
183
			$os = 'webos';
184
		} elseif ($detectmobile->is('MaemoOS')) {
185
			$os = 'maemo';
186
		} elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
187
			$os = 'windows';
188
		}
189
	}
190
191
	// OS
192
	if (preg_match('/linux/i', $user_agent))			{ $os='linux'; }
193
	elseif (preg_match('/macintosh/i', $user_agent))	{ $os='macintosh'; }
194
	elseif (preg_match('/windows/i', $user_agent))		{ $os='windows'; }
195
196
	// Name
197
	if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg))      { $name='firefox';   $version=$reg[2]; }
198
	elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg))     { $name='edge';      $version=$reg[2]; }
199
	elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg))   { $name='chrome';    $version=$reg[2]; }    // we can have 'chrome (Mozilla...) chrome x.y' in one string
200
	elseif (preg_match('/chrome/i', $user_agent, $reg))                   { $name='chrome'; }
201
	elseif (preg_match('/iceweasel/i', $user_agent))                      { $name='iceweasel'; }
202
	elseif (preg_match('/epiphany/i', $user_agent))                       { $name='epiphany';  }
203
	elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg))   { $name='safari';    $version=$reg[2]; }	// Safari is often present in string for mobile but its not.
204
	elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg))    { $name='opera';     $version=$reg[2]; }
205
	elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg))  { $name='ie'; $version=end($reg); }    // MS products at end
206
	elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg))  { $name='ie'; $version=end($reg); }    // MS products at end
207
	elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) { $name='lynxlinks'; $version=$reg[4]; }
208
209
	if ($tablet) {
210
		$layout = 'tablet';
211
	} elseif ($phone) {
212
		$layout = 'phone';
213
	} else {
214
		$layout = 'classic';
215
	}
216
217
	return array(
218
		'browsername' => $name,
219
		'browserversion' => $version,
220
		'browseros' => $os,
221
		'layout' => $layout,
222
		'phone' => $phone,
223
		'tablet' => $tablet
224
	);
225
}
226
227
/**
228
 *  Function called at end of web php process
229
 *
230
 *  @return	void
231
 */
232
function dol_shutdown()
233
{
234
	global $conf,$user,$langs,$db;
235
	$disconnectdone=false; $depth=0;
236
	if (is_object($db) && ! empty($db->connected)) { $depth=$db->transaction_opened; $disconnectdone=$db->close(); }
237
	dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth)?' (Warn: db disconnection forced, transaction depth was '.$depth.')':''), (($disconnectdone && $depth)?LOG_WARNING:LOG_INFO));
238
}
239
240
/**
241
 * Return true if we are in a context of submitting a parameter
242
 *
243
 * @param 	string	$paramname		Name or parameter to test
244
 * @return 	boolean					True if we have just submit a POST or GET request with the parameter provided (even if param is empty)
245
 */
246
function GETPOSTISSET($paramname)
247
{
248
	return (isset($_POST[$paramname]) || isset($_GET[$paramname]));
249
}
250
251
/**
252
 *  Return value of a param into GET or POST supervariable.
253
 *  Use the property $user->default_values[path]['creatform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder']
254
 *  Note: The property $user->default_values is loaded by main.php when loading the user.
255
 *
256
 *  @param	string	$paramname   Name of parameter to found
257
 *  @param	string	$check	     Type of check
258
 *                                  ''=no check (deprecated)
259
 *                                  'none'=no check (only for param that should have very rich content)
260
 *                                  'int'=check it's numeric (integer or float)
261
 *                                  'alpha'=check it's text and sign
262
 *                                  'aZ'=check it's a-z only
263
 *                                  'aZ09'=check it's simple alpha string (recommended for keys)
264
 *                                  'array'=check it's array
265
 *                                  'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string)
266
 *                                  'nohtml', 'alphanohtml'=check there is no html content
267
 *                                  'custom'= custom filter specify $filter and $options)
268
 *  @param	int		$method	     Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get, 4 = post then get then cookie)
269
 *  @param  int     $filter      Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails)
270
 *  @param  mixed   $options     Options to pass to filter_var when $check is set to 'custom'
271
 *  @param	string	$noreplace	 Force disable of replacement of __xxx__ strings.
272
 *  @return string|string[]      Value found (string or array), or '' if check fails
273
 */
274
function GETPOST($paramname, $check='none', $method=0, $filter=NULL, $options=NULL, $noreplace=0)
275
{
276
	global $mysoc,$user,$conf;
277
278
	if (empty($paramname)) return 'BadFirstParameterForGETPOST';
279
	if (empty($check))
280
	{
281
		dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
282
		// Enable this line to know who call the GETPOST with '' $check parameter.
283
		//var_dump(debug_backtrace()[0]);
284
	}
285
286
	if (empty($method)) $out = isset($_GET[$paramname])?$_GET[$paramname]:(isset($_POST[$paramname])?$_POST[$paramname]:'');
287
	elseif ($method==1) $out = isset($_GET[$paramname])?$_GET[$paramname]:'';
288
	elseif ($method==2) $out = isset($_POST[$paramname])?$_POST[$paramname]:'';
289
	elseif ($method==3) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:'');
290
	elseif ($method==4) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:(isset($_COOKIE[$paramname])?$_COOKIE[$paramname]:''));
291
	else return 'BadThirdParameterForGETPOST';
292
293
	if (empty($method) || $method == 3 || $method == 4)
294
	{
295
296
		$relativepathstring = $_SERVER["PHP_SELF"];
297
		// Clean $relativepathstring
298
		if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'),'/').'/', '', $relativepathstring);
299
		$relativepathstring = preg_replace('/^\//', '', $relativepathstring);
300
		$relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
301
		//var_dump($relativepathstring);
302
		//var_dump($user->default_values);
303
304
		// Code for search criteria persistence.
305
		// Retrieve values if restore_lastsearch_values is set and there is saved values
306
		if (! empty($_GET['restore_lastsearch_values']) && ! empty($_SESSION['lastsearch_values_'.$relativepathstring]))        // Keep $_GET here
307
		{
308
			$tmp=json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
309
			if (is_array($tmp))
310
			{
311
				foreach($tmp as $key => $val)
312
				{
313
					if ($key == $paramname)
314
					{
315
						$out=$val;
316
						break;
317
					}
318
				}
319
			}
320
		}
321
		// Else, retreive default values if we are not doing a sort
322
		elseif (! isset($_GET['sortfield']))	// If we did a click on a field to sort, we do no apply default values. Same if option MAIN_ENABLE_DEFAULT_VALUES is not set
323
		{
324
			if (! empty($_GET['action']) && $_GET['action'] == 'create' && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
325
			{
326
				// Search default value from $object->field
327
				global $object;
328
				if (is_object($object) && isset($object->fields[$paramname]['default']))
329
				{
330
					$out = $object->fields[$paramname]['default'];
331
				}
332
			}
333
			if (! empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES))
334
			{
335
				if (! empty($_GET['action']) && $_GET['action'] == 'create' && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
336
				{
337
					// Now search in setup to overwrite default values
338
					if (! empty($user->default_values))		// $user->default_values defined from menu 'Setup - Default values'
339
					{
340
						if (isset($user->default_values[$relativepathstring]['createform']))
341
						{
342
							foreach($user->default_values[$relativepathstring]['createform'] as $defkey => $defval)
343
							{
344
								$qualified = 0;
345
								if ($defkey != '_noquery_')
346
								{
347
									$tmpqueryarraytohave=explode('&', $defkey);
348
									$tmpqueryarraywehave=explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
349
									$foundintru=0;
350
									foreach($tmpqueryarraytohave as $tmpquerytohave)
351
									{
352
										if (! in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru=1;
353
									}
354
									if (! $foundintru) $qualified=1;
355
									//var_dump($defkey.'-'.$qualified);
356
								}
357
								else $qualified = 1;
358
359
								if ($qualified)
360
								{
361
									//var_dump($user->default_values[$relativepathstring][$defkey]['createform']);
362
									if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname]))
363
									{
364
										$out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
365
										break;
366
									}
367
								}
368
							}
369
						}
370
					}
371
				}
372
				// Management of default search_filters and sort order
373
				//elseif (preg_match('/list.php$/', $_SERVER["PHP_SELF"]) && ! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
374
				elseif (! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
375
				{
376
					if (! empty($user->default_values))		// $user->default_values defined from menu 'Setup - Default values'
377
					{
378
						//var_dump($user->default_values[$relativepathstring]);
379
						if ($paramname == 'sortfield' || $paramname == 'sortorder')			// Sorted on which fields ? ASC or DESC ?
380
						{
381
							if (isset($user->default_values[$relativepathstring]['sortorder']))	// Even if paramname is sortfield, data are stored into ['sortorder...']
382
							{
383
								foreach($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval)
384
								{
385
									$qualified = 0;
386
									if ($defkey != '_noquery_')
387
									{
388
										$tmpqueryarraytohave=explode('&', $defkey);
389
										$tmpqueryarraywehave=explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
390
										$foundintru=0;
391
										foreach($tmpqueryarraytohave as $tmpquerytohave)
392
										{
393
											if (! in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru=1;
394
										}
395
										if (! $foundintru) $qualified=1;
396
										//var_dump($defkey.'-'.$qualified);
397
									}
398
									else $qualified = 1;
399
400
									if ($qualified)
401
									{
402
										$forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
403
										foreach($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val)
404
										{
405
											if ($out) $out.=', ';
406
											if ($paramname == 'sortfield')
407
											{
408
												$out.=dol_string_nospecial($key, '', $forbidden_chars_to_replace);
409
											}
410
											if ($paramname == 'sortorder')
411
											{
412
												$out.=dol_string_nospecial($val, '', $forbidden_chars_to_replace);
413
											}
414
										}
415
										//break;	// No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
416
									}
417
								}
418
							}
419
						}
420
						elseif (isset($user->default_values[$relativepathstring]['filters']))
421
						{
422
							foreach($user->default_values[$relativepathstring]['filters'] as $defkey => $defval)
423
							{
424
								$qualified = 0;
425
								if ($defkey != '_noquery_')
426
								{
427
									$tmpqueryarraytohave=explode('&', $defkey);
428
									$tmpqueryarraywehave=explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
429
									$foundintru=0;
430
									foreach($tmpqueryarraytohave as $tmpquerytohave)
431
									{
432
										if (! in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru=1;
433
									}
434
									if (! $foundintru) $qualified=1;
435
									//var_dump($defkey.'-'.$qualified);
436
								}
437
								else $qualified = 1;
438
439
								if ($qualified)
440
								{
441
									if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all']))
442
									{
443
										// We made a search from quick search menu, do we still use default filter ?
444
										if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH))
445
										{
446
											$forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
447
											$out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
448
										}
449
									}
450
									else
451
									{
452
										$forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
453
										$out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
454
									}
455
									break;
456
								}
457
							}
458
						}
459
					}
460
				}
461
			}
462
		}
463
	}
464
465
	// Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable paramaters)
466
	// Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
467
	// We do this only if var is a GET. If it is a POST, may be we want to post the text with vars as the setup text.
468
	if (! is_array($out) && empty($_POST[$paramname]) && empty($noreplace))
469
	{
470
		$maxloop=20; $loopnb=0;    // Protection against infinite loop
471
		while (preg_match('/__([A-Z0-9]+_?[A-Z0-9]+)__/i', $out, $reg) && ($loopnb < $maxloop))    // Detect '__ABCDEF__' as key 'ABCDEF' and '__ABC_DEF__' as key 'ABC_DEF'. Detection is also correct when 2 vars are side by side.
472
		{
473
				$loopnb++; $newout = '';
474
475
				if ($reg[1] == 'DAY')                { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['mday']; }
476
				elseif ($reg[1] == 'MONTH')          { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['mon'];  }
477
				elseif ($reg[1] == 'YEAR')           { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['year']; }
478
				elseif ($reg[1] == 'PREVIOUS_DAY')   { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; }
479
				elseif ($reg[1] == 'PREVIOUS_MONTH') { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_prev_month($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['month']; }
480
				elseif ($reg[1] == 'PREVIOUS_YEAR')  { $tmp=dol_getdate(dol_now(), true); $newout = ($tmp['year'] - 1); }
481
				elseif ($reg[1] == 'NEXT_DAY')       { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; }
482
				elseif ($reg[1] == 'NEXT_MONTH')     { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_next_month($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['month']; }
483
				elseif ($reg[1] == 'NEXT_YEAR')      { $tmp=dol_getdate(dol_now(), true); $newout = ($tmp['year'] + 1); }
484
				elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID')
485
				{
486
					$newout = $mysoc->country_id;
487
				}
488
				elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID')
489
				{
490
					$newout = $user->id;
491
				}
492
				elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID')
493
				{
494
					$newout = $user->fk_user;
495
				}
496
				elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID')
497
				{
498
					$newout = $conf->entity;
499
				}
500
				else $newout = '';     // Key not found, we replace with empty string
501
				//var_dump('__'.$reg[1].'__ -> '.$newout);
502
				$out = preg_replace('/__'.preg_quote($reg[1],'/').'__/', $newout, $out);
503
		}
504
	}
505
506
	// Check is done after replacement
507
	switch ($check)
508
	{
509
		case 'none':
510
			break;
511
		case 'int':    // Check param is a numeric value (integer but also float or hexadecimal)
512
			if (! is_numeric($out)) { $out=''; }
513
			break;
514
		case 'intcomma':
515
			if (preg_match('/[^0-9,-]+/i',$out)) $out='';
516
			break;
517
		case 'alpha':
518
			if (! is_array($out))
519
			{
520
				$out=trim($out);
521
				// '"' is dangerous because param in url can close the href= or src= and add javascript functions.
522
				// '../' is dangerous because it allows dir transversals
523
				if (preg_match('/"/',$out)) $out='';
524
				else if (preg_match('/\.\.\//',$out)) $out='';
525
			}
526
			break;
527
		case 'san_alpha':
528
			$out=filter_var($out,FILTER_SANITIZE_STRING);
529
			break;
530
		case 'aZ':
531
			if (! is_array($out))
532
			{
533
				$out=trim($out);
534
				if (preg_match('/[^a-z]+/i',$out)) $out='';
535
			}
536
			break;
537
		case 'aZ09':
538
			if (! is_array($out))
539
			{
540
				$out=trim($out);
541
				if (preg_match('/[^a-z0-9_\-\.]+/i',$out)) $out='';
542
			}
543
			break;
544
		case 'array':
545
			if (! is_array($out) || empty($out)) $out=array();
546
			break;
547
		case 'nohtml':
548
			$out=dol_string_nohtmltag($out, 0);
549
			break;
550
		case 'alphanohtml':	// Recommended for search params
551
			if (! is_array($out))
552
			{
553
				$out=trim($out);
554
				// '"' is dangerous because param in url can close the href= or src= and add javascript functions.
555
				// '../' is dangerous because it allows dir transversals
556
				if (preg_match('/"/',$out)) $out='';
557
				else if (preg_match('/\.\.\//',$out)) $out='';
558
				$out=dol_string_nohtmltag($out);
559
			}
560
			break;
561
		case 'custom':
562
			if (empty($filter)) return 'BadFourthParameterForGETPOST';
563
			$out=filter_var($out, $filter, $options);
564
			break;
565
	}
566
567
	// Code for search criteria persistence.
568
	// Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
569
	if (empty($method) || $method == 3 || $method == 4)
570
	{
571
		//if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield", 'smonth', 'syear', 'month', 'year')))
572
		if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder','sortfield')))
573
		{
574
			//var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
575
576
			// We save search key only if:
577
			// - not empty, or
578
			// - if value is empty and a default value exists that is not empty (it means we did a filter to an empty value when default was not).
579
580
			//if (! empty($out) || ! empty($user->default_values[$relativepathstring]['filters'][$paramname]))
581
			if (! empty($out))
582
			{
583
				$user->lastsearch_values_tmp[$relativepathstring][$paramname]=$out;
0 ignored issues
show
Bug introduced by
The variable $relativepathstring does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
584
			}
585
		}
586
	}
587
588
	return $out;
589
}
590
591
592
/**
593
 *  Return a prefix to use for this Dolibarr instance, for session/cookie names or email id.
594
 *  This prefix is valid in a web context only and is unique for instance and avoid conflict
595
 *  between multi-instances, even when having two instances with one root dir or two instances
596
 *  in virtual servers.
597
 *
598
 *  @param  string  $mode       			'' (prefix for session name) or 'email' (prefix for email id)
599
 *  @return	string      					A calculated prefix
600
 */
601
if (! function_exists('dol_getprefix'))
602
{
603
	function dol_getprefix($mode='')
604
	{
605
		global $conf;
606
607
		// If MAIL_PREFIX_FOR_EMAIL_ID is set and prefix is for email
608
		if ($mode == 'email' && ! empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID))
609
		{
610
			if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
611
			else if (isset($_SERVER["SERVER_NAME"])) return $_SERVER["SERVER_NAME"];
612
		}
613
614
		if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"]))
615
		{
616
			return dol_hash($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
617
			// Use this for a "readable" cookie name
618
			//return dol_sanitizeFileName($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
619
		}
620
		else return dol_hash(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
621
	}
622
}
623
624
/**
625
 *	Make an include_once using default root and alternate root if it fails.
626
 *  To link to a core file, use include(DOL_DOCUMENT_ROOT.'/pathtofile')
627
 *  To link to a module file from a module file, use include './mymodulefile';
628
 *  To link to a module file from a core file, then this function can be used (call by hook / trigger / speciales pages)
629
 *
630
 * 	@param	string	$relpath	Relative path to file (Ie: mydir/myfile, ../myfile, ...)
631
 * 	@param	string	$classname	Class name (deprecated)
632
 *  @return bool                True if load is a success, False if it fails
633
 */
634
function dol_include_once($relpath, $classname='')
635
{
636
	global $conf,$langs,$user,$mysoc;   // Do not remove this. They must be defined for files we include. Other globals var must be retreived with $GLOBALS['var']
637
638
	$fullpath = dol_buildpath($relpath);
639
640
	if (!file_exists($fullpath)) {
641
		dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_ERR);
642
		return false;
643
	}
644
645
	if (! empty($classname) && ! class_exists($classname)) {
646
		return include $fullpath;
647
	} else {
648
		return include_once $fullpath;
649
	}
650
}
651
652
653
/**
654
 *	Return path of url or filesystem. Return alternate root if exists.
655
 *
656
 * 	@param	string	$path						Relative path to file (if mode=0) or relative url (if mode=1). Ie: mydir/myfile, ../myfile
657
 *  @param	int		$type						0=Used for a Filesystem path, 1=Used for an URL path (output relative), 2=Used for an URL path (output full path using same host that current url), 3=Used for an URL path (output full path using host defined into $dolibarr_main_url_root of conf file)
658
 *  @param	int		$returnemptyifnotfound		0:If $type==0 and if file was not found into alternate dir, return default path into main dir (no test on it)
659
 *  											1:If $type==0 and if file was not found into alternate dir, return empty string
660
 *  											2:If $type==0 and if file was not found into alternate dir, test into main dir, return default path if found, empty string if not found
661
 *  @return string								Full filesystem path (if path=0), Full url path (if mode=1)
662
 */
663
function dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
664
{
665
	global $conf;
666
667
	$path=preg_replace('/^\//','',$path);
668
669
	if (empty($type))	// For a filesystem path
670
	{
671
		$res = DOL_DOCUMENT_ROOT.'/'.$path;		// Standard default path
672
		foreach ($conf->file->dol_document_root as $key => $dirroot)	// ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
673
		{
674
			if ($key == 'main') continue;
675
			if (file_exists($dirroot.'/'.$path))
676
			{
677
				$res=$dirroot.'/'.$path;
678
				return $res;
679
			}
680
		}
681
		if ($returnemptyifnotfound)								// Not found into alternate dir
682
		{
683
			if ($returnemptyifnotfound == 1 || ! file_exists($res)) return '';
684
		}
685
	}
686
	else				// For an url path
687
	{
688
		// We try to get local path of file on filesystem from url
689
		// Note that trying to know if a file on disk exist by forging path on disk from url
690
		// works only for some web server and some setup. This is bugged when
691
		// using proxy, rewriting, virtual path, etc...
692
		$res='';
693
		if ($type == 1) $res = DOL_URL_ROOT.'/'.$path;			// Standard value
694
		if ($type == 2) $res = DOL_MAIN_URL_ROOT.'/'.$path;		// Standard value
695
		if ($type == 3) $res = DOL_URL_ROOT.'/'.$path;
696
697
		foreach ($conf->file->dol_document_root as $key => $dirroot)	// ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
698
		{
699
			if ($key == 'main')
700
			{
701
				if ($type == 3)
702
				{
703
					global $dolibarr_main_url_root;
704
705
					// Define $urlwithroot
706
					$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
707
					$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
708
					//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
709
710
					$res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':$urlwithroot).'/'.$path;     // Test on start with http is for old conf syntax
711
				}
712
				continue;
713
			}
714
			preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i',$path,$regs);    // Take part before '?'
715
			if (! empty($regs[1]))
716
			{
717
				//print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
718
				if (file_exists($dirroot.'/'.$regs[1]))
719
				{
720
					if ($type == 1)
721
					{
722
						$res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
723
					}
724
					if ($type == 2)
725
					{
726
						$res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
727
					}
728
					if ($type == 3)
729
					{
730
						global $dolibarr_main_url_root;
731
732
						// Define $urlwithroot
733
						$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
734
						$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
735
						//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
736
737
						$res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':$urlwithroot).$conf->file->dol_url_root[$key].'/'.$path;     // Test on start with http is for old conf syntax
738
					}
739
					break;
740
				}
741
			}
742
		}
743
	}
744
745
	return $res;
746
}
747
748
/**
749
 *	Create a clone of instance of object (new instance with same value for properties)
750
 *  With native = 0: Property that are reference are also new object (true clone). This means $this->db is not valid.
751
 *  With native = 1: Use PHP clone. Property that are reference are same pointer. This means $this->db is still valid.
752
 *
753
 * 	@param	object	$object		Object to clone
754
 *  @param	int		$native		Native method or true method
755
 *	@return object				Object clone
756
 *  @see https://php.net/manual/language.oop5.cloning.php
757
 */
758
function dol_clone($object, $native=0)
759
{
760
	//dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
761
762
	if (empty($native))
763
	{
764
		$myclone=unserialize(serialize($object));
765
	}
766
	else
767
	{
768
		$myclone = clone $object;     // PHP clone is a shallow copy only, not a real clone, so properties of references will keep references (refer to the same target/variable)
769
	}
770
771
	return $myclone;
772
}
773
774
/**
775
 *	Optimize a size for some browsers (phone, smarphone, ...)
776
 *
777
 * 	@param	int		$size		Size we want
778
 * 	@param	string	$type		Type of optimizing:
779
 * 								'' = function used to define a size for truncation
780
 * 								'width' = function is used to define a width
781
 *	@return int					New size after optimizing
782
 */
783
function dol_size($size,$type='')
784
{
785
	global $conf;
786
	if (empty($conf->dol_optimize_smallscreen)) return $size;
787
	if ($type == 'width' && $size > 250) return 250;
788
	else return 10;
789
}
790
791
792
/**
793
 *	Clean a string to use it as a file name
794
 *
795
 *	@param	string	$str            String to clean
796
 * 	@param	string	$newstr			String to replace bad chars with
797
 *  @param	int	    $unaccent		1=Remove also accent (default), 0 do not remove them
798
 *	@return string          		String cleaned (a-zA-Z_)
799
 *
800
 * 	@see        	dol_string_nospecial, dol_string_unaccent, dol_sanitizePathName
801
 */
802
function dol_sanitizeFileName($str,$newstr='_',$unaccent=1)
803
{
804
	$filesystem_forbidden_chars = array('<','>','/','\\','?','*','|','"','°');
805
	return dol_string_nospecial($unaccent?dol_string_unaccent($str):$str, $newstr, $filesystem_forbidden_chars);
806
}
807
808
/**
809
 *	Clean a string to use it as a path name
810
 *
811
 *	@param	string	$str            String to clean
812
 * 	@param	string	$newstr			String to replace bad chars with
813
 *  @param	int	    $unaccent		1=Remove also accent (default), 0 do not remove them
814
 *	@return string          		String cleaned (a-zA-Z_)
815
 *
816
 * 	@see        	dol_string_nospecial, dol_string_unaccent, dol_sanitizeFileName
817
 */
818
function dol_sanitizePathName($str,$newstr='_',$unaccent=1)
819
{
820
	$filesystem_forbidden_chars = array('<','>','?','*','|','"','°');
821
	return dol_string_nospecial($unaccent?dol_string_unaccent($str):$str, $newstr, $filesystem_forbidden_chars);
822
}
823
824
/**
825
 *	Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName
826
 *
827
 *	@param	string	$str			String to clean
828
 *	@return string   	       		Cleaned string
829
 *
830
 * 	@see    		dol_sanitizeFilename, dol_string_nospecial
831
 */
832
function dol_string_unaccent($str)
833
{
834
	if (utf8_check($str))
835
	{
836
		// See http://www.utf8-chartable.de/
837
		$string = rawurlencode($str);
838
		$replacements = array(
839
		'%C3%80' => 'A','%C3%81' => 'A','%C3%82' => 'A','%C3%83' => 'A','%C3%84' => 'A','%C3%85' => 'A',
840
		'%C3%88' => 'E','%C3%89' => 'E','%C3%8A' => 'E','%C3%8B' => 'E',
841
		'%C3%8C' => 'I','%C3%8D' => 'I','%C3%8E' => 'I','%C3%8F' => 'I',
842
		'%C3%92' => 'O','%C3%93' => 'O','%C3%94' => 'O','%C3%95' => 'O','%C3%96' => 'O',
843
		'%C3%99' => 'U','%C3%9A' => 'U','%C3%9B' => 'U','%C3%9C' => 'U',
844
		'%C3%A0' => 'a','%C3%A1' => 'a','%C3%A2' => 'a','%C3%A3' => 'a','%C3%A4' => 'a','%C3%A5' => 'a',
845
		'%C3%A7' => 'c',
846
		'%C3%A8' => 'e','%C3%A9' => 'e','%C3%AA' => 'e','%C3%AB' => 'e',
847
		'%C3%AC' => 'i','%C3%AD' => 'i','%C3%AE' => 'i','%C3%AF' => 'i',
848
		'%C3%B1' => 'n',
849
		'%C3%B2' => 'o','%C3%B3' => 'o','%C3%B4' => 'o','%C3%B5' => 'o','%C3%B6' => 'o',
850
		'%C3%B9' => 'u','%C3%BA' => 'u','%C3%BB' => 'u','%C3%BC' => 'u',
851
		'%C3%BF' => 'y'
852
		);
853
		$string=strtr($string, $replacements);
854
		return rawurldecode($string);
855
	}
856
	else
857
	{
858
		// See http://www.ascii-code.com/
859
		$string = strtr(
860
			$str,
861
			"\xC0\xC1\xC2\xC3\xC4\xC5\xC7
862
			\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
863
			\xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
864
			\xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
865
			\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
866
			\xF9\xFA\xFB\xFC\xFD\xFF",
867
			"AAAAAAC
868
			EEEEIIIIDN
869
			OOOOOUUUY
870
			aaaaaaceeee
871
			iiiidnooooo
872
			uuuuyy"
873
		);
874
		$string = strtr($string, array("\xC4"=>"Ae", "\xC6"=>"AE", "\xD6"=>"Oe", "\xDC"=>"Ue", "\xDE"=>"TH", "\xDF"=>"ss", "\xE4"=>"ae", "\xE6"=>"ae", "\xF6"=>"oe", "\xFC"=>"ue", "\xFE"=>"th"));
875
		return $string;
876
	}
877
}
878
879
/**
880
 *	Clean a string from all punctuation characters to use it as a ref or login.
881
 *  This is a more complete function than dol_sanitizeFileName.
882
 *
883
 *	@param	string	$str            	String to clean
884
 * 	@param	string	$newstr				String to replace forbidden chars with
885
 *  @param  array	$badcharstoreplace  List of forbidden characters
886
 * 	@return string          			Cleaned string
887
 *
888
 * 	@see    		dol_sanitizeFilename, dol_string_unaccent
889
 */
890
function dol_string_nospecial($str,$newstr='_',$badcharstoreplace='')
891
{
892
	$forbidden_chars_to_replace=array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°');  // more complete than dol_sanitizeFileName
893
	$forbidden_chars_to_remove=array();
894
	if (is_array($badcharstoreplace)) $forbidden_chars_to_replace=$badcharstoreplace;
895
	//$forbidden_chars_to_remove=array("(",")");
896
897
	return str_replace($forbidden_chars_to_replace,$newstr,str_replace($forbidden_chars_to_remove,"",$str));
898
}
899
900
901
/**
902
 * Encode string for xml usage
903
 *
904
 * @param 	string	$string		String to encode
905
 * @return	string				String encoded
906
 */
907
function dolEscapeXML($string)
908
{
909
	return strtr($string, array('\''=>'&apos;','"'=>'&quot;','&'=>'&amp;','<'=>'&lt;','>'=>'&gt;'));
910
}
911
912
/**
913
 *  Returns text escaped for inclusion into javascript code
914
 *
915
 *  @param      string		$stringtoescape		String to escape
916
 *  @param		int		$mode				0=Escape also ' and " into ', 1=Escape ' but not " for usage into 'string', 2=Escape " but not ' for usage into "string", 3=Escape ' and " with \
917
 *  @param		int		$noescapebackslashn	0=Escape also \n. 1=Do not escape \n.
918
 *  @return     string     		 				Escaped string. Both ' and " are escaped into ' if they are escaped.
919
 */
920
function dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
921
{
922
	// escape quotes and backslashes, newlines, etc.
923
	$substitjs=array("&#039;"=>"\\'","\r"=>'\\r');
924
	//$substitjs['</']='<\/';	// We removed this. Should be useless.
925
	if (empty($noescapebackslashn)) { $substitjs["\n"]='\\n'; $substitjs['\\']='\\\\'; }
926
	if (empty($mode)) { $substitjs["'"]="\\'"; $substitjs['"']="\\'"; }
927
	else if ($mode == 1) $substitjs["'"]="\\'";
928
	else if ($mode == 2) { $substitjs['"']='\\"'; }
929
	else if ($mode == 3) { $substitjs["'"]="\\'"; $substitjs['"']="\\\""; }
930
	return strtr($stringtoescape, $substitjs);
931
}
932
933
934
/**
935
 *  Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
936
 *
937
 *  @param      string		$stringtoescape		String to escape
938
 *  @param		int			$keepb				1=Preserve b tags (otherwise, remove them)
939
 *  @param      int         $keepn              1=Preserve \r\n strings (otherwise, replace them with escaped value)
940
 *  @return     string     				 		Escaped string
941
 *  @see		dol_string_nohtmltag
942
 */
943
function dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0)
944
{
945
	// escape quotes and backslashes, newlines, etc.
946
	$tmp=html_entity_decode($stringtoescape, ENT_COMPAT, 'UTF-8');		// TODO Use htmlspecialchars_decode instead, that make only required change for html tags
947
	if (! $keepb) $tmp=strtr($tmp, array("<b>"=>'','</b>'=>''));
948
	if (! $keepn) $tmp=strtr($tmp, array("\r"=>'\\r',"\n"=>'\\n'));
949
	return htmlentities($tmp, ENT_COMPAT, 'UTF-8');						// TODO Use htmlspecialchars instead, that make only required change for html tags
950
}
951
952
953
/**
954
 * Convert a string to lower. Never use strtolower because it does not works with UTF8 strings.
955
 *
956
 * @param 	string		$utf8_string		String to encode
957
 * @return 	string							String converted
958
 */
959
function dol_strtolower($utf8_string)
960
{
961
	return mb_strtolower($utf8_string, "UTF-8");
962
}
963
964
/**
965
 * Convert a string to upper. Never use strtolower because it does not works with UTF8 strings.
966
 *
967
 * @param 	string		$utf8_string		String to encode
968
 * @return 	string							String converted
969
 */
970
function dol_strtoupper($utf8_string)
971
{
972
	return mb_strtoupper($utf8_string, "UTF-8");
973
}
974
975
976
/**
977
 *	Write log message into outputs. Possible outputs can be:
978
 *	SYSLOG_HANDLERS = ["mod_syslog_file"]  		file name is then defined by SYSLOG_FILE
979
 *	SYSLOG_HANDLERS = ["mod_syslog_syslog"]  	facility is then defined by SYSLOG_FACILITY
980
 *  Warning, syslog functions are bugged on Windows, generating memory protection faults. To solve
981
 *  this, use logging to files instead of syslog (see setup of module).
982
 *  Note: If constant 'SYSLOG_FILE_NO_ERROR' defined, we never output any error message when writing to log fails.
983
 *  Note: You can get log message into html sources by adding parameter &logtohtml=1 (constant MAIN_LOGTOHTML must be set)
984
 *  This function works only if syslog module is enabled.
985
 * 	This must not use any call to other function calling dol_syslog (avoid infinite loop).
986
 *
987
 * 	@param  string		$message				Line to log. ''=Show nothing
988
 *  @param  int			$level					Log level
989
 *												On Windows LOG_ERR=4, LOG_WARNING=5, LOG_NOTICE=LOG_INFO=6, LOG_DEBUG=6 si define_syslog_variables ou PHP 5.3+, 7 si dolibarr
990
 *												On Linux   LOG_ERR=3, LOG_WARNING=4, LOG_INFO=6, LOG_DEBUG=7
991
 *  @param	int			$ident					1=Increase ident of 1, -1=Decrease ident of 1
992
 *  @param	string		$suffixinfilename		When output is a file, append this suffix into default log filename.
993
 *  @param	string		$restricttologhandler	Output log only for this log handler
994
 *  @return	void
995
 */
996
function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename='', $restricttologhandler='')
997
{
998
	global $conf, $user;
999
1000
	// If syslog module enabled
1001
	if (empty($conf->syslog->enabled)) return;
1002
1003
	if (! empty($message))
1004
	{
1005
		// Test log level
1006
		$logLevels = array(LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG);
1007
		if (!in_array($level, $logLevels, true))
1008
		{
1009
			throw new Exception('Incorrect log level');
1010
		}
1011
		if ($level > $conf->global->SYSLOG_LEVEL) return;
1012
1013
		// If adding log inside HTML page is required
1014
		if (! empty($_REQUEST['logtohtml']) && (! empty($conf->global->MAIN_ENABLE_LOG_TO_HTML) || ! empty($conf->global->MAIN_LOGTOHTML)))   // MAIN_LOGTOHTML kept for backward compatibility
1015
		{
1016
			$conf->logbuffer[] = dol_print_date(time(),"%Y-%m-%d %H:%M:%S")." ".$message;
1017
		}
1018
1019
		//TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1020
		// If enable html log tag enabled and url parameter log defined, we show output log on HTML comments
1021
		if (! empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && ! empty($_GET["log"]))
1022
		{
1023
			print "\n\n<!-- Log start\n";
1024
			print $message."\n";
1025
			print "Log end -->\n";
1026
		}
1027
1028
		$data = array(
1029
			'message' => $message,
1030
			'script' => (isset($_SERVER['PHP_SELF'])? basename($_SERVER['PHP_SELF'],'.php') : false),
1031
			'level' => $level,
1032
			'user' => ((is_object($user) && $user->id) ? $user->login : false),
1033
			'ip' => false
1034
		);
1035
1036
		// This is when server run behind a reverse proxy
1037
		if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].(empty($_SERVER["REMOTE_ADDR"])?'':'->'.$_SERVER['REMOTE_ADDR']);
1038
		// This is when server run normally on a server
1039
		else if (! empty($_SERVER["REMOTE_ADDR"])) $data['ip'] = $_SERVER['REMOTE_ADDR'];
1040
		// This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1041
		else if (! empty($_SERVER['SERVER_ADDR'])) $data['ip'] = $_SERVER['SERVER_ADDR'];
1042
		// This is when PHP session is ran outside a web server, like from Windows command line (Not always defined, but useful if OS defined it).
1043
		else if (! empty($_SERVER['COMPUTERNAME'])) $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME'])?'':'@'.$_SERVER['USERNAME']);
1044
		// This is when PHP session is ran outside a web server, like from Linux command line (Not always defined, but usefull if OS defined it).
1045
		else if (! empty($_SERVER['LOGNAME'])) $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1046
		// Loop on each log handler and send output
1047
		foreach ($conf->loghandlers as $loghandlerinstance)
1048
		{
1049
			if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) continue;
1050
			$loghandlerinstance->export($data,$suffixinfilename);
1051
		}
1052
		unset($data);
1053
	}
1054
1055
	if (! empty($ident))
1056
	{
1057
		foreach ($conf->loghandlers as $loghandlerinstance)
1058
		{
1059
			$loghandlerinstance->setIdent($ident);
1060
		}
1061
	}
1062
}
1063
1064
1065
/**
1066
 *	Show tab header of a card
1067
 *
1068
 *	@param	array	$links				Array of tabs. Currently initialized by calling a function xxx_admin_prepare_head
1069
 *	@param	string	$active     		Active tab name (document', 'info', 'ldap', ....)
1070
 *	@param  string	$title      		Title
1071
 *	@param  int		$notab				-1 or 0=Add tab header, 1=no tab header. If you set this to 1, using dol_fiche_end() to close tab is not required.
1072
 * 	@param	string	$picto				Add a picto on tab title
1073
 *	@param	int		$pictoisfullpath	If 1, image path is a full path. If you set this to 1, you can use url returned by dol_buildpath('/mymodyle/img/myimg.png',1) for $picto.
1074
 *  @param	string	$morehtmlright		Add more html content on right of tabs title
1075
 *  @param	string	$morecss			More Css
1076
 * 	@return	void
1077
 */
1078
function dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='')
1079
{
1080
	print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss);
1081
}
1082
1083
/**
1084
 *  Show tab header of a card
1085
 *
1086
 *	@param	array	$links				Array of tabs
1087
 *	@param	string	$active     		Active tab name
1088
 *	@param  string	$title      		Title
1089
 *	@param  int		$notab				-1 or 0=Add tab header, 1=no tab header. If you set this to 1, using dol_fiche_end() to close tab is not required.
1090
 * 	@param	string	$picto				Add a picto on tab title
1091
 *	@param	int		$pictoisfullpath	If 1, image path is a full path. If you set this to 1, you can use url returned by dol_buildpath('/mymodyle/img/myimg.png',1) for $picto.
1092
 *  @param	string	$morehtmlright		Add more html content on right of tabs title
1093
 *  @param	string	$morecss			More Css
1094
 * 	@return	string
1095
 */
1096
function dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='')
1097
{
1098
	global $conf, $langs, $hookmanager;
1099
1100
	$out="\n".'<div class="tabs" data-role="controlgroup" data-type="horizontal">'."\n";
1101
1102
	if ($morehtmlright) $out.='<div class="inline-block floatright tabsElem">'.$morehtmlright.'</div>';	// Output right area first so when space is missing, text is in front of tabs and not under.
1103
1104
	// Show title
1105
	$showtitle=1;
1106
	if (! empty($conf->dol_optimize_smallscreen)) $showtitle=0;
1107
	if (! empty($title) && $showtitle)
1108
	{
1109
		$limittitle=30;
1110
		$out.='<a class="tabTitle">';
1111
		if ($picto) $out.=img_picto($title,($pictoisfullpath?'':'object_').$picto,'',$pictoisfullpath).' ';
1112
		$out.='<span class="tabTitleText">'.dol_trunc($title,$limittitle).'</span>';
1113
		$out.='</a>';
1114
	}
1115
1116
	// Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
1117
	$maxkey=-1;
1118
	if (is_array($links) && ! empty($links))
1119
	{
1120
		$keys=array_keys($links);
1121
		if (count($keys)) $maxkey=max($keys);
1122
	}
1123
1124
	if (! empty($conf->dol_optimize_smallscreen)) $conf->global->MAIN_MAXTABS_IN_CARD=2;
1125
1126
	// Show tabs
1127
	$bactive=false;
1128
	// if =0 we don't use the feature
1129
	$limittoshow=(empty($conf->global->MAIN_MAXTABS_IN_CARD)?99:$conf->global->MAIN_MAXTABS_IN_CARD);
1130
	$displaytab=0;
1131
	$nbintab=0;
1132
	$popuptab=0; $outmore='';
1133
	for ($i = 0 ; $i <= $maxkey ; $i++)
1134
	{
1135
		if ((is_numeric($active) && $i == $active) || (! empty($links[$i][2]) && ! is_numeric($active) && $active == $links[$i][2]))
1136
		{
1137
			// If active tab is already present
1138
			if ($i >= $limittoshow) $limittoshow--;
1139
		}
1140
	}
1141
1142
	for ($i = 0 ; $i <= $maxkey ; $i++)
1143
	{
1144
		if ((is_numeric($active) && $i == $active) || (! empty($links[$i][2]) && ! is_numeric($active) && $active == $links[$i][2]))
1145
		{
1146
			$isactive=true;
1147
			$bactive=true;
1148
		}
1149
		else
1150
		{
1151
			$isactive=false;
1152
		}
1153
1154
		if ($i < $limittoshow || $isactive)
1155
		{
1156
			$out.='<div class="inline-block tabsElem'.($isactive ? ' tabsElemActive' : '').((! $isactive && ! empty($conf->global->MAIN_HIDE_INACTIVETAB_ON_PRINT))?' hideonprint':'').'"><!-- id tab = '.(empty($links[$i][2])?'':$links[$i][2]).' -->';
1157
			if (isset($links[$i][2]) && $links[$i][2] == 'image')
1158
			{
1159
				if (!empty($links[$i][0]))
1160
				{
1161
					$out.='<a class="tabimage'.($morecss?' '.$morecss:'').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1162
				}
1163
				else
1164
				{
1165
					$out.='<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1166
				}
1167
			}
1168
			else if (! empty($links[$i][1]))
1169
			{
1170
				//print "x $i $active ".$links[$i][2]." z";
1171
				if ($isactive)
1172
				{
1173
					$out.='<a'.(! empty($links[$i][2])?' id="'.$links[$i][2].'"':'').' class="tabactive tab inline-block'.($morecss?' '.$morecss:'').'" href="'.$links[$i][0].'">';
1174
					$out.=$links[$i][1];
1175
					$out.='</a>'."\n";
1176
				}
1177
				else
1178
				{
1179
					$out.='<a'.(! empty($links[$i][2])?' id="'.$links[$i][2].'"':'').' class="tabunactive tab inline-block'.($morecss?' '.$morecss:'').'" href="'.$links[$i][0].'">';
1180
					$out.=$links[$i][1];
1181
					$out.='</a>'."\n";
1182
				}
1183
			}
1184
			$out.='</div>';
1185
		}
1186
		else
1187
		{
1188
			// The popup with the other tabs
1189
			if (! $popuptab)
1190
			{
1191
				$popuptab=1;
1192
				$outmore.='<div class="popuptabset wordwrap">';	// The css used to hide/show popup
1193
			}
1194
			$outmore.='<div class="popuptab wordwrap" style="display:inherit;">';
1195
			if (isset($links[$i][2]) && $links[$i][2] == 'image')
1196
			{
1197
				if (!empty($links[$i][0]))
1198
					$outmore.='<a class="tabimage'.($morecss?' '.$morecss:'').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1199
				else
1200
					$outmore.='<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1201
1202
			}
1203
			else if (! empty($links[$i][1]))
1204
			{
1205
				$outmore.='<a'.(! empty($links[$i][2])?' id="'.$links[$i][2].'"':'').' class="wordwrap inline-block'.($morecss?' '.$morecss:'').'" href="'.$links[$i][0].'">';
1206
				$outmore.=preg_replace('/([a-z])\/([a-z])/i', '\\1 / \\2', $links[$i][1]);	// Replace x/y with x / y to allow wrap on long composed texts.
1207
				$outmore.='</a>'."\n";
1208
			}
1209
			$outmore.='</div>';
1210
1211
			$nbintab++;
1212
		}
1213
		$displaytab=$i;
1214
	}
1215
	if ($popuptab) $outmore.='</div>';
1216
1217
	if ($displaytab > $limittoshow)
1218
	{
1219
		$left=($langs->trans("DIRECTION") == 'rtl'?'right':'left');
1220
		$right=($langs->trans("DIRECTION") == 'rtl'?'left':'right');
1221
1222
		$tabsname=str_replace("@", "", $picto);
1223
		$out.='<div id="moretabs'.$tabsname.'" class="inline-block tabsElem">';
1224
		$out.='<a href="#" class="tab moretab inline-block tabunactive reposition">'.$langs->trans("More").'... ('.$nbintab.')</a>';
1225
		$out.='<div id="moretabsList'.$tabsname.'" style="position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px">';
1226
		$out.=$outmore;
1227
		$out.='</div>';
1228
		$out.='<div></div>';
1229
		$out.="</div>\n";
1230
1231
		$out.="<script>";
1232
		$out.="$('#moretabs".$tabsname."').mouseenter( function() { console.log('mouseenter ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','auto');});";
1233
		$out.="$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
1234
		$out.="</script>";
1235
	}
1236
1237
	$out.="</div>\n";
1238
1239
	if (! $notab || $notab == -1) $out.="\n".'<div class="tabBar'.($notab == -1 ? '' : ' tabBarWithBottom').'">'."\n";
1240
1241
	$parameters=array('tabname' => $active, 'out' => $out);
1242
	$reshook=$hookmanager->executeHooks('printTabsHead',$parameters);	// This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
1243
	if ($reshook > 0)
1244
	{
1245
		$out = $hookmanager->resPrint;
1246
	}
1247
1248
	return $out;
1249
}
1250
1251
/**
1252
 *  Show tab footer of a card
1253
 *
1254
 *  @param	int		$notab       -1 or 0=Add tab footer, 1=no tab footer
1255
 *  @return	void
1256
 */
1257
function dol_fiche_end($notab=0)
1258
{
1259
	print dol_get_fiche_end($notab);
1260
}
1261
1262
/**
1263
 *	Return tab footer of a card
1264
 *
1265
 *	@param  int		$notab		-1 or 0=Add tab footer, 1=no tab footer
1266
 *  @return	string
1267
 */
1268
function dol_get_fiche_end($notab=0)
1269
{
1270
	if (! $notab || $notab == -1) return "\n</div>\n";
1271
	else return '';
1272
}
1273
1274
/**
1275
 *  Show tab footer of a card.
1276
 *  Note: $object->next_prev_filter can be set to restrict select to find next or previous record by $form->showrefnav.
1277
 *
1278
 *  @param	Object	$object			Object to show
1279
 *  @param	string	$paramid   		Name of parameter to use to name the id into the URL next/previous link
1280
 *  @param	string	$morehtml  		More html content to output just before the nav bar
1281
 *  @param	int		$shownav	  	Show Condition (navigation is shown if value is 1)
1282
 *  @param	string	$fieldid   		Nom du champ en base a utiliser pour select next et previous (we make the select max and min on this field). Use 'none' for no prev/next search.
1283
 *  @param	string	$fieldref   	Nom du champ objet ref (object->ref) a utiliser pour select next et previous
1284
 *  @param	string	$morehtmlref  	More html to show after ref
1285
 *  @param	string	$moreparam  	More param to add in nav link url.
1286
 *	@param	int		$nodbprefix		Do not include DB prefix to forge table name
1287
 *	@param	string	$morehtmlleft	More html code to show before ref
1288
 *	@param	string	$morehtmlstatus	More html code to show under navigation arrows
1289
 *  @param  int     $onlybanner     Put this to 1, if the card will contains only a banner (this add css 'arearefnobottom' on div)
1290
 *	@param	string	$morehtmlright	More html code to show before navigation arrows
1291
 *  @return	void
1292
 */
1293
function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $onlybanner=0, $morehtmlright='')
1294
{
1295
	global $conf, $form, $user, $langs;
1296
1297
	$error = 0;
1298
1299
	$maxvisiblephotos=1;
1300
	$showimage=1;
1301
	$showbarcode=empty($conf->barcode->enabled)?0:($object->barcode?1:0);
1302
	if (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) $showbarcode=0;
1303
	$modulepart='unknown';
1304
1305
	if ($object->element == 'societe')         $modulepart='societe';
1306
	if ($object->element == 'contact')         $modulepart='contact';
1307
	if ($object->element == 'member')          $modulepart='memberphoto';
1308
	if ($object->element == 'user')            $modulepart='userphoto';
1309
	if ($object->element == 'product')         $modulepart='product';
1310
1311
	if (class_exists("Imagick"))
1312
	{
1313
		if ($object->element == 'propal')            $modulepart='propal';
1314
		if ($object->element == 'commande')          $modulepart='commande';
1315
		if ($object->element == 'facture')           $modulepart='facture';
1316
		if ($object->element == 'fichinter')         $modulepart='ficheinter';
1317
		if ($object->element == 'contrat')           $modulepart='contract';
1318
		if ($object->element == 'supplier_proposal') $modulepart='supplier_proposal';
1319
		if ($object->element == 'order_supplier')    $modulepart='supplier_order';
1320
		if ($object->element == 'invoice_supplier')  $modulepart='supplier_invoice';
1321
		if ($object->element == 'expensereport')     $modulepart='expensereport';
1322
	}
1323
1324
	if ($object->element == 'product')
1325
	{
1326
		$width=80; $cssclass='photoref';
1327
		$showimage=$object->is_photo_available($conf->product->multidir_output[$object->entity]);
1328
		$maxvisiblephotos=(isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO)?$conf->global->PRODUCT_MAX_VISIBLE_PHOTO:5);
1329
		if ($conf->browser->phone) $maxvisiblephotos=1;
1330
		if ($showimage) $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos($conf->product->multidir_output[$object->entity],'small',$maxvisiblephotos,0,0,0,$width,0).'</div>';
1331
		else
1332
		{
1333
			if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
1334
				$nophoto='';
1335
				$morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"></div>';
1336
			}
1337
			//elseif ($conf->browser->layout != 'phone') {    // Show no photo link
1338
				$nophoto='/public/theme/common/nophoto.png';
1339
				$morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass?' '.$cssclass:'').'" alt="No photo" border="0"'.($width?' width="'.$width.'"':'').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
1340
			//}
1341
		}
1342
	}
1343
	else
1344
	{
1345
		if ($showimage)
1346
		{
1347
			if ($modulepart != 'unknown')
1348
			{
1349
				$phototoshow='';
1350
				// Check if a preview file is available
1351
				if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick"))
1352
				{
1353
					$objectref = dol_sanitizeFileName($object->ref);
1354
					$dir_output = $conf->$modulepart->dir_output . "/";
1355
					if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice')))
1356
					{
1357
						$subdir = get_exdir($object->id, 2, 0, 0, $object, $modulepart).$objectref;		// the objectref dir is not include into get_exdir when used with level=2, so we add it here
1358
					}
1359
					else
1360
					{
1361
						$subdir = get_exdir($object->id, 0, 0, 0, $object, $modulepart);
1362
					}
1363
1364
					$filepath = $dir_output . $subdir . "/";
1365
					$file = $filepath . $objectref . ".pdf";
1366
					$relativepath = $subdir.'/'.$objectref.'.pdf';
1367
1368
					// Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
1369
					$fileimage = $file.'_preview.png';              // If PDF has 1 page
1370
					$fileimagebis = $file.'_preview-0.png';         // If PDF has more than one page
1371
					$relativepathimage = $relativepath.'_preview.png';
1372
1373
					// Si fichier PDF existe
1374
					if (file_exists($file))
1375
					{
1376
						$encfile = urlencode($file);
1377
						// Conversion du PDF en image png si fichier png non existant
1378
						if ( (! file_exists($fileimage) || (filemtime($fileimage) < filemtime($file)))
1379
						  && (! file_exists($fileimagebis) || (filemtime($fileimagebis) < filemtime($file)))
1380
						   )
1381
						{
1382
							if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS))		// If you experienc trouble with pdf thumb generation and imagick, you can disable here.
1383
							{
1384
								$ret = dol_convert_file($file, 'png', $fileimage);
1385
								if ($ret < 0) $error++;
1386
							}
1387
						}
1388
1389
						$heightforphotref=70;
1390
						if (! empty($conf->dol_optimize_smallscreen)) $heightforphotref=60;
1391
						// Si fichier png PDF d'1 page trouve
1392
						if (file_exists($fileimage))
1393
						{
1394
							$phototoshow = '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
1395
							$phototoshow.= '<img height="'.$heightforphotref.'" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT . '/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
1396
							$phototoshow.= '</div></div>';
1397
						}
1398
						// Si fichier png PDF de plus d'1 page trouve
1399
						elseif (file_exists($fileimagebis))
1400
						{
1401
							$preview = preg_replace('/\.png/','',$relativepathimage) . "-0.png";
1402
							$phototoshow = '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
1403
							$phototoshow.= '<img height="'.$heightforphotref.'" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT . '/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($preview).'"><p>';
1404
							$phototoshow.= '</div></div>';
1405
						}
1406
					}
1407
				}
1408
				else if (! $phototoshow)
1409
				{
1410
					$phototoshow = $form->showphoto($modulepart,$object,0,0,0,'photoref','small',1,0,$maxvisiblephotos);
1411
				}
1412
1413
				if ($phototoshow)
1414
				{
1415
					$morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">';
1416
					$morehtmlleft.=$phototoshow;
1417
					$morehtmlleft.='</div>';
1418
				}
1419
			}
1420
1421
			if (! $phototoshow)      // Show No photo link (picto of pbject)
0 ignored issues
show
Bug introduced by
The variable $phototoshow does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1422
			{
1423
				$morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">';
1424
				if ($object->element == 'action')
1425
				{
1426
					$width=80;
1427
					$cssclass='photorefcenter';
1428
					$nophoto=img_picto('', 'title_agenda', '', false, 1);
1429
				}
1430
				else
1431
				{
1432
					$width=14; $cssclass='photorefcenter';
1433
					$picto = $object->picto;
1434
					if ($object->element == 'project' && ! $object->public) $picto = 'project'; // instead of projectpub
1435
					$nophoto=img_picto('', 'object_'.$picto, '', false, 1);
1436
				}
1437
				$morehtmlleft.='<!-- No photo to show -->';
1438
				$morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo'.$modulepart.($cssclass?' '.$cssclass:'').'" alt="No photo" border="0"'.($width?' width="'.$width.'"':'').' src="'.$nophoto.'"></div></div>';
1439
1440
				$morehtmlleft.='</div>';
1441
			}
1442
		}
1443
	}
1444
1445
	if ($showbarcode) $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object).'</div>';
1446
1447
	if ($object->element == 'societe')
1448
	{
1449
		if (! empty($conf->use_javascript_ajax) && $user->rights->societe->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE))
1450
		{
1451
		   	$morehtmlstatus.=ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
1452
		}
1453
		else {
1454
			$morehtmlstatus.=$object->getLibStatut(6);
1455
		}
1456
	}
1457
	elseif ($object->element == 'product')
1458
	{
1459
		//$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
1460
		if (! empty($conf->use_javascript_ajax) && $user->rights->produit->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1461
			$morehtmlstatus.=ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
1462
		} else {
1463
			$morehtmlstatus.='<span class="statusrefsell">'.$object->getLibStatut(5,0).'</span>';
1464
		}
1465
		$morehtmlstatus.=' &nbsp; ';
1466
		//$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
1467
		if (! empty($conf->use_javascript_ajax) && $user->rights->produit->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1468
			$morehtmlstatus.=ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
1469
		} else {
1470
			$morehtmlstatus.='<span class="statusrefbuy">'.$object->getLibStatut(5,1).'</span>';
1471
		}
1472
	}
1473
	elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan')))
1474
	{
1475
		$tmptxt=$object->getLibStatut(6, $object->totalpaye);
1476
		if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3) || $conf->browser->layout=='phone') $tmptxt=$object->getLibStatut(5, $object->totalpaye);
1477
		$morehtmlstatus.=$tmptxt;
1478
	}
1479
	elseif ($object->element == 'contrat' || $object->element == 'contract')
1480
	{
1481
		if ($object->statut == 0) $morehtmlstatus.=$object->getLibStatut(5);
1482
		else $morehtmlstatus.=$object->getLibStatut(4);
1483
	}
1484
	elseif ($object->element == 'facturerec')
1485
	{
1486
		if ($object->frequency == 0) $morehtmlstatus.=$object->getLibStatut(2);
1487
		else $morehtmlstatus.=$object->getLibStatut(5);
1488
	}
1489
	elseif ($object->element == 'project_task')
1490
	{
1491
		$object->fk_statut = 1;
1492
		if ($object->progress > 0) $object->fk_statut = 2;
1493
		if ($object->progress >= 100) $object->fk_statut = 3;
1494
		$tmptxt=$object->getLibStatut(5);
1495
		$morehtmlstatus.=$tmptxt;		// No status on task
1496
	}
1497
	else { // Generic case
1498
		$tmptxt=$object->getLibStatut(6);
1499
		if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3) || $conf->browser->layout=='phone') $tmptxt=$object->getLibStatut(5);
1500
		$morehtmlstatus.=$tmptxt;
1501
	}
1502
1503
	// Add if object was dispatched "into accountancy"
1504
	if (! empty($conf->accounting->enabled) && in_array($object->element, array('bank', 'facture', 'invoice', 'invoice_supplier', 'expensereport')))
1505
	{
1506
		if (method_exists($object, 'getVentilExportCompta'))
1507
		{
1508
			$accounted = $object->getVentilExportCompta();
1509
			$langs->load("accountancy");
1510
			$morehtmlstatus.='</div><div class="statusref statusrefbis">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted"));
1511
		}
1512
	}
1513
1514
	// Add alias for thirdparty
1515
	if (! empty($object->name_alias)) $morehtmlref.='<div class="refidno">'.$object->name_alias.'</div>';
1516
1517
	// Add label
1518
	if ($object->element == 'product' || $object->element == 'bank_account' || $object->element == 'project_task')
1519
	{
1520
		if (! empty($object->label)) $morehtmlref.='<div class="refidno">'.$object->label.'</div>';
1521
	}
1522
1523
	if (method_exists($object, 'getBannerAddress') && $object->element != 'product' && $object->element != 'bookmark' && $object->element != 'ecm_directories' && $object->element != 'ecm_files')
1524
	{
1525
		$morehtmlref.='<div class="refidno">';
1526
		$morehtmlref.=$object->getBannerAddress('refaddress',$object);
1527
		$morehtmlref.='</div>';
1528
	}
1529
	if (! empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && in_array($object->element, array('societe', 'contact', 'member', 'product')))
1530
	{
1531
		$morehtmlref.='<div style="clear: both;"></div><div class="refidno">';
1532
		$morehtmlref.=$langs->trans("TechnicalID").': '.$object->id;
1533
		$morehtmlref.='</div>';
1534
	}
1535
1536
	print '<div class="'.($onlybanner?'arearefnobottom ':'arearef ').'heightref valignmiddle" width="100%">';
1537
	print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
1538
	print '</div>';
1539
	print '<div class="underrefbanner clearboth"></div>';
1540
}
1541
1542
/**
1543
 * Show a string with the label tag dedicated to the HTML edit field.
1544
 *
1545
 * @param	string	$langkey		Translation key
1546
 * @param 	string	$fieldkey		Key of the html select field the text refers to
1547
 * @param	int		$fieldrequired	1=Field is mandatory
1548
 * @deprecated Form::editfieldkey
1549
 */
1550
function fieldLabel($langkey, $fieldkey, $fieldrequired=0)
1551
{
1552
	global $conf, $langs;
1553
	$ret='';
1554
	if ($fieldrequired) $ret.='<span class="fieldrequired">';
1555
	if (($conf->dol_use_jmobile != 4)) $ret.='<label for="'.$fieldkey.'">';
1556
	$ret.=$langs->trans($langkey);
1557
	if (($conf->dol_use_jmobile != 4)) $ret.='</label>';
1558
	if ($fieldrequired) $ret.='</span>';
1559
	return $ret;
1560
}
1561
1562
/**
1563
 * Return string to add class property on html element with pair/impair.
1564
 *
1565
 * @param	string	$var			0 or 1
1566
 * @param	string	$moreclass		More class to add
1567
 * @return	string					String to add class onto HTML element
1568
 */
1569
function dol_bc($var,$moreclass='')
1570
{
1571
	global $bc;
1572
	$ret=' '.$bc[$var];
1573
	if ($moreclass) $ret=preg_replace('/class=\"/','class="'.$moreclass.' ',$ret);
1574
	return $ret;
1575
}
1576
1577
/**
1578
 *      Return a formated address (part address/zip/town/state) according to country rules
1579
 *
1580
 *      @param  Object		$object			A company or contact object
1581
 * 	    @param	int			$withcountry		1=Add country into address string
1582
 *      @param	string		$sep				Separator to use to build string
1583
 *      @param	Translate	$outputlangs		Object lang that contains language for text translation.
1584
 *      @param	int		$mode		0=Standard output, 1=Remove address
1585
 *      @return string						Formated string
1586
 *      @see dol_print_address
1587
 */
1588
function dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs='', $mode=0)
1589
{
1590
	global $conf,$langs;
1591
1592
	$ret='';
1593
	$countriesusingstate=array('AU','CA','US','IN','GB','ES','UK','TR');    // See also MAIN_FORCE_STATE_INTO_ADDRESS
1594
1595
	// Address
1596
	if (empty($mode)) {
1597
		$ret .= $object->address;
1598
	}
1599
	// Zip/Town/State
1600
	if (in_array($object->country_code,array('AU', 'CA', 'US')) || ! empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS))   	// US: title firstname name \n address lines \n town, state, zip \n country
1601
	{
1602
		$ret .= ($ret ? $sep : '' ).$object->town;
1603
		if ($object->state)
1604
		{
1605
			$ret.=($ret?", ":'').$object->state;
1606
		}
1607
		if ($object->zip) $ret .= ($ret?", ":'').$object->zip;
1608
	}
1609
	else if (in_array($object->country_code,array('GB','UK'))) // UK: title firstname name \n address lines \n town state \n zip \n country
1610
	{
1611
		$ret .= ($ret ? $sep : '' ).$object->town;
1612
		if ($object->state)
1613
		{
1614
			$ret.=($ret?", ":'').$object->state;
1615
		}
1616
		if ($object->zip) $ret .= ($ret ? $sep : '' ).$object->zip;
1617
	}
1618
	else if (in_array($object->country_code,array('ES','TR'))) // ES: title firstname name \n address lines \n zip town \n state \n country
1619
	{
1620
		$ret .= ($ret ? $sep : '' ).$object->zip;
1621
		$ret .= ($object->town?(($object->zip?' ':'').$object->town):'');
1622
		if ($object->state)
1623
		{
1624
			$ret.="\n".$object->state;
1625
		}
1626
	}
1627
	else if (in_array($object->country_code,array('IT'))) // IT: tile firstname name\n address lines \n zip (Code Departement) \n country
1628
	{
1629
                $ret .= ($ret ? $sep : '' ).$object->zip;
1630
                $ret .= ($object->town?(($object->zip?' ':'').$object->town):'');
1631
                $ret .= ($object->departement_id?(' ('.($object->departement_id).')'):'');
1632
	}
1633
	else                                        		// Other: title firstname name \n address lines \n zip town \n country
1634
	{
1635
		$ret .= $object->zip ? (($ret ? $sep : '' ).$object->zip) : '';
1636
		$ret .= ($object->town?(($object->zip?' ':($ret ? $sep : '' )).$object->town):'');
1637
		if ($object->state && in_array($object->country_code,$countriesusingstate))
1638
		{
1639
			$ret.=($ret?", ":'').$object->state;
1640
		}
1641
	}
1642
	if (! is_object($outputlangs)) $outputlangs=$langs;
1643
	if ($withcountry) $ret.=($object->country_code?($ret?$sep:'').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)):'');
1644
1645
	return $ret;
1646
}
1647
1648
1649
1650
/**
1651
 *	Format a string.
1652
 *
1653
 *	@param	string	$fmt		Format of strftime function (http://php.net/manual/fr/function.strftime.php)
1654
 *  @param	int		$ts			Timesamp (If is_gmt is true, timestamp is already includes timezone and daylight saving offset, if is_gmt is false, timestamp is a GMT timestamp and we must compensate with server PHP TZ)
1655
 *  @param	int		$is_gmt		See comment of timestamp parameter
1656
 *	@return	string				A formatted string
1657
 */
1658
function dol_strftime($fmt, $ts=false, $is_gmt=false)
1659
{
1660
	if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
1661
		return ($is_gmt)? @gmstrftime($fmt,$ts): @strftime($fmt,$ts);
1662
	}
1663
	else return 'Error date into a not supported range';
1664
}
1665
1666
/**
1667
 *	Output date in a string format according to outputlangs (or langs if not defined).
1668
 * 	Return charset is always UTF-8, except if encodetoouput is defined. In this case charset is output charset
1669
 *
1670
 *	@param	int			$time			GM Timestamps date
1671
 *	@param	string		$format      	Output date format (tag of strftime function)
1672
 *										"%d %b %Y",
1673
 *										"%d/%m/%Y %H:%M",
1674
 *										"%d/%m/%Y %H:%M:%S",
1675
 *                                      "%B"=Long text of month, "%A"=Long text of day, "%b"=Short text of month, "%a"=Short text of day
1676
 *										"day", "daytext", "dayhour", "dayhourldap", "dayhourtext", "dayrfc", "dayhourrfc", "...reduceformat"
1677
 * 	@param	string		$tzoutput		true or 'gmt' => string is for Greenwich location
1678
 * 										false or 'tzserver' => output string is for local PHP server TZ usage
1679
 * 										'tzuser' => output string is for user TZ (current browser TZ with current dst) => In a future, we should have same behaviour than 'tzuserrel'
1680
 *                                      'tzuserrel' => output string is for user TZ (current browser TZ with dst or not, depending on date position) (TODO not implemented yet)
1681
 *	@param	Translate	$outputlangs	Object lang that contains language for text translation.
1682
 *  @param  boolean		$encodetooutput false=no convert into output pagecode
1683
 * 	@return string      				Formated date or '' if time is null
1684
 *
1685
 *  @see        dol_mktime, dol_stringtotime, dol_getdate
1686
 */
1687
function dol_print_date($time,$format='',$tzoutput='tzserver',$outputlangs='',$encodetooutput=false)
1688
{
1689
	global $conf,$langs;
1690
1691
	// Clean parameters
1692
	$to_gmt=false;
1693
	$offsettz=$offsetdst=0;
1694
	if ($tzoutput)
1695
	{
1696
		$to_gmt=true;	// For backward compatibility
1697
		if (is_string($tzoutput))
1698
		{
1699
			if ($tzoutput == 'tzserver')
1700
			{
1701
				$to_gmt=false;
1702
				$offsettzstring=@date_default_timezone_get();		// Example 'Europe/Berlin' or 'Indian/Reunion'
1703
				$offsettz=0;
1704
				$offsetdst=0;
1705
			}
1706
			elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel')
1707
			{
1708
				$to_gmt=true;
1709
				$offsettzstring=(empty($_SESSION['dol_tz_string'])?'UTC':$_SESSION['dol_tz_string']);	// Example 'Europe/Berlin' or 'Indian/Reunion'
1710
				$offsettz=(empty($_SESSION['dol_tz'])?0:$_SESSION['dol_tz'])*60*60;		// Will not be used anymore
1711
				$offsetdst=(empty($_SESSION['dol_dst'])?0:$_SESSION['dol_dst'])*60*60;	// Will not be used anymore
1712
			}
1713
		}
1714
	}
1715
	if (! is_object($outputlangs)) $outputlangs=$langs;
1716
	if (! $format) $format='daytextshort';
1717
	$reduceformat=(! empty($conf->dol_optimize_smallscreen) && in_array($format,array('day','dayhour')))?1:0;
1718
	$formatwithoutreduce = preg_replace('/reduceformat/','',$format);
1719
	if ($formatwithoutreduce != $format) { $format = $formatwithoutreduce; $reduceformat=1; }  // so format 'dayreduceformat' is processed like day
1720
1721
	// Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
1722
	// TODO Add format daysmallyear and dayhoursmallyear
1723
	if ($format == 'day')				$format=($outputlangs->trans("FormatDateShort")!="FormatDateShort"?$outputlangs->trans("FormatDateShort"):$conf->format_date_short);
1724
	else if ($format == 'hour')			$format=($outputlangs->trans("FormatHourShort")!="FormatHourShort"?$outputlangs->trans("FormatHourShort"):$conf->format_hour_short);
1725
	else if ($format == 'hourduration')	$format=($outputlangs->trans("FormatHourShortDuration")!="FormatHourShortDuration"?$outputlangs->trans("FormatHourShortDuration"):$conf->format_hour_short_duration);
1726
	else if ($format == 'daytext')			 $format=($outputlangs->trans("FormatDateText")!="FormatDateText"?$outputlangs->trans("FormatDateText"):$conf->format_date_text);
1727
	else if ($format == 'daytextshort')	$format=($outputlangs->trans("FormatDateTextShort")!="FormatDateTextShort"?$outputlangs->trans("FormatDateTextShort"):$conf->format_date_text_short);
1728
	else if ($format == 'dayhour')			 $format=($outputlangs->trans("FormatDateHourShort")!="FormatDateHourShort"?$outputlangs->trans("FormatDateHourShort"):$conf->format_date_hour_short);
1729
	else if ($format == 'dayhoursec')		 $format=($outputlangs->trans("FormatDateHourSecShort")!="FormatDateHourSecShort"?$outputlangs->trans("FormatDateHourSecShort"):$conf->format_date_hour_sec_short);
1730
	else if ($format == 'dayhourtext')		 $format=($outputlangs->trans("FormatDateHourText")!="FormatDateHourText"?$outputlangs->trans("FormatDateHourText"):$conf->format_date_hour_text);
1731
	else if ($format == 'dayhourtextshort') $format=($outputlangs->trans("FormatDateHourTextShort")!="FormatDateHourTextShort"?$outputlangs->trans("FormatDateHourTextShort"):$conf->format_date_hour_text_short);
1732
	// Format not sensitive to language
1733
	else if ($format == 'dayhourlog')		 $format='%Y%m%d%H%M%S';
1734
	else if ($format == 'dayhourldap')		 $format='%Y%m%d%H%M%SZ';
1735
	else if ($format == 'dayhourxcard')	$format='%Y%m%dT%H%M%SZ';
1736
	else if ($format == 'dayxcard')	 	$format='%Y%m%d';
1737
	else if ($format == 'dayrfc')			 $format='%Y-%m-%d';             // DATE_RFC3339
1738
	else if ($format == 'dayhourrfc')		 $format='%Y-%m-%dT%H:%M:%SZ';   // DATETIME RFC3339
1739
	else if ($format == 'standard')		$format='%Y-%m-%d %H:%M:%S';
1740
1741
	if ($reduceformat)
1742
	{
1743
		$format=str_replace('%Y','%y',$format);
1744
		$format=str_replace('yyyy','yy',$format);
1745
	}
1746
1747
	// If date undefined or "", we return ""
1748
	if (dol_strlen($time) == 0) return '';		// $time=0 allowed (it means 01/01/1970 00:00:00)
1749
1750
	// Clean format
1751
	if (preg_match('/%b/i',$format))		// There is some text to translate
1752
	{
1753
		// We inhibate translation to text made by strftime functions. We will use trans instead later.
1754
		$format=str_replace('%b','__b__',$format);
1755
		$format=str_replace('%B','__B__',$format);
1756
	}
1757
	if (preg_match('/%a/i',$format))		// There is some text to translate
1758
	{
1759
		// We inhibate translation to text made by strftime functions. We will use trans instead later.
1760
		$format=str_replace('%a','__a__',$format);
1761
		$format=str_replace('%A','__A__',$format);
1762
	}
1763
1764
	// Analyze date
1765
	if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i',$time,$reg)
1766
	|| preg_match('/^([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])$/i',$time,$reg))	// Deprecated. Ex: 1970-01-01, 1970-01-01 01:00:00, 19700101010000
1767
	{
1768
		// TODO Remove this.
1769
		// This part of code should not be used.
1770
		dol_syslog("Functions.lib::dol_print_date function call with deprecated value of time in page ".$_SERVER["PHP_SELF"], LOG_ERR);
1771
		// Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' or 'YYYYMMDDHHMMSS'
1772
		$syear	= (! empty($reg[1]) ? $reg[1] : '');
1773
		$smonth	= (! empty($reg[2]) ? $reg[2] : '');
1774
		$sday	= (! empty($reg[3]) ? $reg[3] : '');
1775
		$shour	= (! empty($reg[4]) ? $reg[4] : '');
1776
		$smin	= (! empty($reg[5]) ? $reg[5] : '');
1777
		$ssec	= (! empty($reg[6]) ? $reg[6] : '');
1778
1779
		$time=dol_mktime($shour,$smin,$ssec,$smonth,$sday,$syear,true);
1780
		$ret=adodb_strftime($format, $time+$offsettz+$offsetdst, $to_gmt);
1781
	}
1782
	else
1783
	{
1784
		// Date is a timestamps
1785
		if ($time < 100000000000)	// Protection against bad date values
1786
		{
1787
			$timetouse = $time+$offsettz+$offsetdst;	// TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1788
1789
			$ret=adodb_strftime($format, $timetouse, $to_gmt);
1790
		}
1791
		else $ret='Bad value '.$time.' for date';
1792
	}
1793
1794
	if (preg_match('/__b__/i',$format))
1795
	{
1796
		$timetouse = $time+$offsettz+$offsetdst;	// TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1797
1798
		// Here ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs.
1799
		$month=adodb_strftime('%m', $timetouse);
1800
		$month=sprintf("%02d", $month);	// $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
1801
		if ($encodetooutput)
1802
		{
1803
			$monthtext=$outputlangs->transnoentities('Month'.$month);
1804
			$monthtextshort=$outputlangs->transnoentities('MonthShort'.$month);
1805
		}
1806
		else
1807
		{
1808
			$monthtext=$outputlangs->transnoentitiesnoconv('Month'.$month);
1809
			$monthtextshort=$outputlangs->transnoentitiesnoconv('MonthShort'.$month);
1810
		}
1811
		//print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
1812
		$ret=str_replace('__b__',$monthtextshort,$ret);
1813
		$ret=str_replace('__B__',$monthtext,$ret);
1814
		//print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
1815
		//return $ret;
1816
	}
1817
	if (preg_match('/__a__/i',$format))
1818
	{
1819
		$timetouse = $time+$offsettz+$offsetdst;	// TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1820
1821
		$w=adodb_strftime('%w', $timetouse);						// TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1822
		$dayweek=$outputlangs->transnoentitiesnoconv('Day'.$w);
1823
		$ret=str_replace('__A__',$dayweek,$ret);
1824
		$ret=str_replace('__a__',dol_substr($dayweek,0,3),$ret);
1825
	}
1826
1827
	return $ret;
1828
}
1829
1830
1831
/**
1832
 *	Return an array with locale date info.
1833
 *  PHP getdate is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
1834
 *  WARNING: This function always use PHP server timezone to return locale informations !!!
1835
 *  Usage must be avoid.
1836
 *  FIXME: Replace this with PHP date function and a parameter $gm
1837
 *
1838
 *	@param	int			$timestamp      Timestamp
1839
 *	@param	boolean		$fast           Fast mode
1840
 *	@return	array						Array of informations
1841
 *										If no fast mode:
1842
 *										'seconds' => $secs,
1843
 *										'minutes' => $min,
1844
 *										'hours' => $hour,
1845
 *										'mday' => $day,
1846
 *										'wday' => $dow,		0=sunday, 6=saturday
1847
 *										'mon' => $month,
1848
 *										'year' => $year,
1849
 *										'yday' => floor($secsInYear/$_day_power),
1850
 *										'weekday' => gmdate('l',$_day_power*(3+$dow)),
1851
 *										'month' => gmdate('F',mktime(0,0,0,$month,2,1971)),
1852
 *										If fast mode:
1853
 *										'seconds' => $secs,
1854
 *										'minutes' => $min,
1855
 *										'hours' => $hour,
1856
 *										'mday' => $day,
1857
 *										'mon' => $month,
1858
 *										'year' => $year,
1859
 *										'yday' => floor($secsInYear/$_day_power),
1860
 *										'leap' => $leaf,
1861
 *										'ndays' => $ndays
1862
 * 	@see 								dol_print_date, dol_stringtotime, dol_mktime
1863
 */
1864
function dol_getdate($timestamp,$fast=false)
1865
{
1866
	global $conf;
1867
1868
	$usealternatemethod=false;
1869
	if ($timestamp <= 0) $usealternatemethod=true;				// <= 1970
1870
	if ($timestamp >= 2145913200) $usealternatemethod=true;		// >= 2038
1871
1872
	if ($usealternatemethod)
1873
	{
1874
		$arrayinfo=adodb_getdate($timestamp,$fast);
1875
	}
1876
	else
1877
	{
1878
		$arrayinfo=getdate($timestamp);
1879
	}
1880
1881
	return $arrayinfo;
1882
}
1883
1884
/**
1885
 *	Return a timestamp date built from detailed informations (by default a local PHP server timestamp)
1886
 * 	Replace function mktime not available under Windows if year < 1970
1887
 *	PHP mktime is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
1888
 *
1889
 * 	@param	int			$hour			Hour	(can be -1 for undefined)
1890
 *	@param	int			$minute			Minute	(can be -1 for undefined)
1891
 *	@param	int			$second			Second	(can be -1 for undefined)
1892
 *	@param	int			$month			Month (1 to 12)
1893
 *	@param	int			$day			Day (1 to 31)
1894
 *	@param	int			$year			Year
1895
 *	@param	mixed		$gm				True or 1 or 'gmt'=Input informations are GMT values
1896
 *										False or 0 or 'server' = local to server TZ
1897
 *										'user' = local to user TZ
1898
 *										'tz,TimeZone' = use specified timezone
1899
 *	@param	int			$check			0=No check on parameters (Can use day 32, etc...)
1900
 *	@return	int|string					Date as a timestamp, '' or false if error
1901
 * 	@see 								dol_print_date, dol_stringtotime, dol_getdate
1902
 */
1903
function dol_mktime($hour,$minute,$second,$month,$day,$year,$gm=false,$check=1)
1904
{
1905
	global $conf;
1906
	//print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
1907
1908
	// Clean parameters
1909
	if ($hour   == -1 || empty($hour)) $hour=0;
1910
	if ($minute == -1 || empty($minute)) $minute=0;
1911
	if ($second == -1 || empty($second)) $second=0;
1912
1913
	// Check parameters
1914
	if ($check)
1915
	{
1916
		if (! $month || ! $day)  return '';
1917
		if ($day   > 31) return '';
1918
		if ($month > 12) return '';
1919
		if ($hour  < 0 || $hour   > 24) return '';
1920
		if ($minute< 0 || $minute > 60) return '';
1921
		if ($second< 0 || $second > 60) return '';
1922
	}
1923
1924
	if (method_exists('DateTime','getTimestamp'))
1925
	{
1926
		if (empty($gm) || $gm === 'server')
1927
		{
1928
			$default_timezone=@date_default_timezone_get();		// Example 'Europe/Berlin'
1929
			$localtz = new DateTimeZone($default_timezone);
1930
		}
1931
		else if ($gm === 'user')
1932
		{
1933
			// We use dol_tz_string first because it is more reliable.
1934
			$default_timezone=(empty($_SESSION["dol_tz_string"])?@date_default_timezone_get():$_SESSION["dol_tz_string"]);		// Example 'Europe/Berlin'
1935
			try {
1936
				$localtz = new DateTimeZone($default_timezone);
1937
			}
1938
			catch(Exception $e)
1939
			{
1940
				dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
1941
				$default_timezone=@date_default_timezone_get();
1942
			}
1943
		}
1944
		else if (strrpos($gm, "tz,") !== false)
1945
		{
1946
			$timezone=str_replace("tz,", "", $gm);  // Example 'tz,Europe/Berlin'
1947
			try
1948
			{
1949
				$localtz = new DateTimeZone($timezone);
1950
			}
1951
			catch(Exception $e)
1952
			{
1953
				dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
1954
			}
1955
		}
1956
1957
		if (empty($localtz)) {
1958
			$localtz = new DateTimeZone('UTC');
1959
		}
1960
		//var_dump($localtz);
1961
		//var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
1962
		$dt = new DateTime(null,$localtz);
1963
		$dt->setDate($year,$month,$day);
1964
		$dt->setTime((int) $hour, (int) $minute, (int) $second);
1965
		$date=$dt->getTimestamp();	// should include daylight saving time
1966
		//var_dump($date);
1967
		return $date;
1968
	}
1969
	else
1970
	{
1971
		dol_print_error('','PHP version must be 5.3+');
1972
		return '';
1973
	}
1974
}
1975
1976
1977
/**
1978
 *	Return date for now. In most cases, we use this function without parameters (that means GMT time).
1979
 *
1980
 * 	@param	string		$mode	'gmt' => we return GMT timestamp,
1981
 * 								'tzserver' => we add the PHP server timezone
1982
 *  							'tzref' => we add the company timezone
1983
 * 								'tzuser' => we add the user timezone
1984
 *	@return int   $date	Timestamp
1985
 */
1986
function dol_now($mode='gmt')
1987
{
1988
	$ret=0;
1989
1990
	// Note that gmmktime and mktime return same value (GMT) when used without parameters
1991
	//if ($mode == 'gmt') $ret=gmmktime(); // Strict Standards: gmmktime(): You should be using the time() function instead
1992
	if ($mode == 'gmt') $ret=time();	// Time for now at greenwich.
1993
	else if ($mode == 'tzserver')		// Time for now with PHP server timezone added
1994
	{
1995
		require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1996
		$tzsecond=getServerTimeZoneInt('now');    // Contains tz+dayling saving time
1997
		$ret=(int) dol_now('gmt')+($tzsecond*3600);
1998
	}
1999
	/*else if ($mode == 'tzref')				// Time for now with parent company timezone is added
2000
	{
2001
		require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2002
		$tzsecond=getParentCompanyTimeZoneInt();    // Contains tz+dayling saving time
2003
		$ret=dol_now('gmt')+($tzsecond*3600);
2004
	}*/
2005
	else if ($mode == 'tzuser')				// Time for now with user timezone added
2006
	{
2007
		//print 'time: '.time().'-'.mktime().'-'.gmmktime();
2008
		$offsettz=(empty($_SESSION['dol_tz'])?0:$_SESSION['dol_tz'])*60*60;
2009
		$offsetdst=(empty($_SESSION['dol_dst'])?0:$_SESSION['dol_dst'])*60*60;
2010
		$ret=(int) dol_now('gmt')+($offsettz+$offsetdst);
2011
	}
2012
2013
	return $ret;
2014
}
2015
2016
2017
/**
2018
 * Return string with formated size
2019
 *
2020
 * @param	int		$size		Size to print
2021
 * @param	int		$shortvalue	Tell if we want long value to use another unit (Ex: 1.5Kb instead of 1500b)
2022
 * @param	int		$shortunit	Use short value of size unit
2023
 * @return	string				Link
2024
 */
2025
function dol_print_size($size,$shortvalue=0,$shortunit=0)
2026
{
2027
	global $conf,$langs;
2028
	$level=1024;
2029
2030
	if (! empty($conf->dol_optimize_smallscreen)) $shortunit=1;
2031
2032
	// Set value text
2033
	if (empty($shortvalue) || $size < ($level*10))
2034
	{
2035
		$ret=$size;
2036
		$textunitshort=$langs->trans("b");
2037
		$textunitlong=$langs->trans("Bytes");
2038
	}
2039
	else
2040
	{
2041
		$ret=round($size/$level,0);
2042
		$textunitshort=$langs->trans("Kb");
2043
		$textunitlong=$langs->trans("KiloBytes");
2044
	}
2045
	// Use long or short text unit
2046
	if (empty($shortunit)) { $ret.=' '.$textunitlong; }
2047
	else { $ret.=' '.$textunitshort; }
2048
2049
	return $ret;
2050
}
2051
2052
/**
2053
 * Show Url link
2054
 *
2055
 * @param	string		$url		Url to show
2056
 * @param	string		$target		Target for link
2057
 * @param	int			$max		Max number of characters to show
2058
 * @param	int			$withpicto	With picto
2059
 * @return	string					HTML Link
2060
 */
2061
function dol_print_url($url,$target='_blank',$max=32,$withpicto=0)
2062
{
2063
	global $langs;
2064
2065
	if (empty($url)) return '';
2066
2067
	$link='<a href="';
2068
	if (! preg_match('/^http/i',$url)) $link.='http://';
2069
	$link.=$url;
2070
	$link.='"';
2071
	if ($target) $link.=' target="'.$target.'"';
2072
	$link.='>';
2073
	if (! preg_match('/^http/i',$url)) $link.='http://';
2074
	$link.=dol_trunc($url,$max);
2075
	$link.='</a>';
2076
	return '<div class="nospan float" style="margin-right: 10px">'.($withpicto?img_picto($langs->trans("Url"), 'object_globe.png').' ':'').$link.'</div>';
2077
}
2078
2079
/**
2080
 * Show EMail link
2081
 *
2082
 * @param	string		$email			EMail to show (only email, without 'Name of recipient' before)
2083
 * @param 	int			$cid 			Id of contact if known
2084
 * @param 	int			$socid 			Id of third party if known
2085
 * @param 	int			$addlink		0=no link, 1=email has a html email link (+ link to create action if constant AGENDA_ADDACTIONFOREMAIL is on)
2086
 * @param	int			$max			Max number of characters to show
2087
 * @param	int			$showinvalid	Show warning if syntax email is wrong
2088
 * @param	int			$withpicto		Show picto
2089
 * @return	string						HTML Link
2090
 */
2091
function dol_print_email($email,$cid=0,$socid=0,$addlink=0,$max=64,$showinvalid=1,$withpicto=0)
2092
{
2093
	global $conf,$user,$langs;
2094
2095
	$newemail=$email;
2096
2097
	if (empty($email)) return '&nbsp;';
2098
2099
	if (! empty($addlink))
2100
	{
2101
		$newemail='<a style="text-overflow: ellipsis;" href="';
2102
		if (! preg_match('/^mailto:/i',$email)) $newemail.='mailto:';
2103
		$newemail.=$email;
2104
		$newemail.='">';
2105
		$newemail.=dol_trunc($email,$max);
2106
		$newemail.='</a>';
2107
		if ($showinvalid && ! isValidEmail($email))
2108
		{
2109
			$langs->load("errors");
2110
			$newemail.=img_warning($langs->trans("ErrorBadEMail",$email));
2111
		}
2112
2113
		if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2114
		{
2115
			$type='AC_EMAIL'; $link='';
2116
			if (! empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) $link='<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.$type.'&amp;contactid='.$cid.'&amp;socid='.$socid.'">'.img_object($langs->trans("AddAction"),"calendar").'</a>';
2117
			if ($link) $newemail='<div>'.$newemail.' '.$link.'</div>';
2118
		}
2119
	}
2120
	else
2121
	{
2122
		if ($showinvalid && ! isValidEmail($email))
2123
		{
2124
			$langs->load("errors");
2125
			$newemail.=img_warning($langs->trans("ErrorBadEMail",$email));
2126
		}
2127
	}
2128
	return '<div class="nospan float" style="margin-right: 10px">'.($withpicto?img_picto($langs->trans("EMail"), 'object_email.png').' ':'').$newemail.'</div>';
2129
}
2130
2131
/**
2132
 * Show Skype link
2133
 *
2134
 * @param	string		$skype			Skype to show (only skype, without 'Name of recipient' before)
2135
 * @param	int 		$cid 			Id of contact if known
2136
 * @param	int 		$socid 			Id of third party if known
2137
 * @param	int 		$addlink		0=no link to create action
2138
 * @param	int			$max			Max number of characters to show
2139
 * @return	string						HTML Link
2140
 */
2141
function dol_print_skype($skype,$cid=0,$socid=0,$addlink=0,$max=64)
2142
{
2143
	global $conf,$user,$langs;
2144
2145
	$newskype=$skype;
2146
2147
	if (empty($skype)) return '&nbsp;';
2148
2149
	if (! empty($addlink))
2150
	{
2151
		$newskype =img_picto($langs->trans("Skype"), 'object_skype.png');
2152
		$newskype.= '&nbsp;';
2153
		$newskype.=dol_trunc($skype,$max);
2154
		$newskype.= '&nbsp;';
2155
		$newskype.='<a href="skype:';
2156
		$newskype.=dol_trunc($skype,$max);
2157
		$newskype.='?call" alt="'.$langs->trans("Call").'&nbsp;'.$skype.'" title="'.$langs->trans("Call").'&nbsp;'.$skype.'">';
2158
		$newskype.='<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
2159
		$newskype.='</a>&nbsp;&nbsp;&nbsp;<a href="skype:';
2160
		$newskype.=dol_trunc($skype,$max);
2161
		$newskype.='?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$skype.'" title="'.$langs->trans("Chat").'&nbsp;'.$skype.'">';
2162
		$newskype.='<img src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
2163
		$newskype.='</a>';
2164
2165
		if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2166
		{
2167
			$type='AC_SKYPE'; $link='';
2168
			if (! empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) $link='<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.$type.'&amp;contactid='.$cid.'&amp;socid='.$socid.'">'.img_object($langs->trans("AddAction"),"calendar").'</a>';
2169
			$newskype='<div class="divskype nowrap">'.$newskype.($link?' '.$link:'').'</div>';
2170
		}
2171
	}
2172
	else
2173
	{
2174
		$langs->load("errors");
2175
		$newskype.=img_warning($langs->trans("ErrorBadSkype",$skype));
2176
	}
2177
	return $newskype;
2178
}
2179
2180
/**
2181
 * 	Format phone numbers according to country
2182
 *
2183
 * 	@param  string  $phone          Phone number to format
2184
 * 	@param  string  $countrycode    Country code to use for formatting
2185
 * 	@param 	int		$cid 		    Id of contact if known
2186
 * 	@param 	int		$socid          Id of third party if known
2187
 * 	@param 	string	$addlink	    ''=no link to create action, 'AC_TEL'=add link to clicktodial (if module enabled) and add link to create event (if conf->global->AGENDA_ADDACTIONFORPHONE set)
2188
 * 	@param 	string	$separ 		    Separation between numbers for a better visibility example : xx.xx.xx.xx.xx
2189
 *  @param	string  $withpicto      Show picto
2190
 *  @param	string	$titlealt	    Text to show on alt
2191
 *  @param  int     $adddivfloat    Add div float around phone.
2192
 * 	@return string 				    Formated phone number
2193
 */
2194
function dol_print_phone($phone,$countrycode='',$cid=0,$socid=0,$addlink='',$separ="&nbsp;",$withpicto='',$titlealt='',$adddivfloat=0)
2195
{
2196
	global $conf, $user, $langs, $mysoc, $hookmanager;
2197
2198
	// Clean phone parameter
2199
	$phone = preg_replace("/[\s.-]/","",trim($phone));
2200
	if (empty($phone)) { return ''; }
2201
	if (empty($countrycode)) $countrycode=$mysoc->country_code;
2202
2203
	// Short format for small screens
2204
	if ($conf->dol_optimize_smallscreen) $separ='';
2205
2206
	$newphone=$phone;
2207
	if (strtoupper($countrycode) == "FR")
2208
	{
2209
		// France
2210
		if (dol_strlen($phone) == 10) {
2211
			$newphone=substr($newphone,0,2).$separ.substr($newphone,2,2).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2);
2212
		}
2213
		elseif (dol_strlen($phone) == 7)
2214
		{
2215
			$newphone=substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,2);
2216
		}
2217
		elseif (dol_strlen($phone) == 9)
2218
		{
2219
			$newphone=substr($newphone,0,2).$separ.substr($newphone,2,3).$separ.substr($newphone,5,2).$separ.substr($newphone,7,2);
2220
		}
2221
		elseif (dol_strlen($phone) == 11)
2222
		{
2223
			$newphone=substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,2).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2);
2224
		}
2225
		elseif (dol_strlen($phone) == 12)
2226
		{
2227
			$newphone=substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2228
		}
2229
	}
2230
2231
	elseif (strtoupper($countrycode) == "CA")
2232
	{
2233
		if (dol_strlen($phone) == 10) {
2234
			$newphone=($separ!=''?'(':'').substr($newphone,0,3).($separ!=''?')':'').$separ.substr($newphone,3,3).($separ!=''?'-':'').substr($newphone,6,4);
2235
		}
2236
	}
2237
	elseif (strtoupper($countrycode) == "PT" )
2238
	{//Portugal
2239
		if (dol_strlen($phone) == 13)
2240
		{//ex: +351_ABC_DEF_GHI
2241
			$newphone= substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3).$separ.substr($newphone,10,3);
2242
		}
2243
	}
2244
	elseif (strtoupper($countrycode) == "SR" )
2245
	{//Suriname
2246
		if (dol_strlen($phone) == 10)
2247
		{//ex: +597_ABC_DEF
2248
			$newphone= substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3);
2249
		}
2250
		elseif (dol_strlen($phone) == 11)
2251
		{//ex: +597_ABC_DEFG
2252
			$newphone= substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,4);
2253
		}
2254
	}
2255
	elseif (strtoupper($countrycode) == "DE" )
2256
	{//Allemagne
2257
		if (dol_strlen($phone) == 14)
2258
		{//ex:  +49_ABCD_EFGH_IJK
2259
			$newphone= substr($newphone,0,3).$separ.substr($newphone,3,4).$separ.substr($newphone,7,4).$separ.substr($newphone,11,3);
2260
		}
2261
		elseif (dol_strlen($phone) == 13)
2262
		{//ex: +49_ABC_DEFG_HIJ
2263
			$newphone= substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,4).$separ.substr($newphone,10,3);
2264
		}
2265
	}
2266
	elseif (strtoupper($countrycode) == "ES")
2267
	{//Espagne
2268
		if (dol_strlen($phone) == 12)
2269
		{//ex:  +34_ABC_DEF_GHI
2270
			$newphone= substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,3);
2271
		}
2272
	}
2273
	elseif (strtoupper($countrycode) == "BF")
2274
	{// Burkina Faso
2275
		if (dol_strlen($phone) == 12)
2276
		{//ex :  +22 A BC_DE_FG_HI
2277
			$newphone= substr($newphone,0,3).$separ.substr($newphone,3,1).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2278
		}
2279
	}
2280
	elseif (strtoupper($countrycode) == "RO")
2281
	{// Roumanie
2282
		if (dol_strlen($phone) == 12)
2283
		{//ex :  +40 AB_CDE_FG_HI
2284
			$newphone= substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,3).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2285
		}
2286
	}
2287
	elseif (strtoupper($countrycode) == "TR")
2288
	{//Turquie
2289
		if (dol_strlen($phone) == 13)
2290
		{//ex :  +90 ABC_DEF_GHIJ
2291
			$newphone= substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,4);
2292
		}
2293
	}
2294
	elseif (strtoupper($countrycode) == "US")
2295
	{//Etat-Unis
2296
		if (dol_strlen($phone) == 12)
2297
		{//ex: +1 ABC_DEF_GHIJ
2298
			$newphone= substr($newphone,0,2).$separ.substr($newphone,2,3).$separ.substr($newphone,5,3).$separ.substr($newphone,8,4);
2299
		}
2300
	}
2301
	elseif (strtoupper($countrycode) == "MX")
2302
	{//Mexique
2303
		if (dol_strlen($phone) == 12)
2304
		{//ex: +52 ABCD_EFG_HI
2305
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,4).$separ.substr($newphone,7,3).$separ.substr($newphone,10,2);
2306
		}
2307
		elseif (dol_strlen($phone) == 11)
2308
		{//ex: +52 AB_CD_EF_GH
2309
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,2).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2);
2310
		}
2311
		elseif (dol_strlen($phone) == 13)
2312
		{//ex: +52 ABC_DEF_GHIJ
2313
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,4);
2314
		}
2315
	}
2316
	elseif (strtoupper($countrycode) == "ML")
2317
	{//Mali
2318
		if(dol_strlen($phone) == 12)
2319
		{//ex: +223 AB_CD_EF_GH
2320
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2321
		}
2322
	}
2323
	elseif (strtoupper($countrycode) == "TH")
2324
	{//Thaïlande
2325
		if(dol_strlen($phone) == 11)
2326
		{//ex: +66_ABC_DE_FGH
2327
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,2).$separ.substr($newphone,8,3);
2328
		}
2329
		elseif(dol_strlen($phone) == 12)
2330
		{//ex: +66_A_BCD_EF_GHI
2331
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,1).$separ.substr($newphone,4,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,3);
2332
		}
2333
		}
2334
	elseif (strtoupper($countrycode) == "MU")
2335
	{//Maurice
2336
		if(dol_strlen($phone) == 11)
2337
		{//ex: +230_ABC_DE_FG
2338
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2);
2339
		}
2340
		elseif(dol_strlen($phone) == 12)
2341
		{//ex: +230_ABCD_EF_GH
2342
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,4).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2343
		}
2344
	}
2345
	elseif (strtoupper($countrycode) == "ZA")
2346
	{//Afrique du sud
2347
		if(dol_strlen($phone) == 12)
2348
		{//ex: +27_AB_CDE_FG_HI
2349
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,3).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2350
		}
2351
	}
2352
	elseif (strtoupper($countrycode) == "SY")
2353
	{//Syrie
2354
		if(dol_strlen($phone) == 12)
2355
		{//ex: +963_AB_CD_EF_GH
2356
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2357
		}
2358
		elseif(dol_strlen($phone) == 13)
2359
		{//ex: +963_AB_CD_EF_GHI
2360
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,3);
2361
		}
2362
	}
2363
	elseif (strtoupper($countrycode) == "AE")
2364
	{//Emirats Arabes Unis
2365
		if(dol_strlen($phone) == 12)
2366
		{//ex: +971_ABC_DEF_GH
2367
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3).$separ.substr($newphone,10,2);
2368
		}
2369
		elseif(dol_strlen($phone) == 13)
2370
		{//ex: +971_ABC_DEF_GHI
2371
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3).$separ.substr($newphone,10,3);
2372
		}
2373
		elseif(dol_strlen($phone) == 14)
2374
		{//ex: +971_ABC_DEF_GHIK
2375
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3).$separ.substr($newphone,10,4);
2376
		}
2377
	}
2378
	elseif (strtoupper($countrycode) == "DZ")
2379
	{//Algérie
2380
		if(dol_strlen($phone) == 13)
2381
		{//ex: +213_ABC_DEF_GHI
2382
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3).$separ.substr($newphone,10,3);
2383
		}
2384
	}
2385
	elseif (strtoupper($countrycode) == "BE")
2386
	{//Belgique
2387
		if(dol_strlen($phone) == 11)
2388
		{//ex: +32_ABC_DE_FGH
2389
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,2).$separ.substr($newphone,8,3);
2390
		}
2391
		elseif(dol_strlen($phone) == 12)
2392
		{//ex: +32_ABC_DEF_GHI
2393
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,3);
2394
		}
2395
	}
2396
	elseif (strtoupper($countrycode) == "PF")
2397
	{//Polynésie française
2398
		if(dol_strlen($phone) == 12)
2399
		{//ex: +689_AB_CD_EF_GH
2400
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2401
		}
2402
	}
2403
	elseif (strtoupper($countrycode) == "CO")
2404
	{//Colombie
2405
		if(dol_strlen($phone) == 13)
2406
		{//ex: +57_ABC_DEF_GH_IJ
2407
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,2).$separ.substr($newphone,11,2);
2408
		}
2409
	}
2410
	elseif (strtoupper($countrycode) == "JO")
2411
	{//Jordanie
2412
		if(dol_strlen($phone) == 12)
2413
		{//ex: +962_A_BCD_EF_GH
2414
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,1).$separ.substr($newphone,5,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2);
2415
		}
2416
	}
2417
	elseif (strtoupper($countrycode) == "MG")
2418
	{//Madagascar
2419
		if(dol_strlen($phone) == 13)
2420
		{//ex: +261_AB_CD_EF_GHI
2421
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,3);
2422
		}
2423
	}
2424
	elseif (strtoupper($countrycode) == "GB")
2425
	{//Royaume uni
2426
		if(dol_strlen($phone) == 13)
2427
		{//ex: +44_ABCD_EFG_HIJ
2428
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,4).$separ.substr($newphone,7,3).$separ.substr($newphone,10,3);
2429
		}
2430
	}
2431
	elseif (strtoupper($countrycode) == "CH")
2432
	{//Suisse
2433
		if(dol_strlen($phone) == 12)
2434
		{//ex: +41_AB_CDE_FG_HI
2435
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,3).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2436
		}
2437
		elseif(dol_strlen($phone) == 15)
2438
		{// +41_AB_CDE_FGH_IJKL
2439
			$newphone =$newphone = substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,3).$separ.substr($newphone,8,3).$separ.substr($newphone,11,4);
2440
		}
2441
	}
2442
	elseif (strtoupper($countrycode) == "TN")
2443
	{//Tunisie
2444
		if(dol_strlen($phone) == 12)
2445
		{//ex: +216_AB_CDE_FGH
2446
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,3).$separ.substr($newphone,9,3);
2447
		}
2448
	}
2449
	elseif (strtoupper($countrycode) == "GF")
2450
	{//Guyane francaise
2451
		if(dol_strlen($phone) == 13)
2452
		{//ex: +594_ABC_DE_FG_HI  (ABC=594 de nouveau)
2453
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2).$separ.substr($newphone,11,2);
2454
		}
2455
	}
2456
	elseif (strtoupper($countrycode) == "GP")
2457
	{//Guadeloupe
2458
		if(dol_strlen($phone) == 13)
2459
		{//ex: +590_ABC_DE_FG_HI  (ABC=590 de nouveau)
2460
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2).$separ.substr($newphone,11,2);
2461
		}
2462
	}
2463
	elseif (strtoupper($countrycode) == "MQ")
2464
	{//Martinique
2465
		if(dol_strlen($phone) == 13)
2466
		{//ex: +596_ABC_DE_FG_HI  (ABC=596 de nouveau)
2467
			$newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2).$separ.substr($newphone,11,2);
2468
		}
2469
	}
2470
	elseif (strtoupper($countrycode) == "IT")
2471
	{//Italie
2472
		if(dol_strlen($phone) == 12)
2473
		{//ex: +39_ABC_DEF_GHI
2474
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,3);
2475
		}
2476
		elseif(dol_strlen($phone) == 13)
2477
		{//ex: +39_ABC_DEF_GH_IJ
2478
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,2).$separ.substr($newphone,11,2);
2479
		}
2480
	}
2481
	elseif(strtoupper($countrycode) == "AU")
2482
	{//Australie
2483
		 if(dol_strlen($phone) == 12)
2484
		{//ex: +61_A_BCDE_FGHI
2485
			$newphone = substr($newphone,0,3).$separ.substr($newphone,3,1).$separ.substr($newphone,4,4).$separ.substr($newphone,8,4);
2486
		}
2487
	}
2488
	if (! empty($addlink))	// Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
2489
	{
2490
		if (! empty($conf->browser->phone) || (! empty($conf->clicktodial->enabled) && ! empty($conf->global->CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS)))	// If phone or option for, we use link of phone
2491
		{
2492
			$newphone ='<a href="tel:'.$phone.'"';
2493
			$newphone.='>'.$phone.'</a>';
2494
		}
2495
		else if (! empty($conf->clicktodial->enabled) && $addlink == 'AC_TEL')		// If click to dial, we use click to dial url
2496
		{
2497
			if (empty($user->clicktodial_loaded)) $user->fetch_clicktodial();
2498
2499
			// Define urlmask
2500
			$urlmask='ErrorClickToDialModuleNotConfigured';
2501
			if (! empty($conf->global->CLICKTODIAL_URL)) $urlmask=$conf->global->CLICKTODIAL_URL;
2502
			if (! empty($user->clicktodial_url)) $urlmask=$user->clicktodial_url;
2503
2504
			$clicktodial_poste=(! empty($user->clicktodial_poste)?urlencode($user->clicktodial_poste):'');
2505
			$clicktodial_login=(! empty($user->clicktodial_login)?urlencode($user->clicktodial_login):'');
2506
			$clicktodial_password=(! empty($user->clicktodial_password)?urlencode($user->clicktodial_password):'');
2507
			// This line is for backward compatibility
2508
			$url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
2509
			// Thoose lines are for substitution
2510
			$substitarray=array('__PHONEFROM__'=>$clicktodial_poste,
2511
								'__PHONETO__'=>urlencode($phone),
2512
								'__LOGIN__'=>$clicktodial_login,
2513
								'__PASS__'=>$clicktodial_password);
2514
			$url = make_substitutions($url, $substitarray);
2515
			$newphonesav=$newphone;
2516
			$newphone ='<a href="'.$url.'"';
2517
			if (! empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) $newphone.=' target="_blank"';
2518
			$newphone.='>'.$newphonesav.'</a>';
2519
		}
2520
2521
		//if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2522
		if (! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2523
		{
2524
			$type='AC_TEL'; $link='';
2525
			if ($addlink == 'AC_FAX') $type='AC_FAX';
2526
			if (! empty($conf->global->AGENDA_ADDACTIONFORPHONE)) $link='<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.$type.($cid?'&amp;contactid='.$cid:'').($socid?'&amp;socid='.$socid:'').'">'.img_object($langs->trans("AddAction"),"calendar").'</a>';
2527
			if ($link) $newphone='<div>'.$newphone.' '.$link.'</div>';
2528
		}
2529
	}
2530
2531
	if (empty($titlealt))
2532
	{
2533
		$titlealt=($withpicto=='fax'?$langs->trans("Fax"):$langs->trans("Phone"));
2534
	}
2535
	$rep='';
2536
2537
	if ($hookmanager) {
2538
            $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid,'titlealt' => $titlealt, 'picto' => $withpicto);
2539
            $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
2540
            $rep.=$hookmanager->resPrint;
2541
        }
2542
	 if (empty($reshook))
2543
        {
2544
		$picto = '';
2545
		if($withpicto){
2546
			if($withpicto=='fax'){
2547
				$picto = 'phoning_fax';
2548
			}elseif($withpicto=='phone'){
2549
				$picto = 'phoning';
2550
			}elseif($withpicto=='mobile'){
2551
				$picto = 'phoning_mobile';
2552
			}else{
2553
				$picto = '';
2554
			}
2555
		}
2556
		if ($adddivfloat) $rep.='<div class="nospan float" style="margin-right: 10px">';
2557
		else $rep.='<span style="margin-right: 10px;">';
2558
		$rep.=($withpicto?img_picto($titlealt, 'object_'.$picto.'.png').' ':'').$newphone;
2559
		if ($adddivfloat) $rep.='</div>';
2560
		else $rep.='</span>';
2561
	  }
2562
2563
	return $rep;
2564
}
2565
2566
/**
2567
 * 	Return an IP formated to be shown on screen
2568
 *
2569
 * 	@param	string	$ip			IP
2570
 * 	@param	int		$mode		0=return IP + country/flag, 1=return only country/flag, 2=return only IP
2571
 * 	@return string 				Formated IP, with country if GeoIP module is enabled
2572
 */
2573
function dol_print_ip($ip,$mode=0)
2574
{
2575
	global $conf,$langs;
2576
2577
	$ret='';
2578
2579
	if (empty($mode)) $ret.=$ip;
2580
2581
	if ($mode != 2)
2582
	{
2583
		$countrycode=dolGetCountryCodeFromIp($ip);
2584
		if ($countrycode)	// If success, countrycode is us, fr, ...
2585
		{
2586
			if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png'))
2587
			{
2588
				$ret.=' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"),DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png','',1);
2589
			}
2590
			else $ret.=' ('.$countrycode.')';
2591
		}
2592
	}
2593
2594
	return $ret;
2595
}
2596
2597
/**
2598
 * 	Return a country code from IP. Empty string if not found.
2599
 *
2600
 * 	@param	string	$ip			IP
2601
 * 	@return string 				Country code ('us', 'fr', ...)
2602
 */
2603
function dolGetCountryCodeFromIp($ip)
2604
{
2605
	global $conf;
2606
2607
	$countrycode='';
2608
2609
	if (! empty($conf->geoipmaxmind->enabled))
2610
	{
2611
		$datafile=$conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
2612
		//$ip='24.24.24.24';
2613
		//$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';    Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
2614
2615
		include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
2616
		$geoip=new DolGeoIP('country',$datafile);
2617
		//print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
2618
		//print "geoip_country_id_by_addr=".geoip_country_id_by_addr($geoip->gi,$ip)."\n";
2619
		$countrycode=$geoip->getCountryCodeFromIP($ip);
2620
	}
2621
2622
	return $countrycode;
2623
}
2624
2625
2626
/**
2627
 *  Return country code for current user.
2628
 *  If software is used inside a local network, detection may fails (we need a public ip)
2629
 *
2630
 *  @return     string      Country code (fr, es, it, us, ...)
2631
 */
2632
function dol_user_country()
2633
{
2634
	global $conf,$langs,$user;
2635
2636
	//$ret=$user->xxx;
2637
	$ret='';
2638
	if (! empty($conf->geoipmaxmind->enabled))
2639
	{
2640
		$ip=$_SERVER["REMOTE_ADDR"];
2641
		$datafile=$conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
2642
		//$ip='24.24.24.24';
2643
		//$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
2644
		include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
2645
		$geoip=new DolGeoIP('country',$datafile);
2646
		$countrycode=$geoip->getCountryCodeFromIP($ip);
2647
		$ret=$countrycode;
2648
	}
2649
	return $ret;
2650
}
2651
2652
/**
2653
 *  Format address string
2654
 *
2655
 *  @param	string	$address    Address
2656
 *  @param  int		$htmlid     Html ID (for example 'gmap')
2657
 *  @param  int		$mode       thirdparty|contact|member|other
2658
 *  @param  int		$id         Id of object
2659
 *  @param	int		$noprint	No output. Result is the function return
2660
 *  @param  string  $charfornl  Char to use instead of nl2br. '' means we use a standad nl2br.
2661
 *  @return string|void			Nothing if noprint is 0, formatted address if noprint is 1
2662
 *  @see dol_format_address
2663
 */
2664
function dol_print_address($address, $htmlid, $mode, $id, $noprint=0, $charfornl='')
2665
{
2666
	global $conf, $user, $langs, $hookmanager;
2667
2668
	$out = '';
2669
2670
	if ($address)
2671
	{
2672
		if ($hookmanager) {
2673
			$parameters = array('element' => $mode, 'id' => $id);
2674
			$reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
2675
			$out.=$hookmanager->resPrint;
2676
		}
2677
		if (empty($reshook))
2678
		{
2679
			if (empty($charfornl)) $out.=nl2br($address);
2680
			else $out.=preg_replace('/[\r\n]+/', $charfornl, $address);
2681
2682
			$showgmap=$showomap=0;
2683
2684
			// TODO Add a hook here
2685
			if (($mode=='thirdparty' || $mode =='societe') && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS)) $showgmap=1;
2686
			if ($mode=='contact' && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) $showgmap=1;
2687
			if ($mode=='member' && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) $showgmap=1;
2688
			if (($mode=='thirdparty' || $mode =='societe') && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) $showomap=1;
2689
			if ($mode=='contact' && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) $showomap=1;
2690
			if ($mode=='member' && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) $showomap=1;
2691
2692
			if ($showgmap)
2693
			{
2694
				$url=dol_buildpath('/google/gmaps.php?mode='.$mode.'&id='.$id,1);
2695
				$out.=' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
2696
			}
2697
			if ($showomap)
2698
			{
2699
				$url=dol_buildpath('/openstreetmap/maps.php?mode='.$mode.'&id='.$id,1);
2700
				$out.=' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
2701
			}
2702
		}
2703
	}
2704
	if ($noprint) return $out;
2705
	else print $out;
2706
}
2707
2708
2709
/**
2710
 *	Return true if email syntax is ok
2711
 *
2712
 *	@param	    string		$address    			email (Ex: "[email protected]", "John Do <[email protected]>")
2713
 *  @param		int			$acceptsupervisorkey	If 1, the special string '__SUPERVISOREMAIL__' is also accepted as valid
2714
 *	@return     boolean     						true if email syntax is OK, false if KO or empty string
2715
 */
2716
function isValidEmail($address, $acceptsupervisorkey=0)
2717
{
2718
	if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') return true;
2719
	if (filter_var($address, FILTER_VALIDATE_EMAIL)) return true;
2720
2721
	return false;
2722
}
2723
2724
/**
2725
 *  Return true if phone number syntax is ok
2726
 *  TODO Decide what to do with this
2727
 *
2728
 *  @param	string		$phone		phone (Ex: "0601010101")
2729
 *  @return boolean     			true if phone syntax is OK, false if KO or empty string
2730
 */
2731
function isValidPhone($phone)
2732
{
2733
	return true;
2734
}
2735
2736
2737
/**
2738
 * Make a strlen call. Works even if mbstring module not enabled
2739
 *
2740
 * @param   string		$string				String to calculate length
2741
 * @param   string		$stringencoding		Encoding of string
2742
 * @return  int								Length of string
2743
 */
2744
function dol_strlen($string,$stringencoding='UTF-8')
2745
{
2746
	if (function_exists('mb_strlen')) return mb_strlen($string,$stringencoding);
2747
	else return strlen($string);
2748
}
2749
2750
/**
2751
 * Make a substring. Works even in mbstring module is not enabled.
2752
 *
2753
 * @param	string	$string				String to scan
2754
 * @param	string	$start				Start position
2755
 * @param	int		$length				Length
2756
 * @param   string	$stringencoding		Page code used for input string encoding
2757
 * @return  string						substring
2758
 */
2759
function dol_substr($string,$start,$length,$stringencoding='')
2760
{
2761
	global $langs;
2762
2763
	if (empty($stringencoding)) $stringencoding=$langs->charset_output;
2764
2765
	$ret='';
2766
	if (function_exists('mb_substr'))
2767
	{
2768
		$ret=mb_substr($string,$start,$length,$stringencoding);
2769
	}
2770
	else
2771
	{
2772
		$ret=substr($string,$start,$length);
2773
	}
2774
	return $ret;
2775
}
2776
2777
2778
/**
2779
 *  Show a javascript graph.
2780
 *  Do not use this function anymore. Use DolGraph class instead.
2781
 *
2782
 *  @param		string	$htmlid			Html id name
2783
 *  @param		int		$width			Width in pixel
2784
 *  @param		int		$height			Height in pixel
2785
 *  @param		array	$data			Data array
2786
 *  @param		int		$showlegend		1 to show legend, 0 otherwise
2787
 *  @param		string	$type			Type of graph ('pie', 'barline')
2788
 *  @param		int		$showpercent	Show percent (with type='pie' only)
2789
 *  @param		string	$url			Param to add an url to click values
2790
 *  @param		int		$combineother	0=No combine, 0.05=Combine if lower than 5%
2791
 *  @param      int     $shownographyet Show graph to say there is not enough data
2792
 *  @return		void
2793
 *  @deprecated
2794
 *  @see DolGraph
2795
 */
2796
function dol_print_graph($htmlid,$width,$height,$data,$showlegend=0,$type='pie',$showpercent=0,$url='',$combineother=0.05,$shownographyet=0)
2797
{
2798
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
2799
2800
	global $conf,$langs;
2801
	global $theme_datacolor;    // To have var kept when function is called several times
2802
2803
	if ($shownographyet)
2804
	{
2805
		print '<div class="nographyet" style="width:'.$width.'px;height:'.$height.'px;"></div>';
2806
		print '<div class="nographyettext">'.$langs->trans("NotEnoughDataYet").'</div>';
2807
		return;
2808
	}
2809
2810
	if (empty($conf->use_javascript_ajax)) return;
2811
	$jsgraphlib='flot';
2812
	$datacolor=array();
2813
2814
	// Load colors of theme into $datacolor array
2815
	$color_file = DOL_DOCUMENT_ROOT."/theme/".$conf->theme."/graph-color.php";
2816
	if (is_readable($color_file))
2817
	{
2818
		include_once $color_file;
2819
		if (isset($theme_datacolor))
2820
		{
2821
			$datacolor=array();
2822
			foreach($theme_datacolor as $val)
2823
			{
2824
				$datacolor[]="#".sprintf("%02x",$val[0]).sprintf("%02x",$val[1]).sprintf("%02x",$val[2]);
2825
			}
2826
		}
2827
	}
2828
	print '<div id="'.$htmlid.'" style="width:'.$width.'px;height:'.$height.'px;"></div>';
2829
2830
	// We use Flot js lib
2831
	if ($jsgraphlib == 'flot')
2832
	{
2833
		if ($type == 'pie')
2834
		{
2835
			// data is   array('series'=>array(serie1,serie2,...),
2836
			//                 'seriestype'=>array('bar','line',...),
2837
			//                 'seriescolor'=>array(0=>'#999999',1=>'#999999',...)
2838
			//                 'xlabel'=>array(0=>labelx1,1=>labelx2,...));
2839
			// serieX is array('label'=>'label', data=>val)
2840
			print '
2841
			<script type="text/javascript">
2842
			$(function () {
2843
				var data = '.json_encode($data['series']).';
2844
2845
				function plotWithOptions() {
2846
					$.plot($("#'.$htmlid.'"), data,
2847
					{
2848
						series: {
2849
							pie: {
2850
								show: true,
2851
								radius: 0.8,';
2852
			if ($combineother)
2853
			{
2854
				print '
2855
								combine: {
2856
								 	threshold: '.$combineother.'
2857
								},';
2858
			}
2859
			print '
2860
								label: {
2861
									show: true,
2862
									radius: 0.9,
2863
									formatter: function(label, series) {
2864
										var percent=Math.round(series.percent);
2865
										var number=series.data[0][1];
2866
										return \'';
2867
										print '<div style="font-size:8pt;text-align:center;padding:2px;color:black;">';
2868
										if ($url) print '<a style="color: #FFFFFF;" border="0" href="'.$url.'">';
2869
										print '\'+'.($showlegend?'number':'label+\' \'+number');
2870
										if (! empty($showpercent)) print '+\'<br/>\'+percent+\'%\'';
2871
										print '+\'';
2872
										if ($url) print '</a>';
2873
										print '</div>\';
2874
									},
2875
									background: {
2876
										opacity: 0.0,
2877
										color: \'#000000\'
2878
									},
2879
								}
2880
							}
2881
						},
2882
						zoom: {
2883
							interactive: true
2884
						},
2885
						pan: {
2886
							interactive: true
2887
						},';
2888
						if (count($datacolor))
2889
						{
2890
							print 'colors: '.(! empty($data['seriescolor']) ? json_encode($data['seriescolor']) : json_encode($datacolor)).',';
2891
						}
2892
						print 'legend: {show: '.($showlegend?'true':'false').', position: \'ne\' }
2893
					});
2894
				}
2895
				plotWithOptions();
2896
			});
2897
			</script>';
2898
		}
2899
		else if ($type == 'barline')
2900
		{
2901
			// data is   array('series'=>array(serie1,serie2,...),
2902
			//                 'seriestype'=>array('bar','line',...),
2903
			//                 'seriescolor'=>array(0=>'#999999',1=>'#999999',...)
2904
			//                 'xlabel'=>array(0=>labelx1,1=>labelx2,...));
2905
			// serieX is array('label'=>'label', data=>array(0=>y1,1=>y2,...)) with same nb of value than into xlabel
2906
			print '
2907
			<script type="text/javascript">
2908
			$(function () {
2909
				var data = [';
2910
				$i=0; $outputserie=0;
2911
				foreach($data['series'] as $serie)
2912
				{
2913
					if ($data['seriestype'][$i]=='line') { $i++; continue; };
2914
					if ($outputserie > 0) print ',';
2915
					print '{ bars: { stack: 0, show: true, barWidth: 0.9, align: \'center\' }, label: \''.dol_escape_js($serie['label']).'\', data: '.json_encode($serie['data']).'}'."\n";
2916
					$outputserie++; $i++;
2917
				}
2918
				if ($outputserie) print ', ';
2919
				//print '];
2920
				//var datalines = [';
2921
				$i=0; $outputserie=0;
2922
				foreach($data['series'] as $serie)
2923
				{
2924
					if (empty($data['seriestype'][$i]) || $data['seriestype'][$i]=='bar') { $i++; continue; };
2925
					if ($outputserie > 0) print ',';
2926
					print '{ lines: { show: true }, label: \''.dol_escape_js($serie['label']).'\', data: '.json_encode($serie['data']).'}'."\n";
2927
					$outputserie++; $i++;
2928
				}
2929
				print '];
2930
				var dataticks = '.json_encode($data['xlabel']).'
2931
2932
				function plotWithOptions() {
2933
					$.plot(jQuery("#'.$htmlid.'"), data,
2934
					{
2935
						series: {
2936
							stack: 0
2937
						},
2938
						zoom: {
2939
							interactive: true
2940
						},
2941
						pan: {
2942
							interactive: true
2943
						},';
2944
						if (count($datacolor))
2945
						{
2946
							print 'colors: '.json_encode($datacolor).',';
2947
						}
2948
						print 'legend: {show: '.($showlegend?'true':'false').'},
2949
						xaxis: {ticks: dataticks}
2950
					});
2951
				}
2952
				plotWithOptions();
2953
			});
2954
			</script>';
2955
		}
2956
		else print 'BadValueForParameterType';
2957
	}
2958
}
2959
2960
/**
2961
 *	Truncate a string to a particular length adding '...' if string larger than length.
2962
 * 	If length = max length+1, we do no truncate to avoid having just 1 char replaced with '...'.
2963
 *  MAIN_DISABLE_TRUNC=1 can disable all truncings
2964
 *
2965
 *	@param	string	$string				String to truncate
2966
 *	@param  int		$size				Max string size visible (excluding ...). 0 for no limit. WARNING: Final string size can have 3 more chars (if we added ..., or if size was max+1 or max+2 or max+3 so it does not worse to replace with ...)
2967
 *	@param	string	$trunc				Where to trunc: right, left, middle (size must be a 2 power), wrap
2968
 * 	@param	string	$stringencoding		Tell what is source string encoding
2969
 *  @param	int		$nodot				Truncation do not add ... after truncation. So it's an exact truncation.
2970
 *  @param  int     $display            Trunc is use to display and can be changed for small screen. TODO Remove this param (must be dealt with CSS)
2971
 *	@return string						Truncated string. WARNING: length is never higher than $size if $nodot is set, but can be 3 chars higher otherwise.
2972
 */
2973
function dol_trunc($string,$size=40,$trunc='right',$stringencoding='UTF-8',$nodot=0, $display=0)
2974
{
2975
	global $conf;
2976
2977
	if ($size==0 || ! empty($conf->global->MAIN_DISABLE_TRUNC)) return $string;
2978
2979
	if (empty($stringencoding)) $stringencoding='UTF-8';
2980
	// reduce for small screen
2981
	if ($conf->dol_optimize_smallscreen==1 && $display==1) $size = round($size/3);
2982
2983
	// We go always here
2984
	if ($trunc == 'right')
2985
	{
2986
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2987
		if (dol_strlen($newstring,$stringencoding) > ($size+($nodot?0:3)))    // If nodot is 0 and size is 1,2 or 3 chars more, we don't trunc and don't add ...
2988
		return dol_substr($newstring,0,$size,$stringencoding).($nodot?'':'...');
2989
		else
2990
		//return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
2991
		return $string;
2992
	}
2993
	elseif ($trunc == 'middle')
2994
	{
2995
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2996
		if (dol_strlen($newstring,$stringencoding) > 2 && dol_strlen($newstring,$stringencoding) > ($size+1))
2997
		{
2998
			$size1=round($size/2);
2999
			$size2=round($size/2);
3000
			return dol_substr($newstring,0,$size1,$stringencoding).'...'.dol_substr($newstring,dol_strlen($newstring,$stringencoding) - $size2,$size2,$stringencoding);
3001
		}
3002
		else
3003
		return $string;
3004
	}
3005
	elseif ($trunc == 'left')
3006
	{
3007
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
3008
		if (dol_strlen($newstring,$stringencoding) > ($size+($nodot?0:3)))    // If nodot is 0 and size is 1,2 or 3 chars more, we don't trunc and don't add ...
3009
		return '...'.dol_substr($newstring,dol_strlen($newstring,$stringencoding) - $size,$size,$stringencoding);
3010
		else
3011
		return $string;
3012
	}
3013
	elseif ($trunc == 'wrap')
3014
	{
3015
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
3016
		if (dol_strlen($newstring,$stringencoding) > ($size+1))
3017
		return dol_substr($newstring,0,$size,$stringencoding)."\n".dol_trunc(dol_substr($newstring,$size,dol_strlen($newstring,$stringencoding)-$size,$stringencoding),$size,$trunc);
3018
		else
3019
		return $string;
3020
	}
3021
	else return 'BadParam3CallingDolTrunc';
3022
}
3023
3024
/**
3025
 *	Show picto whatever it's its name (generic function)
3026
 *
3027
 *	@param      string		$titlealt         	Text on title tag for tooltip. Not used if param notitle is set to 1.
3028
 *	@param      string		$picto       		Name of image file to show ('filenew', ...)
3029
 *												If no extension provided, we use '.png'. Image must be stored into theme/xxx/img directory.
3030
 *                                  			Example: picto.png                  if picto.png is stored into htdocs/theme/mytheme/img
3031
 *                                  			Example: picto.png@mymodule         if picto.png is stored into htdocs/mymodule/img
3032
 *                                  			Example: /mydir/mysubdir/picto.png  if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1)
3033
 *	@param		string		$moreatt			Add more attribute on img tag (For example 'style="float: right"')
3034
 *	@param		int			$pictoisfullpath	If 1, image path is a full path
3035
 *	@param		int			$srconly			Return only content of the src attribute of img.
3036
 *  @param		int			$notitle			1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip.
3037
 *  @param		string		$alt				Force alt for bind peoplae
3038
 *  @param		string		$morecss			Add more class css on img tag (For example 'myclascss')
3039
 *  @return     string       				    Return img tag
3040
 *  @see        #img_object, #img_picto_common
3041
 */
3042
function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly=0, $notitle=0, $alt='', $morecss='')
3043
{
3044
	global $conf, $langs;
3045
3046
	// Define fullpathpicto to use into src
3047
	if ($pictoisfullpath)
3048
	{
3049
		// Clean parameters
3050
		if (! preg_match('/(\.png|\.gif|\.svg)$/i',$picto)) $picto .= '.png';
3051
		$fullpathpicto = $picto;
3052
	}
3053
	else
3054
	{
3055
		//if (in_array($picto, array('switch_off', 'switch_on', 'off', 'on')))
3056
		if (in_array($picto, array('switch_off', 'switch_on', 'off', 'on')))
3057
		{
3058
			$fakey = $picto; $facolor=''; $fasize='';
3059
			if ($picto == 'switch_off') { $fakey = 'fa-toggle-off'; $facolor='#999';    $fasize='2em'; }
3060
			if ($picto == 'switch_on')  { $fakey = 'fa-toggle-on';  $facolor='#227722'; $fasize='2em'; }
3061
			if ($picto == 'off') { $fakey = 'fa-square-o'; $fasize='1.3em'; }
3062
			if ($picto == 'on')  { $fakey = 'fa-check-square-o'; $fasize='1.3em'; }
3063
			$enabledisablehtml='';
3064
			$enabledisablehtml.='<span class="fa '.$fakey.' valignmiddle'.($morecss?' '.$morecss:'').'" style="'.($fasize?('font-size: '.$fasize.';'):'').($facolor?(' color: '.$facolor.';'):'').'" alt="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'"'.($moreatt?' '.$moreatt:'').'">';
3065
			if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) $enabledisablehtml.=$titlealt;
3066
			$enabledisablehtml.='</span>';
3067
			return $enabledisablehtml;
3068
		}
3069
3070
		// We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
3071
		$url = DOL_URL_ROOT;
3072
		$theme = $conf->theme;
3073
3074
		$path = 'theme/'.$theme;
3075
		if (! empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme;	// If the theme does not have the same name as the module
3076
		else if (! empty($conf->global->MAIN_OVERWRITE_THEME_RES)) $path = $conf->global->MAIN_OVERWRITE_THEME_RES.'/theme/'.$conf->global->MAIN_OVERWRITE_THEME_RES;  // To allow an external module to overwrite image resources whatever is activated theme
3077
		else if (! empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) $path = $theme.'/theme/'.$theme;	// If the theme have the same name as the module
3078
3079
		// If we ask an image into $url/$mymodule/img (instead of default path)
3080
		if (preg_match('/^([^@]+)@([^@]+)$/i',$picto,$regs))
3081
		{
3082
			$picto = $regs[1];
3083
			$path = $regs[2];	// $path is $mymodule
3084
		}
3085
3086
		// Clean parameters
3087
		if (! preg_match('/(\.png|\.gif|\.svg)$/i',$picto)) $picto .= '.png';
3088
		// If alt path are defined, define url where img file is, according to physical path
3089
		foreach ($conf->file->dol_document_root as $type => $dirroot)	// ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
3090
		{
3091
			if ($type == 'main') continue;
3092
			if (file_exists($dirroot.'/'.$path.'/img/'.$picto))	// This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
3093
			{
3094
				$url=DOL_URL_ROOT.$conf->file->dol_url_root[$type];
3095
				break;
3096
			}
3097
		}
3098
3099
		// $url is '' or '/custom', $path is current theme or
3100
		$fullpathpicto = $url.'/'.$path.'/img/'.$picto;
3101
	}
3102
3103
	if ($srconly) return $fullpathpicto;
3104
	else
3105
	{
3106
		// tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for bind people
3107
		//$tmparray=array(0=>$titlealt);
3108
		//if (empty($notitle) && preg_match('/:[^\s0-9]/',$titlealt)) $tmparray=explode(':',$titlealt);		// We explode if we have TextA:TextB. Not if we have TextA: TextB
3109
		//$title=$tmparray[0];
3110
		//$alt=empty($tmparray[1])?'':$tmparray[1];
3111
		$title=$titlealt;
3112
		return '<img src="'.$fullpathpicto.'" alt="'.dol_escape_htmltag($alt).'"'.(($notitle || empty($title))?'':' title="'.dol_escape_htmltag($title).'"').($moreatt?' '.$moreatt:' class="inline-block"').'>';	// Alt is used for accessibility, title for popup
3113
	}
3114
}
3115
3116
/**
3117
 *	Show a picto called object_picto (generic function)
3118
 *
3119
 *	@param	string	$titlealt			Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3120
 *	@param	string	$picto				Name of image to show object_picto (example: user, group, action, bill, contract, propal, product, ...)
3121
 *										For external modules use imagename@mymodule to search into directory "img" of module.
3122
 *	@param	string	$moreatt			Add more attribute on img tag (ie: class="datecallink")
3123
 *	@param	int		$pictoisfullpath	If 1, image path is a full path
3124
 *	@param	int		$srconly			Return only content of the src attribute of img.
3125
 *  @param	int		$notitle			1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip.
3126
 *	@return	string						Return img tag
3127
 *	@see	#img_picto, #img_picto_common
3128
 */
3129
function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly=0, $notitle=0)
3130
{
3131
	return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
3132
}
3133
3134
/**
3135
 *	Show weather picto
3136
 *
3137
 *	@param      string		$titlealt         	Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3138
 *	@param      string		$picto       		Name of image file to show (If no extension provided, we use '.png'). Image must be stored into htdocs/theme/common directory.
3139
 *	@param		string		$moreatt			Add more attribute on img tag
3140
 *	@param		int			$pictoisfullpath	If 1, image path is a full path
3141
 *	@return     string      					Return img tag
3142
 *  @see        #img_object, #img_picto
3143
 */
3144
function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0)
3145
{
3146
	global $conf;
3147
3148
	if (! preg_match('/(\.png|\.gif)$/i', $picto)) $picto .= '.png';
3149
3150
	$path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
3151
3152
	return img_picto($titlealt, $path, $moreatt, 1);
3153
}
3154
3155
/**
3156
 *	Show picto (generic function)
3157
 *
3158
 *	@param      string		$titlealt         	Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3159
 *	@param      string		$picto       		Name of image file to show (If no extension provided, we use '.png'). Image must be stored into htdocs/theme/common directory.
3160
 *	@param		string		$moreatt			Add more attribute on img tag
3161
 *	@param		int			$pictoisfullpath	If 1, image path is a full path
3162
 *	@return     string      					Return img tag
3163
 *  @see        #img_object, #img_picto
3164
 */
3165
function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0)
3166
{
3167
	global $conf;
3168
3169
	if (! preg_match('/(\.png|\.gif)$/i', $picto)) $picto .= '.png';
3170
3171
	if ($pictoisfullpath) $path = $picto;
3172
	else
3173
	{
3174
		$path = DOL_URL_ROOT.'/theme/common/'.$picto;
3175
3176
		if (! empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS))
3177
		{
3178
			$themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
3179
3180
			if (file_exists($themepath)) $path = $themepath;
3181
		}
3182
	}
3183
3184
	return img_picto($titlealt, $path, $moreatt, 1);
3185
}
3186
3187
/**
3188
 *	Show logo action
3189
 *
3190
 *	@param	string		$titlealt       Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3191
 *	@param  string		$numaction   	Action id or code to show
3192
 *	@return string      				Return an img tag
3193
 */
3194
function img_action($titlealt, $numaction)
3195
{
3196
	global $conf, $langs;
3197
3198
	if (empty($titlealt) || $titlealt == 'default')
3199
	{
3200
		if ($numaction == '-1' || $numaction == 'ST_NO')			{ $numaction = -1; $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact'); }
3201
		elseif ($numaction ==  '0' || $numaction == 'ST_NEVER') 	{ $numaction = 0; $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted'); }
3202
		elseif ($numaction ==  '1' || $numaction == 'ST_TODO')  	{ $numaction = 1; $titlealt = $langs->transnoentitiesnoconv('ChangeToContact'); }
3203
		elseif ($numaction ==  '2' || $numaction == 'ST_PEND')  	{ $numaction = 2; $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess'); }
3204
		elseif ($numaction ==  '3' || $numaction == 'ST_DONE')  	{ $numaction = 3; $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone'); }
3205
		else { $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction); $numaction = 0; }
3206
	}
3207
	if (! is_numeric($numaction)) $numaction=0;
3208
3209
	return img_picto($titlealt, 'stcomm'.$numaction.'.png');
3210
}
3211
3212
/**
3213
 *  Show pdf logo
3214
 *
3215
 *  @param	string		$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3216
 *  @param  int		    $size       Taille de l'icone : 3 = 16x16px , 2 = 14x14px
3217
 *  @return string      			Retourne tag img
3218
 */
3219
function img_pdf($titlealt = 'default', $size = 3)
3220
{
3221
	global $conf, $langs;
3222
3223
	if ($titlealt == 'default') $titlealt = $langs->trans('Show');
3224
3225
	return img_picto($titlealt, 'pdf'.$size.'.png');
3226
}
3227
3228
/**
3229
 *	Show logo +
3230
 *
3231
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3232
 *	@param  string	$other      Add more attributes on img
3233
 *	@return string      		Return tag img
3234
 */
3235
function img_edit_add($titlealt = 'default', $other = '')
3236
{
3237
	global $conf, $langs;
3238
3239
	if ($titlealt == 'default') $titlealt = $langs->trans('Add');
3240
3241
	return img_picto($titlealt, 'edit_add.png', $other);
3242
}
3243
/**
3244
 *	Show logo -
3245
 *
3246
 *	@param	string	$titlealt	Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3247
 *	@param  string	$other      Add more attributes on img
3248
 *	@return string      		Return tag img
3249
 */
3250
function img_edit_remove($titlealt = 'default', $other='')
3251
{
3252
	global $conf, $langs;
3253
3254
	if ($titlealt == 'default') $titlealt = $langs->trans('Remove');
3255
3256
	return img_picto($titlealt, 'edit_remove.png', $other);
3257
}
3258
3259
/**
3260
 *	Show logo editer/modifier fiche
3261
 *
3262
 *	@param  string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3263
 *	@param  integer	$float      Si il faut y mettre le style "float: right"
3264
 *	@param  string	$other		Add more attributes on img
3265
 *	@return string      		Return tag img
3266
 */
3267
function img_edit($titlealt = 'default', $float = 0, $other = 'class="pictoedit"')
3268
{
3269
	global $conf, $langs;
3270
3271
	if ($titlealt == 'default') $titlealt = $langs->trans('Modify');
3272
3273
	return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl'?'left':'right').'"' : "") . ($other?' '.$other:''));
3274
}
3275
3276
/**
3277
 *	Show logo view card
3278
 *
3279
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3280
 *	@param  integer	$float      Si il faut y mettre le style "float: right"
3281
 *	@param  string	$other		Add more attributes on img
3282
 *	@return string      		Return tag img
3283
 */
3284
function img_view($titlealt = 'default', $float = 0, $other = '')
3285
{
3286
	global $conf, $langs;
3287
3288
	if ($titlealt == 'default') $titlealt = $langs->trans('View');
3289
3290
	$moreatt = ($float ? 'style="float: right" ' : '').$other;
3291
3292
	return img_picto($titlealt, 'view.png', $moreatt);
3293
}
3294
3295
/**
3296
 *  Show delete logo
3297
 *
3298
 *  @param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3299
 *	@param  string	$other      Add more attributes on img
3300
 *  @return string      		Retourne tag img
3301
 */
3302
function img_delete($titlealt = 'default', $other = 'class="pictodelete"')
3303
{
3304
	global $conf, $langs;
3305
3306
	if ($titlealt == 'default') $titlealt = $langs->trans('Delete');
3307
3308
	return img_picto($titlealt, 'delete.png', $other);
3309
}
3310
3311
/**
3312
 *  Show printer logo
3313
 *
3314
 *  @param  string  $titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3315
 *  @param  string  $other      Add more attributes on img
3316
 *  @return string              Retourne tag img
3317
 */
3318
function img_printer($titlealt = "default", $other='')
3319
{
3320
	global $conf,$langs;
3321
	if ($titlealt=="default") $titlealt=$langs->trans("Print");
3322
	return img_picto($titlealt,'printer.png',$other);
3323
}
3324
3325
/**
3326
 *  Show split logo
3327
 *
3328
 *  @param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3329
 *	@param  string	$other      Add more attributes on img
3330
 *  @return string      		Retourne tag img
3331
 */
3332
function img_split($titlealt = 'default', $other = 'class="pictosplit"')
3333
{
3334
	global $conf, $langs;
3335
3336
	if ($titlealt == 'default') $titlealt = $langs->trans('Split');
3337
3338
	return img_picto($titlealt, 'split.png', $other);
3339
}
3340
3341
/**
3342
 *	Show help logo with cursor "?"
3343
 *
3344
 * 	@param	int              	$usehelpcursor		1=Use help cursor, 2=Use click pointer cursor, 0=No specific cursor
3345
 * 	@param	int|string	        $usealttitle		Text to use as alt title
3346
 * 	@return string            	           			Return tag img
3347
 */
3348
function img_help($usehelpcursor = 1, $usealttitle = 1)
3349
{
3350
	global $conf, $langs;
3351
3352
	if ($usealttitle)
3353
	{
3354
		if (is_string($usealttitle)) $usealttitle = dol_escape_htmltag($usealttitle);
3355
		else $usealttitle = $langs->trans('Info');
3356
	}
3357
3358
	return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help': ($usehelpcursor == 2 ? ' cursor: pointer':'')).'"');
3359
}
3360
3361
/**
3362
 *	Show info logo
3363
 *
3364
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3365
 *	@return string      		Return img tag
3366
 */
3367
function img_info($titlealt = 'default')
3368
{
3369
	global $conf, $langs;
3370
3371
	if ($titlealt == 'default') $titlealt = $langs->trans('Informations');
3372
3373
	return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
3374
}
3375
3376
/**
3377
 *	Show warning logo
3378
 *
3379
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3380
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"'). If 1, add float: right. Can't be "class" attribute.
3381
 *	@return string      		Return img tag
3382
 */
3383
function img_warning($titlealt = 'default', $moreatt = '')
3384
{
3385
	global $conf, $langs;
3386
3387
	if ($titlealt == 'default') $titlealt = $langs->trans('Warning');
3388
3389
	//return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
3390
	return img_picto($titlealt, 'warning.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): ''));
3391
}
3392
3393
/**
3394
 *  Show error logo
3395
 *
3396
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3397
 *	@return string      		Return img tag
3398
 */
3399
function img_error($titlealt = 'default')
3400
{
3401
	global $conf, $langs;
3402
3403
	if ($titlealt == 'default') $titlealt = $langs->trans('Error');
3404
3405
	return img_picto($titlealt, 'error.png', 'class="valigntextbottom"');
3406
}
3407
3408
/**
3409
 *	Show next logo
3410
 *
3411
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3412
*	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3413
 *	@return string      		Return img tag
3414
 */
3415
function img_next($titlealt = 'default', $moreatt='')
3416
{
3417
	global $conf, $langs;
3418
3419
	if ($titlealt == 'default') $titlealt = $langs->trans('Next');
3420
3421
	//return img_picto($titlealt, 'next.png', $moreatt);
3422
	return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
3423
}
3424
3425
/**
3426
 *	Show previous logo
3427
 *
3428
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3429
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3430
 *	@return string      		Return img tag
3431
 */
3432
function img_previous($titlealt = 'default', $moreatt='')
3433
{
3434
	global $conf, $langs;
3435
3436
	if ($titlealt == 'default') $titlealt = $langs->trans('Previous');
3437
3438
	//return img_picto($titlealt, 'previous.png', $moreatt);
3439
	return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
3440
}
3441
3442
/**
3443
 *	Show down arrow logo
3444
 *
3445
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3446
 *	@param  int		$selected   Selected
3447
 *  @param	string	$moreclass	Add more CSS classes
3448
 *	@return string      		Return img tag
3449
 */
3450
function img_down($titlealt = 'default', $selected = 0, $moreclass='')
3451
{
3452
	global $conf, $langs;
3453
3454
	if ($titlealt == 'default') $titlealt = $langs->trans('Down');
3455
3456
	return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass?" ".$moreclass:"").'"');
3457
}
3458
3459
/**
3460
 *	Show top arrow logo
3461
 *
3462
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3463
 *	@param  int		$selected	Selected
3464
 *  @param	string	$moreclass	Add more CSS classes
3465
 *	@return string      		Return img tag
3466
 */
3467
function img_up($titlealt = 'default', $selected = 0, $moreclass='')
3468
{
3469
	global $conf, $langs;
3470
3471
	if ($titlealt == 'default') $titlealt = $langs->trans('Up');
3472
3473
	return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass?" ".$moreclass:"").'"');
3474
}
3475
3476
/**
3477
 *	Show left arrow logo
3478
 *
3479
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3480
 *	@param  int		$selected	Selected
3481
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3482
 *	@return string      		Return img tag
3483
 */
3484
function img_left($titlealt = 'default', $selected = 0, $moreatt='')
3485
{
3486
	global $conf, $langs;
3487
3488
	if ($titlealt == 'default') $titlealt = $langs->trans('Left');
3489
3490
	return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
3491
}
3492
3493
/**
3494
 *	Show right arrow logo
3495
 *
3496
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3497
 *	@param  int		$selected	Selected
3498
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3499
 *	@return string      		Return img tag
3500
 */
3501
function img_right($titlealt = 'default', $selected = 0, $moreatt='')
3502
{
3503
	global $conf, $langs;
3504
3505
	if ($titlealt == 'default') $titlealt = $langs->trans('Right');
3506
3507
	return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
3508
}
3509
3510
/**
3511
 *	Show tick logo if allowed
3512
 *
3513
 *	@param	string	$allow		Allow
3514
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3515
 *	@return string      		Return img tag
3516
 */
3517
function img_allow($allow, $titlealt = 'default')
3518
{
3519
	global $conf, $langs;
3520
3521
	if ($titlealt == 'default') $titlealt = $langs->trans('Active');
3522
3523
	if ($allow == 1) return img_picto($titlealt, 'tick.png');
3524
3525
	return '-';
3526
}
3527
3528
3529
/**
3530
 *	Show MIME img of a file
3531
 *
3532
 *	@param	string	$file		Filename
3533
 * 	@param	string	$titlealt	Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3534
 *  @param	string	$morecss	More css
3535
 *	@return string     			Return img tag
3536
 */
3537
function img_mime($file, $titlealt = '', $morecss='')
3538
{
3539
	require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3540
3541
	$mimetype = dol_mimetype($file, '', 1);
3542
	$mimeimg = dol_mimetype($file, '', 2);
3543
	$mimefa = dol_mimetype($file, '', 4);
3544
3545
	if (empty($titlealt)) $titlealt = 'Mime type: '.$mimetype;
3546
3547
	//return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
3548
	return '<i class="fa fa-'.$mimefa.' paddingright"></i>';
3549
}
3550
3551
3552
/**
3553
 *	Show phone logo.
3554
 *  Use img_picto instead.
3555
 *
3556
 *	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3557
 *	@param  int		$option		Option
3558
 *	@return string      		Return img tag
3559
 *  @deprecated
3560
 *  @see img_picto
3561
 */
3562
function img_phone($titlealt = 'default', $option = 0)
3563
{
3564
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
3565
3566
	global $conf,$langs;
3567
3568
	if ($titlealt == 'default') $titlealt = $langs->trans('Call');
3569
3570
	if ($option == 1) $img = 'call';
3571
	else $img = 'call_out';
3572
3573
	return img_picto($titlealt, $img);
3574
}
3575
3576
/**
3577
 *  Show search logo
3578
 *
3579
 *  @param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3580
 *	@param  string	$other      Add more attributes on img
3581
 *  @return string      		Retourne tag img
3582
 */
3583
function img_search($titlealt = 'default', $other = '')
3584
{
3585
	global $conf, $langs;
3586
3587
	if ($titlealt == 'default') $titlealt = $langs->trans('Search');
3588
3589
	$img = img_picto($titlealt, 'search.png', $other, false, 1);
3590
3591
	$input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
3592
	$input.= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
3593
3594
	return $input;
3595
}
3596
3597
/**
3598
 *  Show search logo
3599
 *
3600
 *  @param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3601
 *	@param  string	$other      Add more attributes on img
3602
 *  @return string      		Retourne tag img
3603
 */
3604
function img_searchclear($titlealt = 'default', $other = '')
3605
{
3606
	global $conf, $langs;
3607
3608
	if ($titlealt == 'default') $titlealt = $langs->trans('Search');
3609
3610
	$img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
3611
3612
	$input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
3613
	$input.= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
3614
3615
	return $input;
3616
}
3617
3618
/**
3619
 *	Show information for admin users or standard users
3620
 *
3621
 *	@param	string	$text			Text info
3622
 *	@param  integer	$infoonimgalt	Info is shown only on alt of star picto, otherwise it is show on output after the star picto
3623
 *	@param	int		$nodiv			No div
3624
 *  @param  string  $admin          '1'=Info for admin users. '0'=Info for standard users (change only the look), 'xxx'=Other
3625
 *  @param	string	$morecss		More CSS
3626
 *	@return	string					String with info text
3627
 */
3628
function info_admin($text, $infoonimgalt = 0, $nodiv=0, $admin='1', $morecss='')
3629
{
3630
	global $conf, $langs;
3631
3632
	if ($infoonimgalt)
3633
	{
3634
		return img_picto($text, 'info', 'class="hideonsmartphone'.($morecss?' '.$morecss:'').'"');
3635
	}
3636
3637
	return ($nodiv?'':'<div class="'.(empty($admin)?'':($admin=='1'?'info':$admin)).' hideonsmartphone'.($morecss?' '.$morecss:'').'">').'<span class="fa fa-info-circle" title="'.dol_escape_htmltag($admin?$langs->trans('InfoAdmin'):$langs->trans('Note')).'"></span> '.$text.($nodiv?'':'</div>');
3638
}
3639
3640
3641
/**
3642
 *	Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remontee des bugs.
3643
 *	On doit appeler cette fonction quand une erreur technique bloquante est rencontree.
3644
 *	Toutefois, il faut essayer de ne l'appeler qu'au sein de pages php, les classes devant
3645
 *	renvoyer leur erreur par l'intermediaire de leur propriete "error".
3646
 *
3647
 *	@param	 	DoliDB	$db      	Database handler
3648
 *	@param  	mixed	$error		String or array of errors strings to show
3649
 *  @param		array	$errors		Array of errors
3650
 *	@return 	void
3651
 *  @see    	dol_htmloutput_errors
3652
 */
3653
function dol_print_error($db='',$error='',$errors=null)
3654
{
3655
	global $conf,$langs,$argv;
3656
	global $dolibarr_main_prod;
3657
3658
	$out = '';
3659
	$syslog = '';
3660
3661
	// Si erreur intervenue avant chargement langue
3662
	if (! $langs)
3663
	{
3664
		require_once DOL_DOCUMENT_ROOT .'/core/class/translate.class.php';
3665
		$langs = new Translate('', $conf);
3666
		$langs->load("main");
3667
	}
3668
	$langs->load("main");
3669
	$langs->load("errors");
3670
3671
	if ($_SERVER['DOCUMENT_ROOT'])    // Mode web
3672
	{
3673
		$out.=$langs->trans("DolibarrHasDetectedError").".<br>\n";
3674
		if (! empty($conf->global->MAIN_FEATURES_LEVEL)) $out.="You use an experimental or develop level of features, so please do NOT report any bugs, except if problem is confirmed moving option MAIN_FEATURES_LEVEL back to 0.<br>\n";
3675
		$out.=$langs->trans("InformationToHelpDiagnose").":<br>\n";
3676
3677
		$out.="<b>".$langs->trans("Date").":</b> ".dol_print_date(time(),'dayhourlog')."<br>\n";
3678
		$out.="<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION."<br>\n";
3679
		if (isset($conf->global->MAIN_FEATURES_LEVEL)) $out.="<b>".$langs->trans("LevelOfFeature").":</b> ".$conf->global->MAIN_FEATURES_LEVEL."<br>\n";
3680
		if (function_exists("phpversion"))
3681
		{
3682
			$out.="<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
3683
		}
3684
		$out.="<b>".$langs->trans("Server").":</b> ".$_SERVER["SERVER_SOFTWARE"]."<br>\n";
3685
		if (function_exists("php_uname"))
3686
		{
3687
			$out.="<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
3688
		}
3689
		$out.="<b>".$langs->trans("UserAgent").":</b> ".$_SERVER["HTTP_USER_AGENT"]."<br>\n";
3690
		$out.="<br>\n";
3691
		$out.="<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"],ENT_COMPAT,'UTF-8')."<br>\n";
3692
		$out.="<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"])?dol_htmlentities($_SERVER["HTTP_REFERER"],ENT_COMPAT,'UTF-8'):'')."<br>\n";
3693
		$out.="<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu)?$conf->standard_menu:'')."<br>\n";
3694
		$out.="<br>\n";
3695
		$syslog.="url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
3696
		$syslog.=", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
3697
	}
3698
	else                              // Mode CLI
3699
	{
3700
		$out.='> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
3701
		$syslog.="pid=".dol_getmypid();
3702
	}
3703
3704
	if (is_object($db))
3705
	{
3706
		if ($_SERVER['DOCUMENT_ROOT'])  // Mode web
3707
		{
3708
			$out.="<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
3709
			$out.="<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror()?dol_escape_htmltag($db->lastqueryerror()):$langs->trans("ErrorNoRequestInError"))."<br>\n";
3710
			$out.="<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno()?dol_escape_htmltag($db->lasterrno()):$langs->trans("ErrorNoRequestInError"))."<br>\n";
3711
			$out.="<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror()?dol_escape_htmltag($db->lasterror()):$langs->trans("ErrorNoRequestInError"))."<br>\n";
3712
			$out.="<br>\n";
3713
		}
3714
		else                            // Mode CLI
3715
		{
3716
			// No dol_escape_htmltag for output, we are in CLI mode
3717
			$out.='> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
3718
			$out.='> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror()?$db->lastqueryerror():$langs->transnoentities("ErrorNoRequestInError"))."\n";
3719
			$out.='> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno()?$db->lasterrno():$langs->transnoentities("ErrorNoRequestInError"))."\n";
3720
			$out.='> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror()?$db->lasterror():$langs->transnoentities("ErrorNoRequestInError"))."\n";
3721
3722
		}
3723
		$syslog.=", sql=".$db->lastquery();
3724
		$syslog.=", db_error=".$db->lasterror();
3725
	}
3726
3727
	if ($error || $errors)
3728
	{
3729
		$langs->load("errors");
3730
3731
		// Merge all into $errors array
3732
		if (is_array($error) && is_array($errors)) $errors=array_merge($error,$errors);
3733
		elseif (is_array($error)) $errors=$error;
3734
		elseif (is_array($errors)) $errors=array_merge(array($error),$errors);
3735
		else $errors=array_merge(array($error));
3736
3737
		foreach($errors as $msg)
3738
		{
3739
			if (empty($msg)) continue;
3740
			if ($_SERVER['DOCUMENT_ROOT'])  // Mode web
3741
			{
3742
				$out.="<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n" ;
3743
			}
3744
			else                        // Mode CLI
3745
			{
3746
				$out.='> '.$langs->transnoentities("Message").":\n".$msg."\n" ;
3747
			}
3748
			$syslog.=", msg=".$msg;
3749
		}
3750
	}
3751
	if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file'))
3752
	{
3753
		xdebug_print_function_stack();
3754
		$out.='<b>XDebug informations:</b>'."<br>\n";
3755
		$out.='File: '.xdebug_call_file()."<br>\n";
3756
		$out.='Line: '.xdebug_call_line()."<br>\n";
3757
		$out.='Function: '.xdebug_call_function()."<br>\n";
3758
		$out.="<br>\n";
3759
	}
3760
3761
	if (empty($dolibarr_main_prod)) print $out;
3762
	else
3763
	{
3764
		print $langs->trans("DolibarrHasDetectedError").'. ';
3765
		print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
3766
		define("MAIN_CORE_ERROR", 1);
3767
	}
3768
	//else print 'Sorry, an error occured but the parameter $dolibarr_main_prod is defined in conf file so no message is reported to your browser. Please read the log file for error message.';
3769
	dol_syslog("Error ".$syslog, LOG_ERR);
3770
}
3771
3772
/**
3773
 * Show a public email and error code to contact if technical error
3774
 *
3775
 * @param	string	$prefixcode		Prefix of public error code
3776
 * @param   string  $errormessage   Complete error message
3777
 * @param	array	$errormessages	Array of error messages
3778
 * @param	string	$morecss		More css
3779
 * @return	void
3780
 */
3781
function dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error')
3782
{
3783
	global $langs,$conf;
3784
3785
	$langs->load("errors");
3786
	$now=dol_now();
3787
	print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
3788
	print $langs->trans("ErrorContactEMail", $conf->global->MAIN_INFO_SOCIETE_MAIL, $prefixcode.dol_print_date($now,'%Y%m%d'));
3789
	if ($errormessage) print '<br><br>'.$errormessage;
3790
	if (is_array($errormessages) && count($errormessages))
3791
	{
3792
		foreach($errormessages as $mesgtoshow)
3793
		{
3794
			print '<br><br>'.$mesgtoshow;
3795
		}
3796
	}
3797
	print '</div></div>';
3798
}
3799
3800
/**
3801
 *	Show title line of an array
3802
 *
3803
 *	@param	string	$name        Label of field
3804
 *	@param	string	$file        Url used when we click on sort picto
3805
 *	@param	string	$field       Field to use for new sorting
3806
 *	@param	string	$begin       ("" by defaut)
3807
 *	@param	string	$moreparam   Add more parameters on sort url links ("" by default)
3808
 *	@param  string	$moreattrib  Options of attribute td ("" by defaut, example: 'align="center"')
3809
 *	@param  string	$sortfield   Current field used to sort
3810
 *	@param  string	$sortorder   Current sort order
3811
 *  @param	string	$prefix		 Prefix for css. Use space after prefix to add your own CSS tag.
3812
 *  @param	string	$tooltip	 Tooltip
3813
 *	@return	void
3814
 */
3815
function print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="")
3816
{
3817
	print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip);
3818
}
3819
3820
/**
3821
 *	Get title line of an array
3822
 *
3823
 *	@param	string	$name        		Translation key of field
3824
 *	@param	int		$thead		 		0=To use with standard table format, 1=To use inside <thead><tr>, 2=To use with <div>
3825
 *	@param	string	$file        		Url used when we click on sort picto
3826
 *	@param	string	$field       		Field to use for new sorting. Empty if this field is not sortable.
3827
 *	@param	string	$begin       		("" by defaut)
3828
 *	@param	string	$moreparam   		Add more parameters on sort url links ("" by default)
3829
 *	@param  string	$moreattrib  		Add more attributes on th ("" by defaut, example: 'align="center"'). To add more css class, use param $prefix.
3830
 *	@param  string	$sortfield   		Current field used to sort (Ex: 'd.datep,d.id')
3831
 *	@param  string	$sortorder   		Current sort order (Ex: 'asc,desc')
3832
 *  @param	string	$prefix		 		Prefix for css. Use space after prefix to add your own CSS tag, for example 'mycss '.
3833
 *  @param	string	$disablesortlink	1=Disable sort link
3834
 *  @param	string	$tooltip	 		Tooltip
3835
 *	@return	string
3836
 */
3837
function getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip='')
3838
{
3839
	global $conf, $langs, $form;
3840
	//print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
3841
3842
	$sortorder=strtoupper($sortorder);
3843
	$out='';
3844
	$sortimg='';
3845
3846
	$tag='th';
3847
	if ($thead==2) $tag='div';
3848
3849
	$tmpsortfield=explode(',',$sortfield);
3850
	$sortfield1=trim($tmpsortfield[0]);    // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
3851
	$tmpfield=explode(',',$field);
3852
	$field1=trim($tmpfield[0]);            // If $field is 'd.datep,d.id', it becomes 'd.datep'
3853
3854
	//var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
3855
	// If field is used as sort criteria we use a specific css class liste_titre_sel
3856
	// Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
3857
	if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./","",$field1))) $out.= '<'.$tag.' class="'.$prefix.'liste_titre_sel" '. $moreattrib.'>';
3858
	else $out.= '<'.$tag.' class="'.$prefix.'liste_titre" '. $moreattrib.'>';
3859
3860
	if (empty($thead) && $field && empty($disablesortlink))    // If this is a sort field
3861
	{
3862
		$options=preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i','',$moreparam);
3863
		$options=preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i','',$options);
3864
		$options=preg_replace('/&+/i','&',$options);
3865
		if (! preg_match('/^&/',$options)) $options='&'.$options;
3866
3867
		if ($field1 != $sortfield1) // We are on another field
3868
		{
3869
			if (preg_match('/^DESC/', $sortorder)) $out.= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">';
3870
			else $out.= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">';
3871
		}
3872
		else                      // We are of first sorting criteria
3873
		{
3874
			if (preg_match('/^ASC/', $sortorder)) $out.= '<a class="reposition" href="'.$file.'?sortfield='.$sortfield.'&sortorder=desc&begin='.$begin.$options.'">';
3875
			else $out.= '<a class="reposition" href="'.$file.'?sortfield='.$sortfield.'&sortorder=asc&begin='.$begin.$options.'">';
3876
		}
3877
	}
3878
3879
	if ($tooltip) $out.=$form->textwithpicto($langs->trans($name), $langs->trans($tooltip));
3880
	else $out.=$langs->trans($name);
3881
3882
	if (empty($thead) && $field && empty($disablesortlink))    // If this is a sort field
3883
	{
3884
		$out.='</a>';
3885
	}
3886
3887
	if (empty($thead) && $field)    // If this is a sort field
3888
	{
3889
		$options=preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i','',$moreparam);
3890
		$options=preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i','',$options);
3891
		$options=preg_replace('/&+/i','&',$options);
3892
		if (! preg_match('/^&/',$options)) $options='&'.$options;
3893
3894
		//print "&nbsp;";
3895
		//$sortimg.= '<img width="2" src="'.DOL_URL_ROOT.'/theme/common/transparent.png" alt="">';
3896
		//$sortimg.= '<span class="nowrap">';
3897
3898
		if (! $sortorder || $field1 != $sortfield1)
3899
		{
3900
			//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
3901
			//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
3902
		}
3903
		else
3904
		{
3905
			if (preg_match('/^DESC/', $sortorder)) {
3906
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
3907
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
3908
				$sortimg.= '<span class="nowrap">'.img_up("Z-A",0).'</span>';
3909
			}
3910
			if (preg_match('/^ASC/', $sortorder)) {
3911
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
3912
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
3913
				$sortimg.= '<span class="nowrap">'.img_down("A-Z",0).'</span>';
3914
			}
3915
		}
3916
3917
		//$sortimg.= '</span>';
3918
	}
3919
3920
	$out.=$sortimg;
3921
3922
	$out.='</'.$tag.'>';
3923
3924
	return $out;
3925
}
3926
3927
/**
3928
 *	Show a title.
3929
 *
3930
 *	@param	string	$title			Title to show
3931
 *	@return	string					Title to show
3932
 *  @deprecated						Use load_fiche_titre instead
3933
 *  @see load_fiche_titre
3934
 */
3935
function print_titre($title)
3936
{
3937
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
3938
3939
	print '<div class="titre">'.$title.'</div>';
3940
}
3941
3942
/**
3943
 *	Show a title with picto
3944
 *
3945
 *	@param	string	$title				Title to show
3946
 *	@param	string	$mesg				Added message to show on right
3947
 *	@param	string	$picto				Icon to use before title (should be a 32x32 transparent png file)
3948
 *	@param	int		$pictoisfullpath	1=Icon name is a full absolute url of image
3949
 * 	@param	int		$id					To force an id on html objects
3950
 * 	@return	void
3951
 *  @deprecated Use print load_fiche_titre instead
3952
 */
3953
function print_fiche_titre($title, $mesg='', $picto='title_generic.png', $pictoisfullpath=0, $id='')
3954
{
3955
	print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
3956
}
3957
3958
/**
3959
 *	Load a title with picto
3960
 *
3961
 *	@param	string	$titre				Title to show
3962
 *	@param	string	$morehtmlright		Added message to show on right
3963
 *	@param	string	$picto				Icon to use before title (should be a 32x32 transparent png file)
3964
 *	@param	int		$pictoisfullpath	1=Icon name is a full absolute url of image
3965
 * 	@param	int		$id					To force an id on html objects
3966
 *  @param  string  $morecssontable     More css on table
3967
 *	@param	string	$morehtmlcenter		Added message to show on center
3968
 * 	@return	string
3969
 *  @see print_barre_liste
3970
 */
3971
function load_fiche_titre($titre, $morehtmlright='', $picto='title_generic.png', $pictoisfullpath=0, $id=0, $morecssontable='', $morehtmlcenter='')
3972
{
3973
	global $conf;
3974
3975
	$return='';
3976
3977
	if ($picto == 'setup') $picto='title.png';
3978
	if (($conf->browser->name == 'ie') && $picto=='title.png') $picto='title.gif';
3979
3980
	$return.= "\n";
3981
	$return.= '<table '.($id?'id="'.$id.'" ':'').'summary="" class="centpercent notopnoleftnoright'.($morecssontable?' '.$morecssontable:'').'" style="margin-bottom: 2px;"><tr>';
3982
	if ($picto) $return.= '<td class="nobordernopadding widthpictotitle" valign="middle">'.img_picto('',$picto, 'class="valignmiddle widthpictotitle" id="pictotitle"', $pictoisfullpath).'</td>';
3983
	$return.= '<td class="nobordernopadding" valign="middle">';
3984
	$return.= '<div class="titre">'.$titre.'</div>';
3985
	$return.= '</td>';
3986
	if (dol_strlen($morehtmlcenter))
3987
	{
3988
		$return.= '<td class="nobordernopadding" align="center" valign="middle">'.$morehtmlcenter.'</td>';
3989
	}
3990
	if (dol_strlen($morehtmlright))
3991
	{
3992
		$return.= '<td class="nobordernopadding titre_right wordbreak" align="right" valign="middle">'.$morehtmlright.'</td>';
3993
	}
3994
	$return.= '</tr></table>'."\n";
3995
3996
	return $return;
3997
}
3998
3999
/**
4000
 *	Print a title with navigation controls for pagination
4001
 *
4002
 *	@param	string	    $titre				Title to show (required)
4003
 *	@param	int   	    $page				Numero of page to show in navigation links (required)
4004
 *	@param	string	    $file				Url of page (required)
4005
 *	@param	string	    $options         	More parameters for links ('' by default, does not include sortfield neither sortorder). Value must be 'urlencoded' before calling function.
4006
 *	@param	string    	$sortfield       	Field to sort on ('' by default)
4007
 *	@param	string	    $sortorder       	Order to sort ('' by default)
4008
 *	@param	string	    $morehtmlcenter     String in the middle ('' by default). We often find here string $massaction comming from $form->selectMassAction()
4009
 *	@param	int		    $num				Number of records found by select with limit+1
4010
 *	@param	int|string  $totalnboflines		Total number of records/lines for all pages (if known). Use a negative value of number to not show number. Use '' if unknown.
4011
 *	@param	string	    $picto				Icon to use before title (should be a 32x32 transparent png file)
4012
 *	@param	int		    $pictoisfullpath	1=Icon name is a full absolute url of image
4013
 *  @param	string	    $morehtmlright			More html to show
4014
 *  @param  string      $morecss            More css to the table
4015
 *  @param  int         $limit              Max number of lines (-1 = use default, 0 = no limit, > 0 = limit).
4016
 *  @param  int         $hideselectlimit    Force to hide select limit
4017
 *  @param  int         $hidenavigation     Force to hide all navigation tools
4018
 *	@return	void
4019
 */
4020
function print_barre_liste($titre, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='title_generic.png', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $hideselectlimit=0, $hidenavigation=0)
4021
{
4022
	global $conf,$langs;
4023
4024
	$savlimit = $limit;
4025
	$savtotalnboflines = $totalnboflines;
4026
	$totalnboflines=abs($totalnboflines);
4027
4028
	if ($picto == 'setup') $picto='title_setup.png';
4029
	if (($conf->browser->name == 'ie') && $picto=='title_generic.png') $picto='title.gif';
4030
	if ($limit < 0) $limit = $conf->liste_limit;
4031
	if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0)))
4032
	{
4033
		$nextpage = 1;
4034
	}
4035
	else
4036
	{
4037
		$nextpage = 0;
4038
	}
4039
	//print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage;
4040
4041
	print "\n";
4042
	print "<!-- Begin title '".$titre."' -->\n";
4043
	print '<table width="100%" border="0" class="notopnoleftnoright'.($morecss?' '.$morecss:'').'" style="margin-bottom: 6px;"><tr>';
4044
4045
	// Left
4046
	//if ($picto && $titre) print '<td class="nobordernopadding hideonsmartphone" width="40" align="left" valign="middle">'.img_picto('', $picto, 'id="pictotitle"', $pictoisfullpath).'</td>';
4047
	print '<td class="nobordernopadding valignmiddle">';
4048
	if ($picto && $titre) print img_picto('', $picto, 'class="hideonsmartphone valignmiddle" id="pictotitle"', $pictoisfullpath);
4049
	print '<div class="titre inline-block">'.$titre;
4050
	if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') print ' ('.$totalnboflines.')';
4051
	print '</div></td>';
4052
4053
	// Center
4054
	if ($morehtmlcenter)
4055
	{
4056
		print '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>';
4057
	}
4058
4059
	// Right
4060
	print '<td class="nobordernopadding valignmiddle" align="right">';
4061
	if ($sortfield) $options .= "&sortfield=".urlencode($sortfield);
4062
	if ($sortorder) $options .= "&sortorder=".urlencode($sortorder);
4063
	// Show navigation bar
4064
	$pagelist = '';
4065
	if ($savlimit != 0 && ($page > 0 || $num > $limit))
4066
	{
4067
		if ($totalnboflines)	// If we know total nb of lines
4068
		{
4069
			// Define nb of extra page links before and after selected page + ... + first or last
4070
			$maxnbofpage=(empty($conf->dol_optimize_smallscreen) ? 4 : 1);
4071
4072
			if ($limit > 0) $nbpages=ceil($totalnboflines/$limit);
4073
			else $nbpages=1;
4074
			$cpt=($page-$maxnbofpage);
4075
			if ($cpt < 0) { $cpt=0; }
4076
4077
			if ($cpt>=1)
4078
			{
4079
				$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page=0'.$options.'">1</a></li>';
4080
				if ($cpt > 2) $pagelist.='<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="inactive"':'').'>...</span></li>';
4081
				else if ($cpt == 2) $pagelist.='<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page=1'.$options.'">2</a></li>';
4082
			}
4083
4084
			do
4085
			{
4086
				if ($cpt==$page)
4087
				{
4088
					$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="active"':'').'>'.($page+1).'</span></li>';
4089
				}
4090
				else
4091
				{
4092
					$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt+1).'</a></li>';
4093
				}
4094
				$cpt++;
4095
			}
4096
			while ($cpt < $nbpages && $cpt<=$page+$maxnbofpage);
4097
4098
			if ($cpt<$nbpages)
4099
			{
4100
				if ($cpt<$nbpages-2) $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="inactive"':'').'>...</span></li>';
4101
				else if ($cpt == $nbpages-2) $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page='.($nbpages-2).$options.'">'.($nbpages - 1).'</a></li>';
4102
				$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page='.($nbpages-1).$options.'">'.$nbpages.'</a></li>';
4103
			}
4104
		}
4105
		else
4106
		{
4107
			$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="active"':'').'>'.($page+1)."</li>";
4108
		}
4109
	}
4110
4111
	print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $hideselectlimit);		// output the div and ul for previous/last completed with page numbers into $pagelist
4112
4113
	print '</td>';
4114
4115
	print '</tr></table>'."\n";
4116
	print "<!-- End title -->\n\n";
4117
}
4118
4119
/**
4120
 *	Function to show navigation arrows into lists
4121
 *
4122
 *	@param	int				$page				Number of page
4123
 *	@param	string			$file				Page URL (in most cases provided with $_SERVER["PHP_SELF"])
4124
 *	@param	string			$options         	Other url paramaters to propagate ("" by default, may include sortfield and sortorder)
4125
 *	@param	integer			$nextpage	    	Do we show a next page button
4126
 *	@param	string			$betweenarrows		HTML content to show between arrows. MUST contains '<li> </li>' tags or '<li><span> </span></li>'.
4127
 *  @param	string			$afterarrows		HTML content to show after arrows. Must NOT contains '<li> </li>' tags.
4128
 *  @param  int             $limit              Max nb of record to show  (-1 = no combo with limit, 0 = no limit, > 0 = limit)
4129
 *	@param	int		        $totalnboflines		Total number of records/lines for all pages (if known)
4130
 *  @param  int             $hideselectlimit    Force to hide select limit
4131
 *	@return	void
4132
 */
4133
function print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $hideselectlimit=0)
4134
{
4135
	global $conf, $langs;
4136
4137
	print '<div class="pagination"><ul>';
4138
	if ((int) $limit >= 0 && empty($hideselectlimit))
4139
	{
4140
		$pagesizechoices='10:10,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000,5000:5000';
4141
		//$pagesizechoices.=',0:'.$langs->trans("All");     // Not yet supported
4142
		//$pagesizechoices.=',2:2';
4143
		if (! empty($conf->global->MAIN_PAGESIZE_CHOICES)) $pagesizechoices=$conf->global->MAIN_PAGESIZE_CHOICES;
4144
4145
		print '<li class="pagination">';
4146
		print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
4147
		$tmpchoice=explode(',',$pagesizechoices);
4148
		$tmpkey=$limit.':'.$limit;
4149
		if (! in_array($tmpkey, $tmpchoice)) $tmpchoice[]=$tmpkey;
4150
		$tmpkey=$conf->liste_limit.':'.$conf->liste_limit;
4151
		if (! in_array($tmpkey, $tmpchoice)) $tmpchoice[]=$tmpkey;
4152
		asort($tmpchoice, SORT_NUMERIC);
4153
		$found=false;
4154
		foreach($tmpchoice as $val)
4155
		{
4156
			$selected='';
4157
			$tmp=explode(':',$val);
4158
			$key=$tmp[0];
4159
			$val=$tmp[1];
4160
			if ($key != '' && $val != '')
4161
			{
4162
				if ((int) $key == (int) $limit)
4163
				{
4164
					$selected = ' selected="selected"';
4165
					$found = true;
4166
				}
4167
				print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
4168
			}
4169
		}
4170
		print '</select>';
4171
		if ($conf->use_javascript_ajax)
4172
		{
4173
			print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
4174
            		<script type="text/javascript">
4175
                	jQuery(document).ready(function () {
4176
            	  		jQuery(".selectlimit").change(function() {
4177
                            console.log("Change limit. Send submit");
4178
                            $(this).parents(\'form:first\').submit();
4179
            	  		});
4180
                	});
4181
            		</script>
4182
                ';
4183
		}
4184
		print '</li>';
4185
	}
4186
	if ($page > 0)
4187
	{
4188
		print '<li class="pagination"><a class="paginationprevious" href="'.$file.'?page='.($page-1).$options.'"><i class="fa fa-chevron-left" title="'.dol_escape_htmltag($langs->trans("Previous")).'"></i></a></li>';
4189
	}
4190
	if ($betweenarrows)
4191
	{
4192
		print $betweenarrows;
4193
	}
4194
	if ($nextpage > 0)
4195
	{
4196
		print '<li class="pagination"><a class="paginationnext" href="'.$file.'?page='.($page+1).$options.'"><i class="fa fa-chevron-right" title="'.dol_escape_htmltag($langs->trans("Next")).'"></i></a></li>';
4197
	}
4198
	if ($afterarrows)
4199
	{
4200
		print '<li class="paginationafterarrows">';
4201
		print $afterarrows;
4202
		print '</li>';
4203
	}
4204
	print '</ul></div>'."\n";
4205
}
4206
4207
4208
/**
4209
 *	Return a string with VAT rate label formated for view output
4210
 *	Used into pdf and HTML pages
4211
 *
4212
 *	@param	string	$rate			Rate value to format ('19.6', '19,6', '19.6%', '19,6%', '19.6 (CODEX)', ...)
4213
 *  @param	boolean	$addpercent		Add a percent % sign in output
4214
 *	@param	int		$info_bits		Miscellaneous information on vat (0=Default, 1=French NPR vat)
4215
 *	@param	int		$usestarfornpr	-1=Never show, 0 or 1=Use '*' for NPR vat rates
4216
 *  @return	string					String with formated amounts ('19,6' or '19,6%' or '8.5% (NPR)' or '8.5% *' or '19,6 (CODEX)')
4217
 */
4218
function vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0)
4219
{
4220
	$morelabel='';
4221
4222
	if (preg_match('/%/',$rate))
4223
	{
4224
		$rate=str_replace('%','',$rate);
4225
		$addpercent=true;
4226
	}
4227
	if (preg_match('/\((.*)\)/',$rate,$reg))
4228
	{
4229
		$morelabel=' ('.$reg[1].')';
4230
		$rate=preg_replace('/\s*'.preg_quote($morelabel,'/').'/','',$rate);
4231
	}
4232
	if (preg_match('/\*/',$rate))
4233
	{
4234
		$rate=str_replace('*','',$rate);
4235
		$info_bits |= 1;
4236
	}
4237
4238
	// If rate is '9/9/9' we don't change it.  If rate is '9.000' we apply price()
4239
	if (! preg_match('/\//', $rate)) $ret=price($rate,0,'',0,0).($addpercent?'%':'');
4240
	else
4241
	{
4242
		// TODO Split on / and output with a price2num to have clean numbers without ton of 000.
4243
		$ret=$rate.($addpercent?'%':'');
4244
	}
4245
	if (($info_bits & 1) && $usestarfornpr >= 0) $ret.=' *';
4246
	$ret.=$morelabel;
4247
	return $ret;
4248
}
4249
4250
4251
/**
4252
 *		Function to format a value into an amount for visual output
4253
 *		Function used into PDF and HTML pages
4254
 *
4255
 *		@param	float		$amount			Amount to format
4256
 *		@param	integer		$form			Type of format, HTML or not (not by default)
4257
 *		@param	Translate	$outlangs		Object langs for output
4258
 *		@param	int			$trunc			1=Truncate if there is more decimals than MAIN_MAX_DECIMALS_SHOWN (default), 0=Does not truncate. Deprecated because amount are rounded (to unit or total amount accurancy) before beeing inserted into database or after a computation, so this parameter should be useless.
4259
 *		@param	int			$rounding		Minimum number of decimal to show. If 0, no change, if -1, we use min($conf->global->MAIN_MAX_DECIMALS_UNIT,$conf->global->MAIN_MAX_DECIMALS_TOTAL)
4260
 *		@param	int			$forcerounding	Force the number of decimal to forcerounding decimal (-1=do not force)
4261
 *		@param	string		$currency_code	To add currency symbol (''=add nothing, 'auto'=Use default currency, 'XXX'=add currency symbols for XXX currency)
4262
 *		@return	string						Chaine avec montant formate
4263
 *
4264
 *		@see	price2num					Revert function of price
4265
 */
4266
function price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
4267
{
4268
	global $langs,$conf;
4269
4270
	// Clean parameters
4271
	if (empty($amount)) $amount=0;	// To have a numeric value if amount not defined or = ''
4272
	$amount = (is_numeric($amount)?$amount:0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
4273
	if ($rounding < 0) $rounding=min($conf->global->MAIN_MAX_DECIMALS_UNIT,$conf->global->MAIN_MAX_DECIMALS_TOT);
4274
	$nbdecimal=$rounding;
4275
4276
	// Output separators by default (french)
4277
	$dec=','; $thousand=' ';
4278
4279
	// If $outlangs not forced, we use use language
4280
	if (! is_object($outlangs)) $outlangs=$langs;
4281
4282
	if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal")  $dec=$outlangs->transnoentitiesnoconv("SeparatorDecimal");
4283
	if ($outlangs->transnoentitiesnoconv("SeparatorThousand")!= "SeparatorThousand") $thousand=$outlangs->transnoentitiesnoconv("SeparatorThousand");
4284
	if ($thousand == 'None') $thousand='';
4285
	else if ($thousand == 'Space') $thousand=' ';
4286
	//print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
4287
4288
	//print "amount=".$amount."-";
4289
	$amount = str_replace(',','.',$amount);	// should be useless
4290
	//print $amount."-";
4291
	$datas = explode('.',$amount);
4292
	$decpart = isset($datas[1])?$datas[1]:'';
4293
	$decpart = preg_replace('/0+$/i','',$decpart);	// Supprime les 0 de fin de partie decimale
4294
	//print "decpart=".$decpart."<br>";
4295
	$end='';
4296
4297
	// We increase nbdecimal if there is more decimal than asked (to not loose information)
4298
	if (dol_strlen($decpart) > $nbdecimal) $nbdecimal=dol_strlen($decpart);
4299
	// Si on depasse max
4300
	if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN)
4301
	{
4302
		$nbdecimal=$conf->global->MAIN_MAX_DECIMALS_SHOWN;
4303
		if (preg_match('/\.\.\./i',$conf->global->MAIN_MAX_DECIMALS_SHOWN))
4304
		{
4305
			// Si un affichage est tronque, on montre des ...
4306
			$end='...';
4307
		}
4308
	}
4309
4310
	// If force rounding
4311
	if ($forcerounding >= 0) $nbdecimal = $forcerounding;
4312
4313
	// Format number
4314
	$output=number_format($amount, $nbdecimal, $dec, $thousand);
4315
	if ($form)
4316
	{
4317
		$output=preg_replace('/\s/','&nbsp;',$output);
4318
		$output=preg_replace('/\'/','&#039;',$output);
4319
	}
4320
	// Add symbol of currency if requested
4321
	$cursymbolbefore=$cursymbolafter='';
4322
	if ($currency_code)
4323
	{
4324
		if ($currency_code == 'auto') $currency_code=$conf->currency;
4325
4326
		$listofcurrenciesbefore=array('USD','GBP','AUD','MXN','PEN','CNY');
4327
		if (in_array($currency_code,$listofcurrenciesbefore)) $cursymbolbefore.=$outlangs->getCurrencySymbol($currency_code);
4328
		else
4329
		{
4330
			$tmpcur=$outlangs->getCurrencySymbol($currency_code);
4331
			$cursymbolafter.=($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
4332
		}
4333
	}
4334
	$output=$cursymbolbefore.$output.$end.($cursymbolafter?' ':'').$cursymbolafter;
4335
4336
	return $output;
4337
}
4338
4339
/**
4340
 *	Function that return a number with universal decimal format (decimal separator is '.') from an amount typed by a user.
4341
 *	Function to use on each input amount before any numeric test or database insert
4342
 *
4343
 *	@param	float	$amount			Amount to convert/clean
4344
 *	@param	string	$rounding		''=No rounding
4345
 * 									'MU'=Round to Max unit price (MAIN_MAX_DECIMALS_UNIT)
4346
 *									'MT'=Round to Max for totals with Tax (MAIN_MAX_DECIMALS_TOT)
4347
 *									'MS'=Round to Max for stock quantity (MAIN_MAX_DECIMALS_STOCK)
4348
 * 	@param	int		$alreadysqlnb	Put 1 if you know that content is already universal format number
4349
 *	@return	string					Amount with universal numeric format (Example: '99.99999') or unchanged text if conversion fails.
4350
 *
4351
 *	@see    price					Opposite function of price2num
4352
 */
4353
function price2num($amount,$rounding='',$alreadysqlnb=0)
4354
{
4355
	global $langs,$conf;
4356
4357
	// Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
4358
	// Numbers must be '1234.56'
4359
	// Decimal delimiter for PHP and database SQL requests must be '.'
4360
	$dec=','; $thousand=' ';
4361
	if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal")  $dec=$langs->transnoentitiesnoconv("SeparatorDecimal");
4362
	if ($langs->transnoentitiesnoconv("SeparatorThousand")!= "SeparatorThousand") $thousand=$langs->transnoentitiesnoconv("SeparatorThousand");
4363
	if ($thousand == 'None') $thousand='';
4364
	elseif ($thousand == 'Space') $thousand=' ';
4365
	//print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
4366
4367
	// Convert value to universal number format (no thousand separator, '.' as decimal separator)
4368
	if ($alreadysqlnb != 1)	// If not a PHP number or unknown, we change format
4369
	{
4370
		//print 'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
4371
4372
		// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
4373
		// to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
4374
		if (is_numeric($amount))
4375
		{
4376
			// We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
4377
			$temps=sprintf("%0.10F",$amount-intval($amount));	// temps=0.0000000000 or 0.0000200000 or 9999.1000000000
4378
			$temps=preg_replace('/([\.1-9])0+$/','\\1',$temps); // temps=0. or 0.00002 or 9999.1
4379
			$nbofdec=max(0,dol_strlen($temps)-2);	// -2 to remove "0."
4380
			$amount=number_format($amount,$nbofdec,$dec,$thousand);
4381
		}
4382
		//print "QQ".$amount.'<br>';
4383
4384
		// Now make replace (the main goal of function)
4385
		if ($thousand != ',' && $thousand != '.') $amount=str_replace(',','.',$amount);	// To accept 2 notations for french users
4386
		$amount=str_replace(' ','',$amount);		// To avoid spaces
4387
		$amount=str_replace($thousand,'',$amount);	// Replace of thousand before replace of dec to avoid pb if thousand is .
4388
		$amount=str_replace($dec,'.',$amount);
4389
	}
4390
4391
	// Now, make a rounding if required
4392
	if ($rounding)
4393
	{
4394
		$nbofdectoround='';
4395
		if ($rounding == 'MU')     $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_UNIT;
4396
		elseif ($rounding == 'MT') $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_TOT;
4397
		elseif ($rounding == 'MS') $nbofdectoround=empty($conf->global->MAIN_MAX_DECIMALS_STOCK)?5:$conf->global->MAIN_MAX_DECIMALS_STOCK;
4398
		elseif (is_numeric($rounding))  $nbofdectoround=$rounding; 	// For admin info page
4399
		//print "RR".$amount.' - '.$nbofdectoround.'<br>';
4400
		if (dol_strlen($nbofdectoround)) $amount = round($amount,$nbofdectoround);	// $nbofdectoround can be 0.
4401
		else return 'ErrorBadParameterProvidedToFunction';
4402
		//print 'SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
4403
4404
		// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
4405
		// to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
4406
		if (is_numeric($amount))
4407
		{
4408
			// We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
4409
			$temps=sprintf("%0.10F",$amount-intval($amount));	// temps=0.0000000000 or 0.0000200000 or 9999.1000000000
4410
			$temps=preg_replace('/([\.1-9])0+$/','\\1',$temps); // temps=0. or 0.00002 or 9999.1
4411
			$nbofdec=max(0,dol_strlen($temps)-2);	// -2 to remove "0."
4412
			$amount=number_format($amount,min($nbofdec,$nbofdectoround),$dec,$thousand);		// Convert amount to format with dolibarr dec and thousand
4413
		}
4414
		//print "TT".$amount.'<br>';
4415
4416
		// Always make replace because each math function (like round) replace
4417
		// with local values and we want a number that has a SQL string format x.y
4418
		if ($thousand != ',' && $thousand != '.') $amount=str_replace(',','.',$amount);	// To accept 2 notations for french users
4419
		$amount=str_replace(' ','',$amount);		// To avoid spaces
4420
		$amount=str_replace($thousand,'',$amount);	// Replace of thousand before replace of dec to avoid pb if thousand is .
4421
		$amount=str_replace($dec,'.',$amount);
4422
	}
4423
4424
	return $amount;
4425
}
4426
4427
4428
/**
4429
 * Output a dimension with best unit
4430
 *
4431
 * @param   float       $dimension      Dimension
4432
 * @param   int         $unit           Unit of dimension (0, -3, ...)
4433
 * @param   string      $type           'weight', 'volume', ...
4434
 * @param   Translate   $outputlangs    Translate language object
4435
 * @param   int         $round          -1 = non rounding, x = number of decimal
4436
 * @param   string      $forceunitoutput    'no' or numeric (-3, -6, ...) compared to $unit
4437
 * @return  string                      String to show dimensions
4438
 */
4439
function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no')
4440
{
4441
	require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
4442
4443
	if (($forceunitoutput == 'no' && $dimension < 1/10000) || (is_numeric($forceunitoutput) && $forceunitoutput == -6))
4444
	{
4445
		$dimension = $dimension * 1000000;
4446
		$unit = $unit - 6;
4447
	}
4448
	elseif (($forceunitoutput == 'no' && $dimension < 1/10) || (is_numeric($forceunitoutput) && $forceunitoutput == -3))
4449
	{
4450
		$dimension = $dimension * 1000;
4451
		$unit = $unit - 3;
4452
	}
4453
	elseif (($forceunitoutput == 'no' && $dimension > 100000000) || (is_numeric($forceunitoutput) && $forceunitoutput == 6))
4454
	{
4455
		$dimension = $dimension / 1000000;
4456
		$unit = $unit + 6;
4457
	}
4458
	elseif (($forceunitoutput == 'no' && $dimension > 100000) || (is_numeric($forceunitoutput) && $forceunitoutput == 3))
4459
	{
4460
		$dimension = $dimension / 1000;
4461
		$unit = $unit + 3;
4462
	}
4463
4464
	$ret=price($dimension, 0, $outputlangs, 0, 0, $round).' '.measuring_units_string($unit, $type);
4465
4466
	return $ret;
4467
}
4468
4469
4470
/**
4471
 *	Return localtax rate for a particular vat, when selling a product with vat $vatrate, from a $thirdparty_buyer to a $thirdparty_seller
4472
 *  Note: This function applies same rules than get_default_tva
4473
 *
4474
 * 	@param	float		$vatrate		        Vat rate. Can be '8.5' or '8.5 (VATCODEX)' for example
4475
 * 	@param  int			$local		         	Local tax to search and return (1 or 2 return only tax rate 1 or tax rate 2)
4476
 *  @param  Societe		$thirdparty_buyer    	Object of buying third party
4477
 *  @param	Societe		$thirdparty_seller		Object of selling third party ($mysoc if not defined)
4478
 *  @param	int			$vatnpr					If vat rate is NPR or not
4479
 * 	@return	mixed			   					0 if not found, localtax rate if found
4480
 *  @see get_default_tva
4481
 */
4482
function get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
4483
{
4484
	global $db, $conf, $mysoc;
4485
4486
	if (empty($thirdparty_seller) || ! is_object($thirdparty_seller)) $thirdparty_seller=$mysoc;
4487
4488
	dol_syslog("get_localtax tva=".$vatrate." local=".$local." thirdparty_buyer id=".(is_object($thirdparty_buyer)?$thirdparty_buyer->id:'')."/country_code=".(is_object($thirdparty_buyer)?$thirdparty_buyer->country_code:'')." thirdparty_seller id=".$thirdparty_seller->id."/country_code=".$thirdparty_seller->country_code." thirdparty_seller localtax1_assuj=".$thirdparty_seller->localtax1_assuj."  thirdparty_seller localtax2_assuj=".$thirdparty_seller->localtax2_assuj);
4489
4490
	$vatratecleaned = $vatrate;
4491
	if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
4492
	{
4493
		$vatratecleaned = trim($reg[1]);
4494
		$vatratecode = $reg[2];
4495
	}
4496
4497
	/*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
4498
	{
4499
		return 0;
4500
	}*/
4501
4502
	// Some test to guess with no need to make database access
4503
	if ($mysoc->country_code == 'ES') // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
4504
	{
4505
		if ($local == 1)
4506
		{
4507
			if (! $mysoc->localtax1_assuj || (string) $vatratecleaned == "0") return 0;
4508
			if ($thirdparty_seller->id == $mysoc->id)
4509
			{
4510
				if (! $thirdparty_buyer->localtax1_assuj) return 0;
4511
			}
4512
			else
4513
			{
4514
				if (! $thirdparty_seller->localtax1_assuj) return 0;
4515
			}
4516
		}
4517
4518
		if ($local == 2)
4519
		{
4520
			if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
4521
			if ($thirdparty_seller->id == $mysoc->id)
4522
			{
4523
				if (! $thirdparty_buyer->localtax2_assuj) return 0;
4524
			}
4525
			else
4526
			{
4527
				if (! $thirdparty_seller->localtax2_assuj) return 0;
4528
			}
4529
		}
4530
	}
4531
	else
4532
	{
4533
		if ($local == 1 && ! $thirdparty_seller->localtax1_assuj) return 0;
4534
		if ($local == 2 && ! $thirdparty_seller->localtax2_assuj) return 0;
4535
	}
4536
4537
	// For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
4538
	if (in_array($mysoc->country_code, array('ES')))
4539
	{
4540
		$conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
4541
	}
4542
4543
	// Search local taxes
4544
	if (! empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY))
4545
	{
4546
		if ($local==1)
4547
		{
4548
			if ($thirdparty_seller != $mysoc)
4549
			{
4550
				if (!isOnlyOneLocalTax($local))  // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
4551
				{
4552
					return $thirdparty_seller->localtax1_value;
4553
				}
4554
			}
4555
			else  // i am the seller
4556
			{
4557
				if (!isOnlyOneLocalTax($local))  // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
4558
				{
4559
					return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
4560
				}
4561
			}
4562
		}
4563
		if ($local==2)
4564
		{
4565
			if ($thirdparty_seller != $mysoc)
4566
			{
4567
				if (!isOnlyOneLocalTax($local))  // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
4568
				// TODO We should also return value defined on thirdparty only if defined
4569
				{
4570
					return $thirdparty_seller->localtax2_value;
4571
				}
4572
			}
4573
			else  // i am the seller
4574
			{
4575
				if (!isOnlyOneLocalTax($local))  // This is for spain only, we don't return value found into datbase even if there is only one locatax vat.
4576
				{
4577
					return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
4578
				}
4579
			}
4580
		}
4581
	}
4582
4583
	// By default, search value of local tax on line of common tax
4584
	$sql  = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
4585
   	$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4586
   	$sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$thirdparty_seller->country_code."'";
4587
   	$sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4588
   	if ($vatratecode) $sql.= " AND t.code ='".$vatratecode."'";		// If we have the code, we use it in priority
0 ignored issues
show
Bug introduced by
The variable $vatratecode does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
4589
   	else $sql.= " AND t.recuperableonly ='".$vatnpr."'";
4590
   	dol_syslog("get_localtax", LOG_DEBUG);
4591
   	$resql=$db->query($sql);
4592
4593
   	if ($resql)
4594
   	{
4595
   		$obj = $db->fetch_object($resql);
4596
   		if ($local==1) return $obj->localtax1;
4597
   		elseif ($local==2) return $obj->localtax2;
4598
	}
4599
4600
	return 0;
4601
}
4602
4603
4604
/**
4605
 * Return true if LocalTax (1 or 2) is unique.
4606
 * Example: If localtax1 is 5 on line with highest common vat rate, return true
4607
 * Example: If localtax1 is 5:8:15 on line with highest common vat rate, return false
4608
 *
4609
 * @param   int 	$local	Local tax to test (1 or 2)
4610
 * @return  boolean 		True if LocalTax have multiple values, False if not
4611
 */
4612
function isOnlyOneLocalTax($local)
4613
{
4614
	$tax=get_localtax_by_third($local);
4615
4616
	$valors=explode(":", $tax);
4617
4618
	if (count($valors)>1)
4619
	{
4620
		return false;
4621
	}
4622
	else
4623
	{
4624
		return true;
4625
	}
4626
}
4627
4628
/**
4629
 * Get values of localtaxes (1 or 2) for company country for the common vat with the highest value
4630
 *
4631
 * @param	int		$local 	LocalTax to get
4632
 * @return	number			Values of localtax
4633
 */
4634
function get_localtax_by_third($local)
4635
{
4636
	global $db, $mysoc;
4637
	$sql ="SELECT t.localtax1, t.localtax2 ";
4638
	$sql.=" FROM ".MAIN_DB_PREFIX."c_tva as t inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=t.fk_pays";
4639
	$sql.=" WHERE c.code = '".$mysoc->country_code."' AND t.active = 1 AND t.taux=(";
4640
	$sql.="  SELECT max(tt.taux) FROM ".MAIN_DB_PREFIX."c_tva as tt inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=tt.fk_pays";
4641
	$sql.="  WHERE c.code = '".$mysoc->country_code."' AND tt.active = 1";
4642
	$sql.="  )";
4643
4644
	$resql=$db->query($sql);
4645
	if ($resql)
4646
	{
4647
		$obj = $db->fetch_object($resql);
4648
		if ($local==1) return $obj->localtax1;
4649
		elseif ($local==2) return $obj->localtax2;
4650
	}
4651
4652
	return 0;
4653
4654
}
4655
4656
4657
/**
4658
 *  Get vat main information from Id.
4659
 *  You can call getLocalTaxesFromRate after to get other fields.
4660
 *
4661
 *  @param	int|string  $vatrate		    VAT ID or Rate. Value can be value or the string with code into parenthesis or rowid if $firstparamisid is 1. Example: '8.5' or '8.5 (8.5NPR)' or 123.
4662
 *  @param	Societe	    $buyer         		Company object
4663
 *  @param	Societe	    $seller        		Company object
4664
 *  @param  int         $firstparamisid     1 if first param is id into table (use this if you can)
4665
 *  @return	array       	  				array('rowid'=> , 'code'=> ...)
4666
 *  @see getLocalTaxesFromRate
4667
 */
4668
function getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
4669
{
4670
	global $db, $mysoc;
4671
4672
	dol_syslog("getTaxesFromId vatrowid=".$vatrate);
4673
4674
	// Search local taxes
4675
	$sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy";
4676
	$sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t";
4677
	if ($firstparamisid) $sql.= " WHERE t.rowid = ".(int) $vatrate;
4678
	else
4679
	{
4680
		$vatratecleaned = $vatrate;
4681
		$vatratecode = '';
4682
		if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
4683
		{
4684
			$vatratecleaned = $reg[1];
4685
			$vatratecode = $reg[2];
4686
		}
4687
4688
		$sql.=", ".MAIN_DB_PREFIX."c_country as c";
4689
		if ($mysoc->country_code == 'ES') $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$buyer->country_code."'";    // local tax in spain use the buyer country ??
4690
		else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
4691
		$sql.= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4692
		if ($vatratecode) $sql.= " AND t.code = '".$vatratecode."'";
4693
	}
4694
4695
	$resql=$db->query($sql);
4696
	if ($resql)
4697
	{
4698
		$obj = $db->fetch_object($resql);
4699
		if ($obj) return array('rowid'=>$obj->rowid, 'code'=>$obj->code, 'rate'=>$obj->rate, 'npr'=>$obj->npr, 'accountancy_code_sell'=>$obj->accountancy_code_sell, 'accountancy_code_buy'=>$obj->accountancy_code_buy);
4700
		else return array();
4701
	}
4702
	else dol_print_error($db);
4703
4704
	return array();
4705
}
4706
4707
/**
4708
 *  Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
4709
 *  This does not take into account the seller setup if subject to vat or not, only country.
4710
 *  TODO
4711
 *  This function is ALSO called to retrieve type for building PDF. Such call of function must be removed.
4712
 *  Instead this function must be called when adding a line to get the array of localtax and type, and then
4713
 *  provide it to the function calcul_price_total.
4714
 *
4715
 *  @param	int|string  $vatrate			VAT ID or Rate+Code. Value can be value or the string with code into parenthesis or rowid if $firstparamisid is 1. Example: '8.5' or '8.5 (8.5NPR)' or 123.
4716
 *  @param	int		    $local              Number of localtax (1 or 2, or 0 to return 1 & 2)
4717
 *  @param	Societe	    $buyer         		Company object
4718
 *  @param	Societe	    $seller        		Company object
4719
 *  @param  int         $firstparamisid     1 if first param is ID into table instead of Rate+code (use this if you can)
4720
 *  @return	array    	    				array(localtax_type1(1-6/0 if not found), rate localtax1, localtax_type2, rate localtax2, accountancycodecust, accountancycodesupp)
4721
 *  @see getTaxesFromId
4722
 */
4723
function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
4724
{
4725
	global $db, $mysoc;
4726
4727
	dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
4728
4729
	// Search local taxes
4730
	$sql  = "SELECT t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy";
4731
	$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
4732
	if ($firstparamisid) $sql.= " WHERE t.rowid = ".(int) $vatrate;
4733
	else
4734
	{
4735
		$vatratecleaned = $vatrate;
4736
		$vatratecode = '';
4737
		if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
4738
		{
4739
			$vatratecleaned = $reg[1];
4740
			$vatratecode = $reg[2];
4741
		}
4742
4743
		$sql.=", ".MAIN_DB_PREFIX."c_country as c";
4744
		if ($mysoc->country_code == 'ES') $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$buyer->country_code."'";    // local tax in spain use the buyer country ??
4745
		else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
4746
		$sql.= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4747
		if ($vatratecode) $sql.= " AND t.code = '".$vatratecode."'";
4748
	}
4749
4750
	$resql=$db->query($sql);
4751
	if ($resql)
4752
	{
4753
		$obj = $db->fetch_object($resql);
4754
		if ($local == 1)
4755
		{
4756
			if (! isOnlyOneLocalTax(1))
4757
			{
4758
				return array($obj->localtax1_type, get_localtax($vatrate, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4759
			}
4760
			else
4761
			{
4762
				return array($obj->localtax1_type, $obj->localtax1,$obj->accountancy_code_sell, $obj->accountancy_code_buy);
4763
			}
4764
		}
4765
		elseif ($local == 2)
4766
		{
4767
			if (! isOnlyOneLocalTax(2))
4768
			{
4769
				return array($obj->localtax2_type, get_localtax($vatrate, $local, $buyer, $seller),$obj->accountancy_code_sell, $obj->accountancy_code_buy);
4770
			}
4771
			else
4772
			{
4773
				return array($obj->localtax2_type, $obj->localtax2,$obj->accountancy_code_sell, $obj->accountancy_code_buy);
4774
			}
4775
		}
4776
		else
4777
		{
4778
			if(! isOnlyOneLocalTax(1))
4779
			{
4780
				if(! isOnlyOneLocalTax(2))
4781
				{
4782
					return array($obj->localtax1_type, get_localtax($vatrate, 1, $buyer, $seller), $obj->localtax2_type, get_localtax($vatrate, 2, $buyer, $seller), $obj->accountancy_code_sell,$obj->accountancy_code_buy);
4783
				}
4784
				else
4785
				{
4786
					return array($obj->localtax1_type, get_localtax($vatrate, 1, $buyer, $seller), $obj->localtax2_type, $obj->localtax2, $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4787
				}
4788
			}
4789
			else
4790
			{
4791
				if(! isOnlyOneLocalTax(2))
4792
				{
4793
					return array($obj->localtax1_type, $obj->localtax1, $obj->localtax2_type, get_localtax($vatrate, 2, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4794
				}
4795
				else
4796
				{
4797
					return array($obj->localtax1_type, $obj->localtax1, $obj->localtax2_type, $obj->localtax2, $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4798
				}
4799
			}
4800
		}
4801
	}
4802
4803
	return 0;
4804
}
4805
4806
/**
4807
 *	Return vat rate of a product in a particular selling country or default country vat if product is unknown
4808
 *  Function called by get_default_tva
4809
 *
4810
 *  @param	int			$idprod          	Id of product or 0 if not a predefined product
4811
 *  @param  Societe		$thirdparty_seller  Thirdparty with a ->country_code defined (FR, US, IT, ...)
4812
 *	@param	int			$idprodfournprice	Id product_fournisseur_price (for "supplier" order/invoice)
4813
 *  @return float|string   				    Vat rate to use with format 5.0 or '5.0 (XXX)'
4814
 *  @see get_product_localtax_for_country
4815
 */
4816
function get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice=0)
4817
{
4818
	global $db,$conf,$mysoc;
4819
4820
	require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4821
4822
	$ret=0;
4823
	$found=0;
4824
4825
	if ($idprod > 0)
4826
	{
4827
		// Load product
4828
		$product=new Product($db);
4829
		$result=$product->fetch($idprod);
4830
4831
		if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
4832
		{
4833
			if ($idprodfournprice > 0)     // We want vat for product for a "supplier" order or invoice
4834
			{
4835
				$product->get_buyprice($idprodfournprice,0,0,0);
4836
				$ret=$product->vatrate_supplier;
4837
				if ($product->default_vat_code) $ret.=' ('.$product->default_vat_code.')';
4838
			}
4839
			else
4840
			{
4841
				$ret=$product->tva_tx;    // Default vat of product we defined
4842
				if ($product->default_vat_code) $ret.=' ('.$product->default_vat_code.')';
4843
			}
4844
			$found=1;
4845
		}
4846
		else
4847
		{
4848
			// TODO Read default product vat according to countrycode and product. Vat for couple countrycode/product is a feature not implemeted yet.
4849
			// May be usefull/required if hidden option SERVICE_ARE_ECOMMERCE_200238EC is on
4850
		}
4851
	}
4852
4853
	if (! $found)
4854
	{
4855
		if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS))
4856
		{
4857
			// If vat of product for the country not found or not defined, we return the first higher vat of country.
4858
			$sql = "SELECT taux as vat_rate";
4859
			$sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4860
			$sql.= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
4861
			$sql.= " ORDER BY t.taux DESC, t.code ASC, t.recuperableonly ASC";
4862
			$sql.= $db->plimit(1);
4863
4864
			$resql=$db->query($sql);
4865
			if ($resql)
4866
			{
4867
				$obj=$db->fetch_object($resql);
4868
				if ($obj)
4869
				{
4870
					$ret=$obj->vat_rate;
4871
				}
4872
				$db->free($sql);
4873
			}
4874
			else dol_print_error($db);
4875
		}
4876
		else $ret=$conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;    // Forced value if autodetect fails
4877
	}
4878
4879
	dol_syslog("get_product_vat_for_country: ret=".$ret);
4880
	return $ret;
4881
}
4882
4883
/**
4884
 *	Return localtax vat rate of a product in a particular selling country or default country vat if product is unknown
4885
 *
4886
 *  @param	int		$idprod         		Id of product
4887
 *  @param  int		$local          		1 for localtax1, 2 for localtax 2
4888
 *  @param  Societe	$thirdparty_seller    	Thirdparty with a ->country_code defined (FR, US, IT, ...)
4889
 *  @return int             				<0 if KO, Vat rate if OK
4890
 *  @see get_product_vat_for_country
4891
 */
4892
function get_product_localtax_for_country($idprod, $local, $thirdparty_seller)
4893
{
4894
	global $db,$mysoc;
4895
4896
	if (! class_exists('Product')) {
4897
		require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4898
	}
4899
4900
	$ret=0;
4901
	$found=0;
4902
4903
	if ($idprod > 0)
4904
	{
4905
		// Load product
4906
		$product=new Product($db);
4907
		$result=$product->fetch($idprod);
4908
4909
		if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
4910
		{
4911
			/* Not defined yet, so we don't use this
4912
			if ($local==1) $ret=$product->localtax1_tx;
4913
			elseif ($local==2) $ret=$product->localtax2_tx;
4914
			$found=1;
4915
			*/
4916
		}
4917
		else
4918
		{
4919
			// TODO Read default product vat according to countrycode and product
4920
4921
4922
		}
4923
	}
4924
4925
	if (! $found)
4926
	{
4927
		// If vat of product for the country not found or not defined, we return higher vat of country.
4928
		$sql = "SELECT taux as vat_rate, localtax1, localtax2";
4929
		$sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4930
		$sql.= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
4931
		$sql.= " ORDER BY t.taux DESC, t.recuperableonly ASC";
4932
		$sql.= $db->plimit(1);
4933
4934
		$resql=$db->query($sql);
4935
		if ($resql)
4936
		{
4937
			$obj=$db->fetch_object($resql);
4938
			if ($obj)
4939
			{
4940
				if ($local==1) $ret=$obj->localtax1;
4941
				elseif ($local==2) $ret=$obj->localtax2;
4942
			}
4943
		}
4944
		else dol_print_error($db);
4945
	}
4946
4947
	dol_syslog("get_product_localtax_for_country: ret=".$ret);
4948
	return $ret;
4949
}
4950
4951
/**
4952
 *	Function that return vat rate of a product line (according to seller, buyer and product vat rate)
4953
 *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
4954
 *	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
4955
 *	 Si (vendeur et acheteur dans Communaute europeenne) et (bien vendu = moyen de transports neuf comme auto, bateau, avion) alors TVA par defaut=0 (La TVA doit etre paye par acheteur au centre d'impots de son pays et non au vendeur). Fin de regle.
4956
 *	 Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier ou entreprise sans num TVA intra) alors TVA par defaut=TVA du produit vendu. Fin de regle
4957
 *	 Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise avec num TVA) intra alors TVA par defaut=0. Fin de regle
4958
 *	 Sinon TVA proposee par defaut=0. Fin de regle.
4959
 *
4960
 *	@param	Societe		$thirdparty_seller    	Objet societe vendeuse
4961
 *	@param  Societe		$thirdparty_buyer   	Objet societe acheteuse
4962
 *	@param  int			$idprod					Id product
4963
 *	@param	int			$idprodfournprice		Id product_fournisseur_price (for supplier order/invoice)
4964
 *	@return float|string   				      	Vat rate to use with format 5.0 or '5.0 (XXX)', -1 if we can't guess it
4965
 *  @see get_default_npr, get_default_localtax
4966
 */
4967
function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
4968
{
4969
	global $conf;
4970
4971
	require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
4972
4973
	// Note: possible values for tva_assuj are 0/1 or franchise/reel
4974
	$seller_use_vat=((is_numeric($thirdparty_seller->tva_assuj) && ! $thirdparty_seller->tva_assuj) || (! is_numeric($thirdparty_seller->tva_assuj) && $thirdparty_seller->tva_assuj=='franchise'))?0:1;
4975
4976
	$seller_country_code = $thirdparty_seller->country_code;
4977
	$seller_in_cee = isInEEC($thirdparty_seller);
4978
4979
	$buyer_country_code = $thirdparty_buyer->country_code;
4980
	$buyer_in_cee = isInEEC($thirdparty_buyer);
4981
4982
	dol_syslog("get_default_tva: seller use vat=".$seller_use_vat.", seller country=".$seller_country_code.", seller in cee=".$seller_in_cee.", buyer country=".$buyer_country_code.", buyer in cee=".$buyer_in_cee.", idprod=".$idprod.", idprodfournprice=".$idprodfournprice.", SERVICE_ARE_ECOMMERCE_200238EC=".(! empty($conf->global->SERVICES_ARE_ECOMMERCE_200238EC)?$conf->global->SERVICES_ARE_ECOMMERCE_200238EC:''));
4983
4984
	// If services are eServices according to EU Council Directive 2002/38/EC (http://ec.europa.eu/taxation_customs/taxation/vat/traders/e-commerce/article_1610_en.htm)
4985
	// we use the buyer VAT.
4986
	if (! empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC))
4987
	{
4988
		if ($seller_in_cee && $buyer_in_cee && ! $thirdparty_buyer->isACompany())
4989
		{
4990
			//print 'VATRULE 0';
4991
			return get_product_vat_for_country($idprod,$thirdparty_buyer,$idprodfournprice);
4992
		}
4993
	}
4994
4995
	// If seller does not use VAT
4996
	if (! $seller_use_vat)
4997
	{
4998
		//print 'VATRULE 1';
4999
		return 0;
5000
	}
5001
5002
	// Le test ci-dessus ne devrait pas etre necessaire. Me signaler l'exemple du cas juridique concerne si le test suivant n'est pas suffisant.
5003
5004
	// Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
5005
	if (($seller_country_code == $buyer_country_code)
5006
	|| (in_array($seller_country_code,array('FR,MC')) && in_array($buyer_country_code,array('FR','MC')))) // Warning ->country_code not always defined
5007
	{
5008
		//print 'VATRULE 2';
5009
		return get_product_vat_for_country($idprod,$thirdparty_seller,$idprodfournprice);
5010
	}
5011
5012
	// Si (vendeur et acheteur dans Communaute europeenne) et (bien vendu = moyen de transports neuf comme auto, bateau, avion) alors TVA par defaut=0 (La TVA doit etre paye par l'acheteur au centre d'impots de son pays et non au vendeur). Fin de regle.
5013
	// Not supported
5014
5015
	// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
5016
	// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
5017
	if (($seller_in_cee && $buyer_in_cee))
5018
	{
5019
		$isacompany=$thirdparty_buyer->isACompany();
5020
		if ($isacompany)
5021
		{
5022
			//print 'VATRULE 3';
5023
			return 0;
5024
		}
5025
		else
5026
		{
5027
			//print 'VATRULE 4';
5028
			return get_product_vat_for_country($idprod,$thirdparty_seller,$idprodfournprice);
5029
		}
5030
	}
5031
5032
	// Sinon la TVA proposee par defaut=0. Fin de regle.
5033
	// Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
5034
	//print 'VATRULE 5';
5035
	return 0;
5036
}
5037
5038
5039
/**
5040
 *	Fonction qui renvoie si tva doit etre tva percue recuperable
5041
 *
5042
 *	@param	Societe		$thirdparty_seller    	Thirdparty seller
5043
 *	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
5044
 *  @param  int			$idprod                 Id product
5045
 *  @param	int			$idprodfournprice		Id supplier price for product
5046
 *	@return float       			        	0 or 1
5047
 *  @see get_default_tva, get_default_localtax
5048
 */
5049
function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
5050
{
5051
	global $db;
5052
5053
	if ($idprodfournprice > 0)
5054
	{
5055
		if (! class_exists('ProductFournisseur'))
5056
			require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
5057
		$prodprice = new ProductFournisseur($db);
5058
		$prodprice->fetch_product_fournisseur_price($idprodfournprice);
5059
		return $prodprice->fourn_tva_npr;
5060
	}
5061
	elseif ($idprod > 0)
5062
	{
5063
		if (! class_exists('Product'))
5064
			require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
5065
		$prod = new Product($db);
5066
		$prod->fetch($idprod);
5067
		return $prod->tva_npr;
5068
	}
5069
5070
	return 0;
5071
}
5072
5073
/**
5074
 *	Function that return localtax of a product line (according to seller, buyer and product vat rate)
5075
 *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
5076
 *	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
5077
 *	 Sinon TVA proposee par defaut=0. Fin de regle.
5078
 *
5079
 *	@param	Societe		$thirdparty_seller    	Thirdparty seller
5080
 *	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
5081
 *  @param	int			$local					Localtax to process (1 or 2)
5082
 *	@param  int			$idprod					Id product
5083
 *	@return integer        				       	localtax, -1 si ne peut etre determine
5084
 *  @see get_default_tva, get_default_npr
5085
 */
5086
function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
5087
{
5088
	global $mysoc;
5089
5090
	if (!is_object($thirdparty_seller)) return -1;
5091
	if (!is_object($thirdparty_buyer)) return -1;
5092
5093
	if ($local==1) // Localtax 1
5094
	{
5095
		if ($mysoc->country_code == 'ES')
5096
		{
5097
			if (is_numeric($thirdparty_buyer->localtax1_assuj) && ! $thirdparty_buyer->localtax1_assuj) return 0;
5098
		}
5099
		else
5100
		{
5101
			// Si vendeur non assujeti a Localtax1, localtax1 par default=0
5102
			if (is_numeric($thirdparty_seller->localtax1_assuj) && ! $thirdparty_seller->localtax1_assuj) return 0;
5103
			if (! is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj=='localtax1off') return 0;
5104
		}
5105
	}
5106
	elseif ($local==2) //I Localtax 2
5107
	{
5108
		// Si vendeur non assujeti a Localtax2, localtax2 par default=0
5109
		if (is_numeric($thirdparty_seller->localtax2_assuj) && ! $thirdparty_seller->localtax2_assuj) return 0;
5110
		if (! is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj=='localtax2off') return 0;
5111
	}
5112
5113
	if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code)
5114
	{
5115
		return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
5116
	}
5117
5118
	return 0;
5119
}
5120
5121
/**
5122
 *	Return yes or no in current language
5123
 *
5124
 *	@param	string	$yesno			Value to test (1, 'yes', 'true' or 0, 'no', 'false')
5125
 *	@param	integer	$case			1=Yes/No, 0=yes/no, 2=Disabled checkbox, 3=Disabled checkbox + Yes/No
5126
 *	@param	int		$color			0=texte only, 1=Text is formated with a color font style ('ok' or 'error'), 2=Text is formated with 'ok' color.
5127
 *	@return	string					HTML string
5128
 */
5129
function yn($yesno, $case=1, $color=0)
5130
{
5131
	global $langs;
5132
	$result='unknown'; $classname='';
5133
	if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') 	// A mettre avant test sur no a cause du == 0
5134
	{
5135
		$result=$langs->trans('yes');
5136
		if ($case == 1 || $case == 3) $result=$langs->trans("Yes");
5137
		if ($case == 2) $result='<input type="checkbox" value="1" checked disabled>';
5138
		if ($case == 3) $result='<input type="checkbox" value="1" checked disabled> '.$result;
5139
5140
		$classname='ok';
5141
	}
5142
	elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false')
5143
	{
5144
		$result=$langs->trans("no");
5145
		if ($case == 1 || $case == 3) $result=$langs->trans("No");
5146
		if ($case == 2) $result='<input type="checkbox" value="0" disabled>';
5147
		if ($case == 3) $result='<input type="checkbox" value="0" disabled> '.$result;
5148
5149
		if ($color == 2) $classname='ok';
5150
		else $classname='error';
5151
	}
5152
	if ($color) return '<font class="'.$classname.'">'.$result.'</font>';
5153
	return $result;
5154
}
5155
5156
5157
/**
5158
 *	Return a path to have a the directory according to object where files are stored.
5159
 *  New usage:       $conf->module->multidir_output[$object->entity].'/'.get_exdir(0, 0, 0, 1, $object, $modulepart)
5160
 *         or:       $conf->module->dir_output.'/'.get_exdir(0, 0, 0, 1, $object, $modulepart)     if multidir_output not defined.
5161
 *  Example our with new usage:       $object is invoice -> 'INYYMM-ABCD'
5162
 *  Example our with old usage:       '015' with level 3->"0/1/5/", '015' with level 1->"5/", 'ABC-1' with level 3 ->"0/0/1/"
5163
 *
5164
 *	@param	string	$num            Id of object (deprecated, $object will be used in future)
5165
 *	@param  int		$level		    Level of subdirs to return (1, 2 or 3 levels). (deprecated, global option will be used in future)
5166
 * 	@param	int		$alpha		    0=Keep number only to forge path, 1=Use alpha part afer the - (By default, use 0). (deprecated, global option will be used in future)
5167
 *  @param  int		$withoutslash   0=With slash at end (except if '/', we return ''), 1=without slash at end
5168
 *  @param	Object	$object			Object
5169
 *  @param	string	$modulepart		Type of object ('invoice_supplier, 'donation', 'invoice', ...')
5170
 *  @return	string					Dir to use ending. Example '' or '1/' or '1/2/'
5171
 */
5172
function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart)
5173
{
5174
	global $conf;
5175
5176
	$path = '';
5177
5178
	$arrayforoldpath=array('cheque','user','category','holiday','supplier_invoice','invoice_supplier','mailing','supplier_payment');
5179
	if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) $arrayforoldpath[]='product';
5180
	if (! empty($level) && in_array($modulepart, $arrayforoldpath))
5181
	{
5182
		// This part should be removed once all code is using "get_exdir" to forge path, with all parameters provided.
5183
		if (empty($alpha)) $num = preg_replace('/([^0-9])/i','',$num);
5184
		else $num = preg_replace('/^.*\-/i','',$num);
5185
		$num = substr("000".$num, -$level);
5186
		if ($level == 1) $path = substr($num,0,1);
5187
		if ($level == 2) $path = substr($num,1,1).'/'.substr($num,0,1);
5188
		if ($level == 3) $path = substr($num,2,1).'/'.substr($num,1,1).'/'.substr($num,0,1);
5189
	}
5190
	else
5191
	{
5192
		// TODO
5193
		// We will enhance here a common way of forging path for document storage
5194
		// Here, object->id, object->ref and modulepart are required.
5195
		if (in_array($modulepart, array('thirdparty','contact','member','propal','proposal','commande','order','facture','invoice','shipment')))
5196
		{
5197
			$path=($object->ref?$object->ref:$object->id);
5198
		}
5199
	}
5200
5201
	if (empty($withoutslash) && ! empty($path)) $path.='/';
5202
5203
	return $path;
5204
}
5205
5206
/**
5207
 *	Creation of a directory (this can create recursive subdir)
5208
 *
5209
 *	@param	string	$dir		Directory to create (Separator must be '/'. Example: '/mydir/mysubdir')
5210
 *	@param	string	$dataroot	Data root directory (To avoid having the data root in the loop. Using this will also lost the warning on first dir PHP has no permission when open_basedir is used)
5211
 *  @param	int		$newmask	Mask for new file (Defaults to $conf->global->MAIN_UMASK or 0755 if unavailable). Example: '0444'
5212
 *	@return int         		< 0 if KO, 0 = already exists, > 0 if OK
5213
 */
5214
function dol_mkdir($dir, $dataroot='', $newmask=null)
5215
{
5216
	global $conf;
5217
5218
	dol_syslog("functions.lib::dol_mkdir: dir=".$dir,LOG_INFO);
5219
5220
	$dir_osencoded=dol_osencode($dir);
5221
	if (@is_dir($dir_osencoded)) return 0;
5222
5223
	$nberr=0;
5224
	$nbcreated=0;
5225
5226
	$ccdir='';
5227
	if (! empty($dataroot)) {
5228
		// Remove data root from loop
5229
		$dir = str_replace($dataroot.'/', '', $dir);
5230
		$ccdir = $dataroot.'/';
5231
	}
5232
5233
	$cdir = explode("/", $dir);
5234
	$num=count($cdir);
5235
	for ($i = 0; $i < $num; $i++)
5236
	{
5237
		if ($i > 0) $ccdir .= '/'.$cdir[$i];
5238
		else $ccdir .= $cdir[$i];
5239
		if (preg_match("/^.:$/",$ccdir,$regs)) continue;	// Si chemin Windows incomplet, on poursuit par rep suivant
5240
5241
		// Attention, le is_dir() peut echouer bien que le rep existe.
5242
		// (ex selon config de open_basedir)
5243
		if ($ccdir)
5244
		{
5245
			$ccdir_osencoded=dol_osencode($ccdir);
5246
			if (! @is_dir($ccdir_osencoded))
5247
			{
5248
				dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.",LOG_DEBUG);
5249
5250
				umask(0);
5251
				$dirmaskdec=octdec($newmask);
5252
				if (empty($newmask)) {
5253
					$dirmaskdec = empty( $conf->global->MAIN_UMASK ) ? octdec( '0755' ) : octdec( $conf->global->MAIN_UMASK );
5254
				}
5255
				$dirmaskdec |= octdec('0111');  // Set x bit required for directories
5256
				if (! @mkdir($ccdir_osencoded, $dirmaskdec))
5257
				{
5258
					// Si le is_dir a renvoye une fausse info, alors on passe ici.
5259
					dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.",LOG_WARNING);
5260
					$nberr++;
5261
				}
5262
				else
5263
				{
5264
					dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created",LOG_DEBUG);
5265
					$nberr=0;	// On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
5266
					$nbcreated++;
5267
				}
5268
			}
5269
			else
5270
			{
5271
				$nberr=0;	// On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
5272
			}
5273
		}
5274
	}
5275
	return ($nberr ? -$nberr : $nbcreated);
5276
}
5277
5278
5279
/**
5280
 *	Return picto saying a field is required
5281
 *
5282
 *	@return  string		Chaine avec picto obligatoire
5283
 */
5284
function picto_required()
5285
{
5286
	return '<span class="fieldrequired">*</span>';
5287
}
5288
5289
5290
/**
5291
 *	Clean a string from all HTML tags and entities.
5292
 *  This function differs from strip_tags because:
5293
 *  - <br> are replace with \n
5294
 *  - if entities are found, they are decoded before the strip
5295
 *  - you can decide to convert line feed into spaces
5296
 *
5297
 *	@param	string	$stringtoclean		String to clean
5298
 *	@param	integer	$removelinefeed		1=Replace also new lines by a space, 0=Only last one are removed
5299
 *  @param  string	$pagecodeto      	Encoding of input/output string
5300
 *	@return string	    				String cleaned
5301
 *
5302
 * 	@see	dol_escape_htmltag strip_tags
5303
 */
5304
function dol_string_nohtmltag($stringtoclean,$removelinefeed=1,$pagecodeto='UTF-8')
5305
{
5306
	// TODO Try to replace with  strip_tags($stringtoclean)
5307
	$pattern = "/<[^<>]+>/";
5308
	$stringtoclean = preg_replace('/<br[^>]*>/', "\n", $stringtoclean);
5309
	$temp = dol_html_entity_decode($stringtoclean,ENT_COMPAT,$pagecodeto);
5310
5311
	// Exemple of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
5312
	$temp = preg_replace($pattern,"",$temp);    // pass 1
5313
	// $temp after pass 1: <a href="/myurl" title="A title">0000-021
5314
	$temp = preg_replace($pattern,"",$temp);    // pass 2
5315
	// $temp after pass 2: 0000-021
5316
5317
	// Supprime aussi les retours
5318
	if ($removelinefeed) $temp=str_replace(array("\r\n","\r","\n")," ",$temp);
5319
5320
	// et les espaces doubles
5321
	while(strpos($temp,"  "))
5322
	{
5323
		$temp = str_replace("  "," ",$temp);
5324
	}
5325
5326
	return trim($temp);
5327
}
5328
5329
5330
/**
5331
 * Return first line of text. Cut will depends if content is HTML or not.
5332
 *
5333
 * @param 	string	$text		Input text
5334
 * @param	int		$nboflines  Nb of lines to get (default is 1 = first line only)
5335
 * @return	string				Output text
5336
 * @see dol_nboflines_bis, dol_string_nohtmltag, dol_escape_htmltag
5337
 */
5338
function dolGetFirstLineOfText($text, $nboflines=1)
5339
{
5340
	if ($nboflines == 1)
5341
	{
5342
		if (dol_textishtml($text))
5343
		{
5344
			$firstline=preg_replace('/<br[^>]*>.*$/s','',$text);		// The s pattern modifier means the . can match newline characters
5345
			$firstline=preg_replace('/<div[^>]*>.*$/s','',$firstline);	// The s pattern modifier means the . can match newline characters
5346
5347
		}
5348
		else
5349
		{
5350
			$firstline=preg_replace('/[\n\r].*/','',$text);
5351
		}
5352
		return $firstline.((strlen($firstline) != strlen($text))?'...':'');
5353
	}
5354
	else
5355
	{
5356
		$ishtml=0;
5357
		if (dol_textishtml($text))
5358
		{
5359
			$text=preg_replace('/\n/','',$text);
5360
			$ishtml=1;
5361
			$repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5362
		}
5363
		else
5364
		{
5365
			$repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5366
		}
5367
5368
		$text = strtr($text, $repTable);
5369
		if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; }	// /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
0 ignored issues
show
Bug introduced by
The variable $charset does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
5370
		else $pattern = '/(<br[^>]*>)/U';							// /U is to have UNGREEDY regex to limit to one html tag.
5371
		$a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5372
5373
		$firstline='';
5374
		$i=0;
5375
		$nba = count($a);	// 2x nb of lines in $a because $a contains also a line for each new line separator
5376
		while (($i < $nba) && ($i < ($nboflines * 2)))
5377
		{
5378
			if ($i % 2 == 0) $firstline .= $a[$i];
5379
			elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) $firstline .= ($ishtml?"<br>\n":"\n");
5380
			$i++;
5381
		}
5382
		unset($a);
5383
		return $firstline.(($i < $nba)?'...':'');
5384
	}
5385
}
5386
5387
5388
/**
5389
 * Replace CRLF in string with a HTML BR tag
5390
 *
5391
 * @param	string	$stringtoencode		String to encode
5392
 * @param	int     $nl2brmode			0=Adding br before \n, 1=Replacing \n by br
5393
 * @param   bool	$forxml             false=Use <br>, true=Use <br />
5394
 * @return	string						String encoded
5395
 * @see dol_nboflines, dolGetFirstLineOfText
5396
 */
5397
function dol_nl2br($stringtoencode,$nl2brmode=0,$forxml=false)
5398
{
5399
	if (!$nl2brmode) {
5400
		return nl2br($stringtoencode, $forxml);
5401
	} else {
5402
		$ret=preg_replace('/(\r\n|\r|\n)/i', ($forxml?'<br />':'<br>'), $stringtoencode);
5403
		return $ret;
5404
	}
5405
}
5406
5407
5408
/**
5409
 *	This function is called to encode a string into a HTML string but differs from htmlentities because
5410
 * 	a detection is done before to see if text is already HTML or not. Also, all entities but &,<,> are converted.
5411
 *  This permits to encode special chars to entities with no double encoding for already encoded HTML strings.
5412
 * 	This function also remove last EOL or BR if $removelasteolbr=1 (default).
5413
 *  For PDF usage, you can show text by 2 ways:
5414
 *              - writeHTMLCell -> param must be encoded into HTML.
5415
 *              - MultiCell -> param must not be encoded into HTML.
5416
 *              Because writeHTMLCell convert also \n into <br>, if function
5417
 *              is used to build PDF, nl2brmode must be 1.
5418
 *
5419
 *	@param	string	$stringtoencode		String to encode
5420
 *	@param	int		$nl2brmode			0=Adding br before \n, 1=Replacing \n by br (for use with FPDF writeHTMLCell function for example)
5421
 *  @param  string	$pagecodefrom       Pagecode stringtoencode is encoded
5422
 *  @param	int		$removelasteolbr	1=Remove last br or lasts \n (default), 0=Do nothing
5423
 *  @return	string						String encoded
5424
 */
5425
function dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
5426
{
5427
	$newstring=$stringtoencode;
5428
	if (dol_textishtml($stringtoencode))	// Check if text is already HTML or not
5429
	{
5430
		$newstring=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i','<br>',$newstring);	// Replace "<br type="_moz" />" by "<br>". It's same and avoid pb with FPDF.
5431
		if ($removelasteolbr) $newstring=preg_replace('/<br>$/i','',$newstring);	// Remove last <br> (remove only last one)
5432
		$newstring=strtr($newstring,array('&'=>'__and__','<'=>'__lt__','>'=>'__gt__','"'=>'__dquot__'));
5433
		$newstring=dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom);	// Make entity encoding
5434
		$newstring=strtr($newstring,array('__and__'=>'&','__lt__'=>'<','__gt__'=>'>','__dquot__'=>'"'));
5435
	}
5436
	else
5437
	{
5438
		if ($removelasteolbr) $newstring=preg_replace('/(\r\n|\r|\n)$/i','',$newstring);	// Remove last \n (may remove several)
5439
		$newstring=dol_nl2br(dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom),$nl2brmode);
5440
	}
5441
	// Other substitutions that htmlentities does not do
5442
	//$newstring=str_replace(chr(128),'&euro;',$newstring);	// 128 = 0x80. Not in html entity table.     // Seems useles with TCPDF. Make bug with UTF8 languages
5443
	return $newstring;
5444
}
5445
5446
/**
5447
 *	This function is called to decode a HTML string (it decodes entities and br tags)
5448
 *
5449
 *	@param	string	$stringtodecode		String to decode
5450
 *	@param	string	$pagecodeto			Page code for result
5451
 *	@return	string						String decoded
5452
 */
5453
function dol_htmlentitiesbr_decode($stringtodecode,$pagecodeto='UTF-8')
5454
{
5455
	$ret=dol_html_entity_decode($stringtodecode,ENT_COMPAT,$pagecodeto);
5456
	$ret=preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i',"<br>",$ret);
5457
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i',"\r\n",$ret);
5458
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i',"\n",$ret);
5459
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i',"\n",$ret);
5460
	return $ret;
5461
}
5462
5463
/**
5464
 *	This function remove all ending \n and br at end
5465
 *
5466
 *	@param	string	$stringtodecode		String to decode
5467
 *	@return	string						String decoded
5468
 */
5469
function dol_htmlcleanlastbr($stringtodecode)
5470
{
5471
	$ret=preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i',"",$stringtodecode);
5472
	return $ret;
5473
}
5474
5475
/**
5476
 * Replace html_entity_decode functions to manage errors
5477
 *
5478
 * @param   string	$a		Operand a
5479
 * @param   string	$b		Operand b (ENT_QUOTES=convert simple and double quotes)
5480
 * @param   string	$c		Operand c
5481
 * @return  string			String decoded
5482
 */
5483
function dol_html_entity_decode($a,$b,$c='UTF-8')
5484
{
5485
	return html_entity_decode($a,$b,$c);
5486
}
5487
5488
/**
5489
 * Replace htmlentities functions.
5490
 * Goal of this function is to be sure to have default values of htmlentities that match what we need.
5491
 *
5492
 * @param   string  $string         The input string to encode
5493
 * @param   int     $flags          Flags (see PHP doc above)
5494
 * @param   string  $encoding       Encoding page code
5495
 * @param   bool    $double_encode  When double_encode is turned off, PHP will not encode existing html entities
5496
 * @return  string  $ret            Encoded string
5497
 */
5498
function dol_htmlentities($string, $flags=null, $encoding='UTF-8', $double_encode=false)
5499
{
5500
	return htmlentities($string, $flags, $encoding, $double_encode);
5501
}
5502
5503
/**
5504
 *	Check if a string is a correct iso string
5505
 *	If not, it will we considered not HTML encoded even if it is by FPDF.
5506
 *	Example, if string contains euro symbol that has ascii code 128
5507
 *
5508
 *	@param	string	$s      String to check
5509
 *	@return	int     		0 if bad iso, 1 if good iso
5510
 */
5511
function dol_string_is_good_iso($s)
5512
{
5513
	$len=dol_strlen($s);
5514
	$ok=1;
5515
	for($scursor=0;$scursor<$len;$scursor++)
5516
	{
5517
		$ordchar=ord($s{$scursor});
5518
		//print $scursor.'-'.$ordchar.'<br>';
5519
		if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) { $ok=0; break; }
5520
		if ($ordchar > 126 && $ordchar < 160) { $ok=0; break; }
5521
	}
5522
	return $ok;
5523
}
5524
5525
5526
/**
5527
 *	Return nb of lines of a clear text
5528
 *
5529
 *	@param	string	$s			String to check
5530
 * 	@param	int     $maxchar	Not yet used
5531
 *	@return	int					Number of lines
5532
 *  @see	dol_nboflines_bis, dolGetFirstLineOfText
5533
 */
5534
function dol_nboflines($s,$maxchar=0)
5535
{
5536
	if ($s == '') return 0;
5537
	$arraystring=explode("\n",$s);
5538
	$nb=count($arraystring);
5539
5540
	return $nb;
5541
}
5542
5543
5544
/**
5545
 *	Return nb of lines of a formated text with \n and <br> (WARNING: string must not have mixed \n and br separators)
5546
 *
5547
 *	@param	string	$text      		Text
5548
 *	@param	int		$maxlinesize  	Largeur de ligne en caracteres (ou 0 si pas de limite - defaut)
5549
 * 	@param	string	$charset		Give the charset used to encode the $text variable in memory.
5550
 *	@return int						Number of lines
5551
 *	@see	dol_nboflines, dolGetFirstLineOfText
5552
 */
5553
function dol_nboflines_bis($text,$maxlinesize=0,$charset='UTF-8')
5554
{
5555
	$repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5556
	if (dol_textishtml($text)) $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5557
5558
	$text = strtr($text, $repTable);
5559
	if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; }	// /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
5560
	else $pattern = '/(<br[^>]*>)/U';							// /U is to have UNGREEDY regex to limit to one html tag.
5561
	$a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5562
5563
	$nblines = (int) floor((count($a)+1)/2);
5564
	// count possible auto line breaks
5565
	if($maxlinesize)
5566
	{
5567
		foreach ($a as $line)
5568
		{
5569
			if (dol_strlen($line)>$maxlinesize)
5570
			{
5571
				//$line_dec = html_entity_decode(strip_tags($line));
5572
				$line_dec = html_entity_decode($line);
5573
				if(dol_strlen($line_dec)>$maxlinesize)
5574
				{
5575
					$line_dec=wordwrap($line_dec,$maxlinesize,'\n',true);
5576
					$nblines+=substr_count($line_dec,'\n');
5577
				}
5578
			}
5579
		}
5580
	}
5581
5582
	unset($a);
5583
	return $nblines;
5584
}
5585
5586
/**
5587
 *	 Same function than microtime in PHP 5 but compatible with PHP4
5588
 *
5589
 * @return		float		Time (millisecondes) with microsecondes in decimal part
5590
 * @deprecated Dolibarr does not support PHP4, you should use native function
5591
 * @see microtime()
5592
 */
5593
function dol_microtime_float()
5594
{
5595
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
5596
5597
	return microtime(true);
5598
}
5599
5600
/**
5601
 *	Return if a text is a html content
5602
 *
5603
 *	@param	string	$msg		Content to check
5604
 *	@param	int		$option		0=Full detection, 1=Fast check
5605
 *	@return	boolean				true/false
5606
 *	@see	dol_concatdesc
5607
 */
5608
function dol_textishtml($msg,$option=0)
5609
{
5610
	if ($option == 1)
5611
	{
5612
		if (preg_match('/<html/i',$msg))				return true;
5613
		elseif (preg_match('/<body/i',$msg))			return true;
5614
		elseif (preg_match('/<br/i',$msg))				return true;
5615
		return false;
5616
	}
5617
	else
5618
	{
5619
		if (preg_match('/<html/i',$msg))				return true;
5620
		elseif (preg_match('/<body/i',$msg))			return true;
5621
		elseif (preg_match('/<(b|em|i|u)>/i',$msg))		return true;
5622
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i',$msg)) 	  return true;
5623
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*>/i',$msg)) return true;
5624
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/>/i',$msg)) return true;
5625
		elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i',$msg)) return true;	// must accept <img src="http://example.com/aaa.png" />
5626
		elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i',$msg)) return true;	// must accept <a href="http://example.com/aaa.png" />
5627
		elseif (preg_match('/<h[0-9]>/i',$msg))			return true;
5628
		elseif (preg_match('/&[A-Z0-9]{1,6};/i',$msg))	return true;    // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
5629
		elseif (preg_match('/&#[0-9]{2,3};/i',$msg))	return true;    // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
5630
		return false;
5631
	}
5632
}
5633
5634
/**
5635
 *  Concat 2 descriptions with a new line between them (second operand after first one with appropriate new line separator)
5636
 *  text1 html + text2 html => text1 + '<br>' + text2
5637
 *  text1 html + text2 txt  => text1 + '<br>' + dol_nl2br(text2)
5638
 *  text1 txt  + text2 html => dol_nl2br(text1) + '<br>' + text2
5639
 *  text1 txt  + text2 txt  => text1 + '\n' + text2
5640
 *
5641
 *  @param	string	$text1		Text 1
5642
 *  @param	string	$text2		Text 2
5643
 *  @param  bool	$forxml     false=Use <br>, true=Use <br />
5644
 *  @return	string				Text 1 + new line + Text2
5645
 *  @see    dol_textishtml
5646
 */
5647
function dol_concatdesc($text1,$text2,$forxml=false)
5648
{
5649
	$ret='';
5650
	$ret.= (! dol_textishtml($text1) && dol_textishtml($text2))?dol_nl2br($text1, 0, $forxml):$text1;
5651
	$ret.= (! empty($text1) && ! empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2))?($forxml?"<br \>\n":"<br>\n") : "\n") : "";
5652
	$ret.= (dol_textishtml($text1) && ! dol_textishtml($text2))?dol_nl2br($text2, 0, $forxml):$text2;
5653
	return $ret;
5654
}
5655
5656
5657
/**
5658
 * Return array of possible common substitutions. This includes several families like: 'system', 'mycompany', 'object', 'objectamount', 'date', 'user'
5659
 *
5660
 * @param	Translate	$outputlangs	Output language
5661
 * @param   int         $onlykey        1=Do not calculate some heavy values of keys (performance enhancement when we need only the keys), 2=Values are trunc and html sanitized (to use for help tooltip)
5662
 * @param   array       $exclude        Array of family keys we want to exclude. For example array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...)
5663
 * @param   Object      $object         Object for keys on object
5664
 * @return	array						Array of substitutions
5665
 * @see setSubstitFromObject
5666
 */
5667
function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
5668
{
5669
	global $db, $conf, $mysoc, $user;
5670
5671
	$substitutionarray=array();
5672
5673
	if (empty($exclude) || ! in_array('system', $exclude))
5674
	{
5675
		$substitutionarray['__(AnyTranslationKey)__']=$outputlangs->trans('TranslationOfKey');
5676
		$substitutionarray['__[AnyConstantKey]__']=$outputlangs->trans('ValueOfConstant');
5677
		$substitutionarray['__DOL_MAIN_URL_ROOT__']=DOL_MAIN_URL_ROOT;
5678
	}
5679
	if ((empty($exclude) || ! in_array('mycompany', $exclude)) && is_object($mysoc))
5680
	{
5681
		$substitutionarray=array_merge($substitutionarray, array(
5682
			'__MYCOMPANY_NAME__'    => $mysoc->name,
5683
			'__MYCOMPANY_EMAIL__'   => $mysoc->email,
5684
			'__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
5685
			'__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
5686
			'__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
5687
			'__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
5688
			'__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
5689
			'__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
5690
			'__MYCOMPANY_CAPITAL__' => $mysoc->capital,
5691
			'__MYCOMPANY_FULLADDRESS__' => $mysoc->getFullAddress(1, ', '),
5692
			'__MYCOMPANY_ADDRESS__' => $mysoc->address,
5693
			'__MYCOMPANY_ZIP__'     => $mysoc->zip,
5694
			'__MYCOMPANY_TOWN__'    => $mysoc->town,
5695
			'__MYCOMPANY_COUNTRY__'    => $mysoc->country,
5696
			'__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id
5697
		));
5698
	}
5699
	if (($onlykey || is_object($object)) && (empty($exclude) || ! in_array('object', $exclude)))
5700
	{
5701
		if ($onlykey)
5702
		{
5703
			$substitutionarray['__ID__'] = '__ID__';
5704
			$substitutionarray['__REF__'] = '__REF__';
5705
			$substitutionarray['__REFCLIENT__'] = '__REFCLIENT__';
5706
			$substitutionarray['__REFSUPPLIER__'] = '__REFSUPPLIER__';
5707
			$substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
5708
5709
			$substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
5710
			$substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
5711
			$substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
5712
5713
			if (is_object($object) && $object->element == 'shipping')
5714
			{
5715
				$substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
5716
				$substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
5717
				$substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
5718
			}
5719
			$substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
5720
			$substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
5721
			$substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
5722
5723
			$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
5724
			$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
5725
			$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
5726
			$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
5727
5728
			$substitutionarray['__ONLINE_PAYMENT_URL__'] = 'LinkToPayOnlineIfApplicable';
5729
			$substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
5730
			$substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
5731
			$substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
5732
			$substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
5733
			$substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a a service';
5734
5735
			if (is_object($object) && $object->element == 'shipping')
5736
			{
5737
				$substitutionarray['__SHIPPINGTRACKNUM__']='Shipping tacking number';
5738
				$substitutionarray['__SHIPPINGTRACKNUMURL__']='Shipping tracking url';
5739
			}
5740
		}
5741
		else
5742
		{
5743
			$substitutionarray['__ID__'] = $object->id;
5744
			$substitutionarray['__REF__'] = $object->ref;
5745
			$substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : ''));
5746
			$substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : '');
5747
5748
			// TODO USe this ?
5749
			$msgishtml = 0;
5750
5751
			$birthday = dol_print_date($object->birth,'day');
5752
5753
			if (method_exists($object, 'getCivilityLabel')) $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
5754
			$substitutionarray['__MEMBER_FIRSTNAME__']=$msgishtml?dol_htmlentitiesbr($object->firstname):$object->firstname;
5755
			$substitutionarray['__MEMBER_LASTNAME__']=$msgishtml?dol_htmlentitiesbr($object->lastname):$object->lastname;
5756
			if (method_exists($object, 'getFullName')) $substitutionarray['__MEMBER_FULLNAME__']=$msgishtml?dol_htmlentitiesbr($object->getFullName($outputlangs)):$object->getFullName($outputlangs);
5757
			$substitutionarray['__MEMBER_COMPANY__']=$msgishtml?dol_htmlentitiesbr($object->societe):$object->societe;
5758
			$substitutionarray['__MEMBER_ADDRESS__']=$msgishtml?dol_htmlentitiesbr($object->address):$object->address;
5759
			$substitutionarray['__MEMBER_ZIP__']=$msgishtml?dol_htmlentitiesbr($object->zip):$object->zip;
5760
			$substitutionarray['__MEMBER_TOWN__']=$msgishtml?dol_htmlentitiesbr($object->town):$object->town;
5761
			$substitutionarray['__MEMBER_COUNTRY__']=$msgishtml?dol_htmlentitiesbr($object->country):$object->country;
5762
			$substitutionarray['__MEMBER_EMAIL__']=$msgishtml?dol_htmlentitiesbr($object->email):$object->email;
5763
			$substitutionarray['__MEMBER_BIRTH__']=$msgishtml?dol_htmlentitiesbr($birthday):$birthday;
5764
			$substitutionarray['__MEMBER_PHOTO__']=$msgishtml?dol_htmlentitiesbr($object->photo):$object->photo;
5765
			$substitutionarray['__MEMBER_LOGIN__']=$msgishtml?dol_htmlentitiesbr($object->login):$object->login;
5766
			$substitutionarray['__MEMBER_PASSWORD__']=$msgishtml?dol_htmlentitiesbr($object->pass):$object->pass;
5767
			$substitutionarray['__MEMBER_PHONE__']=$msgishtml?dol_htmlentitiesbr($object->phone):$object->phone;
5768
			$substitutionarray['__MEMBER_PHONEPRO__']=$msgishtml?dol_htmlentitiesbr($object->phone_perso):$object->phone_perso;
5769
			$substitutionarray['__MEMBER_PHONEMOBILE__']=$msgishtml?dol_htmlentitiesbr($object->phone_mobile):$object->phone_mobile;
5770
5771
			if (is_object($object) && $object->element == 'societe')
5772
			{
5773
				$substitutionarray['__THIRDPARTY_ID__'] = (is_object($object)?$object->id:'');
5774
				$substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object)?$object->name:'');
5775
				$substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object)?$object->name_alias:'');
5776
				$substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object)?$object->email:'');
5777
			}
5778
			elseif (is_object($object->thirdparty) && $object->thirdparty->id > 0)
5779
			{
5780
				$substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty)?$object->thirdparty->id:'');
5781
				$substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty)?$object->thirdparty->name:'');
5782
				$substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty)?$object->thirdparty->name_alias:'');
5783
				$substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty)?$object->thirdparty->email:'');
5784
			}
5785
5786
			if (is_object($object->projet) && $object->projet->id > 0)
5787
			{
5788
				$substitutionarray['__PROJECT_ID__'] = (is_object($object->projet)?$object->projet->id:'');
5789
				$substitutionarray['__PROJECT_REF__'] = (is_object($object->projet)?$object->projet->ref:'');
5790
				$substitutionarray['__PROJECT_NAME__'] = (is_object($object->projet)?$object->projet->title:'');
5791
			}
5792
5793
			if (is_object($object) && $object->element == 'shipping')
5794
			{
5795
				$substitutionarray['__SHIPPINGTRACKNUM__']=$object->tracking_number;
5796
				$substitutionarray['__SHIPPINGTRACKNUMURL__']=$object->tracking_url;
5797
			}
5798
5799
			if (is_object($object) && $object->element == 'contrat' && is_array($object->lines))
5800
			{
5801
				$dateplannedstart='';
5802
				$datenextexpiration='';
5803
				foreach($object->lines as $line)
5804
				{
5805
					if ($line->date_ouverture_prevue > $dateplannedstart) $dateplannedstart = $line->date_ouverture_prevue;
5806
					if ($line->statut == 4 && $line->date_fin_prevue && (! $datenextexpiration || $line->date_fin_prevue < $datenextexpiration)) $datenextexpiration = $line->date_fin_prevue;
5807
				}
5808
				$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'dayrfc');
5809
				$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
5810
				$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'dayrfc');
5811
				$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
5812
			}
5813
5814
			// Create dynamic tags for __EXTRAFIELD_FIELD__
5815
			if ($object->table_element && $object->id > 0)
5816
			{
5817
				$extrafieldstmp = new ExtraFields($db);
5818
				$extralabels = $extrafieldstmp->fetch_name_optionals_label($object->table_element, true);
5819
				$object->fetch_optionals();
5820
				foreach ($extrafieldstmp->attribute_label as $key => $label) {
5821
					$substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = $object->array_options['options_' . $key];
5822
				}
5823
			}
5824
5825
			$substitutionarray['__ONLINE_PAYMENT_URL__'] = 'TODO';
5826
		}
5827
	}
5828
	if (empty($exclude) || ! in_array('objectamount', $exclude))
5829
	{
5830
		$substitutionarray['__DATE_YMD__']        = is_object($object)?(isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : '') : '';
5831
		$substitutionarray['__DATE_DUE_YMD__']    = is_object($object)?(isset($object->date_lim_reglement)? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : '') : '';
5832
		$substitutionarray['__AMOUNT__']          = is_object($object)?$object->total_ttc:'';
5833
		$substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object)?$object->total_ht:'';
5834
		$substitutionarray['__AMOUNT_VAT__']      = is_object($object)?($object->total_vat?$object->total_vat:$object->total_tva):'';
5835
 		if ($onlykey != 2 || $mysoc->useLocalTax(1)) $substitutionarray['__AMOUNT_TAX2__']     = is_object($object)?($object->total_localtax1?$object->total_localtax1:$object->total_localtax1):'';
5836
		if ($onlykey != 2 || $mysoc->useLocalTax(2)) $substitutionarray['__AMOUNT_TAX3__']     = is_object($object)?($object->total_localtax2?$object->total_localtax2:$object->total_localtax2):'';
5837
5838
		/* TODO Add key for multicurrency
5839
    	$substitutionarray['__AMOUNT_FORMATED__']          = is_object($object)?price($object->total_ttc, 0, $outputlangs, 0, 0, -1, $conf->currency_code):'';
5840
		$substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object)?price($object->total_ht, 0, $outputlangs, 0, 0, -1, $conf->currency_code):'';
5841
        $substitutionarray['__AMOUNT_VAT_FORMATED__']      = is_object($object)?($object->total_vat?price($object->total_vat, 0, $outputlangs, 0, 0, -1, $conf->currency_code):price($object->total_tva, 0, $outputlangs, 0, 0, -1, $conf->currency_code)):'';
5842
		*/
5843
		// For backward compatibility
5844
		if ($onlykey != 2)
5845
		{
5846
			$substitutionarray['__TOTAL_TTC__']    = is_object($object)?$object->total_ttc:'';
5847
			$substitutionarray['__TOTAL_HT__']     = is_object($object)?$object->total_ht:'';
5848
			$substitutionarray['__TOTAL_VAT__']    = is_object($object)?($object->total_vat?$object->total_vat:$object->total_tva):'';
5849
		}
5850
	}
5851
5852
	if (empty($exclude) || ! in_array('date', $exclude))
5853
	{
5854
		include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
5855
5856
		$tmp=dol_getdate(dol_now(), true);
5857
		$tmp2=dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
5858
		$tmp3=dol_get_prev_month($tmp['mday'], $tmp['mon'], $tmp['year']);
5859
		$tmp4=dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
5860
		$tmp5=dol_get_next_month($tmp['mday'], $tmp['mon'], $tmp['year']);
5861
5862
		$substitutionarray=array_merge($substitutionarray, array(
5863
			'__DAY__' => (string) $tmp['mday'],
5864
			'__MONTH__' => (string) $tmp['mon'],
5865
			'__YEAR__' => (string) $tmp['year'],
5866
			'__PREVIOUS_DAY__' => (string) $tmp2['day'],
5867
			'__PREVIOUS_MONTH__' => (string) $tmp3['month'],
5868
			'__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
5869
			'__NEXT_DAY__' => (string) $tmp4['day'],
5870
			'__NEXT_MONTH__' => (string) $tmp5['month'],
5871
			'__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
5872
		));
5873
	}
5874
5875
	if (empty($exclude) || ! in_array('user', $exclude))
5876
	{
5877
		// Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
5878
		// this will also replace var found into content of signature
5879
		$signature = $user->signature;
5880
		$substitutionarray=array_merge($substitutionarray, array(
5881
			'__USER_SIGNATURE__' => (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '')
5882
		)
5883
			);
5884
		// For backward compatibility
5885
		if ($onlykey != 2)
5886
		{
5887
			$substitutionarray['__SIGNATURE__'] = (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '');
5888
		}
5889
5890
		$substitutionarray=array_merge($substitutionarray, array(
5891
			'__USER_ID__' => (string) $user->id,
5892
			'__USER_LOGIN__' => (string) $user->login,
5893
			'__USER_LASTNAME__' => (string) $user->lastname,
5894
			'__USER_FIRSTNAME__' => (string) $user->firstname,
5895
			'__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
5896
			'__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
5897
			'__USER_REMOTE_IP__' => (string) $_SERVER['REMOTE_ADDR']
5898
			)
5899
		);
5900
	}
5901
	if (! empty($conf->multicompany->enabled))
5902
	{
5903
		$substitutionarray=array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
5904
	}
5905
5906
	return $substitutionarray;
5907
}
5908
5909
/**
5910
 *  Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newval).
5911
 *  Texts like __(TranslationKey|langfile)__ and __[ConstantKey]__ are also replaced.
5912
 *  Example of usage:
5913
 *  $substitutionarray = getCommonSubstitutionArray($langs, 0, null, $thirdparty);
5914
 *  complete_substitutions_array($substitutionarray, $langs, $thirdparty);
5915
 *  $mesg = make_substitutions($mesg, $substitutionarray, $langs);
5916
 *
5917
 *  @param	string		$text	      			Source string in which we must do substitution
5918
 *  @param  array		$substitutionarray		Array with key->val to substitute. Example: array('__MYKEY__' => 'MyVal', ...)
5919
 *  @param	Translate	$outputlangs			Output language
5920
 * 	@return string  		    				Output string after substitutions
5921
 *  @see	complete_substitutions_array
5922
 */
5923
function make_substitutions($text, $substitutionarray, $outputlangs=null)
5924
{
5925
	global $conf, $langs;
5926
5927
	if (! is_array($substitutionarray)) return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
5928
5929
	if (empty($outputlangs)) $outputlangs=$langs;
5930
5931
	// Make substitution for language keys
5932
	if (is_object($outputlangs))
5933
	{
5934
		while (preg_match('/__\(([^\)]+)\)__/', $text, $reg))
5935
		{
5936
			$msgishtml = 0;
5937
			if (dol_textishtml($text,1)) $msgishtml = 1;
5938
5939
			// If key is __(TranslationKey|langfile)__, then force load of langfile.lang
5940
			$tmp=explode('|',$reg[1]);
5941
			if (! empty($tmp[1])) $outputlangs->load($tmp[1]);
5942
5943
			$text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $msgishtml?dol_htmlentitiesbr($outputlangs->transnoentitiesnoconv($reg[1])):$outputlangs->transnoentitiesnoconv($reg[1]), $text);
5944
		}
5945
	}
5946
5947
	// Make substitution for constant keys. Must be after the substitution of translation, so if text of translation contains a constant,
5948
	// it is also converted.
5949
	while (preg_match('/__\[([^\]]+)\]__/', $text, $reg))
5950
	{
5951
		$msgishtml = 0;
5952
		if (dol_textishtml($text,1)) $msgishtml = 1;
5953
5954
		$keyfound = $reg[1];
5955
		if (preg_match('/(_pass|password|secret|_key|key$)/i', $keyfound)) $newval = '*****forbidden*****';
5956
		else $newval=empty($conf->global->$keyfound)?'':$conf->global->$keyfound;
5957
		$text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml?dol_htmlentitiesbr($newval):$newval, $text);
5958
	}
5959
5960
	// Make substitition for array $substitutionarray
5961
	foreach ($substitutionarray as $key => $value)
5962
	{
5963
		if ($key == '__SIGNATURE__' && (! empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) $value='';		// Protection
5964
		if ($key == '__USER_SIGNATURE__' && (! empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) $value='';	// Protection
5965
5966
		$text=str_replace("$key","$value",$text);	// We must keep the " to work when value is 123.5 for example
5967
	}
5968
5969
	return $text;
5970
}
5971
5972
/**
5973
 *  Complete the $substitutionarray with more entries coming from external module that had set the "substitutions=1" into module_part array.
5974
 *  In this case, method completesubstitutionarray provided by module is called.
5975
 *
5976
 *  @param  array		$substitutionarray		Array substitution old value => new value value
5977
 *  @param  Translate	$outputlangs            Output language
5978
 *  @param  Object		$object                 Source object
5979
 *  @param  mixed		$parameters       		Add more parameters (useful to pass product lines)
5980
 *  @param  string      $callfunc               What is the name of the custom function that will be called? (default: completesubstitutionarray)
5981
 *  @return	void
5982
 *  @see 	make_substitutions
5983
 */
5984
function complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
5985
{
5986
	global $conf,$user;
5987
5988
	require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5989
5990
	// Add a substitution key for each extrafields, using key __EXTRA_XXX__
5991
	// TODO Remove this. Already available into the getCommonSubstitutionArray used to build the substitution array.
5992
	/*if (is_object($object) && is_array($object->array_options))
5993
	{
5994
		foreach($object->array_options as $key => $val)
5995
		{
5996
			$keyshort=preg_replace('/^(options|extra)_/','',$key);
5997
			$substitutionarray['__EXTRAFIELD_'.$keyshort.'__']=$val;
5998
			// For backward compatibiliy
5999
			$substitutionarray['%EXTRA_'.$keyshort.'%']=$val;
6000
		}
6001
	}*/
6002
6003
	// Check if there is external substitution to do, requested by plugins
6004
	$dirsubstitutions=array_merge(array(),(array) $conf->modules_parts['substitutions']);
6005
6006
	foreach($dirsubstitutions as $reldir)
6007
	{
6008
		$dir=dol_buildpath($reldir,0);
6009
6010
		// Check if directory exists
6011
		if (! dol_is_dir($dir)) continue;
6012
6013
		$substitfiles=dol_dir_list($dir,'files',0,'functions_');
6014
		foreach($substitfiles as $substitfile)
6015
		{
6016
			if (preg_match('/functions_(.*)\.lib\.php/i',$substitfile['name'],$reg))
6017
			{
6018
				$module=$reg[1];
6019
6020
				dol_syslog("Library functions_".$substitfile['name']." found into ".$dir);
6021
				// Include the user's functions file
6022
				require_once $dir.$substitfile['name'];
6023
				// Call the user's function, and only if it is defined
6024
				$function_name=$module."_".$callfunc;
6025
				if (function_exists($function_name)) $function_name($substitutionarray,$outputlangs,$object,$parameters);
6026
			}
6027
		}
6028
	}
6029
}
6030
6031
/**
6032
 *    Format output for start and end date
6033
 *
6034
 *    @param	int	$date_start    Start date
6035
 *    @param    int	$date_end      End date
6036
 *    @param    string		$format        Output format
6037
 *    @param	Translate	$outputlangs   Output language
6038
 *    @return	void
6039
 */
6040
function print_date_range($date_start,$date_end,$format = '',$outputlangs='')
6041
{
6042
	print get_date_range($date_start,$date_end,$format,$outputlangs);
6043
}
6044
6045
/**
6046
 *    Format output for start and end date
6047
 *
6048
 *    @param	int			$date_start    		Start date
6049
 *    @param    int			$date_end      		End date
6050
 *    @param    string		$format        		Output format
6051
 *    @param	Translate	$outputlangs   		Output language
6052
 *    @param	integer		$withparenthesis	1=Add parenthesis, 0=non parenthesis
6053
 *    @return	string							String
6054
 */
6055
function get_date_range($date_start,$date_end,$format = '',$outputlangs='', $withparenthesis=1)
6056
{
6057
	global $langs;
6058
6059
	$out='';
6060
6061
	if (! is_object($outputlangs)) $outputlangs=$langs;
6062
6063
	if ($date_start && $date_end)
6064
	{
6065
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFromTo',dol_print_date($date_start, $format, false, $outputlangs),dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
6066
	}
6067
	if ($date_start && ! $date_end)
6068
	{
6069
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFrom',dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis?')':'');
6070
	}
6071
	if (! $date_start && $date_end)
6072
	{
6073
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateUntil',dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
6074
	}
6075
6076
	return $out;
6077
}
6078
6079
/**
6080
 * Return firstname and lastname in correct order
6081
 *
6082
 * @param	string	$firstname		Firstname
6083
 * @param	string	$lastname		Lastname
6084
 * @param	int		$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname
6085
 * @return	string					Firstname + lastname or Lastname + firstname
6086
 */
6087
function dolGetFirstLastname($firstname,$lastname,$nameorder=-1)
6088
{
6089
	global $conf;
6090
6091
	$ret='';
6092
	// If order not defined, we use the setup
6093
	if ($nameorder < 0) $nameorder=(empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION));
6094
	if ($nameorder && ((string) $nameorder != '2'))
6095
	{
6096
		$ret.=$firstname;
6097
		if ($firstname && $lastname) $ret.=' ';
6098
		$ret.=$lastname;
6099
	}
6100
	else if ($nameorder == 2)
6101
	{
6102
	   $ret.=$firstname;
6103
	}
6104
	else
6105
	{
6106
		$ret.=$lastname;
6107
		if ($firstname && $lastname) $ret.=' ';
6108
		$ret.=$firstname;
6109
	}
6110
	return $ret;
6111
}
6112
6113
6114
/**
6115
 *	Set event message in dol_events session object. Will be output by calling dol_htmloutput_events.
6116
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
6117
 *  Note: Prefer to use setEventMessages instead.
6118
 *
6119
 *	@param	mixed	$mesgs			Message string or array
6120
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
6121
 *  @return	void
6122
 *  @see	dol_htmloutput_events
6123
 */
6124
function setEventMessage($mesgs, $style='mesgs')
6125
{
6126
	//dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);		This is not deprecated, it is used by setEventMessages function
6127
	if (! is_array($mesgs))		// If mesgs is a string
6128
	{
6129
		if ($mesgs) $_SESSION['dol_events'][$style][] = $mesgs;
6130
	}
6131
	else						// If mesgs is an array
6132
	{
6133
		foreach($mesgs as $mesg)
6134
		{
6135
			if ($mesg) $_SESSION['dol_events'][$style][] = $mesg;
6136
		}
6137
	}
6138
}
6139
6140
/**
6141
 *	Set event messages in dol_events session object. Will be output by calling dol_htmloutput_events.
6142
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
6143
 *
6144
 *	@param	string	$mesg			Message string
6145
 *	@param	array	$mesgs			Message array
6146
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
6147
 *  @return	void
6148
 *  @see	dol_htmloutput_events
6149
 */
6150
function setEventMessages($mesg, $mesgs, $style='mesgs')
6151
{
6152
	if (! in_array((string) $style, array('mesgs','warnings','errors'))) dol_print_error('','Bad parameter style='.$style.' for setEventMessages');
6153
	if (empty($mesgs)) setEventMessage($mesg, $style);
6154
	else
6155
	{
6156
		if (! empty($mesg) && ! in_array($mesg, $mesgs)) setEventMessage($mesg, $style);	// Add message string if not already into array
6157
		setEventMessage($mesgs, $style);
6158
	}
6159
}
6160
6161
/**
6162
 *	Print formated messages to output (Used to show messages on html output).
6163
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function, so there is
6164
 *  no need to call it explicitely.
6165
 *
6166
 *  @param	int		$disabledoutputofmessages	Clear all messages stored into session without diplaying them
6167
 *  @return	void
6168
 *  @see    									dol_htmloutput_mesg
6169
 */
6170
function dol_htmloutput_events($disabledoutputofmessages=0)
6171
{
6172
	// Show mesgs
6173
	if (isset($_SESSION['dol_events']['mesgs'])) {
6174
		if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
6175
		unset($_SESSION['dol_events']['mesgs']);
6176
	}
6177
6178
	// Show errors
6179
	if (isset($_SESSION['dol_events']['errors'])) {
6180
		if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
6181
		unset($_SESSION['dol_events']['errors']);
6182
	}
6183
6184
	// Show warnings
6185
	if (isset($_SESSION['dol_events']['warnings'])) {
6186
		if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
6187
		unset($_SESSION['dol_events']['warnings']);
6188
	}
6189
}
6190
6191
/**
6192
 *	Get formated messages to output (Used to show messages on html output).
6193
 *  This include also the translation of the message key.
6194
 *
6195
 *	@param	string		$mesgstring		Message string or message key
6196
 *	@param	string[]	$mesgarray      Array of message strings or message keys
6197
 *  @param  string		$style          Style of message output ('ok' or 'error')
6198
 *  @param  int			$keepembedded   Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
6199
 *	@return	string						Return html output
6200
 *
6201
 *  @see    dol_print_error
6202
 *  @see    dol_htmloutput_errors
6203
 *  @see    setEventMessages
6204
 */
6205
function get_htmloutput_mesg($mesgstring='',$mesgarray='', $style='ok', $keepembedded=0)
6206
{
6207
	global $conf, $langs;
6208
6209
	$ret=0; $return='';
6210
	$out='';
6211
	$divstart=$divend='';
6212
6213
	// If inline message with no format, we add it.
6214
	if ((empty($conf->use_javascript_ajax) || ! empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && ! preg_match('/<div class=".*">/i',$out))
6215
	{
6216
		$divstart='<div class="'.$style.' clearboth">';
6217
		$divend='</div>';
6218
	}
6219
6220
	if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring)
6221
	{
6222
		$langs->load("errors");
6223
		$out.=$divstart;
6224
		if (is_array($mesgarray) && count($mesgarray))
6225
		{
6226
			foreach($mesgarray as $message)
6227
			{
6228
				$ret++;
6229
				$out.= $langs->trans($message);
6230
				if ($ret < count($mesgarray)) $out.= "<br>\n";
6231
			}
6232
		}
6233
		if ($mesgstring)
6234
		{
6235
			$langs->load("errors");
6236
			$ret++;
6237
			$out.= $langs->trans($mesgstring);
6238
		}
6239
		$out.=$divend;
6240
	}
6241
6242
	if ($out)
6243
	{
6244
		if (! empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded))
6245
		{
6246
			$return = '<script type="text/javascript">
6247
					$(document).ready(function() {
6248
						var block = '.(! empty($conf->global->MAIN_USE_JQUERY_BLOCKUI)?"true":"false").'
6249
						if (block) {
6250
							$.dolEventValid("","'.dol_escape_js($out).'");
6251
						} else {
6252
							/* jnotify(message, preset of message type, keepmessage) */
6253
							$.jnotify("'.dol_escape_js($out).'",
6254
							"'.($style=="ok" ? 3000 : $style).'",
6255
							'.($style=="ok" ? "false" : "true").',
6256
							{ remove: function (){} } );
6257
						}
6258
					});
6259
				</script>';
6260
		}
6261
		else
6262
		{
6263
			$return = $out;
6264
		}
6265
	}
6266
6267
	return $return;
6268
}
6269
6270
/**
6271
 *  Get formated error messages to output (Used to show messages on html output).
6272
 *
6273
 *  @param	string	$mesgstring         Error message
6274
 *  @param  array	$mesgarray          Error messages array
6275
 *  @param  int		$keepembedded       Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
6276
 *  @return string                		Return html output
6277
 *
6278
 *  @see    dol_print_error
6279
 *  @see    dol_htmloutput_mesg
6280
 */
6281
function get_htmloutput_errors($mesgstring='', $mesgarray='', $keepembedded=0)
6282
{
6283
	return get_htmloutput_mesg($mesgstring, $mesgarray,'error',$keepembedded);
6284
}
6285
6286
/**
6287
 *	Print formated messages to output (Used to show messages on html output).
6288
 *
6289
 *	@param	string		$mesgstring		Message string or message key
6290
 *	@param	string[]	$mesgarray      Array of message strings or message keys
6291
 *  @param  string      $style          Which style to use ('ok', 'warning', 'error')
6292
 *  @param  int         $keepembedded   Set to 1 if message must be kept embedded into its html place (this disable jnotify)
6293
 *  @return	void
6294
 *
6295
 *  @see    dol_print_error
6296
 *  @see    dol_htmloutput_errors
6297
 *  @see    setEventMessages
6298
 */
6299
function dol_htmloutput_mesg($mesgstring='',$mesgarray='', $style='ok', $keepembedded=0)
6300
{
6301
	if (empty($mesgstring) && (! is_array($mesgarray) || count($mesgarray) == 0)) return;
6302
6303
	$iserror=0;
6304
	$iswarning=0;
6305
	if (is_array($mesgarray))
6306
	{
6307
		foreach($mesgarray as $val)
6308
		{
6309
			if ($val && preg_match('/class="error"/i',$val)) { $iserror++; break; }
6310
			if ($val && preg_match('/class="warning"/i',$val)) { $iswarning++; break; }
6311
		}
6312
	}
6313
	else if ($mesgstring && preg_match('/class="error"/i',$mesgstring)) $iserror++;
6314
	else if ($mesgstring && preg_match('/class="warning"/i',$mesgstring)) $iswarning++;
6315
	if ($style=='error') $iserror++;
6316
	if ($style=='warning') $iswarning++;
6317
6318
	if ($iserror || $iswarning)
6319
	{
6320
		// Remove div from texts
6321
		$mesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$mesgstring);
6322
		$mesgstring=preg_replace('/<div class="(error|warning)">/','',$mesgstring);
6323
		$mesgstring=preg_replace('/<\/div>/','',$mesgstring);
6324
		// Remove div from texts array
6325
		if (is_array($mesgarray))
6326
		{
6327
			$newmesgarray=array();
6328
			foreach($mesgarray as $val)
6329
			{
6330
				$tmpmesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$val);
6331
				$tmpmesgstring=preg_replace('/<div class="(error|warning)">/','',$tmpmesgstring);
6332
				$tmpmesgstring=preg_replace('/<\/div>/','',$tmpmesgstring);
6333
				$newmesgarray[]=$tmpmesgstring;
6334
			}
6335
			$mesgarray=$newmesgarray;
6336
		}
6337
		print get_htmloutput_mesg($mesgstring,$mesgarray,($iserror?'error':'warning'),$keepembedded);
6338
	}
6339
	else print get_htmloutput_mesg($mesgstring,$mesgarray,'ok',$keepembedded);
6340
}
6341
6342
/**
6343
 *  Print formated error messages to output (Used to show messages on html output).
6344
 *
6345
 *  @param	string	$mesgstring          Error message
6346
 *  @param  array	$mesgarray           Error messages array
6347
 *  @param  int		$keepembedded        Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
6348
 *  @return	void
6349
 *
6350
 *  @see    dol_print_error
6351
 *  @see    dol_htmloutput_mesg
6352
 */
6353
function dol_htmloutput_errors($mesgstring='', $mesgarray='', $keepembedded=0)
6354
{
6355
	dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
6356
}
6357
6358
/**
6359
 * 	Advanced sort array by second index function, which produces ascending (default)
6360
 *  or descending output and uses optionally natural case insensitive sorting (which
6361
 *  can be optionally case sensitive as well).
6362
 *
6363
 *  @param      array		$array      		Array to sort (array of array('key','otherkey1','otherkey2'...))
6364
 *  @param      string		$index				Key in array to use for sorting criteria
6365
 *  @param      int			$order				Sort order ('asc' or 'desc')
6366
 *  @param      int			$natsort			1=use "natural" sort (natsort), 0=use "standard" sort (asort)
6367
 *  @param      int			$case_sensitive		1=sort is case sensitive, 0=not case sensitive
6368
 *  @param		int			$keepindex			If 0 and index key of array to sort is a numeric, than index will be rewrote. If 1 or index key is not numeric, key for index is kept after sorting.
6369
 *  @return     array							Sorted array
6370
 */
6371
function dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
6372
{
6373
	// Clean parameters
6374
	$order=strtolower($order);
6375
6376
	$sizearray=count($array);
6377
	if (is_array($array) && $sizearray>0)
6378
	{
6379
		$temp = array();
6380
		foreach(array_keys($array) as $key) $temp[$key]=$array[$key][$index];
6381
6382
		if (!$natsort) ($order=='asc') ? asort($temp) : arsort($temp);
6383
		else
6384
		{
6385
			($case_sensitive) ? natsort($temp) : natcasesort($temp);
6386
			if($order!='asc') $temp=array_reverse($temp,TRUE);
6387
		}
6388
6389
		$sorted = array();
6390
6391
		foreach(array_keys($temp) as $key)
6392
		{
6393
			(is_numeric($key) && empty($keepindex)) ? $sorted[]=$array[$key] : $sorted[$key]=$array[$key];
6394
		}
6395
6396
		return $sorted;
6397
	}
6398
	return $array;
6399
}
6400
6401
6402
/**
6403
 *      Check if a string is in UTF8
6404
 *
6405
 *      @param	string	$str        String to check
6406
 * 		@return	boolean				True if string is UTF8 or ISO compatible with UTF8, False if not (ISO with special char or Binary)
6407
 */
6408
function utf8_check($str)
6409
{
6410
	// We must use here a binary strlen function (so not dol_strlen)
6411
	$strLength = dol_strlen($str);
6412
	for ($i=0; $i<$strLength; $i++)
6413
	{
6414
		if (ord($str[$i]) < 0x80) continue; // 0bbbbbbb
6415
		elseif ((ord($str[$i]) & 0xE0) == 0xC0) $n=1; // 110bbbbb
6416
		elseif ((ord($str[$i]) & 0xF0) == 0xE0) $n=2; // 1110bbbb
6417
		elseif ((ord($str[$i]) & 0xF8) == 0xF0) $n=3; // 11110bbb
6418
		elseif ((ord($str[$i]) & 0xFC) == 0xF8) $n=4; // 111110bb
6419
		elseif ((ord($str[$i]) & 0xFE) == 0xFC) $n=5; // 1111110b
6420
		else return false; // Does not match any model
6421
		for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ?
6422
			if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
6423
			return false;
6424
		}
6425
	}
6426
	return true;
6427
}
6428
6429
6430
/**
6431
 *      Return a string encoded into OS filesystem encoding. This function is used to define
6432
 * 	    value to pass to filesystem PHP functions.
6433
 *
6434
 *      @param	string	$str        String to encode (UTF-8)
6435
 * 		@return	string				Encoded string (UTF-8, ISO-8859-1)
6436
 */
6437
function dol_osencode($str)
6438
{
6439
	global $conf;
6440
6441
	$tmp=ini_get("unicode.filesystem_encoding");						// Disponible avec PHP 6.0
6442
	if (empty($tmp) && ! empty($_SERVER["WINDIR"])) $tmp='iso-8859-1';	// By default for windows
6443
	if (empty($tmp)) $tmp='utf-8';										// By default for other
6444
	if (! empty($conf->global->MAIN_FILESYSTEM_ENCODING)) $tmp=$conf->global->MAIN_FILESYSTEM_ENCODING;
6445
6446
	if ($tmp == 'iso-8859-1') return utf8_decode($str);
6447
	return $str;
6448
}
6449
6450
6451
/**
6452
 *      Return an id or code from a code or id.
6453
 *      Store also Code-Id into a cache to speed up next request on same key.
6454
 *
6455
 * 		@param	DoliDB	$db				Database handler
6456
 * 		@param	string	$key			Code or Id to get Id or Code
6457
 * 		@param	string	$tablename		Table name without prefix
6458
 * 		@param	string	$fieldkey		Field to search the key into
6459
 * 		@param	string	$fieldid		Field to get
6460
 *      @param  int		$entityfilter	Filter by entity
6461
 *      @return int						<0 if KO, Id of code if OK
6462
 *      @see $langs->getLabelFromKey
6463
 */
6464
function dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0)
6465
{
6466
	global $cache_codes;
6467
6468
	// If key empty
6469
	if ($key == '') return '';
6470
6471
	// Check in cache
6472
	if (isset($cache_codes[$tablename][$key]))	// Can be defined to 0 or ''
6473
	{
6474
		return $cache_codes[$tablename][$key];   // Found in cache
6475
	}
6476
6477
	$sql = "SELECT ".$fieldid." as valuetoget";
6478
	$sql.= " FROM ".MAIN_DB_PREFIX.$tablename;
6479
	$sql.= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
6480
	if (! empty($entityfilter))
6481
		$sql.= " AND entity IN (" . getEntity($tablename) . ")";
6482
6483
	dol_syslog('dol_getIdFromCode', LOG_DEBUG);
6484
	$resql = $db->query($sql);
6485
	if ($resql)
6486
	{
6487
		$obj = $db->fetch_object($resql);
6488
		if ($obj) $cache_codes[$tablename][$key]=$obj->valuetoget;
6489
		else $cache_codes[$tablename][$key]='';
6490
		$db->free($resql);
6491
		return $cache_codes[$tablename][$key];
6492
	}
6493
	else
6494
	{
6495
		return -1;
6496
	}
6497
}
6498
6499
/**
6500
 * Verify if condition in string is ok or not
6501
 *
6502
 * @param 	string		$strRights		String with condition to check
6503
 * @return 	boolean						True or False. Return true if strRights is ''
6504
 */
6505
function verifCond($strRights)
6506
{
6507
	global $user,$conf,$langs;
6508
	global $leftmenu;
6509
	global $rights;    // To export to dol_eval function
6510
6511
	//print $strRights."<br>\n";
6512
	$rights = true;
6513
	if ($strRights != '')
6514
	{
6515
		//$tab_rights = explode('&&', $strRights);
6516
		//$i = 0;
6517
		//while (($i < count($tab_rights)) && ($rights == true)) {
6518
		$str = 'if(!(' . $strRights . ')) { $rights = false; }';
6519
		dol_eval($str);
6520
		//	$i++;
6521
		//}
6522
	}
6523
	return $rights;
6524
}
6525
6526
/**
6527
 * Replace eval function to add more security.
6528
 * This function is called by verifCond() or trans() and transnoentitiesnoconv().
6529
 *
6530
 * @param 	string	$s				String to evaluate
6531
 * @param	int		$returnvalue	0=No return (used to execute eval($a=something)). 1=Value of eval is returned (used to eval($something)).
6532
 * @param   int     $hideerrors     1=Hide errors
6533
 * @return	mixed					Nothing or return of eval
6534
 */
6535
function dol_eval($s, $returnvalue=0, $hideerrors=1)
6536
{
6537
	// Only global variables can be changed by eval function and returned to caller
6538
	global $db, $langs, $user, $conf;
6539
	global $mainmenu, $leftmenu;
6540
	global $rights;
6541
	global $object;
6542
	global $mysoc;
6543
6544
	global $obj;       // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
6545
	global $soc;       // For backward compatibility
6546
6547
	//print $s."<br>\n";
6548
	if ($returnvalue)
6549
	{
6550
		if ($hideerrors) return @eval('return '.$s.';');
0 ignored issues
show
Coding Style introduced by
The function dol_eval() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
6551
		else return eval('return '.$s.';');
0 ignored issues
show
Coding Style introduced by
The function dol_eval() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
6552
	}
6553
	else
6554
	{
6555
		if ($hideerrors) @eval($s);
0 ignored issues
show
Coding Style introduced by
The function dol_eval() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
6556
		else eval($s);
0 ignored issues
show
Coding Style introduced by
The function dol_eval() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
6557
	}
6558
}
6559
6560
/**
6561
 * Return if var element is ok
6562
 *
6563
 * @param   string      $element    Variable to check
6564
 * @return  boolean                 Return true of variable is not empty
6565
 */
6566
function dol_validElement($element)
6567
{
6568
	return (trim($element) != '');
6569
}
6570
6571
/**
6572
 * 	Return img flag of country for a language code or country code
6573
 *
6574
 * 	@param	string	$codelang	Language code (en_IN, fr_CA...) or Country code (IN, FR)
6575
 *  @param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
6576
 * 	@return	string				HTML img string with flag.
6577
 */
6578
function picto_from_langcode($codelang, $moreatt = '')
6579
{
6580
	global $langs;
6581
6582
	if (empty($codelang)) return '';
6583
6584
	if (empty($codelang)) return '';
6585
6586
	if ($codelang == 'auto')
6587
	{
6588
		return '<span class="fa fa-globe"></span>';
6589
	}
6590
6591
	$langtocountryflag = array(
6592
		'ar_AR' => '',
6593
		'ca_ES' => 'catalonia',
6594
		'da_DA' => 'dk',
6595
		'fr_CA' => 'mq',
6596
		'sv_SV' => 'se'
6597
	);
6598
6599
	if (isset($langtocountryflag[$codelang])) $flagImage = $langtocountryflag[$codelang];
6600
	else
6601
	{
6602
		$tmparray = explode('_', $codelang);
6603
		$flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
6604
	}
6605
6606
	return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt);
6607
}
6608
6609
/**
6610
 *  Complete or removed entries into a head array (used to build tabs).
6611
 *  For example, with value added by external modules. Such values are declared into $conf->modules_parts['tab'].
6612
 *  Or by change using hook completeTabsHead
6613
 *
6614
 *  @param	Conf			$conf           Object conf
6615
 *  @param  Translate		$langs          Object langs
6616
 *  @param  object|null		$object         Object object
6617
 *  @param  array			$head          	Object head
6618
 *  @param  int				$h				New position to fill
6619
 *  @param  string			$type           Value for object where objectvalue can be
6620
 *                              			'thirdparty'       to add a tab in third party view
6621
 *		                        	      	'intervention'     to add a tab in intervention view
6622
 *     		                    	     	'supplier_order'   to add a tab in supplier order view
6623
 *          		            	        'supplier_invoice' to add a tab in supplier invoice view
6624
 *                  		    	        'invoice'          to add a tab in customer invoice view
6625
 *                          			    'order'            to add a tab in customer order view
6626
 *                          				'contract'		   to add a tabl in contract view
6627
 *                      			        'product'          to add a tab in product view
6628
 *                              			'propal'           to add a tab in propal view
6629
 *                              			'user'             to add a tab in user view
6630
 *                              			'group'            to add a tab in group view
6631
 * 		        	               	     	'member'           to add a tab in fundation member view
6632
 *      		                        	'categories_x'	   to add a tab in category view ('x': type of category (0=product, 1=supplier, 2=customer, 3=member)
6633
 *      									'ecm'			   to add a tab for another ecm view
6634
 *                                          'stock'            to add a tab for warehouse view
6635
 *  @param  string		$mode  	        	'add' to complete head, 'remove' to remove entries
6636
 *	@return	void
6637
 */
6638
function complete_head_from_modules($conf,$langs,$object,&$head,&$h,$type,$mode='add')
6639
{
6640
	global $hookmanager;
6641
6642
	if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type]))
6643
	{
6644
		foreach ($conf->modules_parts['tabs'][$type] as $value)
6645
		{
6646
			$values=explode(':',$value);
6647
6648
			if ($mode == 'add' && ! preg_match('/^\-/',$values[1]))
6649
			{
6650
				if (count($values) == 6)       // new declaration with permissions:  $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
6651
				{
6652
					if ($values[0] != $type) continue;
6653
6654
					if (verifCond($values[4]))
6655
					{
6656
						if ($values[3]) $langs->load($values[3]);
6657
						if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
6658
						{
6659
							$substitutionarray=array();
6660
							complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
6661
							$label=make_substitutions($reg[1], $substitutionarray);
6662
						}
6663
						else $label=$langs->trans($values[2]);
6664
6665
						$head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && ! empty($object->id))?$object->id:''), $values[5]), 1);
6666
						$head[$h][1] = $label;
6667
						$head[$h][2] = str_replace('+','',$values[1]);
6668
						$h++;
6669
					}
6670
				}
6671
				else if (count($values) == 5)       // deprecated
6672
				{
6673
					dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
6674
6675
					if ($values[0] != $type) continue;
6676
					if ($values[3]) $langs->load($values[3]);
6677
					if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
6678
					{
6679
						$substitutionarray=array();
6680
						complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
6681
						$label=make_substitutions($reg[1], $substitutionarray);
6682
					}
6683
					else $label=$langs->trans($values[2]);
6684
6685
					$head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && ! empty($object->id))?$object->id:''), $values[4]), 1);
6686
					$head[$h][1] = $label;
6687
					$head[$h][2] = str_replace('+','',$values[1]);
6688
					$h++;
6689
				}
6690
			}
6691
			else if ($mode == 'remove' && preg_match('/^\-/',$values[1]))
6692
			{
6693
				if ($values[0] != $type) continue;
6694
				$tabname=str_replace('-','',$values[1]);
6695
				foreach($head as $key => $val)
6696
				{
6697
					$condition = (! empty($values[3]) ? verifCond($values[3]) : 1);
6698
					if ($head[$key][2]==$tabname && $condition)
6699
					{
6700
						unset($head[$key]);
6701
						break;
6702
					}
6703
				}
6704
			}
6705
		}
6706
	}
6707
6708
	// No need to make a return $head. Var is modified as a reference
6709
	if (! empty($hookmanager))
6710
	{
6711
		$parameters=array('object' => $object, 'mode' => $mode, 'head'=>$head);
6712
		$reshook=$hookmanager->executeHooks('completeTabsHead',$parameters);
6713
		if ($reshook > 0)
6714
		{
6715
			$head = $hookmanager->resArray;
6716
		}
6717
	}
6718
}
6719
6720
/**
6721
 * Print common footer :
6722
 * 		conf->global->MAIN_HTML_FOOTER
6723
 *      js for switch of menu hider
6724
 * 		js for conf->global->MAIN_GOOGLE_AN_ID
6725
 * 		js for conf->global->MAIN_SHOW_TUNING_INFO or $_SERVER["MAIN_SHOW_TUNING_INFO"]
6726
 * 		js for conf->logbuffer
6727
 *
6728
 * @param	string	$zone	'private' (for private pages) or 'public' (for public pages)
6729
 * @return	void
6730
 */
6731
function printCommonFooter($zone='private')
6732
{
6733
	global $conf, $hookmanager;
6734
	global $micro_start_time;
6735
6736
	if ($zone == 'private') print "\n".'<!-- Common footer for private page -->'."\n";
6737
	else print "\n".'<!-- Common footer for public page -->'."\n";
6738
6739
	$parameters=array();
6740
	$reshook=$hookmanager->executeHooks('printCommonFooter',$parameters);    // Note that $action and $object may have been modified by some hooks
6741
	if (empty($reshook))
6742
	{
6743
		if (! empty($conf->global->MAIN_HTML_FOOTER)) print $conf->global->MAIN_HTML_FOOTER."\n";
6744
6745
		print "\n";
6746
		if (! empty($conf->use_javascript_ajax))
6747
		{
6748
			print '<script type="text/javascript" language="javascript">'."\n";
6749
6750
			if ($zone == 'private' && empty($conf->dol_use_jmobile))
6751
			{
6752
				print "\n";
6753
				print '/* JS CODE TO ENABLE to enable handler to switch left menu page (menuhider) */'."\n";
6754
				print 'jQuery(".menuhider").click(function() {';
6755
				print '  console.log("We click on .menuhider");'."\n";
6756
				//print "  $('.side-nav').animate({width:'toggle'},200);\n";     // OK with eldy theme but not with md
6757
				print "  $('.side-nav').toggle()\n";
6758
				print "  $('.login_block').toggle()\n";
6759
				print '});'."\n";
6760
			}
6761
6762
			// Google Analytics (need Google module)
6763
			if (! empty($conf->google->enabled) && ! empty($conf->global->MAIN_GOOGLE_AN_ID))
6764
			{
6765
				if (($conf->dol_use_jmobile != 4))
6766
				{
6767
					print "\n";
6768
					print "/* JS CODE TO ENABLE for google analtics tag */\n";
6769
					print '  var _gaq = _gaq || [];'."\n";
6770
					print '  _gaq.push([\'_setAccount\', \''.$conf->global->MAIN_GOOGLE_AN_ID.'\']);'."\n";
6771
					print '  _gaq.push([\'_trackPageview\']);'."\n";
6772
					print ''."\n";
6773
					print '  (function() {'."\n";
6774
					print '    var ga = document.createElement(\'script\'); ga.type = \'text/javascript\'; ga.async = true;'."\n";
6775
					print '    ga.src = (\'https:\' == document.location.protocol ? \'https://ssl\' : \'http://www\') + \'.google-analytics.com/ga.js\';'."\n";
6776
					print '    var s = document.getElementsByTagName(\'script\')[0]; s.parentNode.insertBefore(ga, s);'."\n";
6777
					print '  })();'."\n";
6778
				}
6779
			}
6780
6781
			// End of tuning
6782
			if (! empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || ! empty($conf->global->MAIN_SHOW_TUNING_INFO))
6783
			{
6784
				print "\n";
6785
				print "/* JS CODE TO ENABLE to add memory info */\n";
6786
				print 'window.console && console.log("';
6787
				if (! empty($conf->global->MEMCACHED_SERVER)) print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
6788
				print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED)?$conf->global->MAIN_OPTIMIZE_SPEED:'off');
6789
				if (! empty($micro_start_time))   // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
6790
				{
6791
					$micro_end_time = microtime(true);
6792
					print ' - Build time: '.ceil(1000*($micro_end_time-$micro_start_time)).' ms';
6793
				}
6794
				if (function_exists("memory_get_usage"))
6795
				{
6796
					print ' - Mem: '.memory_get_usage();
6797
				}
6798
				if (function_exists("xdebug_memory_usage"))
6799
				{
6800
					print ' - XDebug time: '.ceil(1000*xdebug_time_index()).' ms';
6801
					print ' - XDebug mem: '.xdebug_memory_usage();
6802
					print ' - XDebug mem peak: '.xdebug_peak_memory_usage();
6803
				}
6804
				if (function_exists("zend_loader_file_encoded"))
6805
				{
6806
					print ' - Zend encoded file: '.(zend_loader_file_encoded()?'yes':'no');
6807
				}
6808
				print '");'."\n";
6809
			}
6810
6811
			print "\n".'</script>'."\n";
6812
		}
6813
6814
		// Add Xdebug coverage of code
6815
		if (defined('XDEBUGCOVERAGE'))
6816
		{
6817
			print_r(xdebug_get_code_coverage());
6818
		}
6819
6820
		// If there is some logs in buffer to show
6821
		if (count($conf->logbuffer))
6822
		{
6823
			print "\n";
6824
			print "<!-- Start of log output\n";
6825
			//print '<div class="hidden">'."\n";
6826
			foreach($conf->logbuffer as $logline)
6827
			{
6828
				print $logline."<br>\n";
6829
			}
6830
			//print '</div>'."\n";
6831
			print "End of log output -->\n";
6832
		}
6833
	}
6834
}
6835
6836
/**
6837
 * Split a string with 2 keys into key array.
6838
 * For example: "A=1;B=2;C=2" is exploded into array('A'=>1,'B'=>2,'C'=>3)
6839
 *
6840
 * @param 	string	$string		String to explode
6841
 * @param 	string	$delimiter	Delimiter between each couple of data
6842
 * @param 	string	$kv			Delimiter between key and value
6843
 * @return	array				Array of data exploded
6844
 */
6845
function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
6846
{
6847
	if ($a = explode($delimiter, $string))
6848
	{
6849
		$ka = array();
6850
		foreach ($a as $s) { // each part
6851
			if ($s) {
6852
				if ($pos = strpos($s, $kv)) { // key/value delimiter
6853
					$ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
6854
				} else { // key delimiter not found
6855
					$ka[] = trim($s);
6856
				}
6857
			}
6858
		}
6859
		return $ka;
6860
	}
6861
	return array();
6862
}
6863
6864
6865
/**
6866
 * Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag)
6867
 *
6868
 * @param 	string	$selector	Selector ('#id' or 'input[name="ref"]') to use to find the HTML input field that must get the autofocus. You must use a CSS selector, so unique id preceding with the '#' char.
6869
 * @return	string				HTML code to set focus
6870
 */
6871
function dol_set_focus($selector)
6872
{
6873
	print "\n".'<!-- Set focus onto a specific field -->'."\n";
6874
	print '<script type="text/javascript" language="javascript">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
6875
}
6876
6877
6878
/**
6879
 * Return getmypid() or random PID when function is disabled
6880
 * Some web hosts disable this php function for security reasons
6881
 * and sometimes we can't redeclare function
6882
 *
6883
 * @return	int
6884
 */
6885
function dol_getmypid()
6886
{
6887
	if (! function_exists('getmypid')) {
6888
		return mt_rand(1,32768);
6889
	} else {
6890
		return getmypid();
6891
	}
6892
}
6893
6894
6895
/**
6896
 * Generate natural SQL search string for a criteria (this criteria can be tested on one or several fields)
6897
 *
6898
 * @param 	string|string[]	$fields 	String or array of strings, filled with the name of all fields in the SQL query we must check (combined with a OR). Example: array("p.field1","p.field2")
6899
 * @param 	string 			$value 		The value to look for.
6900
 *                          		    If param $mode is 0, can contains several keywords separated with a space or |
6901
 *                                         like "keyword1 keyword2" = We want record field like keyword1 AND field like keyword2
6902
 *                                         or like "keyword1|keyword2" = We want record field like keyword1 OR field like keyword2
6903
 *                             			If param $mode is 1, can contains an operator <, > or = like "<10" or ">=100.5 < 1000"
6904
 *                             			If param $mode is 2, can contains a list of int id separated by comma like "1,3,4"
6905
 *                             			If param $mode is 3, can contains a list of string separated by comma like "a,b,c"
6906
 * @param	integer			$mode		0=value is list of keyword strings, 1=value is a numeric test (Example ">5.5 <10"), 2=value is a list of id separated with comma (Example '1,3,4')
6907
 * @param	integer			$nofirstand	1=Do not output the first 'AND'
6908
 * @return 	string 			$res 		The statement to append to the SQL query
6909
 */
6910
function natural_search($fields, $value, $mode=0, $nofirstand=0)
6911
{
6912
	global $db,$langs;
6913
6914
	$value=trim($value);
6915
6916
	if ($mode == 0)
6917
	{
6918
		$value=preg_replace('/\*/','%',$value);	// Replace * with %
6919
	}
6920
	if ($mode == 1)
6921
	{
6922
		$value=preg_replace('/([<>=]+)\s+([0-9'.preg_quote($langs->trans("DecimalSeparator"),'/').'\-])/','\1\2',$value);	// Clean string '< 10' into '<10' so we can the explode on space to get all tests to do
6923
	}
6924
6925
	$value = preg_replace('/\s*\|\s*/','|', $value);
6926
6927
	$crits = explode(' ', $value);
6928
	$res = '';
6929
	if (! is_array($fields)) $fields = array($fields);
6930
6931
	$nboffields = count($fields);
6932
	$end2 = count($crits);
6933
	$j = 0;
6934
	foreach ($crits as $crit)
6935
	{
6936
		$i = 0; $i2 = 0;
6937
		$newres = '';
6938
		foreach ($fields as $field)
6939
		{
6940
			if ($mode == 1)
6941
			{
6942
				$operator='=';
6943
				$newcrit = preg_replace('/([<>=]+)/','',trim($crit));
6944
6945
				preg_match('/([<>=]+)/',trim($crit), $reg);
6946
				if ($reg[1])
6947
				{
6948
					$operator = $reg[1];
6949
				}
6950
				if ($newcrit != '')
6951
				{
6952
					$numnewcrit = price2num($newcrit);
6953
					if (is_numeric($numnewcrit))
6954
					{
6955
						$newres .= ($i2 > 0 ? ' OR ' : '') . $field . ' '.$operator.' '.$numnewcrit;
6956
					}
6957
					else
6958
					{
6959
						$newres .= ($i2 > 0 ? ' OR ' : '') . '1 = 2';	// force false
6960
					}
6961
					$i2++;	// a criteria was added to string
6962
				}
6963
			}
6964
			else if ($mode == 2)
6965
			{
6966
				$newres .= ($i2 > 0 ? ' OR ' : '') . $field . " IN (" . $db->escape(trim($crit)) . ")";
6967
				$i2++;	// a criteria was added to string
6968
			}
6969
			else if ($mode == 3)
6970
			{
6971
				$tmparray=explode(',',trim($crit));
6972
				if (count($tmparray))
6973
				{
6974
					$listofcodes='';
6975
					foreach($tmparray as $val)
6976
					{
6977
						if ($val)
6978
						{
6979
							$listofcodes.=($listofcodes?',':'');
6980
							$listofcodes.="'".$db->escape(trim($val))."'";
6981
						}
6982
					}
6983
					$newres .= ($i2 > 0 ? ' OR ' : '') . $field . " IN (" . $listofcodes . ")";
6984
					$i2++;	// a criteria was added to string
6985
				}
6986
			}
6987
			else    // $mode=0
6988
			{
6989
				$textcrit = '';
6990
				$tmpcrits = explode('|',$crit);
6991
				$i3 = 0;
6992
				foreach($tmpcrits as $tmpcrit)
6993
				{
6994
					if(empty($tmpcrit)) continue;
6995
6996
					$newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
6997
6998
					if (preg_match('/\.(id|rowid)$/', $field))	// Special case for rowid that is sometimes a ref so used as a search field
6999
					{
7000
						$newres .= $field . " = " . (is_numeric(trim($tmpcrit))?trim($tmpcrit):'0');
7001
					}
7002
					else
7003
					{
7004
						$newres .= $field . " LIKE '";
7005
7006
						$tmpcrit=trim($tmpcrit);
7007
						$tmpcrit2=$tmpcrit;
7008
						$tmpbefore='%'; $tmpafter='%';
7009
						if (preg_match('/^[\^\$]/', $tmpcrit))
7010
						{
7011
							$tmpbefore='';
7012
							$tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
7013
						}
7014
						if (preg_match('/[\^\$]$/', $tmpcrit))
7015
						{
7016
							$tmpafter='';
7017
							$tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
7018
						}
7019
						$newres .= $tmpbefore;
7020
						$newres .= $db->escape($tmpcrit2);
7021
						$newres .= $tmpafter;
7022
						$newres .= "'";
7023
						if ($tmpcrit2 == '')
7024
						{
7025
							$newres .= ' OR ' . $field . " IS NULL";
7026
						}
7027
					}
7028
7029
					$i3++;
7030
				}
7031
				$i2++;	// a criteria was added to string
7032
			}
7033
			$i++;
7034
		}
7035
		if ($newres) $res = $res . ($res ? ' AND ' : '') . ($i2 > 1 ? '(' : '') .$newres . ($i2 > 1 ? ')' : '');
7036
		$j++;
7037
	}
7038
	$res = ($nofirstand?"":" AND ")."(" . $res . ")";
7039
	//print 'xx'.$res.'yy';
7040
	return $res;
7041
}
7042
7043
/**
7044
 * Return string with full Url
7045
 *
7046
 * @param   Object	$object		Object
7047
 * @return	string				Url string
7048
 */
7049
function showDirectDownloadLink($object)
7050
{
7051
	global $conf, $langs;
7052
7053
	$out='';
7054
	$url = $object->getLastMainDocLink($object->element);
7055
7056
	if ($url)
7057
	{
7058
		$out.= img_picto('','object_globe.png').' '.$langs->trans("DirectDownloadLink").'<br>';
7059
		$out.= '<input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'">';
7060
		$out.= ajax_autoselect("directdownloadlink", 0);
7061
	}
7062
	return $out;
7063
}
7064
7065
/**
7066
 * Return the filename of file to get the thumbs
7067
 *
7068
 * @param   string  $file           Original filename (full or relative path)
7069
 * @param   string  $extName        Extension to differenciate thumb file name ('', '_small', '_mini')
7070
 * @param   string  $extImgTarget   Force image extension for thumbs. Use '' to keep same extension than original image (default).
7071
 * @return  string                  New file name (full or relative path, including the thumbs/)
7072
 */
7073
function getImageFileNameForSize($file, $extName, $extImgTarget='')
7074
{
7075
	$dirName = dirname($file);
7076
	if ($dirName == '.') $dirName='';
7077
7078
	$fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp)$/i','',$file);	// We remove extension, whatever is its case
7079
	$fileName = basename($fileName);
7080
7081
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpg$/i',$file)?'.jpg':'');
7082
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpeg$/i',$file)?'.jpeg':'');
7083
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.gif$/i',$file)?'.gif':'');
7084
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.png$/i',$file)?'.png':'');
7085
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.bmp$/i',$file)?'.bmp':'');
7086
7087
	if (! $extImgTarget) return $file;
7088
7089
	$subdir='';
7090
	if ($extName) $subdir = 'thumbs/';
7091
7092
	return ($dirName?$dirName.'/':'').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
7093
}
7094
7095
7096
/**
7097
 * Return URL we can use for advanced preview links
7098
 *
7099
 * @param   string    $modulepart     propal, facture, facture_fourn, ...
7100
 * @param   string    $relativepath   Relative path of docs.
7101
 * @param	int		  $alldata		  Return array with all components (1 is recommended, then use a simple a href link with the class, target and mime attribute added. 'documentpreview' css class is handled by jquery code into main.inc.php)
7102
 * @param	string	  $param		  More param on http links
7103
 * @return  string|array              Output string with href link or array with all components of link
7104
 */
7105
function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
7106
{
7107
	global $conf, $langs;
7108
7109
	if (empty($conf->use_javascript_ajax)) return '';
7110
7111
	$mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'svg+xml');
7112
	//$mime_preview[]='vnd.oasis.opendocument.presentation';
7113
	//$mime_preview[]='archive';
7114
	$num_mime = array_search(dol_mimetype($relativepath, '', 1), $mime_preview);
7115
7116
	if ($alldata == 1)
7117
	{
7118
		if ($num_mime !== false) return array('target'=>'_blank', 'css'=>'documentpreview', 'url'=>DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&attachment=0&file='.urlencode($relativepath).($param?'&'.$param:''), 'mime'=>dol_mimetype($relativepath), );
7119
		else return array();
7120
	}
7121
7122
	// old behavior
7123
	if ($num_mime !== false) return 'javascript:document_preview(\''.dol_escape_js(DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&attachment=0&file='.urlencode($relativepath).($param?'&'.$param:'')).'\', \''.dol_mimetype($relativepath).'\', \''.dol_escape_js($langs->trans('Preview')).'\')';
7124
	else return '';
7125
}
7126
7127
7128
/**
7129
 * Make content of an input box selected when we click into input field.
7130
 *
7131
 * @param string	$htmlname	Id of html object
7132
 * @param string	$addlink	Add a 'link to' after
7133
 */
7134
function ajax_autoselect($htmlname, $addlink='')
7135
{
7136
	global $langs;
7137
	$out = '<script type="text/javascript">
7138
               jQuery(document).ready(function () {
7139
				    jQuery("#'.$htmlname.'").click(function() { jQuery(this).select(); } );
7140
				});
7141
		    </script>';
7142
	if ($addlink) $out.=' <a href="'.$addlink.'" target="_blank">'.$langs->trans("Link").'</a>';
7143
	return $out;
7144
}
7145
7146
7147
/**
7148
 *	Return mime type of a file
7149
 *
7150
 *	@param	string	$file		Filename we looking for MIME type
7151
 *  @param  string	$default    Default mime type if extension not found in known list
7152
 * 	@param	int		$mode    	0=Return full mime, 1=otherwise short mime string, 2=image for mime type, 3=source language, 4=css of font fa
7153
 *	@return string 		    	Return a mime type family (text/xxx, application/xxx, image/xxx, audio, video, archive)
7154
 *  @see    image_format_supported (images.lib.php)
7155
 */
7156
function dol_mimetype($file, $default='application/octet-stream', $mode=0)
7157
{
7158
	$mime=$default;
7159
	$imgmime='other.png';
7160
	$famime='file-o';
7161
	$srclang='';
7162
7163
	$tmpfile=preg_replace('/\.noexe$/','',$file);
7164
7165
	// Text files
7166
	if (preg_match('/\.txt$/i',$tmpfile))         			   { $mime='text/plain';    $imgmime='text.png'; $famime='file-text-o'; }
7167
	if (preg_match('/\.rtx$/i',$tmpfile))                      { $mime='text/richtext'; $imgmime='text.png'; $famime='file-text-o'; }
7168
	if (preg_match('/\.csv$/i',$tmpfile))					   { $mime='text/csv';      $imgmime='text.png'; $famime='file-text-o'; }
7169
	if (preg_match('/\.tsv$/i',$tmpfile))					   { $mime='text/tab-separated-values'; $imgmime='text.png'; $famime='file-text-o'; }
7170
	if (preg_match('/\.(cf|conf|log)$/i',$tmpfile))            { $mime='text/plain';    $imgmime='text.png'; $famime='file-text-o'; }
7171
	if (preg_match('/\.ini$/i',$tmpfile))                      { $mime='text/plain';    $imgmime='text.png'; $srclang='ini'; $famime='file-text-o'; }
7172
	if (preg_match('/\.css$/i',$tmpfile))                      { $mime='text/css';      $imgmime='css.png'; $srclang='css'; $famime='file-text-o'; }
7173
	// Certificate files
7174
	if (preg_match('/\.(crt|cer|key|pub)$/i',$tmpfile))        { $mime='text/plain';    $imgmime='text.png'; $famime='file-text-o'; }
7175
	// HTML/XML
7176
	if (preg_match('/\.(html|htm|shtml)$/i',$tmpfile))         { $mime='text/html';     $imgmime='html.png'; $srclang='html'; $famime='file-text-o'; }
7177
	if (preg_match('/\.(xml|xhtml)$/i',$tmpfile))              { $mime='text/xml';      $imgmime='other.png'; $srclang='xml'; $famime='file-text-o'; }
7178
	// Languages
7179
	if (preg_match('/\.bas$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='bas';  $famime='file-code-o'; }
7180
	if (preg_match('/\.(c)$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='c';  $famime='file-code-o'; }
7181
	if (preg_match('/\.(cpp)$/i',$tmpfile))                    { $mime='text/plain'; $imgmime='text.png'; $srclang='cpp';  $famime='file-code-o'; }
7182
	if (preg_match('/\.(h)$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='h';  $famime='file-code-o'; }
7183
	if (preg_match('/\.(java|jsp)$/i',$tmpfile))               { $mime='text/plain'; $imgmime='text.png'; $srclang='java';  $famime='file-code-o'; }
7184
	if (preg_match('/\.php([0-9]{1})?$/i',$tmpfile))           { $mime='text/plain'; $imgmime='php.png'; $srclang='php';  $famime='file-code-o'; }
7185
	if (preg_match('/\.phtml$/i',$tmpfile))                    { $mime='text/plain'; $imgmime='php.png'; $srclang='php';  $famime='file-code-o'; }
7186
	if (preg_match('/\.(pl|pm)$/i',$tmpfile))                  { $mime='text/plain'; $imgmime='pl.png'; $srclang='perl';  $famime='file-code-o'; }
7187
	if (preg_match('/\.sql$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='sql';  $famime='file-code-o'; }
7188
	if (preg_match('/\.js$/i',$tmpfile))                       { $mime='text/x-javascript'; $imgmime='jscript.png'; $srclang='js';  $famime='file-code-o'; }
7189
	// Open office
7190
	if (preg_match('/\.odp$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.presentation'; $imgmime='ooffice.png'; $famime='file-powerpoint-o'; }
7191
	if (preg_match('/\.ods$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.spreadsheet';  $imgmime='ooffice.png'; $famime='file-excel-o'; }
7192
	if (preg_match('/\.odt$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.text';         $imgmime='ooffice.png'; $famime='file-word-o'; }
7193
	// MS Office
7194
	if (preg_match('/\.mdb$/i',$tmpfile))					   { $mime='application/msaccess'; $imgmime='mdb.png'; $famime='file-o'; }
7195
	if (preg_match('/\.doc(x|m)?$/i',$tmpfile))				   { $mime='application/msword'; $imgmime='doc.png'; $famime='file-word-o'; }
7196
	if (preg_match('/\.dot(x|m)?$/i',$tmpfile))				   { $mime='application/msword'; $imgmime='doc.png'; $famime='file-word-o'; }
7197
	if (preg_match('/\.xlt(x)?$/i',$tmpfile))				   { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; $famime='file-excel-o'; }
7198
	if (preg_match('/\.xla(m)?$/i',$tmpfile))				   { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; $famime='file-excel-o'; }
7199
	if (preg_match('/\.xls$/i',$tmpfile))			           { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; $famime='file-excel-o'; }
7200
	if (preg_match('/\.xls(b|m|x)$/i',$tmpfile))			   { $mime='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; $imgmime='xls.png'; $famime='file-excel-o'; }
7201
	if (preg_match('/\.pps(m|x)?$/i',$tmpfile))				   { $mime='application/vnd.ms-powerpoint'; $imgmime='ppt.png'; $famime='file-powerpoint-o'; }
7202
	if (preg_match('/\.ppt(m|x)?$/i',$tmpfile))				   { $mime='application/x-mspowerpoint'; $imgmime='ppt.png'; $famime='file-powerpoint-o'; }
7203
	// Other
7204
	if (preg_match('/\.pdf$/i',$tmpfile))                      { $mime='application/pdf'; $imgmime='pdf.png'; $famime='file-pdf-o'; }
7205
	// Scripts
7206
	if (preg_match('/\.bat$/i',$tmpfile))                      { $mime='text/x-bat';  $imgmime='script.png'; $srclang='dos'; $famime='file-code-o'; }
7207
	if (preg_match('/\.sh$/i',$tmpfile))                       { $mime='text/x-sh';   $imgmime='script.png'; $srclang='bash'; $famime='file-code-o'; }
7208
	if (preg_match('/\.ksh$/i',$tmpfile))                      { $mime='text/x-ksh';  $imgmime='script.png'; $srclang='bash'; $famime='file-code-o'; }
7209
	if (preg_match('/\.bash$/i',$tmpfile))                     { $mime='text/x-bash'; $imgmime='script.png'; $srclang='bash'; $famime='file-code-o'; }
7210
	// Images
7211
	if (preg_match('/\.ico$/i',$tmpfile))                      { $mime='image/x-icon'; $imgmime='image.png'; $famime='file-image-o'; }
7212
	if (preg_match('/\.(jpg|jpeg)$/i',$tmpfile))			   { $mime='image/jpeg';   $imgmime='image.png'; $famime='file-image-o'; }
7213
	if (preg_match('/\.png$/i',$tmpfile))					   { $mime='image/png';    $imgmime='image.png'; $famime='file-image-o'; }
7214
	if (preg_match('/\.gif$/i',$tmpfile))					   { $mime='image/gif';    $imgmime='image.png'; $famime='file-image-o'; }
7215
	if (preg_match('/\.bmp$/i',$tmpfile))					   { $mime='image/bmp';    $imgmime='image.png'; $famime='file-image-o'; }
7216
	if (preg_match('/\.(tif|tiff)$/i',$tmpfile))			   { $mime='image/tiff';   $imgmime='image.png'; $famime='file-image-o'; }
7217
	if (preg_match('/\.svg$/i',$tmpfile))					   { $mime='image/svg+xml';$imgmime='image.png'; $famime='file-image-o'; }
7218
	// Calendar
7219
	if (preg_match('/\.vcs$/i',$tmpfile))					   { $mime='text/calendar'; $imgmime='other.png'; $famime='file-text-o'; }
7220
	if (preg_match('/\.ics$/i',$tmpfile))					   { $mime='text/calendar'; $imgmime='other.png'; $famime='file-text-o'; }
7221
	// Other
7222
	if (preg_match('/\.torrent$/i',$tmpfile))				   { $mime='application/x-bittorrent'; $imgmime='other.png'; $famime='file-o'; }
7223
	// Audio
7224
	if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i',$tmpfile)) { $mime='audio'; $imgmime='audio.png'; $famime='file-audio-o'; }
7225
	// Video
7226
	if (preg_match('/\.ogv$/i',$tmpfile))                      { $mime='video/ogg';       $imgmime='video.png'; $famime='file-video-o'; }
7227
	if (preg_match('/\.webm$/i',$tmpfile))                     { $mime='video/webm';      $imgmime='video.png'; $famime='file-video-o'; }
7228
	if (preg_match('/\.avi$/i',$tmpfile))                      { $mime='video/x-msvideo'; $imgmime='video.png'; $famime='file-video-o'; }
7229
	if (preg_match('/\.divx$/i',$tmpfile))                     { $mime='video/divx';      $imgmime='video.png'; $famime='file-video-o'; }
7230
	if (preg_match('/\.xvid$/i',$tmpfile))                     { $mime='video/xvid';      $imgmime='video.png'; $famime='file-video-o'; }
7231
	if (preg_match('/\.(wmv|mpg|mpeg)$/i',$tmpfile))           { $mime='video';           $imgmime='video.png'; $famime='file-video-o'; }
7232
	// Archive
7233
	if (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh)$/i',$tmpfile))   { $mime='archive'; $imgmime='archive.png'; $famime='file-archive-o'; }    // application/xxx where zzz is zip, ...
7234
	// Exe
7235
	if (preg_match('/\.(exe|com)$/i',$tmpfile))                { $mime='application/octet-stream'; $imgmime='other.png'; $famime='file-o'; }
7236
	// Lib
7237
	if (preg_match('/\.(dll|lib|o|so|a)$/i',$tmpfile))         { $mime='library'; $imgmime='library.png'; $famime='file-o'; }
7238
	// Err
7239
	if (preg_match('/\.err$/i',$tmpfile))                      { $mime='error'; $imgmime='error.png'; $famime='file-text-o'; }
7240
7241
	// Return string
7242
	if ($mode == 1)
7243
	{
7244
		$tmp=explode('/',$mime);
7245
		return (! empty($tmp[1])?$tmp[1]:$tmp[0]);
7246
	}
7247
	if ($mode == 2)
7248
	{
7249
		return $imgmime;
7250
	}
7251
	if ($mode == 3)
7252
	{
7253
		return $srclang;
7254
	}
7255
	if ($mode == 4)
7256
	{
7257
		return $famime;
7258
	}
7259
	return $mime;
7260
}
7261
7262
/**
7263
 * Return value from dictionary
7264
 *
7265
 * @param string	$tablename		name of dictionary
7266
 * @param string	$field			the value to return
7267
 * @param int		$id				id of line
7268
 * @param bool		$checkentity	add filter on entity
7269
 * @param string	$rowidfield		name of the column rowid
7270
 */
7271
function getDictvalue($tablename, $field, $id, $checkentity=false, $rowidfield='rowid')
7272
{
7273
	global $dictvalues,$db,$langs;
7274
7275
	if (!isset($dictvalues[$tablename]))
7276
	{
7277
		$dictvalues[$tablename] = array();
7278
		$sql = 'SELECT * FROM '.$tablename.' WHERE 1';
7279
		if ($checkentity) $sql.= ' entity IN (0,'.getEntity('').')';
7280
7281
		$resql = $db->query($sql);
7282
		if ($resql)
7283
		{
7284
			while ($obj = $db->fetch_object($resql))
7285
			{
7286
				$dictvalues[$tablename][$obj->{$rowidfield}] = $obj;
7287
			}
7288
		}
7289
		else
7290
		{
7291
			dol_print_error($db);
7292
		}
7293
	}
7294
7295
	if (!empty($dictvalues[$tablename][$id])) return $dictvalues[$tablename][$id]->{$field}; // Found
7296
	else // Not found
7297
	{
7298
		if ($id > 0) return $id;
7299
		return '';
7300
	}
7301
}
7302
7303
/**
7304
 *	Return true if the color is light
7305
 *
7306
 *  @param	string	$stringcolor		String with hex (FFFFFF) or comma RGB ('255,255,255')
7307
 *  @return	int							-1 : Error with argument passed |0 : color is dark | 1 : color is light
7308
 */
7309
function colorIsLight($stringcolor)
7310
{
7311
	$res = -1;
7312
	if (!empty($stringcolor))
7313
	{
7314
		$res = 0;
7315
		$tmp=explode(',', $stringcolor);
7316
		if (count($tmp) > 1)   // This is a comma RGB ('255','255','255')
7317
		{
7318
			$r = $tmp[0];
7319
			$g = $tmp[1];
7320
			$b = $tmp[2];
7321
		}
7322
		else
7323
		{
7324
			$hexr=$stringcolor[0].$stringcolor[1];
7325
			$hexg=$stringcolor[2].$stringcolor[3];
7326
			$hexb=$stringcolor[4].$stringcolor[5];
7327
			$r = hexdec($hexr);
7328
			$g = hexdec($hexg);
7329
			$b = hexdec($hexb);
7330
		}
7331
		$bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0;    // HSL algorithm
7332
		if ($bright > 0.6) $res = 1;
7333
	}
7334
	return $res;
7335
}
7336
7337
/**
7338
 * Function to test if an entry is enabled or not
7339
 *
7340
 * @param	string		$type_user					0=We test for internal user, 1=We test for external user
7341
 * @param	array		$menuentry					Array for feature entry to test
7342
 * @param	array		$listofmodulesforexternal	Array with list of modules allowed to external users
7343
 * @return	int										0=Hide, 1=Show, 2=Show gray
7344
 */
7345
function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
7346
{
7347
	global $conf;
7348
7349
	//print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
7350
	//print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
7351
	if (empty($menuentry['enabled'])) return 0;	// Entry disabled by condition
7352
	if ($type_user && $menuentry['module'])
7353
	{
7354
		$tmploops=explode('|',$menuentry['module']);
7355
		$found=0;
7356
		foreach($tmploops as $tmploop)
7357
		{
7358
			if (in_array($tmploop, $listofmodulesforexternal)) {
7359
				$found++; break;
7360
			}
7361
		}
7362
		if (! $found) return 0;	// Entry is for menus all excluded to external users
7363
	}
7364
	if (! $menuentry['perms'] && $type_user) return 0; 											// No permissions and user is external
7365
	if (! $menuentry['perms'] && ! empty($conf->global->MAIN_MENU_HIDE_UNAUTHORIZED))	return 0;	// No permissions and option to hide when not allowed, even for internal user, is on
7366
	if (! $menuentry['perms']) return 2;															// No permissions and user is external
7367
	return 1;
7368
}
7369