Completed
Branch develop (4f369b)
by
unknown
33:48
created

functions.lib.php ➔ ajax_autoselect()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 2
dl 0
loc 11
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
	// For backward compatibilty
125
	if ($element == 'actioncomm') $element='agenda';
126
	if ($element == 'fichinter')  $element='intervention';
127
	if ($element == 'categorie')  $element='category';
128
129
	if (is_object($mc))
130
	{
131
		return $mc->getEntity($element, $shared, $forceentity);
132
	}
133
	else
134
	{
135
		$out='';
136
		$addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values');
137
		if (in_array($element, $addzero)) $out.= '0,';
138
		$out.= $conf->entity;
139
		return $out;
140
	}
141
}
142
143
/**
144
 * Return information about user browser
145
 *
146
 * Returns array with the following format:
147
 * array(
148
 *  'browsername' => Browser name (firefox|chrome|iceweasel|epiphany|safari|opera|ie|unknown)
149
 *  'browserversion' => Browser version. Empty if unknown
150
 *  'browseros' => Set with mobile OS (android|blackberry|ios|palm|symbian|webos|maemo|windows|unknown)
151
 *  'layout' => (tablet|phone|classic)
152
 *  'phone' => empty if not mobile, (android|blackberry|ios|palm|unknown) if mobile
153
 *  'tablet' => true/false
154
 * )
155
 *
156
 * @param string $user_agent Content of $_SERVER["HTTP_USER_AGENT"] variable
157
 * @return	array Check function documentation
158
 */
159
function getBrowserInfo($user_agent)
160
{
161
	include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
162
163
	$name='unknown';
164
	$version='';
165
	$os='unknown';
166
	$phone = '';
167
168
	$detectmobile = new Mobile_Detect(null, $user_agent);
169
	$tablet = $detectmobile->isTablet();
170
171
	if ($detectmobile->isMobile()) {
172
173
		$phone = 'unknown';
174
175
		// If phone/smartphone, we set phone os name.
176
		if ($detectmobile->is('AndroidOS')) {
177
			$os = $phone = 'android';
178
		} elseif ($detectmobile->is('BlackBerryOS')) {
179
			$os = $phone = 'blackberry';
180
		} elseif ($detectmobile->is('iOS')) {
181
			$os = 'ios';
182
			$phone = 'iphone';
183
		} elseif ($detectmobile->is('PalmOS')) {
184
			$os = $phone = 'palm';
185
		} elseif ($detectmobile->is('SymbianOS')) {
186
			$os = 'symbian';
187
		} elseif ($detectmobile->is('webOS')) {
188
			$os = 'webos';
189
		} elseif ($detectmobile->is('MaemoOS')) {
190
			$os = 'maemo';
191
		} elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
192
			$os = 'windows';
193
		}
194
	}
195
196
	// OS
197
	if (preg_match('/linux/i', $user_agent))			{ $os='linux'; }
198
	elseif (preg_match('/macintosh/i', $user_agent))	{ $os='macintosh'; }
199
	elseif (preg_match('/windows/i', $user_agent))		{ $os='windows'; }
200
201
	// Name
202
	if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg))      { $name='firefox';   $version=$reg[2]; }
203
	elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg))     { $name='edge';      $version=$reg[2]; }
204
	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
205
	elseif (preg_match('/chrome/i', $user_agent, $reg))                   { $name='chrome'; }
206
	elseif (preg_match('/iceweasel/i', $user_agent))                      { $name='iceweasel'; }
207
	elseif (preg_match('/epiphany/i', $user_agent))                       { $name='epiphany';  }
208
	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.
209
	elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg))    { $name='opera';     $version=$reg[2]; }
210
	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
211
	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
212
	elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) { $name='lynxlinks'; $version=$reg[4]; }
213
214
	if ($tablet) {
215
		$layout = 'tablet';
216
	} elseif ($phone) {
217
		$layout = 'phone';
218
	} else {
219
		$layout = 'classic';
220
	}
221
222
	return array(
223
		'browsername' => $name,
224
		'browserversion' => $version,
225
		'browseros' => $os,
226
		'layout' => $layout,
227
		'phone' => $phone,
228
		'tablet' => $tablet
229
	);
230
}
231
232
/**
233
 *  Function called at end of web php process
234
 *
235
 *  @return	void
236
 */
237
function dol_shutdown()
238
{
239
	global $conf,$user,$langs,$db;
240
	$disconnectdone=false; $depth=0;
241
	if (is_object($db) && ! empty($db->connected)) { $depth=$db->transaction_opened; $disconnectdone=$db->close(); }
242
	dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth)?' (Warn: db disconnection forced, transaction depth was '.$depth.')':''), (($disconnectdone && $depth)?LOG_WARNING:LOG_INFO));
243
}
244
245
/**
246
 * Return true if we are in a context of submitting a parameter
247
 *
248
 * @param 	string	$paramname		Name or parameter to test
249
 * @return 	boolean					True if we have just submit a POST or GET request with the parameter provided (even if param is empty)
250
 */
251
function GETPOSTISSET($paramname)
252
{
253
	return (isset($_POST[$paramname]) || isset($_GET[$paramname]));
254
}
255
256
/**
257
 *  Return value of a param into GET or POST supervariable.
258
 *  Use the property $user->default_values[path]['creatform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder']
259
 *  Note: The property $user->default_values is loaded by main.php when loading the user.
260
 *
261
 *  @param	string	$paramname   Name of parameter to found
262
 *  @param	string	$check	     Type of check
263
 *                                  ''=no check (deprecated)
264
 *                                  'none'=no check (only for param that should have very rich content)
265
 *                                  'int'=check it's numeric (integer or float)
266
 *                                  'alpha'=check it's text and sign
267
 *                                  'aZ'=check it's a-z only
268
 *                                  'aZ09'=check it's simple alpha string (recommended for keys)
269
 *                                  'array'=check it's array
270
 *                                  'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string)
271
 *                                  'nohtml', 'alphanohtml'=check there is no html content
272
 *                                  'custom'= custom filter specify $filter and $options)
273
 *  @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)
274
 *  @param  int     $filter      Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails)
275
 *  @param  mixed   $options     Options to pass to filter_var when $check is set to 'custom'
276
 *  @param	string	$noreplace	 Force disable of replacement of __xxx__ strings.
277
 *  @return string|string[]      Value found (string or array), or '' if check fails
278
 */
279
function GETPOST($paramname, $check='none', $method=0, $filter=NULL, $options=NULL, $noreplace=0)
280
{
281
	global $mysoc,$user,$conf;
282
283
	if (empty($paramname)) return 'BadFirstParameterForGETPOST';
284
	if (empty($check))
285
	{
286
		dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
287
		// Enable this line to know who call the GETPOST with '' $check parameter.
288
		//var_dump(debug_backtrace()[0]);
289
	}
290
291
	if (empty($method)) $out = isset($_GET[$paramname])?$_GET[$paramname]:(isset($_POST[$paramname])?$_POST[$paramname]:'');
292
	elseif ($method==1) $out = isset($_GET[$paramname])?$_GET[$paramname]:'';
293
	elseif ($method==2) $out = isset($_POST[$paramname])?$_POST[$paramname]:'';
294
	elseif ($method==3) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:'');
295
	elseif ($method==4) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:(isset($_COOKIE[$paramname])?$_COOKIE[$paramname]:''));
296
	else return 'BadThirdParameterForGETPOST';
297
298
	if (empty($method) || $method == 3 || $method == 4)
299
	{
300
301
		$relativepathstring = $_SERVER["PHP_SELF"];
302
		// Clean $relativepathstring
303
		if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'),'/').'/', '', $relativepathstring);
304
		$relativepathstring = preg_replace('/^\//', '', $relativepathstring);
305
		$relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
306
		//var_dump($relativepathstring);
307
		//var_dump($user->default_values);
308
309
		// Code for search criteria persistence.
310
		// Retrieve values if restore_lastsearch_values is set and there is saved values
311
		if (! empty($_GET['restore_lastsearch_values']) && ! empty($_SESSION['lastsearch_values_'.$relativepathstring]))        // Keep $_GET here
312
		{
313
			$tmp=json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
314
			if (is_array($tmp))
315
			{
316
				foreach($tmp as $key => $val)
317
				{
318
					if ($key == $paramname)
319
					{
320
						$out=$val;
321
						break;
322
					}
323
				}
324
			}
325
		}
326
		// Else, retreive default values if we are not doing a sort
327
		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
328
		{
329
			if (! empty($_GET['action']) && $_GET['action'] == 'create' && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
330
			{
331
				// Search default value from $object->field
332
				global $object;
333
				if (is_object($object) && isset($object->fields[$paramname]['default']))
334
				{
335
					$out = $object->fields[$paramname]['default'];
336
				}
337
			}
338
			if (! empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES))
339
			{
340
				if (! empty($_GET['action']) && $_GET['action'] == 'create' && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
341
				{
342
					// Now search in setup to overwrite default values
343
					if (! empty($user->default_values))		// $user->default_values defined from menu 'Setup - Default values'
344
					{
345
						if (isset($user->default_values[$relativepathstring]['createform']))
346
						{
347
							foreach($user->default_values[$relativepathstring]['createform'] as $defkey => $defval)
348
							{
349
								$qualified = 0;
350
								if ($defkey != '_noquery_')
351
								{
352
									$tmpqueryarraytohave=explode('&', $defkey);
353
									$tmpqueryarraywehave=explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
354
									$foundintru=0;
355
									foreach($tmpqueryarraytohave as $tmpquerytohave)
356
									{
357
										if (! in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru=1;
358
									}
359
									if (! $foundintru) $qualified=1;
360
									//var_dump($defkey.'-'.$qualified);
361
								}
362
								else $qualified = 1;
363
364
								if ($qualified)
365
								{
366
									//var_dump($user->default_values[$relativepathstring][$defkey]['createform']);
367
									if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname]))
368
									{
369
										$out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
370
										break;
371
									}
372
								}
373
							}
374
						}
375
					}
376
				}
377
				// Management of default search_filters and sort order
378
				//elseif (preg_match('/list.php$/', $_SERVER["PHP_SELF"]) && ! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
379
				elseif (! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
380
				{
381
					if (! empty($user->default_values))		// $user->default_values defined from menu 'Setup - Default values'
382
					{
383
						//var_dump($user->default_values[$relativepathstring]);
384
						if ($paramname == 'sortfield' || $paramname == 'sortorder')			// Sorted on which fields ? ASC or DESC ?
385
						{
386
							if (isset($user->default_values[$relativepathstring]['sortorder']))	// Even if paramname is sortfield, data are stored into ['sortorder...']
387
							{
388
								foreach($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval)
389
								{
390
									$qualified = 0;
391
									if ($defkey != '_noquery_')
392
									{
393
										$tmpqueryarraytohave=explode('&', $defkey);
394
										$tmpqueryarraywehave=explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
395
										$foundintru=0;
396
										foreach($tmpqueryarraytohave as $tmpquerytohave)
397
										{
398
											if (! in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru=1;
399
										}
400
										if (! $foundintru) $qualified=1;
401
										//var_dump($defkey.'-'.$qualified);
402
									}
403
									else $qualified = 1;
404
405
									if ($qualified)
406
									{
407
										$forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
408
										foreach($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val)
409
										{
410
											if ($out) $out.=', ';
411
											if ($paramname == 'sortfield')
412
											{
413
												$out.=dol_string_nospecial($key, '', $forbidden_chars_to_replace);
414
											}
415
											if ($paramname == 'sortorder')
416
											{
417
												$out.=dol_string_nospecial($val, '', $forbidden_chars_to_replace);
418
											}
419
										}
420
										//break;	// No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
421
									}
422
								}
423
							}
424
						}
425
						elseif (isset($user->default_values[$relativepathstring]['filters']))
426
						{
427
							foreach($user->default_values[$relativepathstring]['filters'] as $defkey => $defval)
428
							{
429
								$qualified = 0;
430
								if ($defkey != '_noquery_')
431
								{
432
									$tmpqueryarraytohave=explode('&', $defkey);
433
									$tmpqueryarraywehave=explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
434
									$foundintru=0;
435
									foreach($tmpqueryarraytohave as $tmpquerytohave)
436
									{
437
										if (! in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru=1;
438
									}
439
									if (! $foundintru) $qualified=1;
440
									//var_dump($defkey.'-'.$qualified);
441
								}
442
								else $qualified = 1;
443
444
								if ($qualified)
445
								{
446
									if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all']))
447
									{
448
										// We made a search from quick search menu, do we still use default filter ?
449
										if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH))
450
										{
451
											$forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
452
											$out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
453
										}
454
									}
455
									else
456
									{
457
										$forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
458
										$out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
459
									}
460
									break;
461
								}
462
							}
463
						}
464
					}
465
				}
466
			}
467
		}
468
	}
469
470
	// Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable paramaters)
471
	// Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
472
	// 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.
473
	if (! is_array($out) && empty($_POST[$paramname]) && empty($noreplace))
474
	{
475
		$maxloop=20; $loopnb=0;    // Protection against infinite loop
476
		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.
477
		{
478
				$loopnb++; $newout = '';
479
480
				if ($reg[1] == 'DAY')                { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['mday']; }
481
				elseif ($reg[1] == 'MONTH')          { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['mon'];  }
482
				elseif ($reg[1] == 'YEAR')           { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['year']; }
483
				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']; }
484
				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']; }
485
				elseif ($reg[1] == 'PREVIOUS_YEAR')  { $tmp=dol_getdate(dol_now(), true); $newout = ($tmp['year'] - 1); }
486
				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']; }
487
				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']; }
488
				elseif ($reg[1] == 'NEXT_YEAR')      { $tmp=dol_getdate(dol_now(), true); $newout = ($tmp['year'] + 1); }
489
				elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID')
490
				{
491
					$newout = $mysoc->country_id;
492
				}
493
				elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID')
494
				{
495
					$newout = $user->id;
496
				}
497
				elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID')
498
				{
499
					$newout = $user->fk_user;
500
				}
501
				elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID')
502
				{
503
					$newout = $conf->entity;
504
				}
505
				else $newout = '';     // Key not found, we replace with empty string
506
				//var_dump('__'.$reg[1].'__ -> '.$newout);
507
				$out = preg_replace('/__'.preg_quote($reg[1],'/').'__/', $newout, $out);
508
		}
509
	}
510
511
	// Check is done after replacement
512
	switch ($check)
513
	{
514
		case 'none':
515
			break;
516
		case 'int':    // Check param is a numeric value (integer but also float or hexadecimal)
517
			if (! is_numeric($out)) { $out=''; }
518
			break;
519
		case 'intcomma':
520
			if (preg_match('/[^0-9,-]+/i',$out)) $out='';
521
			break;
522
		case 'alpha':
523
			if (! is_array($out))
524
			{
525
				$out=trim($out);
526
				// '"' is dangerous because param in url can close the href= or src= and add javascript functions.
527
				// '../' is dangerous because it allows dir transversals
528
				if (preg_match('/"/',$out)) $out='';
529
				else if (preg_match('/\.\.\//',$out)) $out='';
530
			}
531
			break;
532
		case 'san_alpha':
533
			$out=filter_var($out,FILTER_SANITIZE_STRING);
534
			break;
535
		case 'aZ':
536
			if (! is_array($out))
537
			{
538
				$out=trim($out);
539
				if (preg_match('/[^a-z]+/i',$out)) $out='';
540
			}
541
			break;
542
		case 'aZ09':
543
			if (! is_array($out))
544
			{
545
				$out=trim($out);
546
				if (preg_match('/[^a-z0-9_\-\.]+/i',$out)) $out='';
547
			}
548
			break;
549
		case 'array':
550
			if (! is_array($out) || empty($out)) $out=array();
551
			break;
552
		case 'nohtml':
553
			$out=dol_string_nohtmltag($out, 0);
554
			break;
555
		case 'alphanohtml':	// Recommended for search params
556
			if (! is_array($out))
557
			{
558
				$out=trim($out);
559
				// '"' is dangerous because param in url can close the href= or src= and add javascript functions.
560
				// '../' is dangerous because it allows dir transversals
561
				if (preg_match('/"/',$out)) $out='';
562
				else if (preg_match('/\.\.\//',$out)) $out='';
563
				$out=dol_string_nohtmltag($out);
564
			}
565
			break;
566
		case 'custom':
567
			if (empty($filter)) return 'BadFourthParameterForGETPOST';
568
			$out=filter_var($out, $filter, $options);
569
			break;
570
	}
571
572
	// Code for search criteria persistence.
573
	// Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
574
	if (empty($method) || $method == 3 || $method == 4)
575
	{
576
		//if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield", 'smonth', 'syear', 'month', 'year')))
577
		if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder','sortfield')))
578
		{
579
			//var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
580
581
			// We save search key only if:
582
			// - not empty, or
583
			// - 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).
584
585
			//if (! empty($out) || ! empty($user->default_values[$relativepathstring]['filters'][$paramname]))
586
			if (! empty($out))
587
			{
588
				$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...
589
			}
590
		}
591
	}
592
593
	return $out;
594
}
595
596
597
/**
598
 *  Return a prefix to use for this Dolibarr instance, for session/cookie names or email id.
599
 *  This prefix is valid in a web context only and is unique for instance and avoid conflict
600
 *  between multi-instances, even when having two instances with one root dir or two instances
601
 *  in virtual servers.
602
 *
603
 *  @param  string  $mode       			'' (prefix for session name) or 'email' (prefix for email id)
604
 *  @return	string      					A calculated prefix
605
 */
606
if (! function_exists('dol_getprefix'))
607
{
608
	function dol_getprefix($mode='')
609
	{
610
		global $conf;
611
612
		// If MAIL_PREFIX_FOR_EMAIL_ID is set and prefix is for email
613
		if ($mode == 'email' && ! empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID))
614
		{
615
			if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
616
			else if (isset($_SERVER["SERVER_NAME"])) return $_SERVER["SERVER_NAME"];
617
		}
618
619
		if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"]))
620
		{
621
			return dol_hash($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
622
			// Use this for a "readable" cookie name
623
			//return dol_sanitizeFileName($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
624
		}
625
		else return dol_hash(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
626
	}
627
}
628
629
/**
630
 *	Make an include_once using default root and alternate root if it fails.
631
 *  To link to a core file, use include(DOL_DOCUMENT_ROOT.'/pathtofile')
632
 *  To link to a module file from a module file, use include './mymodulefile';
633
 *  To link to a module file from a core file, then this function can be used (call by hook / trigger / speciales pages)
634
 *
635
 * 	@param	string	$relpath	Relative path to file (Ie: mydir/myfile, ../myfile, ...)
636
 * 	@param	string	$classname	Class name (deprecated)
637
 *  @return bool                True if load is a success, False if it fails
638
 */
639
function dol_include_once($relpath, $classname='')
640
{
641
	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']
642
643
	$fullpath = dol_buildpath($relpath);
644
645
	if (!file_exists($fullpath)) {
646
		dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_ERR);
647
		return false;
648
	}
649
650
	if (! empty($classname) && ! class_exists($classname)) {
651
		return include $fullpath;
652
	} else {
653
		return include_once $fullpath;
654
	}
655
}
656
657
658
/**
659
 *	Return path of url or filesystem. Return alternate root if exists.
660
 *
661
 * 	@param	string	$path						Relative path to file (if mode=0) or relative url (if mode=1). Ie: mydir/myfile, ../myfile
662
 *  @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)
663
 *  @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)
664
 *  											1:If $type==0 and if file was not found into alternate dir, return empty string
665
 *  											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
666
 *  @return string								Full filesystem path (if path=0), Full url path (if mode=1)
667
 */
668
function dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
669
{
670
	global $conf;
671
672
	$path=preg_replace('/^\//','',$path);
673
674
	if (empty($type))	// For a filesystem path
675
	{
676
		$res = DOL_DOCUMENT_ROOT.'/'.$path;		// Standard default path
677
		foreach ($conf->file->dol_document_root as $key => $dirroot)	// ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
678
		{
679
			if ($key == 'main') continue;
680
			if (file_exists($dirroot.'/'.$path))
681
			{
682
				$res=$dirroot.'/'.$path;
683
				return $res;
684
			}
685
		}
686
		if ($returnemptyifnotfound)								// Not found into alternate dir
687
		{
688
			if ($returnemptyifnotfound == 1 || ! file_exists($res)) return '';
689
		}
690
	}
691
	else				// For an url path
692
	{
693
		// We try to get local path of file on filesystem from url
694
		// Note that trying to know if a file on disk exist by forging path on disk from url
695
		// works only for some web server and some setup. This is bugged when
696
		// using proxy, rewriting, virtual path, etc...
697
		$res='';
698
		if ($type == 1) $res = DOL_URL_ROOT.'/'.$path;			// Standard value
699
		if ($type == 2) $res = DOL_MAIN_URL_ROOT.'/'.$path;		// Standard value
700
		if ($type == 3) $res = DOL_URL_ROOT.'/'.$path;
701
702
		foreach ($conf->file->dol_document_root as $key => $dirroot)	// ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
703
		{
704
			if ($key == 'main')
705
			{
706
				if ($type == 3)
707
				{
708
					global $dolibarr_main_url_root;
709
710
					// Define $urlwithroot
711
					$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
712
					$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
713
					//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
714
715
					$res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':$urlwithroot).'/'.$path;     // Test on start with http is for old conf syntax
716
				}
717
				continue;
718
			}
719
			preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i',$path,$regs);    // Take part before '?'
720
			if (! empty($regs[1]))
721
			{
722
				//print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
723
				if (file_exists($dirroot.'/'.$regs[1]))
724
				{
725
					if ($type == 1)
726
					{
727
						$res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
728
					}
729
					if ($type == 2)
730
					{
731
						$res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
732
					}
733
					if ($type == 3)
734
					{
735
						global $dolibarr_main_url_root;
736
737
						// Define $urlwithroot
738
						$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
739
						$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
740
						//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
741
742
						$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
743
					}
744
					break;
745
				}
746
			}
747
		}
748
	}
749
750
	return $res;
751
}
752
753
/**
754
 *	Create a clone of instance of object (new instance with same value for properties)
755
 *  With native = 0: Property that are reference are also new object (true clone). This means $this->db is not valid.
756
 *  With native = 1: Use PHP clone. Property that are reference are same pointer. This means $this->db is still valid.
757
 *
758
 * 	@param	object	$object		Object to clone
759
 *  @param	int		$native		Native method or true method
760
 *	@return object				Object clone
761
 *  @see https://php.net/manual/language.oop5.cloning.php
762
 */
763
function dol_clone($object, $native=0)
764
{
765
	//dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
766
767
	if (empty($native))
768
	{
769
		$myclone=unserialize(serialize($object));
770
	}
771
	else
772
	{
773
		$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)
774
	}
775
776
	return $myclone;
777
}
778
779
/**
780
 *	Optimize a size for some browsers (phone, smarphone, ...)
781
 *
782
 * 	@param	int		$size		Size we want
783
 * 	@param	string	$type		Type of optimizing:
784
 * 								'' = function used to define a size for truncation
785
 * 								'width' = function is used to define a width
786
 *	@return int					New size after optimizing
787
 */
788
function dol_size($size,$type='')
789
{
790
	global $conf;
791
	if (empty($conf->dol_optimize_smallscreen)) return $size;
792
	if ($type == 'width' && $size > 250) return 250;
793
	else return 10;
794
}
795
796
797
/**
798
 *	Clean a string to use it as a file name
799
 *
800
 *	@param	string	$str            String to clean
801
 * 	@param	string	$newstr			String to replace bad chars with
802
 *  @param	int	    $unaccent		1=Remove also accent (default), 0 do not remove them
803
 *	@return string          		String cleaned (a-zA-Z_)
804
 *
805
 * 	@see        	dol_string_nospecial, dol_string_unaccent, dol_sanitizePathName
806
 */
807
function dol_sanitizeFileName($str,$newstr='_',$unaccent=1)
808
{
809
	$filesystem_forbidden_chars = array('<','>','/','\\','?','*','|','"','°');
810
	return dol_string_nospecial($unaccent?dol_string_unaccent($str):$str, $newstr, $filesystem_forbidden_chars);
811
}
812
813
/**
814
 *	Clean a string to use it as a path name
815
 *
816
 *	@param	string	$str            String to clean
817
 * 	@param	string	$newstr			String to replace bad chars with
818
 *  @param	int	    $unaccent		1=Remove also accent (default), 0 do not remove them
819
 *	@return string          		String cleaned (a-zA-Z_)
820
 *
821
 * 	@see        	dol_string_nospecial, dol_string_unaccent, dol_sanitizeFileName
822
 */
823
function dol_sanitizePathName($str,$newstr='_',$unaccent=1)
824
{
825
	$filesystem_forbidden_chars = array('<','>','?','*','|','"','°');
826
	return dol_string_nospecial($unaccent?dol_string_unaccent($str):$str, $newstr, $filesystem_forbidden_chars);
827
}
828
829
/**
830
 *	Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName
831
 *
832
 *	@param	string	$str			String to clean
833
 *	@return string   	       		Cleaned string
834
 *
835
 * 	@see    		dol_sanitizeFilename, dol_string_nospecial
836
 */
837
function dol_string_unaccent($str)
838
{
839
	if (utf8_check($str))
840
	{
841
		// See http://www.utf8-chartable.de/
842
		$string = rawurlencode($str);
843
		$replacements = array(
844
		'%C3%80' => 'A','%C3%81' => 'A','%C3%82' => 'A','%C3%83' => 'A','%C3%84' => 'A','%C3%85' => 'A',
845
		'%C3%88' => 'E','%C3%89' => 'E','%C3%8A' => 'E','%C3%8B' => 'E',
846
		'%C3%8C' => 'I','%C3%8D' => 'I','%C3%8E' => 'I','%C3%8F' => 'I',
847
		'%C3%92' => 'O','%C3%93' => 'O','%C3%94' => 'O','%C3%95' => 'O','%C3%96' => 'O',
848
		'%C3%99' => 'U','%C3%9A' => 'U','%C3%9B' => 'U','%C3%9C' => 'U',
849
		'%C3%A0' => 'a','%C3%A1' => 'a','%C3%A2' => 'a','%C3%A3' => 'a','%C3%A4' => 'a','%C3%A5' => 'a',
850
		'%C3%A7' => 'c',
851
		'%C3%A8' => 'e','%C3%A9' => 'e','%C3%AA' => 'e','%C3%AB' => 'e',
852
		'%C3%AC' => 'i','%C3%AD' => 'i','%C3%AE' => 'i','%C3%AF' => 'i',
853
		'%C3%B1' => 'n',
854
		'%C3%B2' => 'o','%C3%B3' => 'o','%C3%B4' => 'o','%C3%B5' => 'o','%C3%B6' => 'o',
855
		'%C3%B9' => 'u','%C3%BA' => 'u','%C3%BB' => 'u','%C3%BC' => 'u',
856
		'%C3%BF' => 'y'
857
		);
858
		$string=strtr($string, $replacements);
859
		return rawurldecode($string);
860
	}
861
	else
862
	{
863
		// See http://www.ascii-code.com/
864
		$string = strtr(
865
			$str,
866
			"\xC0\xC1\xC2\xC3\xC4\xC5\xC7
867
			\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
868
			\xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
869
			\xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
870
			\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
871
			\xF9\xFA\xFB\xFC\xFD\xFF",
872
			"AAAAAAC
873
			EEEEIIIIDN
874
			OOOOOUUUY
875
			aaaaaaceeee
876
			iiiidnooooo
877
			uuuuyy"
878
		);
879
		$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"));
880
		return $string;
881
	}
882
}
883
884
/**
885
 *	Clean a string from all punctuation characters to use it as a ref or login.
886
 *  This is a more complete function than dol_sanitizeFileName.
887
 *
888
 *	@param	string	$str            	String to clean
889
 * 	@param	string	$newstr				String to replace forbidden chars with
890
 *  @param  array	$badcharstoreplace  List of forbidden characters
891
 * 	@return string          			Cleaned string
892
 *
893
 * 	@see    		dol_sanitizeFilename, dol_string_unaccent
894
 */
895
function dol_string_nospecial($str,$newstr='_',$badcharstoreplace='')
896
{
897
	$forbidden_chars_to_replace=array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°');  // more complete than dol_sanitizeFileName
898
	$forbidden_chars_to_remove=array();
899
	if (is_array($badcharstoreplace)) $forbidden_chars_to_replace=$badcharstoreplace;
900
	//$forbidden_chars_to_remove=array("(",")");
901
902
	return str_replace($forbidden_chars_to_replace,$newstr,str_replace($forbidden_chars_to_remove,"",$str));
903
}
904
905
906
/**
907
 * Encode string for xml usage
908
 *
909
 * @param 	string	$string		String to encode
910
 * @return	string				String encoded
911
 */
912
function dolEscapeXML($string)
913
{
914
	return strtr($string, array('\''=>'&apos;','"'=>'&quot;','&'=>'&amp;','<'=>'&lt;','>'=>'&gt;'));
915
}
916
917
/**
918
 *  Returns text escaped for inclusion into javascript code
919
 *
920
 *  @param      string		$stringtoescape		String to escape
921
 *  @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 \
922
 *  @param		int		$noescapebackslashn	0=Escape also \n. 1=Do not escape \n.
923
 *  @return     string     		 				Escaped string. Both ' and " are escaped into ' if they are escaped.
924
 */
925
function dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
926
{
927
	// escape quotes and backslashes, newlines, etc.
928
	$substitjs=array("&#039;"=>"\\'","\r"=>'\\r');
929
	//$substitjs['</']='<\/';	// We removed this. Should be useless.
930
	if (empty($noescapebackslashn)) { $substitjs["\n"]='\\n'; $substitjs['\\']='\\\\'; }
931
	if (empty($mode)) { $substitjs["'"]="\\'"; $substitjs['"']="\\'"; }
932
	else if ($mode == 1) $substitjs["'"]="\\'";
933
	else if ($mode == 2) { $substitjs['"']='\\"'; }
934
	else if ($mode == 3) { $substitjs["'"]="\\'"; $substitjs['"']="\\\""; }
935
	return strtr($stringtoescape, $substitjs);
936
}
937
938
939
/**
940
 *  Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
941
 *
942
 *  @param      string		$stringtoescape		String to escape
943
 *  @param		int			$keepb				1=Preserve b tags (otherwise, remove them)
944
 *  @param      int         $keepn              1=Preserve \r\n strings (otherwise, replace them with escaped value)
945
 *  @return     string     				 		Escaped string
946
 *  @see		dol_string_nohtmltag
947
 */
948
function dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0)
949
{
950
	// escape quotes and backslashes, newlines, etc.
951
	$tmp=html_entity_decode($stringtoescape, ENT_COMPAT, 'UTF-8');		// TODO Use htmlspecialchars_decode instead, that make only required change for html tags
952
	if (! $keepb) $tmp=strtr($tmp, array("<b>"=>'','</b>'=>''));
953
	if (! $keepn) $tmp=strtr($tmp, array("\r"=>'\\r',"\n"=>'\\n'));
954
	return htmlentities($tmp, ENT_COMPAT, 'UTF-8');						// TODO Use htmlspecialchars instead, that make only required change for html tags
955
}
956
957
958
/**
959
 * Convert a string to lower. Never use strtolower because it does not works with UTF8 strings.
960
 *
961
 * @param 	string		$utf8_string		String to encode
962
 * @return 	string							String converted
963
 */
964
function dol_strtolower($utf8_string)
965
{
966
	return mb_strtolower($utf8_string, "UTF-8");
967
}
968
969
/**
970
 * Convert a string to upper. Never use strtolower because it does not works with UTF8 strings.
971
 *
972
 * @param 	string		$utf8_string		String to encode
973
 * @return 	string							String converted
974
 */
975
function dol_strtoupper($utf8_string)
976
{
977
	return mb_strtoupper($utf8_string, "UTF-8");
978
}
979
980
981
/**
982
 *	Write log message into outputs. Possible outputs can be:
983
 *	SYSLOG_HANDLERS = ["mod_syslog_file"]  		file name is then defined by SYSLOG_FILE
984
 *	SYSLOG_HANDLERS = ["mod_syslog_syslog"]  	facility is then defined by SYSLOG_FACILITY
985
 *  Warning, syslog functions are bugged on Windows, generating memory protection faults. To solve
986
 *  this, use logging to files instead of syslog (see setup of module).
987
 *  Note: If constant 'SYSLOG_FILE_NO_ERROR' defined, we never output any error message when writing to log fails.
988
 *  Note: You can get log message into html sources by adding parameter &logtohtml=1 (constant MAIN_LOGTOHTML must be set)
989
 *  This function works only if syslog module is enabled.
990
 * 	This must not use any call to other function calling dol_syslog (avoid infinite loop).
991
 *
992
 * 	@param  string		$message				Line to log. ''=Show nothing
993
 *  @param  int			$level					Log level
994
 *												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
995
 *												On Linux   LOG_ERR=3, LOG_WARNING=4, LOG_INFO=6, LOG_DEBUG=7
996
 *  @param	int			$ident					1=Increase ident of 1, -1=Decrease ident of 1
997
 *  @param	string		$suffixinfilename		When output is a file, append this suffix into default log filename.
998
 *  @param	string		$restricttologhandler	Output log only for this log handler
999
 *  @return	void
1000
 */
1001
function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename='', $restricttologhandler='')
1002
{
1003
	global $conf, $user;
1004
1005
	// If syslog module enabled
1006
	if (empty($conf->syslog->enabled)) return;
1007
1008
	if (! empty($message))
1009
	{
1010
		// Test log level
1011
		$logLevels = array(LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG);
1012
		if (!in_array($level, $logLevels, true))
1013
		{
1014
			throw new Exception('Incorrect log level');
1015
		}
1016
		if ($level > $conf->global->SYSLOG_LEVEL) return;
1017
1018
		// If adding log inside HTML page is required
1019
		if (! empty($_REQUEST['logtohtml']) && (! empty($conf->global->MAIN_ENABLE_LOG_TO_HTML) || ! empty($conf->global->MAIN_LOGTOHTML)))   // MAIN_LOGTOHTML kept for backward compatibility
1020
		{
1021
			$conf->logbuffer[] = dol_print_date(time(),"%Y-%m-%d %H:%M:%S")." ".$message;
1022
		}
1023
1024
		//TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1025
		// If enable html log tag enabled and url parameter log defined, we show output log on HTML comments
1026
		if (! empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && ! empty($_GET["log"]))
1027
		{
1028
			print "\n\n<!-- Log start\n";
1029
			print $message."\n";
1030
			print "Log end -->\n";
1031
		}
1032
1033
		$data = array(
1034
			'message' => $message,
1035
			'script' => (isset($_SERVER['PHP_SELF'])? basename($_SERVER['PHP_SELF'],'.php') : false),
1036
			'level' => $level,
1037
			'user' => ((is_object($user) && $user->id) ? $user->login : false),
1038
			'ip' => false
1039
		);
1040
1041
		// This is when server run behind a reverse proxy
1042
		if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].(empty($_SERVER["REMOTE_ADDR"])?'':'->'.$_SERVER['REMOTE_ADDR']);
1043
		// This is when server run normally on a server
1044
		else if (! empty($_SERVER["REMOTE_ADDR"])) $data['ip'] = $_SERVER['REMOTE_ADDR'];
1045
		// This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1046
		else if (! empty($_SERVER['SERVER_ADDR'])) $data['ip'] = $_SERVER['SERVER_ADDR'];
1047
		// 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).
1048
		else if (! empty($_SERVER['COMPUTERNAME'])) $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME'])?'':'@'.$_SERVER['USERNAME']);
1049
		// 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).
1050
		else if (! empty($_SERVER['LOGNAME'])) $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1051
		// Loop on each log handler and send output
1052
		foreach ($conf->loghandlers as $loghandlerinstance)
1053
		{
1054
			if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) continue;
1055
			$loghandlerinstance->export($data,$suffixinfilename);
1056
		}
1057
		unset($data);
1058
	}
1059
1060
	if (! empty($ident))
1061
	{
1062
		foreach ($conf->loghandlers as $loghandlerinstance)
1063
		{
1064
			$loghandlerinstance->setIdent($ident);
1065
		}
1066
	}
1067
}
1068
1069
1070
/**
1071
 *	Show tab header of a card
1072
 *
1073
 *	@param	array	$links				Array of tabs. Currently initialized by calling a function xxx_admin_prepare_head
1074
 *	@param	string	$active     		Active tab name (document', 'info', 'ldap', ....)
1075
 *	@param  string	$title      		Title
1076
 *	@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.
1077
 * 	@param	string	$picto				Add a picto on tab title
1078
 *	@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.
1079
 *  @param	string	$morehtmlright		Add more html content on right of tabs title
1080
 * 	@return	void
1081
 */
1082
function dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='')
1083
{
1084
	print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright);
1085
}
1086
1087
/**
1088
 *  Show tab header of a card
1089
 *
1090
 *	@param	array	$links				Array of tabs
1091
 *	@param	string	$active     		Active tab name
1092
 *	@param  string	$title      		Title
1093
 *	@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.
1094
 * 	@param	string	$picto				Add a picto on tab title
1095
 *	@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.
1096
 *  @param	string	$morehtmlright		Add more html content on right of tabs title
1097
 * 	@return	string
1098
 */
1099
function dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='')
1100
{
1101
	global $conf, $langs, $hookmanager;
1102
1103
	$out="\n".'<div class="tabs" data-role="controlgroup" data-type="horizontal">'."\n";
1104
1105
	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.
1106
1107
	// Show title
1108
	$showtitle=1;
1109
	if (! empty($conf->dol_optimize_smallscreen)) $showtitle=0;
1110
	if (! empty($title) && $showtitle)
1111
	{
1112
		$limittitle=30;
1113
		$out.='<a class="tabTitle">';
1114
		if ($picto) $out.=img_picto($title,($pictoisfullpath?'':'object_').$picto,'',$pictoisfullpath).' ';
1115
		$out.='<span class="tabTitleText">'.dol_trunc($title,$limittitle).'</span>';
1116
		$out.='</a>';
1117
	}
1118
1119
	// Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
1120
	$maxkey=-1;
1121
	if (is_array($links) && ! empty($links))
1122
	{
1123
		$keys=array_keys($links);
1124
		if (count($keys)) $maxkey=max($keys);
1125
	}
1126
1127
	if (! empty($conf->dol_optimize_smallscreen)) $conf->global->MAIN_MAXTABS_IN_CARD=2;
1128
1129
	// Show tabs
1130
	$bactive=false;
1131
	// if =0 we don't use the feature
1132
	$limittoshow=(empty($conf->global->MAIN_MAXTABS_IN_CARD)?99:$conf->global->MAIN_MAXTABS_IN_CARD);
1133
	$displaytab=0;
1134
	$nbintab=0;
1135
	$popuptab=0; $outmore='';
1136
	for ($i = 0 ; $i <= $maxkey ; $i++)
1137
	{
1138
		if ((is_numeric($active) && $i == $active) || (! empty($links[$i][2]) && ! is_numeric($active) && $active == $links[$i][2]))
1139
		{
1140
			// If active tab is already present
1141
			if ($i >= $limittoshow) $limittoshow--;
1142
		}
1143
	}
1144
1145
	for ($i = 0 ; $i <= $maxkey ; $i++)
1146
	{
1147
		if ((is_numeric($active) && $i == $active) || (! empty($links[$i][2]) && ! is_numeric($active) && $active == $links[$i][2]))
1148
		{
1149
			$isactive=true;
1150
			$bactive=true;
1151
		}
1152
		else
1153
		{
1154
			$isactive=false;
1155
		}
1156
1157
		if ($i < $limittoshow || $isactive)
1158
		{
1159
			$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]).' -->';
1160
			if (isset($links[$i][2]) && $links[$i][2] == 'image')
1161
			{
1162
				if (!empty($links[$i][0]))
1163
				{
1164
					$out.='<a class="tabimage" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1165
				}
1166
				else
1167
				{
1168
					$out.='<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1169
				}
1170
			}
1171
			else if (! empty($links[$i][1]))
1172
			{
1173
				//print "x $i $active ".$links[$i][2]." z";
1174
				if ($isactive)
1175
				{
1176
					$out.='<a'.(! empty($links[$i][2])?' id="'.$links[$i][2].'"':'').' class="tabactive tab inline-block" href="'.$links[$i][0].'">';
1177
					$out.=$links[$i][1];
1178
					$out.='</a>'."\n";
1179
				}
1180
				else
1181
				{
1182
					$out.='<a'.(! empty($links[$i][2])?' id="'.$links[$i][2].'"':'').' class="tabunactive tab inline-block" href="'.$links[$i][0].'">';
1183
					$out.=$links[$i][1];
1184
					$out.='</a>'."\n";
1185
				}
1186
			}
1187
			$out.='</div>';
1188
		}
1189
		else
1190
		{
1191
			// The popup with the other tabs
1192
			if (! $popuptab)
1193
			{
1194
				$popuptab=1;
1195
				$outmore.='<div class="popuptabset wordwrap">';	// The css used to hide/show popup
1196
			}
1197
			$outmore.='<div class="popuptab wordwrap" style="display:inherit;">';
1198
			if (isset($links[$i][2]) && $links[$i][2] == 'image')
1199
			{
1200
				if (!empty($links[$i][0]))
1201
					$outmore.='<a class="tabimage" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1202
				else
1203
					$outmore.='<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1204
1205
			}
1206
			else if (! empty($links[$i][1]))
1207
			{
1208
				$outmore.='<a'.(! empty($links[$i][2])?' id="'.$links[$i][2].'"':'').' class="wordwrap inline-block" href="'.$links[$i][0].'">';
1209
				$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.
1210
				$outmore.='</a>'."\n";
1211
			}
1212
			$outmore.='</div>';
1213
1214
			$nbintab++;
1215
		}
1216
		$displaytab=$i;
1217
	}
1218
	if ($popuptab) $outmore.='</div>';
1219
1220
	if ($displaytab > $limittoshow)
1221
	{
1222
		$left=($langs->trans("DIRECTION") == 'rtl'?'right':'left');
1223
		$right=($langs->trans("DIRECTION") == 'rtl'?'left':'right');
1224
1225
		$tabsname=str_replace("@", "", $picto);
1226
		$out.='<div id="moretabs'.$tabsname.'" class="inline-block tabsElem">';
1227
		$out.='<a href="#" class="tab moretab inline-block tabunactive">'.$langs->trans("More").'... ('.$nbintab.')</a>';
1228
		$out.='<div id="moretabsList'.$tabsname.'" style="position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px">';
1229
		$out.=$outmore;
1230
		$out.='</div>';
1231
		$out.='<div></div>';
1232
		$out.="</div>\n";
1233
1234
		$out.="<script>";
1235
		$out.="$('#moretabs".$tabsname."').mouseenter( function() { console.log('mouseenter ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','auto');});";
1236
		$out.="$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
1237
		$out.="</script>";
1238
	}
1239
1240
	$out.="</div>\n";
1241
1242
	if (! $notab || $notab == -1) $out.="\n".'<div class="tabBar'.($notab == -1 ? '' : ' tabBarWithBottom').'">'."\n";
1243
1244
	$parameters=array('tabname' => $active, 'out' => $out);
1245
	$reshook=$hookmanager->executeHooks('printTabsHead',$parameters);	// This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
1246
	if ($reshook > 0)
1247
	{
1248
		$out = $hookmanager->resPrint;
1249
	}
1250
1251
	return $out;
1252
}
1253
1254
/**
1255
 *  Show tab footer of a card
1256
 *
1257
 *  @param	int		$notab       -1 or 0=Add tab footer, 1=no tab footer
1258
 *  @return	void
1259
 */
1260
function dol_fiche_end($notab=0)
1261
{
1262
	print dol_get_fiche_end($notab);
1263
}
1264
1265
/**
1266
 *	Return tab footer of a card
1267
 *
1268
 *	@param  int		$notab		-1 or 0=Add tab footer, 1=no tab footer
1269
 *  @return	string
1270
 */
1271
function dol_get_fiche_end($notab=0)
1272
{
1273
	if (! $notab || $notab == -1) return "\n</div>\n";
1274
	else return '';
1275
}
1276
1277
/**
1278
 *  Show tab footer of a card.
1279
 *  Note: $object->next_prev_filter can be set to restrict select to find next or previous record by $form->showrefnav.
1280
 *
1281
 *  @param	Object	$object			Object to show
1282
 *  @param	string	$paramid   		Name of parameter to use to name the id into the URL next/previous link
1283
 *  @param	string	$morehtml  		More html content to output just before the nav bar
1284
 *  @param	int		$shownav	  	Show Condition (navigation is shown if value is 1)
1285
 *  @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.
1286
 *  @param	string	$fieldref   	Nom du champ objet ref (object->ref) a utiliser pour select next et previous
1287
 *  @param	string	$morehtmlref  	More html to show after ref
1288
 *  @param	string	$moreparam  	More param to add in nav link url.
1289
 *	@param	int		$nodbprefix		Do not include DB prefix to forge table name
1290
 *	@param	string	$morehtmlleft	More html code to show before ref
1291
 *	@param	string	$morehtmlstatus	More html code to show under navigation arrows
1292
 *  @param  int     $onlybanner     Put this to 1, if the card will contains only a banner (this add css 'arearefnobottom' on div)
1293
 *	@param	string	$morehtmlright	More html code to show before navigation arrows
1294
 *  @return	void
1295
 */
1296
function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $onlybanner=0, $morehtmlright='')
1297
{
1298
	global $conf, $form, $user, $langs;
1299
1300
	$error = 0;
1301
1302
	$maxvisiblephotos=1;
1303
	$showimage=1;
1304
	$showbarcode=empty($conf->barcode->enabled)?0:($object->barcode?1:0);
1305
	if (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) $showbarcode=0;
1306
	$modulepart='unknown';
1307
1308
	if ($object->element == 'societe')         $modulepart='societe';
1309
	if ($object->element == 'contact')         $modulepart='contact';
1310
	if ($object->element == 'member')          $modulepart='memberphoto';
1311
	if ($object->element == 'user')            $modulepart='userphoto';
1312
	if ($object->element == 'product')         $modulepart='product';
1313
1314
	if (class_exists("Imagick"))
1315
	{
1316
		if ($object->element == 'propal')            $modulepart='propal';
1317
		if ($object->element == 'commande')          $modulepart='commande';
1318
		if ($object->element == 'facture')           $modulepart='facture';
1319
		if ($object->element == 'fichinter')         $modulepart='ficheinter';
1320
		if ($object->element == 'contrat')           $modulepart='contract';
1321
		if ($object->element == 'supplier_proposal') $modulepart='supplier_proposal';
1322
		if ($object->element == 'order_supplier')    $modulepart='supplier_order';
1323
		if ($object->element == 'invoice_supplier')  $modulepart='supplier_invoice';
1324
		if ($object->element == 'expensereport')     $modulepart='expensereport';
1325
	}
1326
1327
	if ($object->element == 'product')
1328
	{
1329
		$width=80; $cssclass='photoref';
1330
		$showimage=$object->is_photo_available($conf->product->multidir_output[$object->entity]);
1331
		$maxvisiblephotos=(isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO)?$conf->global->PRODUCT_MAX_VISIBLE_PHOTO:5);
1332
		if ($conf->browser->phone) $maxvisiblephotos=1;
1333
		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>';
1334
		else
1335
		{
1336
			if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
1337
				$nophoto='';
1338
				$morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"></div>';
1339
			}
1340
			//elseif ($conf->browser->layout != 'phone') {    // Show no photo link
1341
				$nophoto='/public/theme/common/nophoto.png';
1342
				$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>';
1343
			//}
1344
		}
1345
	}
1346
	else
1347
	{
1348
		if ($showimage)
1349
		{
1350
			if ($modulepart != 'unknown')
1351
			{
1352
				$phototoshow='';
1353
				// Check if a preview file is available
1354
				if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick"))
1355
				{
1356
					$objectref = dol_sanitizeFileName($object->ref);
1357
					$dir_output = $conf->$modulepart->dir_output . "/";
1358
					if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice')))
1359
					{
1360
						$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
1361
					}
1362
					else
1363
					{
1364
						$subdir = get_exdir($object->id, 0, 0, 0, $object, $modulepart);
1365
					}
1366
1367
					$filepath = $dir_output . $subdir . "/";
1368
					$file = $filepath . $objectref . ".pdf";
1369
					$relativepath = $subdir.'/'.$objectref.'.pdf';
1370
1371
					// Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
1372
					$fileimage = $file.'_preview.png';              // If PDF has 1 page
1373
					$fileimagebis = $file.'_preview-0.png';         // If PDF has more than one page
1374
					$relativepathimage = $relativepath.'_preview.png';
1375
1376
					// Si fichier PDF existe
1377
					if (file_exists($file))
1378
					{
1379
						$encfile = urlencode($file);
1380
						// Conversion du PDF en image png si fichier png non existant
1381
						if ( (! file_exists($fileimage) || (filemtime($fileimage) < filemtime($file)))
1382
						  && (! file_exists($fileimagebis) || (filemtime($fileimagebis) < filemtime($file)))
1383
						   )
1384
						{
1385
							if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS))		// If you experienc trouble with pdf thumb generation and imagick, you can disable here.
1386
							{
1387
								$ret = dol_convert_file($file, 'png', $fileimage);
1388
								if ($ret < 0) $error++;
1389
							}
1390
						}
1391
1392
						$heightforphotref=70;
1393
						if (! empty($conf->dol_optimize_smallscreen)) $heightforphotref=60;
1394
						// Si fichier png PDF d'1 page trouve
1395
						if (file_exists($fileimage))
1396
						{
1397
							$phototoshow = '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
1398
							$phototoshow.= '<img height="'.$heightforphotref.'" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT . '/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
1399
							$phototoshow.= '</div></div>';
1400
						}
1401
						// Si fichier png PDF de plus d'1 page trouve
1402
						elseif (file_exists($fileimagebis))
1403
						{
1404
							$preview = preg_replace('/\.png/','',$relativepathimage) . "-0.png";
1405
							$phototoshow = '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
1406
							$phototoshow.= '<img height="'.$heightforphotref.'" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT . '/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($preview).'"><p>';
1407
							$phototoshow.= '</div></div>';
1408
						}
1409
					}
1410
				}
1411
				else if (! $phototoshow)
1412
				{
1413
					$phototoshow = $form->showphoto($modulepart,$object,0,0,0,'photoref','small',1,0,$maxvisiblephotos);
1414
				}
1415
1416
				if ($phototoshow)
1417
				{
1418
					$morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">';
1419
					$morehtmlleft.=$phototoshow;
1420
					$morehtmlleft.='</div>';
1421
				}
1422
			}
1423
1424
			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...
1425
			{
1426
				$morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">';
1427
				if ($object->element == 'action')
1428
				{
1429
					$width=80;
1430
					$cssclass='photorefcenter';
1431
					$nophoto=img_picto('', 'title_agenda', '', false, 1);
1432
				}
1433
				else
1434
				{
1435
					$width=14; $cssclass='photorefcenter';
1436
					$picto = $object->picto;
1437
					if ($object->element == 'project' && ! $object->public) $picto = 'project'; // instead of projectpub
1438
					$nophoto=img_picto('', 'object_'.$picto, '', false, 1);
1439
				}
1440
				$morehtmlleft.='<!-- No photo to show -->';
1441
				$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>';
1442
1443
				$morehtmlleft.='</div>';
1444
			}
1445
		}
1446
	}
1447
1448
	if ($showbarcode) $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object).'</div>';
1449
1450
	if ($object->element == 'societe')
1451
	{
1452
		if (! empty($conf->use_javascript_ajax) && $user->rights->societe->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE))
1453
		{
1454
		   	$morehtmlstatus.=ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
1455
		}
1456
		else {
1457
			$morehtmlstatus.=$object->getLibStatut(6);
1458
		}
1459
	}
1460
	elseif ($object->element == 'product')
1461
	{
1462
		//$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
1463
		if (! empty($conf->use_javascript_ajax) && $user->rights->produit->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1464
			$morehtmlstatus.=ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
1465
		} else {
1466
			$morehtmlstatus.='<span class="statusrefsell">'.$object->getLibStatut(5,0).'</span>';
1467
		}
1468
		$morehtmlstatus.=' &nbsp; ';
1469
		//$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
1470
		if (! empty($conf->use_javascript_ajax) && $user->rights->produit->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1471
			$morehtmlstatus.=ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
1472
		} else {
1473
			$morehtmlstatus.='<span class="statusrefbuy">'.$object->getLibStatut(5,1).'</span>';
1474
		}
1475
	}
1476
	elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan')))
1477
	{
1478
		$tmptxt=$object->getLibStatut(6, $object->totalpaye);
1479
		if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3) || $conf->browser->layout=='phone') $tmptxt=$object->getLibStatut(5, $object->totalpaye);
1480
		$morehtmlstatus.=$tmptxt;
1481
	}
1482
	elseif ($object->element == 'contrat' || $object->element == 'contract')
1483
	{
1484
		if ($object->statut == 0) $morehtmlstatus.=$object->getLibStatut(5);
1485
		else $morehtmlstatus.=$object->getLibStatut(4);
1486
	}
1487
	elseif ($object->element == 'facturerec')
1488
	{
1489
		if ($object->frequency == 0) $morehtmlstatus.=$object->getLibStatut(2);
1490
		else $morehtmlstatus.=$object->getLibStatut(5);
1491
	}
1492
	else { // Generic case
1493
		$tmptxt=$object->getLibStatut(6);
1494
		if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3) || $conf->browser->layout=='phone') $tmptxt=$object->getLibStatut(5);
1495
		$morehtmlstatus.=$tmptxt;
1496
	}
1497
1498
	// Add if object was dispatched "into accountancy"
1499
	if (! empty($conf->accounting->enabled) && in_array($object->element, array('bank', 'facture', 'invoice', 'invoice_supplier', 'expensereport')))
1500
	{
1501
		if (method_exists($object, 'getVentilExportCompta'))
1502
		{
1503
			$accounted = $object->getVentilExportCompta();
1504
			$langs->load("accountancy");
1505
			$morehtmlstatus.='</div><div class="statusref statusrefbis">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted"));
1506
		}
1507
	}
1508
1509
	// Add alias for thirdparty
1510
	if (! empty($object->name_alias)) $morehtmlref.='<div class="refidno">'.$object->name_alias.'</div>';
1511
1512
	// Add label
1513
	if ($object->element == 'product' || $object->element == 'bank_account' || $object->element == 'project_task')
1514
	{
1515
		if (! empty($object->label)) $morehtmlref.='<div class="refidno">'.$object->label.'</div>';
1516
	}
1517
1518
	if (method_exists($object, 'getBannerAddress') && $object->element != 'product' && $object->element != 'bookmark' && $object->element != 'ecm_directories' && $object->element != 'ecm_files')
1519
	{
1520
		$morehtmlref.='<div class="refidno">';
1521
		$morehtmlref.=$object->getBannerAddress('refaddress',$object);
1522
		$morehtmlref.='</div>';
1523
	}
1524
	if (! empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && in_array($object->element, array('societe', 'contact', 'member', 'product')))
1525
	{
1526
		$morehtmlref.='<div style="clear: both;"></div><div class="refidno">';
1527
		$morehtmlref.=$langs->trans("TechnicalID").': '.$object->id;
1528
		$morehtmlref.='</div>';
1529
	}
1530
1531
	print '<div class="'.($onlybanner?'arearefnobottom ':'arearef ').'heightref valignmiddle" width="100%">';
1532
	print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
1533
	print '</div>';
1534
	print '<div class="underrefbanner clearboth"></div>';
1535
}
1536
1537
/**
1538
 * Show a string with the label tag dedicated to the HTML edit field.
1539
 *
1540
 * @param	string	$langkey		Translation key
1541
 * @param 	string	$fieldkey		Key of the html select field the text refers to
1542
 * @param	int		$fieldrequired	1=Field is mandatory
1543
 * @deprecated Form::editfieldkey
1544
 */
1545
function fieldLabel($langkey, $fieldkey, $fieldrequired=0)
1546
{
1547
	global $conf, $langs;
1548
	$ret='';
1549
	if ($fieldrequired) $ret.='<span class="fieldrequired">';
1550
	if (($conf->dol_use_jmobile != 4)) $ret.='<label for="'.$fieldkey.'">';
1551
	$ret.=$langs->trans($langkey);
1552
	if (($conf->dol_use_jmobile != 4)) $ret.='</label>';
1553
	if ($fieldrequired) $ret.='</span>';
1554
	return $ret;
1555
}
1556
1557
/**
1558
 * Return string to add class property on html element with pair/impair.
1559
 *
1560
 * @param	string	$var			0 or 1
1561
 * @param	string	$moreclass		More class to add
1562
 * @return	string					String to add class onto HTML element
1563
 */
1564
function dol_bc($var,$moreclass='')
1565
{
1566
	global $bc;
1567
	$ret=' '.$bc[$var];
1568
	if ($moreclass) $ret=preg_replace('/class=\"/','class="'.$moreclass.' ',$ret);
1569
	return $ret;
1570
}
1571
1572
/**
1573
 *      Return a formated address (part address/zip/town/state) according to country rules
1574
 *
1575
 *      @param  Object		$object			A company or contact object
1576
 * 	    @param	int			$withcountry		1=Add country into address string
1577
 *      @param	string		$sep				Separator to use to build string
1578
 *      @param	Translate	$outputlangs		Object lang that contains language for text translation.
1579
 *      @param	int		$mode		0=Standard output, 1=Remove address
1580
 *      @return string						Formated string
1581
 *      @see dol_print_address
1582
 */
1583
function dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs='', $mode=0)
1584
{
1585
	global $conf,$langs;
1586
1587
	$ret='';
1588
	$countriesusingstate=array('AU','CA','US','IN','GB','ES','UK','TR');    // See also MAIN_FORCE_STATE_INTO_ADDRESS
1589
1590
	// Address
1591
	if (empty($mode)) {
1592
		$ret .= $object->address;
1593
	}
1594
	// Zip/Town/State
1595
	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
1596
	{
1597
		$ret .= ($ret ? $sep : '' ).$object->town;
1598
		if ($object->state)
1599
		{
1600
			$ret.=($ret?", ":'').$object->state;
1601
		}
1602
		if ($object->zip) $ret .= ($ret?", ":'').$object->zip;
1603
	}
1604
	else if (in_array($object->country_code,array('GB','UK'))) // UK: title firstname name \n address lines \n town state \n zip \n country
1605
	{
1606
		$ret .= ($ret ? $sep : '' ).$object->town;
1607
		if ($object->state)
1608
		{
1609
			$ret.=($ret?", ":'').$object->state;
1610
		}
1611
		if ($object->zip) $ret .= ($ret ? $sep : '' ).$object->zip;
1612
	}
1613
	else if (in_array($object->country_code,array('ES','TR'))) // ES: title firstname name \n address lines \n zip town \n state \n country
1614
	{
1615
		$ret .= ($ret ? $sep : '' ).$object->zip;
1616
		$ret .= ($object->town?(($object->zip?' ':'').$object->town):'');
1617
		if ($object->state)
1618
		{
1619
			$ret.="\n".$object->state;
1620
		}
1621
	}
1622
	else if (in_array($object->country_code,array('IT'))) // IT: tile firstname name\n address lines \n zip (Code Departement) \n country
1623
	{
1624
                $ret .= ($ret ? $sep : '' ).$object->zip;
1625
                $ret .= ($object->town?(($object->zip?' ':'').$object->town):'');
1626
                $ret .= ($object->departement_id?(' ('.($object->departement_id).')'):'');
1627
	}
1628
	else                                        		// Other: title firstname name \n address lines \n zip town \n country
1629
	{
1630
		$ret .= $object->zip ? (($ret ? $sep : '' ).$object->zip) : '';
1631
		$ret .= ($object->town?(($object->zip?' ':($ret ? $sep : '' )).$object->town):'');
1632
		if ($object->state && in_array($object->country_code,$countriesusingstate))
1633
		{
1634
			$ret.=($ret?", ":'').$object->state;
1635
		}
1636
	}
1637
	if (! is_object($outputlangs)) $outputlangs=$langs;
1638
	if ($withcountry) $ret.=($object->country_code?($ret?$sep:'').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)):'');
1639
1640
	return $ret;
1641
}
1642
1643
1644
1645
/**
1646
 *	Format a string.
1647
 *
1648
 *	@param	string	$fmt		Format of strftime function (http://php.net/manual/fr/function.strftime.php)
1649
 *  @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)
1650
 *  @param	int		$is_gmt		See comment of timestamp parameter
1651
 *	@return	string				A formatted string
1652
 */
1653
function dol_strftime($fmt, $ts=false, $is_gmt=false)
1654
{
1655
	if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
1656
		return ($is_gmt)? @gmstrftime($fmt,$ts): @strftime($fmt,$ts);
1657
	}
1658
	else return 'Error date into a not supported range';
1659
}
1660
1661
/**
1662
 *	Output date in a string format according to outputlangs (or langs if not defined).
1663
 * 	Return charset is always UTF-8, except if encodetoouput is defined. In this case charset is output charset
1664
 *
1665
 *	@param	int			$time			GM Timestamps date
1666
 *	@param	string		$format      	Output date format (tag of strftime function)
1667
 *										"%d %b %Y",
1668
 *										"%d/%m/%Y %H:%M",
1669
 *										"%d/%m/%Y %H:%M:%S",
1670
 *                                      "%B"=Long text of month, "%A"=Long text of day, "%b"=Short text of month, "%a"=Short text of day
1671
 *										"day", "daytext", "dayhour", "dayhourldap", "dayhourtext", "dayrfc", "dayhourrfc", "...reduceformat"
1672
 * 	@param	string		$tzoutput		true or 'gmt' => string is for Greenwich location
1673
 * 										false or 'tzserver' => output string is for local PHP server TZ usage
1674
 * 										'tzuser' => output string is for user TZ (current browser TZ with current dst) => In a future, we should have same behaviour than 'tzuserrel'
1675
 *                                      'tzuserrel' => output string is for user TZ (current browser TZ with dst or not, depending on date position) (TODO not implemented yet)
1676
 *	@param	Translate	$outputlangs	Object lang that contains language for text translation.
1677
 *  @param  boolean		$encodetooutput false=no convert into output pagecode
1678
 * 	@return string      				Formated date or '' if time is null
1679
 *
1680
 *  @see        dol_mktime, dol_stringtotime, dol_getdate
1681
 */
1682
function dol_print_date($time,$format='',$tzoutput='tzserver',$outputlangs='',$encodetooutput=false)
1683
{
1684
	global $conf,$langs;
1685
1686
	// Clean parameters
1687
	$to_gmt=false;
1688
	$offsettz=$offsetdst=0;
1689
	if ($tzoutput)
1690
	{
1691
		$to_gmt=true;	// For backward compatibility
1692
		if (is_string($tzoutput))
1693
		{
1694
			if ($tzoutput == 'tzserver')
1695
			{
1696
				$to_gmt=false;
1697
				$offsettzstring=@date_default_timezone_get();		// Example 'Europe/Berlin' or 'Indian/Reunion'
1698
				$offsettz=0;
1699
				$offsetdst=0;
1700
			}
1701
			elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel')
1702
			{
1703
				$to_gmt=true;
1704
				$offsettzstring=(empty($_SESSION['dol_tz_string'])?'UTC':$_SESSION['dol_tz_string']);	// Example 'Europe/Berlin' or 'Indian/Reunion'
1705
				$offsettz=(empty($_SESSION['dol_tz'])?0:$_SESSION['dol_tz'])*60*60;		// Will not be used anymore
1706
				$offsetdst=(empty($_SESSION['dol_dst'])?0:$_SESSION['dol_dst'])*60*60;	// Will not be used anymore
1707
			}
1708
		}
1709
	}
1710
	if (! is_object($outputlangs)) $outputlangs=$langs;
1711
	if (! $format) $format='daytextshort';
1712
	$reduceformat=(! empty($conf->dol_optimize_smallscreen) && in_array($format,array('day','dayhour')))?1:0;
1713
	$formatwithoutreduce = preg_replace('/reduceformat/','',$format);
1714
	if ($formatwithoutreduce != $format) { $format = $formatwithoutreduce; $reduceformat=1; }  // so format 'dayreduceformat' is processed like day
1715
1716
	// Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
1717
	// TODO Add format daysmallyear and dayhoursmallyear
1718
	if ($format == 'day')				$format=($outputlangs->trans("FormatDateShort")!="FormatDateShort"?$outputlangs->trans("FormatDateShort"):$conf->format_date_short);
1719
	else if ($format == 'hour')			$format=($outputlangs->trans("FormatHourShort")!="FormatHourShort"?$outputlangs->trans("FormatHourShort"):$conf->format_hour_short);
1720
	else if ($format == 'hourduration')	$format=($outputlangs->trans("FormatHourShortDuration")!="FormatHourShortDuration"?$outputlangs->trans("FormatHourShortDuration"):$conf->format_hour_short_duration);
1721
	else if ($format == 'daytext')			 $format=($outputlangs->trans("FormatDateText")!="FormatDateText"?$outputlangs->trans("FormatDateText"):$conf->format_date_text);
1722
	else if ($format == 'daytextshort')	$format=($outputlangs->trans("FormatDateTextShort")!="FormatDateTextShort"?$outputlangs->trans("FormatDateTextShort"):$conf->format_date_text_short);
1723
	else if ($format == 'dayhour')			 $format=($outputlangs->trans("FormatDateHourShort")!="FormatDateHourShort"?$outputlangs->trans("FormatDateHourShort"):$conf->format_date_hour_short);
1724
	else if ($format == 'dayhoursec')		 $format=($outputlangs->trans("FormatDateHourSecShort")!="FormatDateHourSecShort"?$outputlangs->trans("FormatDateHourSecShort"):$conf->format_date_hour_sec_short);
1725
	else if ($format == 'dayhourtext')		 $format=($outputlangs->trans("FormatDateHourText")!="FormatDateHourText"?$outputlangs->trans("FormatDateHourText"):$conf->format_date_hour_text);
1726
	else if ($format == 'dayhourtextshort') $format=($outputlangs->trans("FormatDateHourTextShort")!="FormatDateHourTextShort"?$outputlangs->trans("FormatDateHourTextShort"):$conf->format_date_hour_text_short);
1727
	// Format not sensitive to language
1728
	else if ($format == 'dayhourlog')		 $format='%Y%m%d%H%M%S';
1729
	else if ($format == 'dayhourldap')		 $format='%Y%m%d%H%M%SZ';
1730
	else if ($format == 'dayhourxcard')	$format='%Y%m%dT%H%M%SZ';
1731
	else if ($format == 'dayxcard')	 	$format='%Y%m%d';
1732
	else if ($format == 'dayrfc')			 $format='%Y-%m-%d';             // DATE_RFC3339
1733
	else if ($format == 'dayhourrfc')		 $format='%Y-%m-%dT%H:%M:%SZ';   // DATETIME RFC3339
1734
	else if ($format == 'standard')		$format='%Y-%m-%d %H:%M:%S';
1735
1736
	if ($reduceformat)
1737
	{
1738
		$format=str_replace('%Y','%y',$format);
1739
		$format=str_replace('yyyy','yy',$format);
1740
	}
1741
1742
	// If date undefined or "", we return ""
1743
	if (dol_strlen($time) == 0) return '';		// $time=0 allowed (it means 01/01/1970 00:00:00)
1744
1745
	// Clean format
1746
	if (preg_match('/%b/i',$format))		// There is some text to translate
1747
	{
1748
		// We inhibate translation to text made by strftime functions. We will use trans instead later.
1749
		$format=str_replace('%b','__b__',$format);
1750
		$format=str_replace('%B','__B__',$format);
1751
	}
1752
	if (preg_match('/%a/i',$format))		// There is some text to translate
1753
	{
1754
		// We inhibate translation to text made by strftime functions. We will use trans instead later.
1755
		$format=str_replace('%a','__a__',$format);
1756
		$format=str_replace('%A','__A__',$format);
1757
	}
1758
1759
	// Analyze date
1760
	if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i',$time,$reg)
1761
	|| 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
1762
	{
1763
		// TODO Remove this.
1764
		// This part of code should not be used.
1765
		dol_syslog("Functions.lib::dol_print_date function call with deprecated value of time in page ".$_SERVER["PHP_SELF"], LOG_ERR);
1766
		// Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' or 'YYYYMMDDHHMMSS'
1767
		$syear	= (! empty($reg[1]) ? $reg[1] : '');
1768
		$smonth	= (! empty($reg[2]) ? $reg[2] : '');
1769
		$sday	= (! empty($reg[3]) ? $reg[3] : '');
1770
		$shour	= (! empty($reg[4]) ? $reg[4] : '');
1771
		$smin	= (! empty($reg[5]) ? $reg[5] : '');
1772
		$ssec	= (! empty($reg[6]) ? $reg[6] : '');
1773
1774
		$time=dol_mktime($shour,$smin,$ssec,$smonth,$sday,$syear,true);
1775
		$ret=adodb_strftime($format, $time+$offsettz+$offsetdst, $to_gmt);
1776
	}
1777
	else
1778
	{
1779
		// Date is a timestamps
1780
		if ($time < 100000000000)	// Protection against bad date values
1781
		{
1782
			$timetouse = $time+$offsettz+$offsetdst;	// TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1783
1784
			$ret=adodb_strftime($format, $timetouse, $to_gmt);
1785
		}
1786
		else $ret='Bad value '.$time.' for date';
1787
	}
1788
1789
	if (preg_match('/__b__/i',$format))
1790
	{
1791
		$timetouse = $time+$offsettz+$offsetdst;	// TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1792
1793
		// Here ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs.
1794
		$month=adodb_strftime('%m', $timetouse);
1795
		$month=sprintf("%02d", $month);	// $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
1796
		if ($encodetooutput)
1797
		{
1798
			$monthtext=$outputlangs->transnoentities('Month'.$month);
1799
			$monthtextshort=$outputlangs->transnoentities('MonthShort'.$month);
1800
		}
1801
		else
1802
		{
1803
			$monthtext=$outputlangs->transnoentitiesnoconv('Month'.$month);
1804
			$monthtextshort=$outputlangs->transnoentitiesnoconv('MonthShort'.$month);
1805
		}
1806
		//print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
1807
		$ret=str_replace('__b__',$monthtextshort,$ret);
1808
		$ret=str_replace('__B__',$monthtext,$ret);
1809
		//print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
1810
		//return $ret;
1811
	}
1812
	if (preg_match('/__a__/i',$format))
1813
	{
1814
		$timetouse = $time+$offsettz+$offsetdst;	// TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1815
1816
		$w=adodb_strftime('%w', $timetouse);						// TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1817
		$dayweek=$outputlangs->transnoentitiesnoconv('Day'.$w);
1818
		$ret=str_replace('__A__',$dayweek,$ret);
1819
		$ret=str_replace('__a__',dol_substr($dayweek,0,3),$ret);
1820
	}
1821
1822
	return $ret;
1823
}
1824
1825
1826
/**
1827
 *	Return an array with locale date info.
1828
 *  PHP getdate is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
1829
 *  WARNING: This function always use PHP server timezone to return locale informations !!!
1830
 *  Usage must be avoid.
1831
 *  FIXME: Replace this with PHP date function and a parameter $gm
1832
 *
1833
 *	@param	int			$timestamp      Timestamp
1834
 *	@param	boolean		$fast           Fast mode
1835
 *	@return	array						Array of informations
1836
 *										If no fast mode:
1837
 *										'seconds' => $secs,
1838
 *										'minutes' => $min,
1839
 *										'hours' => $hour,
1840
 *										'mday' => $day,
1841
 *										'wday' => $dow,		0=sunday, 6=saturday
1842
 *										'mon' => $month,
1843
 *										'year' => $year,
1844
 *										'yday' => floor($secsInYear/$_day_power),
1845
 *										'weekday' => gmdate('l',$_day_power*(3+$dow)),
1846
 *										'month' => gmdate('F',mktime(0,0,0,$month,2,1971)),
1847
 *										If fast mode:
1848
 *										'seconds' => $secs,
1849
 *										'minutes' => $min,
1850
 *										'hours' => $hour,
1851
 *										'mday' => $day,
1852
 *										'mon' => $month,
1853
 *										'year' => $year,
1854
 *										'yday' => floor($secsInYear/$_day_power),
1855
 *										'leap' => $leaf,
1856
 *										'ndays' => $ndays
1857
 * 	@see 								dol_print_date, dol_stringtotime, dol_mktime
1858
 */
1859
function dol_getdate($timestamp,$fast=false)
1860
{
1861
	global $conf;
1862
1863
	$usealternatemethod=false;
1864
	if ($timestamp <= 0) $usealternatemethod=true;				// <= 1970
1865
	if ($timestamp >= 2145913200) $usealternatemethod=true;		// >= 2038
1866
1867
	if ($usealternatemethod)
1868
	{
1869
		$arrayinfo=adodb_getdate($timestamp,$fast);
1870
	}
1871
	else
1872
	{
1873
		$arrayinfo=getdate($timestamp);
1874
	}
1875
1876
	return $arrayinfo;
1877
}
1878
1879
/**
1880
 *	Return a timestamp date built from detailed informations (by default a local PHP server timestamp)
1881
 * 	Replace function mktime not available under Windows if year < 1970
1882
 *	PHP mktime is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
1883
 *
1884
 * 	@param	int			$hour			Hour	(can be -1 for undefined)
1885
 *	@param	int			$minute			Minute	(can be -1 for undefined)
1886
 *	@param	int			$second			Second	(can be -1 for undefined)
1887
 *	@param	int			$month			Month (1 to 12)
1888
 *	@param	int			$day			Day (1 to 31)
1889
 *	@param	int			$year			Year
1890
 *	@param	mixed		$gm				True or 1 or 'gmt'=Input informations are GMT values
1891
 *										False or 0 or 'server' = local to server TZ
1892
 *										'user' = local to user TZ
1893
 *										'tz,TimeZone' = use specified timezone
1894
 *	@param	int			$check			0=No check on parameters (Can use day 32, etc...)
1895
 *	@return	int|string					Date as a timestamp, '' or false if error
1896
 * 	@see 								dol_print_date, dol_stringtotime, dol_getdate
1897
 */
1898
function dol_mktime($hour,$minute,$second,$month,$day,$year,$gm=false,$check=1)
1899
{
1900
	global $conf;
1901
	//print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
1902
1903
	// Clean parameters
1904
	if ($hour   == -1 || empty($hour)) $hour=0;
1905
	if ($minute == -1 || empty($minute)) $minute=0;
1906
	if ($second == -1 || empty($second)) $second=0;
1907
1908
	// Check parameters
1909
	if ($check)
1910
	{
1911
		if (! $month || ! $day)  return '';
1912
		if ($day   > 31) return '';
1913
		if ($month > 12) return '';
1914
		if ($hour  < 0 || $hour   > 24) return '';
1915
		if ($minute< 0 || $minute > 60) return '';
1916
		if ($second< 0 || $second > 60) return '';
1917
	}
1918
1919
	if (method_exists('DateTime','getTimestamp'))
1920
	{
1921
		if (empty($gm) || $gm === 'server')
1922
		{
1923
			$default_timezone=@date_default_timezone_get();		// Example 'Europe/Berlin'
1924
			$localtz = new DateTimeZone($default_timezone);
1925
		}
1926
		else if ($gm === 'user')
1927
		{
1928
			// We use dol_tz_string first because it is more reliable.
1929
			$default_timezone=(empty($_SESSION["dol_tz_string"])?@date_default_timezone_get():$_SESSION["dol_tz_string"]);		// Example 'Europe/Berlin'
1930
			try {
1931
				$localtz = new DateTimeZone($default_timezone);
1932
			}
1933
			catch(Exception $e)
1934
			{
1935
				dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
1936
				$default_timezone=@date_default_timezone_get();
1937
			}
1938
		}
1939
		else if (strrpos($gm, "tz,") !== false)
1940
		{
1941
			$timezone=str_replace("tz,", "", $gm);  // Example 'tz,Europe/Berlin'
1942
			try
1943
			{
1944
				$localtz = new DateTimeZone($timezone);
1945
			}
1946
			catch(Exception $e)
1947
			{
1948
				dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
1949
			}
1950
		}
1951
1952
		if (empty($localtz)) {
1953
			$localtz = new DateTimeZone('UTC');
1954
		}
1955
		//var_dump($localtz);
1956
		//var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
1957
		$dt = new DateTime(null,$localtz);
1958
		$dt->setDate($year,$month,$day);
1959
		$dt->setTime((int) $hour, (int) $minute, (int) $second);
1960
		$date=$dt->getTimestamp();	// should include daylight saving time
1961
		//var_dump($date);
1962
		return $date;
1963
	}
1964
	else
1965
	{
1966
		dol_print_error('','PHP version must be 5.3+');
1967
		return '';
1968
	}
1969
}
1970
1971
1972
/**
1973
 *	Return date for now. In most cases, we use this function without parameters (that means GMT time).
1974
 *
1975
 * 	@param	string		$mode	'gmt' => we return GMT timestamp,
1976
 * 								'tzserver' => we add the PHP server timezone
1977
 *  							'tzref' => we add the company timezone
1978
 * 								'tzuser' => we add the user timezone
1979
 *	@return int   $date	Timestamp
1980
 */
1981
function dol_now($mode='gmt')
1982
{
1983
	$ret=0;
1984
1985
	// Note that gmmktime and mktime return same value (GMT) when used without parameters
1986
	//if ($mode == 'gmt') $ret=gmmktime(); // Strict Standards: gmmktime(): You should be using the time() function instead
1987
	if ($mode == 'gmt') $ret=time();	// Time for now at greenwich.
1988
	else if ($mode == 'tzserver')		// Time for now with PHP server timezone added
1989
	{
1990
		require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1991
		$tzsecond=getServerTimeZoneInt('now');    // Contains tz+dayling saving time
1992
		$ret=(int) dol_now('gmt')+($tzsecond*3600);
1993
	}
1994
	/*else if ($mode == 'tzref')				// Time for now with parent company timezone is added
1995
	{
1996
		require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1997
		$tzsecond=getParentCompanyTimeZoneInt();    // Contains tz+dayling saving time
1998
		$ret=dol_now('gmt')+($tzsecond*3600);
1999
	}*/
2000
	else if ($mode == 'tzuser')				// Time for now with user timezone added
2001
	{
2002
		//print 'eeee'.time().'-'.mktime().'-'.gmmktime();
2003
		$offsettz=(empty($_SESSION['dol_tz'])?0:$_SESSION['dol_tz'])*60*60;
2004
		$offsetdst=(empty($_SESSION['dol_dst'])?0:$_SESSION['dol_dst'])*60*60;
2005
		$ret=(int) dol_now('gmt')+($offsettz+$offsetdst);
2006
	}
2007
2008
	return $ret;
2009
}
2010
2011
2012
/**
2013
 * Return string with formated size
2014
 *
2015
 * @param	int		$size		Size to print
2016
 * @param	int		$shortvalue	Tell if we want long value to use another unit (Ex: 1.5Kb instead of 1500b)
2017
 * @param	int		$shortunit	Use short value of size unit
2018
 * @return	string				Link
2019
 */
2020
function dol_print_size($size,$shortvalue=0,$shortunit=0)
2021
{
2022
	global $conf,$langs;
2023
	$level=1024;
2024
2025
	if (! empty($conf->dol_optimize_smallscreen)) $shortunit=1;
2026
2027
	// Set value text
2028
	if (empty($shortvalue) || $size < ($level*10))
2029
	{
2030
		$ret=$size;
2031
		$textunitshort=$langs->trans("b");
2032
		$textunitlong=$langs->trans("Bytes");
2033
	}
2034
	else
2035
	{
2036
		$ret=round($size/$level,0);
2037
		$textunitshort=$langs->trans("Kb");
2038
		$textunitlong=$langs->trans("KiloBytes");
2039
	}
2040
	// Use long or short text unit
2041
	if (empty($shortunit)) { $ret.=' '.$textunitlong; }
2042
	else { $ret.=' '.$textunitshort; }
2043
2044
	return $ret;
2045
}
2046
2047
/**
2048
 * Show Url link
2049
 *
2050
 * @param	string		$url		Url to show
2051
 * @param	string		$target		Target for link
2052
 * @param	int			$max		Max number of characters to show
2053
 * @param	int			$withpicto	With picto
2054
 * @return	string					HTML Link
2055
 */
2056
function dol_print_url($url,$target='_blank',$max=32,$withpicto=0)
2057
{
2058
	global $langs;
2059
2060
	if (empty($url)) return '';
2061
2062
	$link='<a href="';
2063
	if (! preg_match('/^http/i',$url)) $link.='http://';
2064
	$link.=$url;
2065
	$link.='"';
2066
	if ($target) $link.=' target="'.$target.'"';
2067
	$link.='>';
2068
	if (! preg_match('/^http/i',$url)) $link.='http://';
2069
	$link.=dol_trunc($url,$max);
2070
	$link.='</a>';
2071
	return '<div class="nospan float" style="margin-right: 10px">'.($withpicto?img_picto($langs->trans("Url"), 'object_globe.png').' ':'').$link.'</div>';
2072
}
2073
2074
/**
2075
 * Show EMail link
2076
 *
2077
 * @param	string		$email			EMail to show (only email, without 'Name of recipient' before)
2078
 * @param 	int			$cid 			Id of contact if known
2079
 * @param 	int			$socid 			Id of third party if known
2080
 * @param 	int			$addlink		0=no link, 1=email has a html email link (+ link to create action if constant AGENDA_ADDACTIONFOREMAIL is on)
2081
 * @param	int			$max			Max number of characters to show
2082
 * @param	int			$showinvalid	Show warning if syntax email is wrong
2083
 * @param	int			$withpicto		Show picto
2084
 * @return	string						HTML Link
2085
 */
2086
function dol_print_email($email,$cid=0,$socid=0,$addlink=0,$max=64,$showinvalid=1,$withpicto=0)
2087
{
2088
	global $conf,$user,$langs;
2089
2090
	$newemail=$email;
2091
2092
	if (empty($email)) return '&nbsp;';
2093
2094
	if (! empty($addlink))
2095
	{
2096
		$newemail='<a style="text-overflow: ellipsis;" href="';
2097
		if (! preg_match('/^mailto:/i',$email)) $newemail.='mailto:';
2098
		$newemail.=$email;
2099
		$newemail.='">';
2100
		$newemail.=dol_trunc($email,$max);
2101
		$newemail.='</a>';
2102
		if ($showinvalid && ! isValidEmail($email))
2103
		{
2104
			$langs->load("errors");
2105
			$newemail.=img_warning($langs->trans("ErrorBadEMail",$email));
2106
		}
2107
2108
		if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2109
		{
2110
			$type='AC_EMAIL'; $link='';
2111
			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>';
2112
			if ($link) $newemail='<div>'.$newemail.' '.$link.'</div>';
2113
		}
2114
	}
2115
	else
2116
	{
2117
		if ($showinvalid && ! isValidEmail($email))
2118
		{
2119
			$langs->load("errors");
2120
			$newemail.=img_warning($langs->trans("ErrorBadEMail",$email));
2121
		}
2122
	}
2123
	return '<div class="nospan float" style="margin-right: 10px">'.($withpicto?img_picto($langs->trans("EMail"), 'object_email.png').' ':'').$newemail.'</div>';
2124
}
2125
2126
/**
2127
 * Show Skype link
2128
 *
2129
 * @param	string		$skype			Skype to show (only skype, without 'Name of recipient' before)
2130
 * @param	int 		$cid 			Id of contact if known
2131
 * @param	int 		$socid 			Id of third party if known
2132
 * @param	int 		$addlink		0=no link to create action
2133
 * @param	int			$max			Max number of characters to show
2134
 * @return	string						HTML Link
2135
 */
2136
function dol_print_skype($skype,$cid=0,$socid=0,$addlink=0,$max=64)
2137
{
2138
	global $conf,$user,$langs;
2139
2140
	$newskype=$skype;
2141
2142
	if (empty($skype)) return '&nbsp;';
2143
2144
	if (! empty($addlink))
2145
	{
2146
		$newskype =img_picto($langs->trans("Skype"), 'object_skype.png');
2147
		$newskype.= '&nbsp;';
2148
		$newskype.=dol_trunc($skype,$max);
2149
		$newskype.= '&nbsp;';
2150
		$newskype.='<a href="skype:';
2151
		$newskype.=dol_trunc($skype,$max);
2152
		$newskype.='?call" alt="'.$langs->trans("Call").'&nbsp;'.$skype.'" title="'.$langs->trans("Call").'&nbsp;'.$skype.'">';
2153
		$newskype.='<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
2154
		$newskype.='</a>&nbsp;&nbsp;&nbsp;<a href="skype:';
2155
		$newskype.=dol_trunc($skype,$max);
2156
		$newskype.='?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$skype.'" title="'.$langs->trans("Chat").'&nbsp;'.$skype.'">';
2157
		$newskype.='<img src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
2158
		$newskype.='</a>';
2159
2160
		if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2161
		{
2162
			$type='AC_SKYPE'; $link='';
2163
			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>';
2164
			$newskype='<div class="divskype nowrap">'.$newskype.($link?' '.$link:'').'</div>';
2165
		}
2166
	}
2167
	else
2168
	{
2169
		$langs->load("errors");
2170
		$newskype.=img_warning($langs->trans("ErrorBadSkype",$skype));
2171
	}
2172
	return $newskype;
2173
}
2174
2175
/**
2176
 * 	Format phone numbers according to country
2177
 *
2178
 * 	@param  string  $phone          Phone number to format
2179
 * 	@param  string  $countrycode    Country code to use for formatting
2180
 * 	@param 	int		$cid 		    Id of contact if known
2181
 * 	@param 	int		$socid          Id of third party if known
2182
 * 	@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)
2183
 * 	@param 	string	$separ 		    Separation between numbers for a better visibility example : xx.xx.xx.xx.xx
2184
 *  @param	string  $withpicto      Show picto
2185
 *  @param	string	$titlealt	    Text to show on alt
2186
 *  @param  int     $adddivfloat    Add div float around phone.
2187
 * 	@return string 				    Formated phone number
2188
 */
2189
function dol_print_phone($phone,$countrycode='',$cid=0,$socid=0,$addlink='',$separ="&nbsp;",$withpicto='',$titlealt='',$adddivfloat=0)
2190
{
2191
	global $conf, $user, $langs, $mysoc, $hookmanager;
2192
2193
	// Clean phone parameter
2194
	$phone = preg_replace("/[\s.-]/","",trim($phone));
2195
	if (empty($phone)) { return ''; }
2196
	if (empty($countrycode)) $countrycode=$mysoc->country_code;
2197
2198
	// Short format for small screens
2199
	if ($conf->dol_optimize_smallscreen) $separ='';
2200
2201
	$newphone=$phone;
2202
	if (strtoupper($countrycode) == "FR")
2203
	{
2204
		// France
2205
		if (dol_strlen($phone) == 10) {
2206
			$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);
2207
		}
2208
		elseif (dol_strlen($newphone) == 7)
2209
		{
2210
			$newphone=substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,2);
2211
		}
2212
		elseif (dol_strlen($newphone) == 9)
2213
		{
2214
			$newphone=substr($newphone,0,2).$separ.substr($newphone,2,3).$separ.substr($newphone,5,2).$separ.substr($newphone,7,2);
2215
		}
2216
		elseif (dol_strlen($newphone) == 11)
2217
		{
2218
			$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);
2219
		}
2220
		elseif (dol_strlen($newphone) == 12)
2221
		{
2222
			$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);
2223
		}
2224
	}
2225
2226
	if (strtoupper($countrycode) == "CA")
2227
	{
2228
		if (dol_strlen($phone) == 10) {
2229
			$newphone=($separ!=''?'(':'').substr($newphone,0,3).($separ!=''?')':'').$separ.substr($newphone,3,3).($separ!=''?'-':'').substr($newphone,6,4);
2230
		}
2231
	}
2232
2233
	if (! empty($addlink))	// Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
2234
	{
2235
		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
2236
		{
2237
			$newphone ='<a href="tel:'.$phone.'"';
2238
			$newphone.='>'.$phone.'</a>';
2239
		}
2240
		else if (! empty($conf->clicktodial->enabled) && $addlink == 'AC_TEL')		// If click to dial, we use click to dial url
2241
		{
2242
			if (empty($user->clicktodial_loaded)) $user->fetch_clicktodial();
2243
2244
			// Define urlmask
2245
			$urlmask='ErrorClickToDialModuleNotConfigured';
2246
			if (! empty($conf->global->CLICKTODIAL_URL)) $urlmask=$conf->global->CLICKTODIAL_URL;
2247
			if (! empty($user->clicktodial_url)) $urlmask=$user->clicktodial_url;
2248
2249
			$clicktodial_poste=(! empty($user->clicktodial_poste)?urlencode($user->clicktodial_poste):'');
2250
			$clicktodial_login=(! empty($user->clicktodial_login)?urlencode($user->clicktodial_login):'');
2251
			$clicktodial_password=(! empty($user->clicktodial_password)?urlencode($user->clicktodial_password):'');
2252
			// This line is for backward compatibility
2253
			$url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
2254
			// Thoose lines are for substitution
2255
			$substitarray=array('__PHONEFROM__'=>$clicktodial_poste,
2256
								'__PHONETO__'=>urlencode($phone),
2257
								'__LOGIN__'=>$clicktodial_login,
2258
								'__PASS__'=>$clicktodial_password);
2259
			$url = make_substitutions($url, $substitarray);
2260
			$newphonesav=$newphone;
2261
			$newphone ='<a href="'.$url.'"';
2262
			if (! empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) $newphone.=' target="_blank"';
2263
			$newphone.='>'.$newphonesav.'</a>';
2264
		}
2265
2266
		//if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2267
		if (! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2268
		{
2269
			$type='AC_TEL'; $link='';
2270
			if ($addlink == 'AC_FAX') $type='AC_FAX';
2271
			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>';
2272
			if ($link) $newphone='<div>'.$newphone.' '.$link.'</div>';
2273
		}
2274
	}
2275
2276
	if (empty($titlealt))
2277
	{
2278
		$titlealt=($withpicto=='fax'?$langs->trans("Fax"):$langs->trans("Phone"));
2279
	}
2280
	$rep='';
2281
2282
	if ($hookmanager) {
2283
            $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid,'titlealt' => $titlealt, 'picto' => $withpicto);
2284
            $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
2285
            $rep.=$hookmanager->resPrint;
2286
        }
2287
	 if (empty($reshook))
2288
        {
2289
		$picto = '';
2290
		if($withpicto){
2291
			if($withpicto=='fax'){
2292
				$picto = 'phoning_fax';
2293
			}elseif($withpicto=='phone'){
2294
				$picto = 'phoning';
2295
			}elseif($withpicto=='mobile'){
2296
				$picto = 'phoning_mobile';
2297
			}else{
2298
				$picto = '';
2299
			}
2300
		}
2301
		if ($adddivfloat) $rep.='<div class="nospan float" style="margin-right: 10px">';
2302
		else $rep.='<span style="margin-right: 10px;">';
2303
		$rep.=($withpicto?img_picto($titlealt, 'object_'.$picto.'.png').' ':'').$newphone;
2304
		if ($adddivfloat) $rep.='</div>';
2305
		else $rep.='</span>';
2306
	  }
2307
2308
	return $rep;
2309
}
2310
2311
/**
2312
 * 	Return an IP formated to be shown on screen
2313
 *
2314
 * 	@param	string	$ip			IP
2315
 * 	@param	int		$mode		0=return IP + country/flag, 1=return only country/flag, 2=return only IP
2316
 * 	@return string 				Formated IP, with country if GeoIP module is enabled
2317
 */
2318
function dol_print_ip($ip,$mode=0)
2319
{
2320
	global $conf,$langs;
2321
2322
	$ret='';
2323
2324
	if (empty($mode)) $ret.=$ip;
2325
2326
	if ($mode != 2)
2327
	{
2328
		$countrycode=dolGetCountryCodeFromIp($ip);
2329
		if ($countrycode)	// If success, countrycode is us, fr, ...
2330
		{
2331
			if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png'))
2332
			{
2333
				$ret.=' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"),DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png','',1);
2334
			}
2335
			else $ret.=' ('.$countrycode.')';
2336
		}
2337
	}
2338
2339
	return $ret;
2340
}
2341
2342
/**
2343
 * 	Return a country code from IP. Empty string if not found.
2344
 *
2345
 * 	@param	string	$ip			IP
2346
 * 	@return string 				Country code ('us', 'fr', ...)
2347
 */
2348
function dolGetCountryCodeFromIp($ip)
2349
{
2350
	global $conf;
2351
2352
	$countrycode='';
2353
2354
	if (! empty($conf->geoipmaxmind->enabled))
2355
	{
2356
		$datafile=$conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
2357
		//$ip='24.24.24.24';
2358
		//$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)
2359
2360
		include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
2361
		$geoip=new DolGeoIP('country',$datafile);
2362
		//print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
2363
		//print "geoip_country_id_by_addr=".geoip_country_id_by_addr($geoip->gi,$ip)."\n";
2364
		$countrycode=$geoip->getCountryCodeFromIP($ip);
2365
	}
2366
2367
	return $countrycode;
2368
}
2369
2370
2371
/**
2372
 *  Return country code for current user.
2373
 *  If software is used inside a local network, detection may fails (we need a public ip)
2374
 *
2375
 *  @return     string      Country code (fr, es, it, us, ...)
2376
 */
2377
function dol_user_country()
2378
{
2379
	global $conf,$langs,$user;
2380
2381
	//$ret=$user->xxx;
2382
	$ret='';
2383
	if (! empty($conf->geoipmaxmind->enabled))
2384
	{
2385
		$ip=$_SERVER["REMOTE_ADDR"];
2386
		$datafile=$conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
2387
		//$ip='24.24.24.24';
2388
		//$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
2389
		include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
2390
		$geoip=new DolGeoIP('country',$datafile);
2391
		$countrycode=$geoip->getCountryCodeFromIP($ip);
2392
		$ret=$countrycode;
2393
	}
2394
	return $ret;
2395
}
2396
2397
/**
2398
 *  Format address string
2399
 *
2400
 *  @param	string	$address    Address
2401
 *  @param  int		$htmlid     Html ID (for example 'gmap')
2402
 *  @param  int		$mode       thirdparty|contact|member|other
2403
 *  @param  int		$id         Id of object
2404
 *  @param	int		$noprint	No output. Result is the function return
2405
 *  @param  string  $charfornl  Char to use instead of nl2br. '' means we use a standad nl2br.
2406
 *  @return string|void			Nothing if noprint is 0, formatted address if noprint is 1
2407
 *  @see dol_format_address
2408
 */
2409
function dol_print_address($address, $htmlid, $mode, $id, $noprint=0, $charfornl='')
2410
{
2411
	global $conf, $user, $langs, $hookmanager;
2412
2413
	$out = '';
2414
2415
	if ($address)
2416
	{
2417
		if ($hookmanager) {
2418
			$parameters = array('element' => $mode, 'id' => $id);
2419
			$reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
2420
			$out.=$hookmanager->resPrint;
2421
		}
2422
		if (empty($reshook))
2423
		{
2424
			if (empty($charfornl)) $out.=nl2br($address);
2425
			else $out.=preg_replace('/[\r\n]+/', $charfornl, $address);
2426
2427
			$showgmap=$showomap=0;
2428
2429
			// TODO Add a hook here
2430
			if (($mode=='thirdparty' || $mode =='societe') && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS)) $showgmap=1;
2431
			if ($mode=='contact' && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) $showgmap=1;
2432
			if ($mode=='member' && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) $showgmap=1;
2433
			if (($mode=='thirdparty' || $mode =='societe') && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) $showomap=1;
2434
			if ($mode=='contact' && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) $showomap=1;
2435
			if ($mode=='member' && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) $showomap=1;
2436
2437
			if ($showgmap)
2438
			{
2439
				$url=dol_buildpath('/google/gmaps.php?mode='.$mode.'&id='.$id,1);
2440
				$out.=' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
2441
			}
2442
			if ($showomap)
2443
			{
2444
				$url=dol_buildpath('/openstreetmap/maps.php?mode='.$mode.'&id='.$id,1);
2445
				$out.=' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
2446
			}
2447
		}
2448
	}
2449
	if ($noprint) return $out;
2450
	else print $out;
2451
}
2452
2453
2454
/**
2455
 *	Return true if email syntax is ok
2456
 *
2457
 *	@param	    string		$address    			email (Ex: "[email protected]", "John Do <[email protected]>")
2458
 *  @param		int			$acceptsupervisorkey	If 1, the special string '__SUPERVISOREMAIL__' is also accepted as valid
2459
 *	@return     boolean     						true if email syntax is OK, false if KO or empty string
2460
 */
2461
function isValidEmail($address, $acceptsupervisorkey=0)
2462
{
2463
	if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') return true;
2464
	if (filter_var($address, FILTER_VALIDATE_EMAIL)) return true;
2465
2466
	return false;
2467
}
2468
2469
/**
2470
 *  Return true if phone number syntax is ok
2471
 *  TODO Decide what to do with this
2472
 *
2473
 *  @param	string		$phone		phone (Ex: "0601010101")
2474
 *  @return boolean     			true if phone syntax is OK, false if KO or empty string
2475
 */
2476
function isValidPhone($phone)
2477
{
2478
	return true;
2479
}
2480
2481
2482
/**
2483
 * Make a strlen call. Works even if mbstring module not enabled
2484
 *
2485
 * @param   string		$string				String to calculate length
2486
 * @param   string		$stringencoding		Encoding of string
2487
 * @return  int								Length of string
2488
 */
2489
function dol_strlen($string,$stringencoding='UTF-8')
2490
{
2491
	if (function_exists('mb_strlen')) return mb_strlen($string,$stringencoding);
2492
	else return strlen($string);
2493
}
2494
2495
/**
2496
 * Make a substring. Works even in mbstring module is not enabled.
2497
 *
2498
 * @param	string	$string				String to scan
2499
 * @param	string	$start				Start position
2500
 * @param	int		$length				Length
2501
 * @param   string	$stringencoding		Page code used for input string encoding
2502
 * @return  string						substring
2503
 */
2504
function dol_substr($string,$start,$length,$stringencoding='')
2505
{
2506
	global $langs;
2507
2508
	if (empty($stringencoding)) $stringencoding=$langs->charset_output;
2509
2510
	$ret='';
2511
	if (function_exists('mb_substr'))
2512
	{
2513
		$ret=mb_substr($string,$start,$length,$stringencoding);
2514
	}
2515
	else
2516
	{
2517
		$ret=substr($string,$start,$length);
2518
	}
2519
	return $ret;
2520
}
2521
2522
2523
/**
2524
 *  Show a javascript graph.
2525
 *  Do not use this function anymore. Use DolGraph class instead.
2526
 *
2527
 *  @param		string	$htmlid			Html id name
2528
 *  @param		int		$width			Width in pixel
2529
 *  @param		int		$height			Height in pixel
2530
 *  @param		array	$data			Data array
2531
 *  @param		int		$showlegend		1 to show legend, 0 otherwise
2532
 *  @param		string	$type			Type of graph ('pie', 'barline')
2533
 *  @param		int		$showpercent	Show percent (with type='pie' only)
2534
 *  @param		string	$url			Param to add an url to click values
2535
 *  @param		int		$combineother	0=No combine, 0.05=Combine if lower than 5%
2536
 *  @param      int     $shownographyet Show graph to say there is not enough data
2537
 *  @return		void
2538
 *  @deprecated
2539
 *  @see DolGraph
2540
 */
2541
function dol_print_graph($htmlid,$width,$height,$data,$showlegend=0,$type='pie',$showpercent=0,$url='',$combineother=0.05,$shownographyet=0)
2542
{
2543
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
2544
2545
	global $conf,$langs;
2546
	global $theme_datacolor;    // To have var kept when function is called several times
2547
2548
	if ($shownographyet)
2549
	{
2550
		print '<div class="nographyet" style="width:'.$width.'px;height:'.$height.'px;"></div>';
2551
		print '<div class="nographyettext">'.$langs->trans("NotEnoughDataYet").'</div>';
2552
		return;
2553
	}
2554
2555
	if (empty($conf->use_javascript_ajax)) return;
2556
	$jsgraphlib='flot';
2557
	$datacolor=array();
2558
2559
	// Load colors of theme into $datacolor array
2560
	$color_file = DOL_DOCUMENT_ROOT."/theme/".$conf->theme."/graph-color.php";
2561
	if (is_readable($color_file))
2562
	{
2563
		include_once $color_file;
2564
		if (isset($theme_datacolor))
2565
		{
2566
			$datacolor=array();
2567
			foreach($theme_datacolor as $val)
2568
			{
2569
				$datacolor[]="#".sprintf("%02x",$val[0]).sprintf("%02x",$val[1]).sprintf("%02x",$val[2]);
2570
			}
2571
		}
2572
	}
2573
	print '<div id="'.$htmlid.'" style="width:'.$width.'px;height:'.$height.'px;"></div>';
2574
2575
	// We use Flot js lib
2576
	if ($jsgraphlib == 'flot')
2577
	{
2578
		if ($type == 'pie')
2579
		{
2580
			// data is   array('series'=>array(serie1,serie2,...),
2581
			//                 'seriestype'=>array('bar','line',...),
2582
			//                 'seriescolor'=>array(0=>'#999999',1=>'#999999',...)
2583
			//                 'xlabel'=>array(0=>labelx1,1=>labelx2,...));
2584
			// serieX is array('label'=>'label', data=>val)
2585
			print '
2586
			<script type="text/javascript">
2587
			$(function () {
2588
				var data = '.json_encode($data['series']).';
2589
2590
				function plotWithOptions() {
2591
					$.plot($("#'.$htmlid.'"), data,
2592
					{
2593
						series: {
2594
							pie: {
2595
								show: true,
2596
								radius: 0.8,';
2597
			if ($combineother)
2598
			{
2599
				print '
2600
								combine: {
2601
								 	threshold: '.$combineother.'
2602
								},';
2603
			}
2604
			print '
2605
								label: {
2606
									show: true,
2607
									radius: 0.9,
2608
									formatter: function(label, series) {
2609
										var percent=Math.round(series.percent);
2610
										var number=series.data[0][1];
2611
										return \'';
2612
										print '<div style="font-size:8pt;text-align:center;padding:2px;color:black;">';
2613
										if ($url) print '<a style="color: #FFFFFF;" border="0" href="'.$url.'">';
2614
										print '\'+'.($showlegend?'number':'label+\' \'+number');
2615
										if (! empty($showpercent)) print '+\'<br/>\'+percent+\'%\'';
2616
										print '+\'';
2617
										if ($url) print '</a>';
2618
										print '</div>\';
2619
									},
2620
									background: {
2621
										opacity: 0.0,
2622
										color: \'#000000\'
2623
									},
2624
								}
2625
							}
2626
						},
2627
						zoom: {
2628
							interactive: true
2629
						},
2630
						pan: {
2631
							interactive: true
2632
						},';
2633
						if (count($datacolor))
2634
						{
2635
							print 'colors: '.(! empty($data['seriescolor']) ? json_encode($data['seriescolor']) : json_encode($datacolor)).',';
2636
						}
2637
						print 'legend: {show: '.($showlegend?'true':'false').', position: \'ne\' }
2638
					});
2639
				}
2640
				plotWithOptions();
2641
			});
2642
			</script>';
2643
		}
2644
		else if ($type == 'barline')
2645
		{
2646
			// data is   array('series'=>array(serie1,serie2,...),
2647
			//                 'seriestype'=>array('bar','line',...),
2648
			//                 'seriescolor'=>array(0=>'#999999',1=>'#999999',...)
2649
			//                 'xlabel'=>array(0=>labelx1,1=>labelx2,...));
2650
			// serieX is array('label'=>'label', data=>array(0=>y1,1=>y2,...)) with same nb of value than into xlabel
2651
			print '
2652
			<script type="text/javascript">
2653
			$(function () {
2654
				var data = [';
2655
				$i=0; $outputserie=0;
2656
				foreach($data['series'] as $serie)
2657
				{
2658
					if ($data['seriestype'][$i]=='line') { $i++; continue; };
2659
					if ($outputserie > 0) print ',';
2660
					print '{ bars: { stack: 0, show: true, barWidth: 0.9, align: \'center\' }, label: \''.dol_escape_js($serie['label']).'\', data: '.json_encode($serie['data']).'}'."\n";
2661
					$outputserie++; $i++;
2662
				}
2663
				if ($outputserie) print ', ';
2664
				//print '];
2665
				//var datalines = [';
2666
				$i=0; $outputserie=0;
2667
				foreach($data['series'] as $serie)
2668
				{
2669
					if (empty($data['seriestype'][$i]) || $data['seriestype'][$i]=='bar') { $i++; continue; };
2670
					if ($outputserie > 0) print ',';
2671
					print '{ lines: { show: true }, label: \''.dol_escape_js($serie['label']).'\', data: '.json_encode($serie['data']).'}'."\n";
2672
					$outputserie++; $i++;
2673
				}
2674
				print '];
2675
				var dataticks = '.json_encode($data['xlabel']).'
2676
2677
				function plotWithOptions() {
2678
					$.plot(jQuery("#'.$htmlid.'"), data,
2679
					{
2680
						series: {
2681
							stack: 0
2682
						},
2683
						zoom: {
2684
							interactive: true
2685
						},
2686
						pan: {
2687
							interactive: true
2688
						},';
2689
						if (count($datacolor))
2690
						{
2691
							print 'colors: '.json_encode($datacolor).',';
2692
						}
2693
						print 'legend: {show: '.($showlegend?'true':'false').'},
2694
						xaxis: {ticks: dataticks}
2695
					});
2696
				}
2697
				plotWithOptions();
2698
			});
2699
			</script>';
2700
		}
2701
		else print 'BadValueForParameterType';
2702
	}
2703
}
2704
2705
/**
2706
 *	Truncate a string to a particular length adding '...' if string larger than length.
2707
 * 	If length = max length+1, we do no truncate to avoid having just 1 char replaced with '...'.
2708
 *  MAIN_DISABLE_TRUNC=1 can disable all truncings
2709
 *
2710
 *	@param	string	$string				String to truncate
2711
 *	@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 ...)
2712
 *	@param	string	$trunc				Where to trunc: right, left, middle (size must be a 2 power), wrap
2713
 * 	@param	string	$stringencoding		Tell what is source string encoding
2714
 *  @param	int		$nodot				Truncation do not add ... after truncation. So it's an exact truncation.
2715
 *  @param  int     $display            Trunc is use to display and can be changed for small screen. TODO Remove this param (must be dealt with CSS)
2716
 *	@return string						Truncated string. WARNING: length is never higher than $size if $nodot is set, but can be 3 chars higher otherwise.
2717
 */
2718
function dol_trunc($string,$size=40,$trunc='right',$stringencoding='UTF-8',$nodot=0, $display=0)
2719
{
2720
	global $conf;
2721
2722
	if ($size==0 || ! empty($conf->global->MAIN_DISABLE_TRUNC)) return $string;
2723
2724
	if (empty($stringencoding)) $stringencoding='UTF-8';
2725
	// reduce for small screen
2726
	if ($conf->dol_optimize_smallscreen==1 && $display==1) $size = round($size/3);
2727
2728
	// We go always here
2729
	if ($trunc == 'right')
2730
	{
2731
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2732
		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 ...
2733
		return dol_substr($newstring,0,$size,$stringencoding).($nodot?'':'...');
2734
		else
2735
		//return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
2736
		return $string;
2737
	}
2738
	elseif ($trunc == 'middle')
2739
	{
2740
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2741
		if (dol_strlen($newstring,$stringencoding) > 2 && dol_strlen($newstring,$stringencoding) > ($size+1))
2742
		{
2743
			$size1=round($size/2);
2744
			$size2=round($size/2);
2745
			return dol_substr($newstring,0,$size1,$stringencoding).'...'.dol_substr($newstring,dol_strlen($newstring,$stringencoding) - $size2,$size2,$stringencoding);
2746
		}
2747
		else
2748
		return $string;
2749
	}
2750
	elseif ($trunc == 'left')
2751
	{
2752
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2753
		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 ...
2754
		return '...'.dol_substr($newstring,dol_strlen($newstring,$stringencoding) - $size,$size,$stringencoding);
2755
		else
2756
		return $string;
2757
	}
2758
	elseif ($trunc == 'wrap')
2759
	{
2760
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2761
		if (dol_strlen($newstring,$stringencoding) > ($size+1))
2762
		return dol_substr($newstring,0,$size,$stringencoding)."\n".dol_trunc(dol_substr($newstring,$size,dol_strlen($newstring,$stringencoding)-$size,$stringencoding),$size,$trunc);
2763
		else
2764
		return $string;
2765
	}
2766
	else return 'BadParam3CallingDolTrunc';
2767
}
2768
2769
/**
2770
 *	Show picto whatever it's its name (generic function)
2771
 *
2772
 *	@param      string		$titlealt         	Text on title tag for tooltip. Not used if param notitle is set to 1.
2773
 *	@param      string		$picto       		Name of image file to show ('filenew', ...)
2774
 *												If no extension provided, we use '.png'. Image must be stored into theme/xxx/img directory.
2775
 *                                  			Example: picto.png                  if picto.png is stored into htdocs/theme/mytheme/img
2776
 *                                  			Example: picto.png@mymodule         if picto.png is stored into htdocs/mymodule/img
2777
 *                                  			Example: /mydir/mysubdir/picto.png  if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1)
2778
 *	@param		string		$moreatt			Add more attribute on img tag (For example 'style="float: right"')
2779
 *	@param		int			$pictoisfullpath	If 1, image path is a full path
2780
 *	@param		int			$srconly			Return only content of the src attribute of img.
2781
 *  @param		int			$notitle			1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip.
2782
 *  @param		string		$alt				Force alt for bind peoplae
2783
 *  @param		string		$morecss			Add more class css on img tag (For example 'myclascss')
2784
 *  @return     string       				    Return img tag
2785
 *  @see        #img_object, #img_picto_common
2786
 */
2787
function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly=0, $notitle=0, $alt='', $morecss='')
2788
{
2789
	global $conf, $langs;
2790
2791
	// Define fullpathpicto to use into src
2792
	if ($pictoisfullpath)
2793
	{
2794
		// Clean parameters
2795
		if (! preg_match('/(\.png|\.gif|\.svg)$/i',$picto)) $picto .= '.png';
2796
		$fullpathpicto = $picto;
2797
	}
2798
	else
2799
	{
2800
		//if (in_array($picto, array('switch_off', 'switch_on', 'off', 'on')))
2801
		if (in_array($picto, array('switch_off', 'switch_on', 'off', 'on')))
2802
		{
2803
			$fakey = $picto; $facolor=''; $fasize='';
2804
			if ($picto == 'switch_off') { $fakey = 'fa-toggle-off'; $facolor='#999';    $fasize='2em'; }
2805
			if ($picto == 'switch_on')  { $fakey = 'fa-toggle-on';  $facolor='#227722'; $fasize='2em'; }
2806
			if ($picto == 'off') { $fakey = 'fa-square-o'; $fasize='1.3em'; }
2807
			if ($picto == 'on')  { $fakey = 'fa-check-square-o'; $fasize='1.3em'; }
2808
			$enabledisablehtml='';
2809
			$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:'').'">';
2810
			if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) $enabledisablehtml.=$titlealt;
2811
			$enabledisablehtml.='</span>';
2812
			return $enabledisablehtml;
2813
		}
2814
2815
		// We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
2816
		$url = DOL_URL_ROOT;
2817
		$theme = $conf->theme;
2818
2819
		$path = 'theme/'.$theme;
2820
		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
2821
		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
2822
		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
2823
2824
		// If we ask an image into $url/$mymodule/img (instead of default path)
2825
		if (preg_match('/^([^@]+)@([^@]+)$/i',$picto,$regs))
2826
		{
2827
			$picto = $regs[1];
2828
			$path = $regs[2];	// $path is $mymodule
2829
		}
2830
2831
		// Clean parameters
2832
		if (! preg_match('/(\.png|\.gif|\.svg)$/i',$picto)) $picto .= '.png';
2833
		// If alt path are defined, define url where img file is, according to physical path
2834
		foreach ($conf->file->dol_document_root as $type => $dirroot)	// ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
2835
		{
2836
			if ($type == 'main') continue;
2837
			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
2838
			{
2839
				$url=DOL_URL_ROOT.$conf->file->dol_url_root[$type];
2840
				break;
2841
			}
2842
		}
2843
2844
		// $url is '' or '/custom', $path is current theme or
2845
		$fullpathpicto = $url.'/'.$path.'/img/'.$picto;
2846
	}
2847
2848
	if ($srconly) return $fullpathpicto;
2849
	else
2850
	{
2851
		// tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for bind people
2852
		//$tmparray=array(0=>$titlealt);
2853
		//if (empty($notitle) && preg_match('/:[^\s0-9]/',$titlealt)) $tmparray=explode(':',$titlealt);		// We explode if we have TextA:TextB. Not if we have TextA: TextB
2854
		//$title=$tmparray[0];
2855
		//$alt=empty($tmparray[1])?'':$tmparray[1];
2856
		$title=$titlealt;
2857
		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
2858
	}
2859
}
2860
2861
/**
2862
 *	Show a picto called object_picto (generic function)
2863
 *
2864
 *	@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.
2865
 *	@param	string	$picto				Name of image to show object_picto (example: user, group, action, bill, contract, propal, product, ...)
2866
 *										For external modules use imagename@mymodule to search into directory "img" of module.
2867
 *	@param	string	$moreatt			Add more attribute on img tag (ie: class="datecallink")
2868
 *	@param	int		$pictoisfullpath	If 1, image path is a full path
2869
 *	@param	int		$srconly			Return only content of the src attribute of img.
2870
 *  @param	int		$notitle			1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip.
2871
 *	@return	string						Return img tag
2872
 *	@see	#img_picto, #img_picto_common
2873
 */
2874
function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly=0, $notitle=0)
2875
{
2876
	return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
2877
}
2878
2879
/**
2880
 *	Show weather picto
2881
 *
2882
 *	@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.
2883
 *	@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.
2884
 *	@param		string		$moreatt			Add more attribute on img tag
2885
 *	@param		int			$pictoisfullpath	If 1, image path is a full path
2886
 *	@return     string      					Return img tag
2887
 *  @see        #img_object, #img_picto
2888
 */
2889
function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0)
2890
{
2891
	global $conf;
2892
2893
	if (! preg_match('/(\.png|\.gif)$/i', $picto)) $picto .= '.png';
2894
2895
	$path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
2896
2897
	return img_picto($titlealt, $path, $moreatt, 1);
2898
}
2899
2900
/**
2901
 *	Show picto (generic function)
2902
 *
2903
 *	@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.
2904
 *	@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.
2905
 *	@param		string		$moreatt			Add more attribute on img tag
2906
 *	@param		int			$pictoisfullpath	If 1, image path is a full path
2907
 *	@return     string      					Return img tag
2908
 *  @see        #img_object, #img_picto
2909
 */
2910
function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0)
2911
{
2912
	global $conf;
2913
2914
	if (! preg_match('/(\.png|\.gif)$/i', $picto)) $picto .= '.png';
2915
2916
	if ($pictoisfullpath) $path = $picto;
2917
	else
2918
	{
2919
		$path = DOL_URL_ROOT.'/theme/common/'.$picto;
2920
2921
		if (! empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS))
2922
		{
2923
			$themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
2924
2925
			if (file_exists($themepath)) $path = $themepath;
2926
		}
2927
	}
2928
2929
	return img_picto($titlealt, $path, $moreatt, 1);
2930
}
2931
2932
/**
2933
 *	Show logo action
2934
 *
2935
 *	@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.
2936
 *	@param  string		$numaction   	Action id or code to show
2937
 *	@return string      				Return an img tag
2938
 */
2939
function img_action($titlealt, $numaction)
2940
{
2941
	global $conf, $langs;
2942
2943
	if (empty($titlealt) || $titlealt == 'default')
2944
	{
2945
		if ($numaction == '-1' || $numaction == 'ST_NO')			{ $numaction = -1; $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact'); }
2946
		elseif ($numaction ==  '0' || $numaction == 'ST_NEVER') 	{ $numaction = 0; $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted'); }
2947
		elseif ($numaction ==  '1' || $numaction == 'ST_TODO')  	{ $numaction = 1; $titlealt = $langs->transnoentitiesnoconv('ChangeToContact'); }
2948
		elseif ($numaction ==  '2' || $numaction == 'ST_PEND')  	{ $numaction = 2; $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess'); }
2949
		elseif ($numaction ==  '3' || $numaction == 'ST_DONE')  	{ $numaction = 3; $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone'); }
2950
		else { $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction); $numaction = 0; }
2951
	}
2952
	if (! is_numeric($numaction)) $numaction=0;
2953
2954
	return img_picto($titlealt, 'stcomm'.$numaction.'.png');
2955
}
2956
2957
/**
2958
 *  Show pdf logo
2959
 *
2960
 *  @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.
2961
 *  @param  int		    $size       Taille de l'icone : 3 = 16x16px , 2 = 14x14px
2962
 *  @return string      			Retourne tag img
2963
 */
2964
function img_pdf($titlealt = 'default', $size = 3)
2965
{
2966
	global $conf, $langs;
2967
2968
	if ($titlealt == 'default') $titlealt = $langs->trans('Show');
2969
2970
	return img_picto($titlealt, 'pdf'.$size.'.png');
2971
}
2972
2973
/**
2974
 *	Show logo +
2975
 *
2976
 *	@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.
2977
 *	@param  string	$other      Add more attributes on img
2978
 *	@return string      		Return tag img
2979
 */
2980
function img_edit_add($titlealt = 'default', $other = '')
2981
{
2982
	global $conf, $langs;
2983
2984
	if ($titlealt == 'default') $titlealt = $langs->trans('Add');
2985
2986
	return img_picto($titlealt, 'edit_add.png', $other);
2987
}
2988
/**
2989
 *	Show logo -
2990
 *
2991
 *	@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.
2992
 *	@param  string	$other      Add more attributes on img
2993
 *	@return string      		Return tag img
2994
 */
2995
function img_edit_remove($titlealt = 'default', $other='')
2996
{
2997
	global $conf, $langs;
2998
2999
	if ($titlealt == 'default') $titlealt = $langs->trans('Remove');
3000
3001
	return img_picto($titlealt, 'edit_remove.png', $other);
3002
}
3003
3004
/**
3005
 *	Show logo editer/modifier fiche
3006
 *
3007
 *	@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.
3008
 *	@param  integer	$float      Si il faut y mettre le style "float: right"
3009
 *	@param  string	$other		Add more attributes on img
3010
 *	@return string      		Return tag img
3011
 */
3012
function img_edit($titlealt = 'default', $float = 0, $other = 'class="pictoedit"')
3013
{
3014
	global $conf, $langs;
3015
3016
	if ($titlealt == 'default') $titlealt = $langs->trans('Modify');
3017
3018
	return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl'?'left':'right').'"' : "") . ($other?' '.$other:''));
3019
}
3020
3021
/**
3022
 *	Show logo view card
3023
 *
3024
 *	@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.
3025
 *	@param  integer	$float      Si il faut y mettre le style "float: right"
3026
 *	@param  string	$other		Add more attributes on img
3027
 *	@return string      		Return tag img
3028
 */
3029
function img_view($titlealt = 'default', $float = 0, $other = '')
3030
{
3031
	global $conf, $langs;
3032
3033
	if ($titlealt == 'default') $titlealt = $langs->trans('View');
3034
3035
	$moreatt = ($float ? 'style="float: right" ' : '').$other;
3036
3037
	return img_picto($titlealt, 'view.png', $moreatt);
3038
}
3039
3040
/**
3041
 *  Show delete logo
3042
 *
3043
 *  @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.
3044
 *	@param  string	$other      Add more attributes on img
3045
 *  @return string      		Retourne tag img
3046
 */
3047
function img_delete($titlealt = 'default', $other = 'class="pictodelete"')
3048
{
3049
	global $conf, $langs;
3050
3051
	if ($titlealt == 'default') $titlealt = $langs->trans('Delete');
3052
3053
	return img_picto($titlealt, 'delete.png', $other);
3054
}
3055
3056
/**
3057
 *  Show printer logo
3058
 *
3059
 *  @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.
3060
 *  @param  string  $other      Add more attributes on img
3061
 *  @return string              Retourne tag img
3062
 */
3063
function img_printer($titlealt = "default", $other='')
3064
{
3065
	global $conf,$langs;
3066
	if ($titlealt=="default") $titlealt=$langs->trans("Print");
3067
	return img_picto($titlealt,'printer.png',$other);
3068
}
3069
3070
/**
3071
 *  Show split logo
3072
 *
3073
 *  @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.
3074
 *	@param  string	$other      Add more attributes on img
3075
 *  @return string      		Retourne tag img
3076
 */
3077
function img_split($titlealt = 'default', $other = 'class="pictosplit"')
3078
{
3079
	global $conf, $langs;
3080
3081
	if ($titlealt == 'default') $titlealt = $langs->trans('Split');
3082
3083
	return img_picto($titlealt, 'split.png', $other);
3084
}
3085
3086
/**
3087
 *	Show help logo with cursor "?"
3088
 *
3089
 * 	@param	int              	$usehelpcursor		1=Use help cursor, 2=Use click pointer cursor, 0=No specific cursor
3090
 * 	@param	int|string	        $usealttitle		Text to use as alt title
3091
 * 	@return string            	           			Return tag img
3092
 */
3093
function img_help($usehelpcursor = 1, $usealttitle = 1)
3094
{
3095
	global $conf, $langs;
3096
3097
	if ($usealttitle)
3098
	{
3099
		if (is_string($usealttitle)) $usealttitle = dol_escape_htmltag($usealttitle);
3100
		else $usealttitle = $langs->trans('Info');
3101
	}
3102
3103
	return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help': ($usehelpcursor == 2 ? ' cursor: pointer':'')).'"');
3104
}
3105
3106
/**
3107
 *	Show info logo
3108
 *
3109
 *	@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.
3110
 *	@return string      		Return img tag
3111
 */
3112
function img_info($titlealt = 'default')
3113
{
3114
	global $conf, $langs;
3115
3116
	if ($titlealt == 'default') $titlealt = $langs->trans('Informations');
3117
3118
	return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
3119
}
3120
3121
/**
3122
 *	Show warning logo
3123
 *
3124
 *	@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.
3125
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"'). If 1, add float: right. Can't be "class" attribute.
3126
 *	@return string      		Return img tag
3127
 */
3128
function img_warning($titlealt = 'default', $moreatt = '')
3129
{
3130
	global $conf, $langs;
3131
3132
	if ($titlealt == 'default') $titlealt = $langs->trans('Warning');
3133
3134
	//return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
3135
	return img_picto($titlealt, 'warning.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): ''));
3136
}
3137
3138
/**
3139
 *  Show error logo
3140
 *
3141
 *	@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.
3142
 *	@return string      		Return img tag
3143
 */
3144
function img_error($titlealt = 'default')
3145
{
3146
	global $conf, $langs;
3147
3148
	if ($titlealt == 'default') $titlealt = $langs->trans('Error');
3149
3150
	return img_picto($titlealt, 'error.png', 'class="valigntextbottom"');
3151
}
3152
3153
/**
3154
 *	Show next logo
3155
 *
3156
 *	@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.
3157
*	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3158
 *	@return string      		Return img tag
3159
 */
3160
function img_next($titlealt = 'default', $moreatt='')
3161
{
3162
	global $conf, $langs;
3163
3164
	if ($titlealt == 'default') $titlealt = $langs->trans('Next');
3165
3166
	//return img_picto($titlealt, 'next.png', $moreatt);
3167
	return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
3168
}
3169
3170
/**
3171
 *	Show previous logo
3172
 *
3173
 *	@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.
3174
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3175
 *	@return string      		Return img tag
3176
 */
3177
function img_previous($titlealt = 'default', $moreatt='')
3178
{
3179
	global $conf, $langs;
3180
3181
	if ($titlealt == 'default') $titlealt = $langs->trans('Previous');
3182
3183
	//return img_picto($titlealt, 'previous.png', $moreatt);
3184
	return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
3185
}
3186
3187
/**
3188
 *	Show down arrow logo
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  int		$selected   Selected
3192
 *  @param	string	$moreclass	Add more CSS classes
3193
 *	@return string      		Return img tag
3194
 */
3195
function img_down($titlealt = 'default', $selected = 0, $moreclass='')
3196
{
3197
	global $conf, $langs;
3198
3199
	if ($titlealt == 'default') $titlealt = $langs->trans('Down');
3200
3201
	return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass?" ".$moreclass:"").'"');
3202
}
3203
3204
/**
3205
 *	Show top arrow logo
3206
 *
3207
 *	@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.
3208
 *	@param  int		$selected	Selected
3209
 *  @param	string	$moreclass	Add more CSS classes
3210
 *	@return string      		Return img tag
3211
 */
3212
function img_up($titlealt = 'default', $selected = 0, $moreclass='')
3213
{
3214
	global $conf, $langs;
3215
3216
	if ($titlealt == 'default') $titlealt = $langs->trans('Up');
3217
3218
	return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass?" ".$moreclass:"").'"');
3219
}
3220
3221
/**
3222
 *	Show left arrow logo
3223
 *
3224
 *	@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.
3225
 *	@param  int		$selected	Selected
3226
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3227
 *	@return string      		Return img tag
3228
 */
3229
function img_left($titlealt = 'default', $selected = 0, $moreatt='')
3230
{
3231
	global $conf, $langs;
3232
3233
	if ($titlealt == 'default') $titlealt = $langs->trans('Left');
3234
3235
	return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
3236
}
3237
3238
/**
3239
 *	Show right arrow logo
3240
 *
3241
 *	@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.
3242
 *	@param  int		$selected	Selected
3243
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3244
 *	@return string      		Return img tag
3245
 */
3246
function img_right($titlealt = 'default', $selected = 0, $moreatt='')
3247
{
3248
	global $conf, $langs;
3249
3250
	if ($titlealt == 'default') $titlealt = $langs->trans('Right');
3251
3252
	return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
3253
}
3254
3255
/**
3256
 *	Show tick logo if allowed
3257
 *
3258
 *	@param	string	$allow		Allow
3259
 *	@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.
3260
 *	@return string      		Return img tag
3261
 */
3262
function img_allow($allow, $titlealt = 'default')
3263
{
3264
	global $conf, $langs;
3265
3266
	if ($titlealt == 'default') $titlealt = $langs->trans('Active');
3267
3268
	if ($allow == 1) return img_picto($titlealt, 'tick.png');
3269
3270
	return '-';
3271
}
3272
3273
3274
/**
3275
 *	Show MIME img of a file
3276
 *
3277
 *	@param	string	$file		Filename
3278
 * 	@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.
3279
 *  @param	string	$morecss	More css
3280
 *	@return string     			Return img tag
3281
 */
3282
function img_mime($file, $titlealt = '', $morecss='')
3283
{
3284
	require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3285
3286
	$mimetype = dol_mimetype($file, '', 1);
3287
	$mimeimg = dol_mimetype($file, '', 2);
3288
	$mimefa = dol_mimetype($file, '', 4);
3289
3290
	if (empty($titlealt)) $titlealt = 'Mime type: '.$mimetype;
3291
3292
	//return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
3293
	return '<i class="fa fa-'.$mimefa.' paddingright"></i>';
3294
}
3295
3296
3297
/**
3298
 *	Show phone logo.
3299
 *  Use img_picto instead.
3300
 *
3301
 *	@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.
3302
 *	@param  int		$option		Option
3303
 *	@return string      		Return img tag
3304
 *  @deprecated
3305
 *  @see img_picto
3306
 */
3307
function img_phone($titlealt = 'default', $option = 0)
3308
{
3309
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
3310
3311
	global $conf,$langs;
3312
3313
	if ($titlealt == 'default') $titlealt = $langs->trans('Call');
3314
3315
	if ($option == 1) $img = 'call';
3316
	else $img = 'call_out';
3317
3318
	return img_picto($titlealt, $img);
3319
}
3320
3321
/**
3322
 *  Show search logo
3323
 *
3324
 *  @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.
3325
 *	@param  string	$other      Add more attributes on img
3326
 *  @return string      		Retourne tag img
3327
 */
3328
function img_search($titlealt = 'default', $other = '')
3329
{
3330
	global $conf, $langs;
3331
3332
	if ($titlealt == 'default') $titlealt = $langs->trans('Search');
3333
3334
	$img = img_picto($titlealt, 'search.png', $other, false, 1);
3335
3336
	$input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
3337
	$input.= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
3338
3339
	return $input;
3340
}
3341
3342
/**
3343
 *  Show search logo
3344
 *
3345
 *  @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.
3346
 *	@param  string	$other      Add more attributes on img
3347
 *  @return string      		Retourne tag img
3348
 */
3349
function img_searchclear($titlealt = 'default', $other = '')
3350
{
3351
	global $conf, $langs;
3352
3353
	if ($titlealt == 'default') $titlealt = $langs->trans('Search');
3354
3355
	$img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
3356
3357
	$input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
3358
	$input.= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
3359
3360
	return $input;
3361
}
3362
3363
/**
3364
 *	Show information for admin users or standard users
3365
 *
3366
 *	@param	string	$text			Text info
3367
 *	@param  integer	$infoonimgalt	Info is shown only on alt of star picto, otherwise it is show on output after the star picto
3368
 *	@param	int		$nodiv			No div
3369
 *  @param  string  $admin          '1'=Info for admin users. '0'=Info for standard users (change only the look), 'xxx'=Other
3370
 *  @param	string	$morecss		More CSS
3371
 *	@return	string					String with info text
3372
 */
3373
function info_admin($text, $infoonimgalt = 0, $nodiv=0, $admin='1', $morecss='')
3374
{
3375
	global $conf, $langs;
3376
3377
	if ($infoonimgalt)
3378
	{
3379
		return img_picto($text, 'info', 'class="hideonsmartphone'.($morecss?' '.$morecss:'').'"');
3380
	}
3381
3382
	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>');
3383
}
3384
3385
3386
/**
3387
 *	Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remontee des bugs.
3388
 *	On doit appeler cette fonction quand une erreur technique bloquante est rencontree.
3389
 *	Toutefois, il faut essayer de ne l'appeler qu'au sein de pages php, les classes devant
3390
 *	renvoyer leur erreur par l'intermediaire de leur propriete "error".
3391
 *
3392
 *	@param	 	DoliDB	$db      	Database handler
3393
 *	@param  	mixed	$error		String or array of errors strings to show
3394
 *  @param		array	$errors		Array of errors
3395
 *	@return 	void
3396
 *  @see    	dol_htmloutput_errors
3397
 */
3398
function dol_print_error($db='',$error='',$errors=null)
3399
{
3400
	global $conf,$langs,$argv;
3401
	global $dolibarr_main_prod;
3402
3403
	$out = '';
3404
	$syslog = '';
3405
3406
	// Si erreur intervenue avant chargement langue
3407
	if (! $langs)
3408
	{
3409
		require_once DOL_DOCUMENT_ROOT .'/core/class/translate.class.php';
3410
		$langs = new Translate('', $conf);
3411
		$langs->load("main");
3412
	}
3413
	$langs->load("main");
3414
	$langs->load("errors");
3415
3416
	if ($_SERVER['DOCUMENT_ROOT'])    // Mode web
3417
	{
3418
		$out.=$langs->trans("DolibarrHasDetectedError").".<br>\n";
3419
		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";
3420
		$out.=$langs->trans("InformationToHelpDiagnose").":<br>\n";
3421
3422
		$out.="<b>".$langs->trans("Date").":</b> ".dol_print_date(time(),'dayhourlog')."<br>\n";
3423
		$out.="<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION."<br>\n";
3424
		if (isset($conf->global->MAIN_FEATURES_LEVEL)) $out.="<b>".$langs->trans("LevelOfFeature").":</b> ".$conf->global->MAIN_FEATURES_LEVEL."<br>\n";
3425
		if (function_exists("phpversion"))
3426
		{
3427
			$out.="<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
3428
		}
3429
		$out.="<b>".$langs->trans("Server").":</b> ".$_SERVER["SERVER_SOFTWARE"]."<br>\n";
3430
		if (function_exists("php_uname"))
3431
		{
3432
			$out.="<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
3433
		}
3434
		$out.="<b>".$langs->trans("UserAgent").":</b> ".$_SERVER["HTTP_USER_AGENT"]."<br>\n";
3435
		$out.="<br>\n";
3436
		$out.="<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"],ENT_COMPAT,'UTF-8')."<br>\n";
3437
		$out.="<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"])?dol_htmlentities($_SERVER["HTTP_REFERER"],ENT_COMPAT,'UTF-8'):'')."<br>\n";
3438
		$out.="<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu)?$conf->standard_menu:'')."<br>\n";
3439
		$out.="<br>\n";
3440
		$syslog.="url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
3441
		$syslog.=", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
3442
	}
3443
	else                              // Mode CLI
3444
	{
3445
		$out.='> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
3446
		$syslog.="pid=".dol_getmypid();
3447
	}
3448
3449
	if (is_object($db))
3450
	{
3451
		if ($_SERVER['DOCUMENT_ROOT'])  // Mode web
3452
		{
3453
			$out.="<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
3454
			$out.="<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror()?dol_escape_htmltag($db->lastqueryerror()):$langs->trans("ErrorNoRequestInError"))."<br>\n";
3455
			$out.="<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno()?dol_escape_htmltag($db->lasterrno()):$langs->trans("ErrorNoRequestInError"))."<br>\n";
3456
			$out.="<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror()?dol_escape_htmltag($db->lasterror()):$langs->trans("ErrorNoRequestInError"))."<br>\n";
3457
			$out.="<br>\n";
3458
		}
3459
		else                            // Mode CLI
3460
		{
3461
			// No dol_escape_htmltag for output, we are in CLI mode
3462
			$out.='> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
3463
			$out.='> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror()?$db->lastqueryerror():$langs->transnoentities("ErrorNoRequestInError"))."\n";
3464
			$out.='> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno()?$db->lasterrno():$langs->transnoentities("ErrorNoRequestInError"))."\n";
3465
			$out.='> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror()?$db->lasterror():$langs->transnoentities("ErrorNoRequestInError"))."\n";
3466
3467
		}
3468
		$syslog.=", sql=".$db->lastquery();
3469
		$syslog.=", db_error=".$db->lasterror();
3470
	}
3471
3472
	if ($error || $errors)
3473
	{
3474
		$langs->load("errors");
3475
3476
		// Merge all into $errors array
3477
		if (is_array($error) && is_array($errors)) $errors=array_merge($error,$errors);
3478
		elseif (is_array($error)) $errors=$error;
3479
		elseif (is_array($errors)) $errors=array_merge(array($error),$errors);
3480
		else $errors=array_merge(array($error));
3481
3482
		foreach($errors as $msg)
3483
		{
3484
			if (empty($msg)) continue;
3485
			if ($_SERVER['DOCUMENT_ROOT'])  // Mode web
3486
			{
3487
				$out.="<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n" ;
3488
			}
3489
			else                        // Mode CLI
3490
			{
3491
				$out.='> '.$langs->transnoentities("Message").":\n".$msg."\n" ;
3492
			}
3493
			$syslog.=", msg=".$msg;
3494
		}
3495
	}
3496
	if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file'))
3497
	{
3498
		xdebug_print_function_stack();
3499
		$out.='<b>XDebug informations:</b>'."<br>\n";
3500
		$out.='File: '.xdebug_call_file()."<br>\n";
3501
		$out.='Line: '.xdebug_call_line()."<br>\n";
3502
		$out.='Function: '.xdebug_call_function()."<br>\n";
3503
		$out.="<br>\n";
3504
	}
3505
3506
	if (empty($dolibarr_main_prod)) print $out;
3507
	else
3508
	{
3509
		print $langs->trans("DolibarrHasDetectedError").'. ';
3510
		print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
3511
		define("MAIN_CORE_ERROR", 1);
3512
	}
3513
	//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.';
3514
	dol_syslog("Error ".$syslog, LOG_ERR);
3515
}
3516
3517
/**
3518
 * Show a public email and error code to contact if technical error
3519
 *
3520
 * @param	string	$prefixcode		Prefix of public error code
3521
 * @param   string  $errormessage   Complete error message
3522
 * @param	array	$errormessages	Array of error messages
3523
 * @param	string	$morecss		More css
3524
 * @return	void
3525
 */
3526
function dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error')
3527
{
3528
	global $langs,$conf;
3529
3530
	$langs->load("errors");
3531
	$now=dol_now();
3532
	print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
3533
	print $langs->trans("ErrorContactEMail", $conf->global->MAIN_INFO_SOCIETE_MAIL, $prefixcode.dol_print_date($now,'%Y%m%d'));
3534
	if ($errormessage) print '<br><br>'.$errormessage;
3535
	if (is_array($errormessages) && count($errormessages))
3536
	{
3537
		foreach($errormessages as $mesgtoshow)
3538
		{
3539
			print '<br><br>'.$mesgtoshow;
3540
		}
3541
	}
3542
	print '</div></div>';
3543
}
3544
3545
/**
3546
 *	Show title line of an array
3547
 *
3548
 *	@param	string	$name        Label of field
3549
 *	@param	string	$file        Url used when we click on sort picto
3550
 *	@param	string	$field       Field to use for new sorting
3551
 *	@param	string	$begin       ("" by defaut)
3552
 *	@param	string	$moreparam   Add more parameters on sort url links ("" by default)
3553
 *	@param  string	$moreattrib  Options of attribute td ("" by defaut, example: 'align="center"')
3554
 *	@param  string	$sortfield   Current field used to sort
3555
 *	@param  string	$sortorder   Current sort order
3556
 *  @param	string	$prefix		 Prefix for css. Use space after prefix to add your own CSS tag.
3557
 *  @param	string	$tooltip	 Tooltip
3558
 *	@return	void
3559
 */
3560
function print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="")
3561
{
3562
	print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip);
3563
}
3564
3565
/**
3566
 *	Get title line of an array
3567
 *
3568
 *	@param	string	$name        		Translation key of field
3569
 *	@param	int		$thead		 		0=To use with standard table format, 1=To use inside <thead><tr>, 2=To use with <div>
3570
 *	@param	string	$file        		Url used when we click on sort picto
3571
 *	@param	string	$field       		Field to use for new sorting. Empty if this field is not sortable.
3572
 *	@param	string	$begin       		("" by defaut)
3573
 *	@param	string	$moreparam   		Add more parameters on sort url links ("" by default)
3574
 *	@param  string	$moreattrib  		Add more attributes on th ("" by defaut, example: 'align="center"'). To add more css class, use param $prefix.
3575
 *	@param  string	$sortfield   		Current field used to sort (Ex: 'd.datep,d.id')
3576
 *	@param  string	$sortorder   		Current sort order (Ex: 'asc,desc')
3577
 *  @param	string	$prefix		 		Prefix for css. Use space after prefix to add your own CSS tag, for example 'mycss '.
3578
 *  @param	string	$disablesortlink	1=Disable sort link
3579
 *  @param	string	$tooltip	 		Tooltip
3580
 *	@return	string
3581
 */
3582
function getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip='')
3583
{
3584
	global $conf, $langs, $form;
3585
	//print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
3586
3587
	$sortorder=strtoupper($sortorder);
3588
	$out='';
3589
	$sortimg='';
3590
3591
	$tag='th';
3592
	if ($thead==2) $tag='div';
3593
3594
	$tmpsortfield=explode(',',$sortfield);
3595
	$sortfield1=trim($tmpsortfield[0]);    // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
3596
	$tmpfield=explode(',',$field);
3597
	$field1=trim($tmpfield[0]);            // If $field is 'd.datep,d.id', it becomes 'd.datep'
3598
3599
	//var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
3600
	// If field is used as sort criteria we use a specific css class liste_titre_sel
3601
	// Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
3602
	if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./","",$field1))) $out.= '<'.$tag.' class="'.$prefix.'liste_titre_sel" '. $moreattrib.'>';
3603
	else $out.= '<'.$tag.' class="'.$prefix.'liste_titre" '. $moreattrib.'>';
3604
3605
	if (empty($thead) && $field && empty($disablesortlink))    // If this is a sort field
3606
	{
3607
		$options=preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i','',$moreparam);
3608
		$options=preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i','',$options);
3609
		$options=preg_replace('/&+/i','&',$options);
3610
		if (! preg_match('/^&/',$options)) $options='&'.$options;
3611
3612
		if ($field1 != $sortfield1) // We are on another field
3613
		{
3614
			if (preg_match('/^DESC/', $sortorder)) $out.= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">';
3615
			else $out.= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">';
3616
		}
3617
		else                      // We are of first sorting criteria
3618
		{
3619
			if (preg_match('/^ASC/', $sortorder)) $out.= '<a class="reposition" href="'.$file.'?sortfield='.$sortfield.'&sortorder=desc&begin='.$begin.$options.'">';
3620
			else $out.= '<a class="reposition" href="'.$file.'?sortfield='.$sortfield.'&sortorder=asc&begin='.$begin.$options.'">';
3621
		}
3622
	}
3623
3624
	if ($tooltip) $out.=$form->textwithpicto($langs->trans($name), $langs->trans($tooltip));
3625
	else $out.=$langs->trans($name);
3626
3627
	if (empty($thead) && $field && empty($disablesortlink))    // If this is a sort field
3628
	{
3629
		$out.='</a>';
3630
	}
3631
3632
	if (empty($thead) && $field)    // If this is a sort field
3633
	{
3634
		$options=preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i','',$moreparam);
3635
		$options=preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i','',$options);
3636
		$options=preg_replace('/&+/i','&',$options);
3637
		if (! preg_match('/^&/',$options)) $options='&'.$options;
3638
3639
		//print "&nbsp;";
3640
		//$sortimg.= '<img width="2" src="'.DOL_URL_ROOT.'/theme/common/transparent.png" alt="">';
3641
		//$sortimg.= '<span class="nowrap">';
3642
3643
		if (! $sortorder || $field1 != $sortfield1)
3644
		{
3645
			//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
3646
			//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
3647
		}
3648
		else
3649
		{
3650
			if (preg_match('/^DESC/', $sortorder)) {
3651
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
3652
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
3653
				$sortimg.= '<span class="nowrap">'.img_up("Z-A",0).'</span>';
3654
			}
3655
			if (preg_match('/^ASC/', $sortorder)) {
3656
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
3657
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
3658
				$sortimg.= '<span class="nowrap">'.img_down("A-Z",0).'</span>';
3659
			}
3660
		}
3661
3662
		//$sortimg.= '</span>';
3663
	}
3664
3665
	$out.=$sortimg;
3666
3667
	$out.='</'.$tag.'>';
3668
3669
	return $out;
3670
}
3671
3672
/**
3673
 *	Show a title.
3674
 *
3675
 *	@param	string	$title			Title to show
3676
 *	@return	string					Title to show
3677
 *  @deprecated						Use load_fiche_titre instead
3678
 *  @see load_fiche_titre
3679
 */
3680
function print_titre($title)
3681
{
3682
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
3683
3684
	print '<div class="titre">'.$title.'</div>';
3685
}
3686
3687
/**
3688
 *	Show a title with picto
3689
 *
3690
 *	@param	string	$title				Title to show
3691
 *	@param	string	$mesg				Added message to show on right
3692
 *	@param	string	$picto				Icon to use before title (should be a 32x32 transparent png file)
3693
 *	@param	int		$pictoisfullpath	1=Icon name is a full absolute url of image
3694
 * 	@param	int		$id					To force an id on html objects
3695
 * 	@return	void
3696
 *  @deprecated Use print load_fiche_titre instead
3697
 */
3698
function print_fiche_titre($title, $mesg='', $picto='title_generic.png', $pictoisfullpath=0, $id='')
3699
{
3700
	print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
3701
}
3702
3703
/**
3704
 *	Load a title with picto
3705
 *
3706
 *	@param	string	$titre				Title to show
3707
 *	@param	string	$morehtmlright		Added message to show on right
3708
 *	@param	string	$picto				Icon to use before title (should be a 32x32 transparent png file)
3709
 *	@param	int		$pictoisfullpath	1=Icon name is a full absolute url of image
3710
 * 	@param	int		$id					To force an id on html objects
3711
 *  @param  string  $morecssontable     More css on table
3712
 *	@param	string	$morehtmlcenter		Added message to show on center
3713
 * 	@return	string
3714
 *  @see print_barre_liste
3715
 */
3716
function load_fiche_titre($titre, $morehtmlright='', $picto='title_generic.png', $pictoisfullpath=0, $id=0, $morecssontable='', $morehtmlcenter='')
3717
{
3718
	global $conf;
3719
3720
	$return='';
3721
3722
	if ($picto == 'setup') $picto='title.png';
3723
	if (($conf->browser->name == 'ie') && $picto=='title.png') $picto='title.gif';
3724
3725
	$return.= "\n";
3726
	$return.= '<table '.($id?'id="'.$id.'" ':'').'summary="" class="centpercent notopnoleftnoright'.($morecssontable?' '.$morecssontable:'').'" style="margin-bottom: 2px;"><tr>';
3727
	if ($picto) $return.= '<td class="nobordernopadding widthpictotitle" valign="middle">'.img_picto('',$picto, 'class="valignmiddle widthpictotitle" id="pictotitle"', $pictoisfullpath).'</td>';
3728
	$return.= '<td class="nobordernopadding" valign="middle">';
3729
	$return.= '<div class="titre">'.$titre.'</div>';
3730
	$return.= '</td>';
3731
	if (dol_strlen($morehtmlcenter))
3732
	{
3733
		$return.= '<td class="nobordernopadding" align="center" valign="middle">'.$morehtmlcenter.'</td>';
3734
	}
3735
	if (dol_strlen($morehtmlright))
3736
	{
3737
		$return.= '<td class="nobordernopadding titre_right" align="right" valign="middle">'.$morehtmlright.'</td>';
3738
	}
3739
	$return.= '</tr></table>'."\n";
3740
3741
	return $return;
3742
}
3743
3744
/**
3745
 *	Print a title with navigation controls for pagination
3746
 *
3747
 *	@param	string	    $titre				Title to show (required)
3748
 *	@param	int   	    $page				Numero of page to show in navigation links (required)
3749
 *	@param	string	    $file				Url of page (required)
3750
 *	@param	string	    $options         	More parameters for links ('' by default, does not include sortfield neither sortorder). Value must be 'urlencoded' before calling function.
3751
 *	@param	string    	$sortfield       	Field to sort on ('' by default)
3752
 *	@param	string	    $sortorder       	Order to sort ('' by default)
3753
 *	@param	string	    $morehtmlcenter     String in the middle ('' by default). We often find here string $massaction comming from $form->selectMassAction()
3754
 *	@param	int		    $num				Number of records found by select with limit+1
3755
 *	@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.
3756
 *	@param	string	    $picto				Icon to use before title (should be a 32x32 transparent png file)
3757
 *	@param	int		    $pictoisfullpath	1=Icon name is a full absolute url of image
3758
 *  @param	string	    $morehtmlright			More html to show
3759
 *  @param  string      $morecss            More css to the table
3760
 *  @param  int         $limit              Max number of lines (-1 = use default, 0 = no limit, > 0 = limit).
3761
 *  @param  int         $hideselectlimit    Force to hide select limit
3762
 *  @param  int         $hidenavigation     Force to hide all navigation tools
3763
 *	@return	void
3764
 */
3765
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)
3766
{
3767
	global $conf,$langs;
3768
3769
	$savlimit = $limit;
3770
	$savtotalnboflines = $totalnboflines;
3771
	$totalnboflines=abs($totalnboflines);
3772
3773
	if ($picto == 'setup') $picto='title_setup.png';
3774
	if (($conf->browser->name == 'ie') && $picto=='title_generic.png') $picto='title.gif';
3775
	if ($limit < 0) $limit = $conf->liste_limit;
3776
	if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0)))
3777
	{
3778
		$nextpage = 1;
3779
	}
3780
	else
3781
	{
3782
		$nextpage = 0;
3783
	}
3784
	//print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage;
3785
3786
	print "\n";
3787
	print "<!-- Begin title '".$titre."' -->\n";
3788
	print '<table width="100%" border="0" class="notopnoleftnoright'.($morecss?' '.$morecss:'').'" style="margin-bottom: 6px;"><tr>';
3789
3790
	// Left
3791
	//if ($picto && $titre) print '<td class="nobordernopadding hideonsmartphone" width="40" align="left" valign="middle">'.img_picto('', $picto, 'id="pictotitle"', $pictoisfullpath).'</td>';
3792
	print '<td class="nobordernopadding valignmiddle">';
3793
	if ($picto && $titre) print img_picto('', $picto, 'class="hideonsmartphone valignmiddle" id="pictotitle"', $pictoisfullpath);
3794
	print '<div class="titre inline-block">'.$titre;
3795
	if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') print ' ('.$totalnboflines.')';
3796
	print '</div></td>';
3797
3798
	// Center
3799
	if ($morehtmlcenter)
3800
	{
3801
		print '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>';
3802
	}
3803
3804
	// Right
3805
	print '<td class="nobordernopadding valignmiddle" align="right">';
3806
	if ($sortfield) $options .= "&sortfield=".urlencode($sortfield);
3807
	if ($sortorder) $options .= "&sortorder=".urlencode($sortorder);
3808
	// Show navigation bar
3809
	$pagelist = '';
3810
	if ($savlimit != 0 && ($page > 0 || $num > $limit))
3811
	{
3812
		if ($totalnboflines)	// If we know total nb of lines
3813
		{
3814
			$maxnbofpage=(empty($conf->dol_optimize_smallscreen) ? 4 : 1);		// page nb before and after selected page + ... + first or last
3815
3816
			if ($limit > 0) $nbpages=ceil($totalnboflines/$limit);
3817
			else $nbpages=1;
3818
			$cpt=($page-$maxnbofpage);
3819
			if ($cpt < 0) { $cpt=0; }
3820
3821
			if ($cpt>=1)
3822
			{
3823
				$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page=0'.$options.'">1</a></li>';
3824
				if ($cpt > 2) $pagelist.='<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="inactive"':'').'>...</span></li>';
3825
				else if ($cpt == 2) $pagelist.='<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page=1'.$options.'">2</a></li>';
3826
			}
3827
3828
			do
3829
			{
3830
				if ($cpt==$page)
3831
				{
3832
					$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="active"':'').'>'.($page+1).'</span></li>';
3833
				}
3834
				else
3835
				{
3836
					$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt+1).'</a></li>';
3837
				}
3838
				$cpt++;
3839
			}
3840
			while ($cpt < $nbpages && $cpt<=$page+$maxnbofpage);
3841
3842
			if ($cpt<$nbpages)
3843
			{
3844
				if ($cpt<$nbpages-2) $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="inactive"':'').'>...</span></li>';
3845
				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>';
3846
				$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page='.($nbpages-1).$options.'">'.$nbpages.'</a></li>';
3847
			}
3848
		}
3849
		else
3850
		{
3851
			$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="active"':'').'>'.($page+1)."</li>";
3852
		}
3853
	}
3854
3855
	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
3856
3857
	print '</td>';
3858
3859
	print '</tr></table>'."\n";
3860
	print "<!-- End title -->\n\n";
3861
}
3862
3863
/**
3864
 *	Function to show navigation arrows into lists
3865
 *
3866
 *	@param	int				$page				Number of page
3867
 *	@param	string			$file				Page URL (in most cases provided with $_SERVER["PHP_SELF"])
3868
 *	@param	string			$options         	Other url paramaters to propagate ("" by default, may include sortfield and sortorder)
3869
 *	@param	integer			$nextpage	    	Do we show a next page button
3870
 *	@param	string			$betweenarrows		HTML content to show between arrows. MUST contains '<li> </li>' tags or '<li><span> </span></li>'.
3871
 *  @param	string			$afterarrows		HTML content to show after arrows. Must NOT contains '<li> </li>' tags.
3872
 *  @param  int             $limit              Max nb of record to show  (-1 = no combo with limit, 0 = no limit, > 0 = limit)
3873
 *	@param	int		        $totalnboflines		Total number of records/lines for all pages (if known)
3874
 *  @param  int             $hideselectlimit    Force to hide select limit
3875
 *	@return	void
3876
 */
3877
function print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $hideselectlimit=0)
3878
{
3879
	global $conf, $langs;
3880
3881
	print '<div class="pagination"><ul>';
3882
	if ((int) $limit >= 0 && empty($hideselectlimit))
3883
	{
3884
		$pagesizechoices='10:10,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000,5000:5000';
3885
		//$pagesizechoices.=',0:'.$langs->trans("All");     // Not yet supported
3886
		//$pagesizechoices.=',2:2';
3887
		if (! empty($conf->global->MAIN_PAGESIZE_CHOICES)) $pagesizechoices=$conf->global->MAIN_PAGESIZE_CHOICES;
3888
3889
		print '<li class="pagination">';
3890
		print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
3891
		$tmpchoice=explode(',',$pagesizechoices);
3892
		$tmpkey=$limit.':'.$limit;
3893
		if (! in_array($tmpkey, $tmpchoice)) $tmpchoice[]=$tmpkey;
3894
		$tmpkey=$conf->liste_limit.':'.$conf->liste_limit;
3895
		if (! in_array($tmpkey, $tmpchoice)) $tmpchoice[]=$tmpkey;
3896
		asort($tmpchoice, SORT_NUMERIC);
3897
		$found=false;
3898
		foreach($tmpchoice as $val)
3899
		{
3900
			$selected='';
3901
			$tmp=explode(':',$val);
3902
			$key=$tmp[0];
3903
			$val=$tmp[1];
3904
			if ($key != '' && $val != '')
3905
			{
3906
				if ((int) $key == (int) $limit)
3907
				{
3908
					$selected = ' selected="selected"';
3909
					$found = true;
3910
				}
3911
				print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
3912
			}
3913
		}
3914
		print '</select>';
3915
		if ($conf->use_javascript_ajax)
3916
		{
3917
			print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
3918
            		<script type="text/javascript">
3919
                	jQuery(document).ready(function () {
3920
            	  		jQuery(".selectlimit").change(function() {
3921
                            console.log("Change limit. Send submit");
3922
                            $(this).parents(\'form:first\').submit();
3923
            	  		});
3924
                	});
3925
            		</script>
3926
                ';
3927
		}
3928
		print '</li>';
3929
	}
3930
	if ($page > 0)
3931
	{
3932
		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>';
3933
	}
3934
	if ($betweenarrows)
3935
	{
3936
		print $betweenarrows;
3937
	}
3938
	if ($nextpage > 0)
3939
	{
3940
		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>';
3941
	}
3942
	if ($afterarrows)
3943
	{
3944
		print '<li class="paginationafterarrows">';
3945
		print $afterarrows;
3946
		print '</li>';
3947
	}
3948
	print '</ul></div>'."\n";
3949
}
3950
3951
3952
/**
3953
 *	Return a string with VAT rate label formated for view output
3954
 *	Used into pdf and HTML pages
3955
 *
3956
 *	@param	string	$rate			Rate value to format ('19.6', '19,6', '19.6%', '19,6%', '19.6 (CODEX)', ...)
3957
 *  @param	boolean	$addpercent		Add a percent % sign in output
3958
 *	@param	int		$info_bits		Miscellaneous information on vat (0=Default, 1=French NPR vat)
3959
 *	@param	int		$usestarfornpr	-1=Never show, 0 or 1=Use '*' for NPR vat rates
3960
 *  @return	string					String with formated amounts ('19,6' or '19,6%' or '8.5% (NPR)' or '8.5% *' or '19,6 (CODEX)')
3961
 */
3962
function vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0)
3963
{
3964
	$morelabel='';
3965
3966
	if (preg_match('/%/',$rate))
3967
	{
3968
		$rate=str_replace('%','',$rate);
3969
		$addpercent=true;
3970
	}
3971
	if (preg_match('/\((.*)\)/',$rate,$reg))
3972
	{
3973
		$morelabel=' ('.$reg[1].')';
3974
		$rate=preg_replace('/\s*'.preg_quote($morelabel,'/').'/','',$rate);
3975
	}
3976
	if (preg_match('/\*/',$rate))
3977
	{
3978
		$rate=str_replace('*','',$rate);
3979
		$info_bits |= 1;
3980
	}
3981
3982
	// If rate is '9/9/9' we don't change it.  If rate is '9.000' we apply price()
3983
	if (! preg_match('/\//', $rate)) $ret=price($rate,0,'',0,0).($addpercent?'%':'');
3984
	else
3985
	{
3986
		// TODO Split on / and output with a price2num to have clean numbers without ton of 000.
3987
		$ret=$rate.($addpercent?'%':'');
3988
	}
3989
	if (($info_bits & 1) && $usestarfornpr >= 0) $ret.=' *';
3990
	$ret.=$morelabel;
3991
	return $ret;
3992
}
3993
3994
3995
/**
3996
 *		Function to format a value into an amount for visual output
3997
 *		Function used into PDF and HTML pages
3998
 *
3999
 *		@param	float		$amount			Amount to format
4000
 *		@param	integer		$form			Type of format, HTML or not (not by default)
4001
 *		@param	Translate	$outlangs		Object langs for output
4002
 *		@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.
4003
 *		@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)
4004
 *		@param	int			$forcerounding	Force the number of decimal to forcerounding decimal (-1=do not force)
4005
 *		@param	string		$currency_code	To add currency symbol (''=add nothing, 'auto'=Use default currency, 'XXX'=add currency symbols for XXX currency)
4006
 *		@return	string						Chaine avec montant formate
4007
 *
4008
 *		@see	price2num					Revert function of price
4009
 */
4010
function price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
4011
{
4012
	global $langs,$conf;
4013
4014
	// Clean parameters
4015
	if (empty($amount)) $amount=0;	// To have a numeric value if amount not defined or = ''
4016
	$amount = (is_numeric($amount)?$amount:0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
4017
	if ($rounding < 0) $rounding=min($conf->global->MAIN_MAX_DECIMALS_UNIT,$conf->global->MAIN_MAX_DECIMALS_TOT);
4018
	$nbdecimal=$rounding;
4019
4020
	// Output separators by default (french)
4021
	$dec=','; $thousand=' ';
4022
4023
	// If $outlangs not forced, we use use language
4024
	if (! is_object($outlangs)) $outlangs=$langs;
4025
4026
	if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal")  $dec=$outlangs->transnoentitiesnoconv("SeparatorDecimal");
4027
	if ($outlangs->transnoentitiesnoconv("SeparatorThousand")!= "SeparatorThousand") $thousand=$outlangs->transnoentitiesnoconv("SeparatorThousand");
4028
	if ($thousand == 'None') $thousand='';
4029
	else if ($thousand == 'Space') $thousand=' ';
4030
	//print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
4031
4032
	//print "amount=".$amount."-";
4033
	$amount = str_replace(',','.',$amount);	// should be useless
4034
	//print $amount."-";
4035
	$datas = explode('.',$amount);
4036
	$decpart = isset($datas[1])?$datas[1]:'';
4037
	$decpart = preg_replace('/0+$/i','',$decpart);	// Supprime les 0 de fin de partie decimale
4038
	//print "decpart=".$decpart."<br>";
4039
	$end='';
4040
4041
	// We increase nbdecimal if there is more decimal than asked (to not loose information)
4042
	if (dol_strlen($decpart) > $nbdecimal) $nbdecimal=dol_strlen($decpart);
4043
	// Si on depasse max
4044
	if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN)
4045
	{
4046
		$nbdecimal=$conf->global->MAIN_MAX_DECIMALS_SHOWN;
4047
		if (preg_match('/\.\.\./i',$conf->global->MAIN_MAX_DECIMALS_SHOWN))
4048
		{
4049
			// Si un affichage est tronque, on montre des ...
4050
			$end='...';
4051
		}
4052
	}
4053
4054
	// If force rounding
4055
	if ($forcerounding >= 0) $nbdecimal = $forcerounding;
4056
4057
	// Format number
4058
	$output=number_format($amount, $nbdecimal, $dec, $thousand);
4059
	if ($form)
4060
	{
4061
		$output=preg_replace('/\s/','&nbsp;',$output);
4062
		$output=preg_replace('/\'/','&#039;',$output);
4063
	}
4064
	// Add symbol of currency if requested
4065
	$cursymbolbefore=$cursymbolafter='';
4066
	if ($currency_code)
4067
	{
4068
		if ($currency_code == 'auto') $currency_code=$conf->currency;
4069
4070
		$listofcurrenciesbefore=array('USD','GBP','AUD','MXN','PEN','CNY');
4071
		if (in_array($currency_code,$listofcurrenciesbefore)) $cursymbolbefore.=$outlangs->getCurrencySymbol($currency_code);
4072
		else
4073
		{
4074
			$tmpcur=$outlangs->getCurrencySymbol($currency_code);
4075
			$cursymbolafter.=($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
4076
		}
4077
	}
4078
	$output=$cursymbolbefore.$output.$end.($cursymbolafter?' ':'').$cursymbolafter;
4079
4080
	return $output;
4081
}
4082
4083
/**
4084
 *	Function that return a number with universal decimal format (decimal separator is '.') from an amount typed by a user.
4085
 *	Function to use on each input amount before any numeric test or database insert
4086
 *
4087
 *	@param	float	$amount			Amount to convert/clean
4088
 *	@param	string	$rounding		''=No rounding
4089
 * 									'MU'=Round to Max unit price (MAIN_MAX_DECIMALS_UNIT)
4090
 *									'MT'=Round to Max for totals with Tax (MAIN_MAX_DECIMALS_TOT)
4091
 *									'MS'=Round to Max for stock quantity (MAIN_MAX_DECIMALS_STOCK)
4092
 * 	@param	int		$alreadysqlnb	Put 1 if you know that content is already universal format number
4093
 *	@return	string					Amount with universal numeric format (Example: '99.99999') or unchanged text if conversion fails.
4094
 *
4095
 *	@see    price					Opposite function of price2num
4096
 */
4097
function price2num($amount,$rounding='',$alreadysqlnb=0)
4098
{
4099
	global $langs,$conf;
4100
4101
	// Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
4102
	// Numbers must be '1234.56'
4103
	// Decimal delimiter for PHP and database SQL requests must be '.'
4104
	$dec=','; $thousand=' ';
4105
	if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal")  $dec=$langs->transnoentitiesnoconv("SeparatorDecimal");
4106
	if ($langs->transnoentitiesnoconv("SeparatorThousand")!= "SeparatorThousand") $thousand=$langs->transnoentitiesnoconv("SeparatorThousand");
4107
	if ($thousand == 'None') $thousand='';
4108
	elseif ($thousand == 'Space') $thousand=' ';
4109
	//print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
4110
4111
	// Convert value to universal number format (no thousand separator, '.' as decimal separator)
4112
	if ($alreadysqlnb != 1)	// If not a PHP number or unknown, we change format
4113
	{
4114
		//print 'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
4115
4116
		// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
4117
		// to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
4118
		if (is_numeric($amount))
4119
		{
4120
			// We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
4121
			$temps=sprintf("%0.10F",$amount-intval($amount));	// temps=0.0000000000 or 0.0000200000 or 9999.1000000000
4122
			$temps=preg_replace('/([\.1-9])0+$/','\\1',$temps); // temps=0. or 0.00002 or 9999.1
4123
			$nbofdec=max(0,dol_strlen($temps)-2);	// -2 to remove "0."
4124
			$amount=number_format($amount,$nbofdec,$dec,$thousand);
4125
		}
4126
		//print "QQ".$amount.'<br>';
4127
4128
		// Now make replace (the main goal of function)
4129
		if ($thousand != ',' && $thousand != '.') $amount=str_replace(',','.',$amount);	// To accept 2 notations for french users
4130
		$amount=str_replace(' ','',$amount);		// To avoid spaces
4131
		$amount=str_replace($thousand,'',$amount);	// Replace of thousand before replace of dec to avoid pb if thousand is .
4132
		$amount=str_replace($dec,'.',$amount);
4133
	}
4134
4135
	// Now, make a rounding if required
4136
	if ($rounding)
4137
	{
4138
		$nbofdectoround='';
4139
		if ($rounding == 'MU')     $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_UNIT;
4140
		elseif ($rounding == 'MT') $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_TOT;
4141
		elseif ($rounding == 'MS') $nbofdectoround=empty($conf->global->MAIN_MAX_DECIMALS_STOCK)?5:$conf->global->MAIN_MAX_DECIMALS_STOCK;
4142
		elseif (is_numeric($rounding))  $nbofdectoround=$rounding; 	// For admin info page
4143
		//print "RR".$amount.' - '.$nbofdectoround.'<br>';
4144
		if (dol_strlen($nbofdectoround)) $amount = round($amount,$nbofdectoround);	// $nbofdectoround can be 0.
4145
		else return 'ErrorBadParameterProvidedToFunction';
4146
		//print 'SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
4147
4148
		// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
4149
		// to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
4150
		if (is_numeric($amount))
4151
		{
4152
			// We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
4153
			$temps=sprintf("%0.10F",$amount-intval($amount));	// temps=0.0000000000 or 0.0000200000 or 9999.1000000000
4154
			$temps=preg_replace('/([\.1-9])0+$/','\\1',$temps); // temps=0. or 0.00002 or 9999.1
4155
			$nbofdec=max(0,dol_strlen($temps)-2);	// -2 to remove "0."
4156
			$amount=number_format($amount,min($nbofdec,$nbofdectoround),$dec,$thousand);		// Convert amount to format with dolibarr dec and thousand
4157
		}
4158
		//print "TT".$amount.'<br>';
4159
4160
		// Always make replace because each math function (like round) replace
4161
		// with local values and we want a number that has a SQL string format x.y
4162
		if ($thousand != ',' && $thousand != '.') $amount=str_replace(',','.',$amount);	// To accept 2 notations for french users
4163
		$amount=str_replace(' ','',$amount);		// To avoid spaces
4164
		$amount=str_replace($thousand,'',$amount);	// Replace of thousand before replace of dec to avoid pb if thousand is .
4165
		$amount=str_replace($dec,'.',$amount);
4166
	}
4167
4168
	return $amount;
4169
}
4170
4171
4172
/**
4173
 * Output a dimension with best unit
4174
 *
4175
 * @param   float       $dimension      Dimension
4176
 * @param   int         $unit           Unit of dimension (0, -3, ...)
4177
 * @param   string      $type           'weight', 'volume', ...
4178
 * @param   Translate   $outputlangs    Translate language object
4179
 * @param   int         $round          -1 = non rounding, x = number of decimal
4180
 * @param   string      $forceunitoutput    'no' or numeric (-3, -6, ...) compared to $unit
4181
 * @return  string                      String to show dimensions
4182
 */
4183
function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no')
4184
{
4185
	require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
4186
4187
	if (($forceunitoutput == 'no' && $dimension < 1/10000) || (is_numeric($forceunitoutput) && $forceunitoutput == -6))
4188
	{
4189
		$dimension = $dimension * 1000000;
4190
		$unit = $unit - 6;
4191
	}
4192
	elseif (($forceunitoutput == 'no' && $dimension < 1/10) || (is_numeric($forceunitoutput) && $forceunitoutput == -3))
4193
	{
4194
		$dimension = $dimension * 1000;
4195
		$unit = $unit - 3;
4196
	}
4197
	elseif (($forceunitoutput == 'no' && $dimension > 100000000) || (is_numeric($forceunitoutput) && $forceunitoutput == 6))
4198
	{
4199
		$dimension = $dimension / 1000000;
4200
		$unit = $unit + 6;
4201
	}
4202
	elseif (($forceunitoutput == 'no' && $dimension > 100000) || (is_numeric($forceunitoutput) && $forceunitoutput == 3))
4203
	{
4204
		$dimension = $dimension / 1000;
4205
		$unit = $unit + 3;
4206
	}
4207
4208
	$ret=price($dimension, 0, $outputlangs, 0, 0, $round).' '.measuring_units_string($unit, $type);
4209
4210
	return $ret;
4211
}
4212
4213
4214
/**
4215
 *	Return localtax rate for a particular vat, when selling a product with vat $vatrate, from a $thirdparty_buyer to a $thirdparty_seller
4216
 *  Note: This function applies same rules than get_default_tva
4217
 *
4218
 * 	@param	float		$vatrate		        Vat rate. Can be '8.5' or '8.5 (VATCODEX)' for example
4219
 * 	@param  int			$local		         	Local tax to search and return (1 or 2 return only tax rate 1 or tax rate 2)
4220
 *  @param  Societe		$thirdparty_buyer    	Object of buying third party
4221
 *  @param	Societe		$thirdparty_seller		Object of selling third party ($mysoc if not defined)
4222
 *  @param	int			$vatnpr					If vat rate is NPR or not
4223
 * 	@return	mixed			   					0 if not found, localtax rate if found
4224
 *  @see get_default_tva
4225
 */
4226
function get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
4227
{
4228
	global $db, $conf, $mysoc;
4229
4230
	if (empty($thirdparty_seller) || ! is_object($thirdparty_seller)) $thirdparty_seller=$mysoc;
4231
4232
	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);
4233
4234
	$vatratecleaned = $vatrate;
4235
	if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
4236
	{
4237
		$vatratecleaned = trim($reg[1]);
4238
		$vatratecode = $reg[2];
4239
	}
4240
4241
	/*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
4242
	{
4243
		return 0;
4244
	}*/
4245
4246
	// Some test to guess with no need to make database access
4247
	if ($mysoc->country_code == 'ES') // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
4248
	{
4249
		if ($local == 1)
4250
		{
4251
			if (! $mysoc->localtax1_assuj || (string) $vatratecleaned == "0") return 0;
4252
			if ($thirdparty_seller->id == $mysoc->id)
4253
			{
4254
				if (! $thirdparty_buyer->localtax1_assuj) return 0;
4255
			}
4256
			else
4257
			{
4258
				if (! $thirdparty_seller->localtax1_assuj) return 0;
4259
			}
4260
		}
4261
4262
		if ($local == 2)
4263
		{
4264
			if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
4265
			if ($thirdparty_seller->id == $mysoc->id)
4266
			{
4267
				if (! $thirdparty_buyer->localtax2_assuj) return 0;
4268
			}
4269
			else
4270
			{
4271
				if (! $thirdparty_seller->localtax2_assuj) return 0;
4272
			}
4273
		}
4274
	}
4275
	else
4276
	{
4277
		if ($local == 1 && ! $thirdparty_seller->localtax1_assuj) return 0;
4278
		if ($local == 2 && ! $thirdparty_seller->localtax2_assuj) return 0;
4279
	}
4280
4281
	// For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
4282
	if (in_array($mysoc->country_code, array('ES')))
4283
	{
4284
		$conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
4285
	}
4286
4287
	// Search local taxes
4288
	if (! empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY))
4289
	{
4290
		if ($local==1)
4291
		{
4292
			if ($thirdparty_seller != $mysoc)
4293
			{
4294
				if (!isOnlyOneLocalTax($local))  // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
4295
				{
4296
					return $thirdparty_seller->localtax1_value;
4297
				}
4298
			}
4299
			else  // i am the seller
4300
			{
4301
				if (!isOnlyOneLocalTax($local))  // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
4302
				{
4303
					return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
4304
				}
4305
			}
4306
		}
4307
		if ($local==2)
4308
		{
4309
			if ($thirdparty_seller != $mysoc)
4310
			{
4311
				if (!isOnlyOneLocalTax($local))  // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
4312
				// TODO We should also return value defined on thirdparty only if defined
4313
				{
4314
					return $thirdparty_seller->localtax2_value;
4315
				}
4316
			}
4317
			else  // i am the seller
4318
			{
4319
				if (!isOnlyOneLocalTax($local))  // This is for spain only, we don't return value found into datbase even if there is only one locatax vat.
4320
				{
4321
					return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
4322
				}
4323
			}
4324
		}
4325
	}
4326
4327
	// By default, search value of local tax on line of common tax
4328
	$sql  = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
4329
   	$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4330
   	$sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$thirdparty_seller->country_code."'";
4331
   	$sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4332
   	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...
4333
   	else $sql.= " AND t.recuperableonly ='".$vatnpr."'";
4334
   	dol_syslog("get_localtax", LOG_DEBUG);
4335
   	$resql=$db->query($sql);
4336
4337
   	if ($resql)
4338
   	{
4339
   		$obj = $db->fetch_object($resql);
4340
   		if ($local==1) return $obj->localtax1;
4341
   		elseif ($local==2) return $obj->localtax2;
4342
	}
4343
4344
	return 0;
4345
}
4346
4347
4348
/**
4349
 * Return true if LocalTax (1 or 2) is unique.
4350
 * Example: If localtax1 is 5 on line with highest common vat rate, return true
4351
 * Example: If localtax1 is 5:8:15 on line with highest common vat rate, return false
4352
 *
4353
 * @param   int 	$local	Local tax to test (1 or 2)
4354
 * @return  boolean 		True if LocalTax have multiple values, False if not
4355
 */
4356
function isOnlyOneLocalTax($local)
4357
{
4358
	$tax=get_localtax_by_third($local);
4359
4360
	$valors=explode(":", $tax);
4361
4362
	if (count($valors)>1)
4363
	{
4364
		return false;
4365
	}
4366
	else
4367
	{
4368
		return true;
4369
	}
4370
}
4371
4372
/**
4373
 * Get values of localtaxes (1 or 2) for company country for the common vat with the highest value
4374
 *
4375
 * @param	int		$local 	LocalTax to get
4376
 * @return	number			Values of localtax
4377
 */
4378
function get_localtax_by_third($local)
4379
{
4380
	global $db, $mysoc;
4381
	$sql ="SELECT t.localtax1, t.localtax2 ";
4382
	$sql.=" FROM ".MAIN_DB_PREFIX."c_tva as t inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=t.fk_pays";
4383
	$sql.=" WHERE c.code = '".$mysoc->country_code."' AND t.active = 1 AND t.taux=(";
4384
	$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";
4385
	$sql.="  WHERE c.code = '".$mysoc->country_code."' AND tt.active = 1";
4386
	$sql.="  )";
4387
4388
	$resql=$db->query($sql);
4389
	if ($resql)
4390
	{
4391
		$obj = $db->fetch_object($resql);
4392
		if ($local==1) return $obj->localtax1;
4393
		elseif ($local==2) return $obj->localtax2;
4394
	}
4395
4396
	return 0;
4397
4398
}
4399
4400
4401
/**
4402
 *  Get vat main information from Id.
4403
 *  You can call getLocalTaxesFromRate after to get other fields.
4404
 *
4405
 *  @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.
4406
 *  @param	Societe	    $buyer         		Company object
4407
 *  @param	Societe	    $seller        		Company object
4408
 *  @param  int         $firstparamisid     1 if first param is id into table (use this if you can)
4409
 *  @return	array       	  				array('rowid'=> , 'code'=> ...)
4410
 *  @see getLocalTaxesFromRate
4411
 */
4412
function getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
4413
{
4414
	global $db, $mysoc;
4415
4416
	dol_syslog("getTaxesFromId vatrowid=".$vatrate);
4417
4418
	// Search local taxes
4419
	$sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy";
4420
	$sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t";
4421
	if ($firstparamisid) $sql.= " WHERE t.rowid = ".(int) $vatrate;
4422
	else
4423
	{
4424
		$vatratecleaned = $vatrate;
4425
		$vatratecode = '';
4426
		if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
4427
		{
4428
			$vatratecleaned = $reg[1];
4429
			$vatratecode = $reg[2];
4430
		}
4431
4432
		$sql.=", ".MAIN_DB_PREFIX."c_country as c";
4433
		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 ??
4434
		else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
4435
		$sql.= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4436
		if ($vatratecode) $sql.= " AND t.code = '".$vatratecode."'";
4437
	}
4438
4439
	$resql=$db->query($sql);
4440
	if ($resql)
4441
	{
4442
		$obj = $db->fetch_object($resql);
4443
		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);
4444
		else return array();
4445
	}
4446
	else dol_print_error($db);
4447
4448
	return array();
4449
}
4450
4451
/**
4452
 *  Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
4453
 *  This does not take into account the seller setup if subject to vat or not, only country.
4454
 *  TODO
4455
 *  This function is ALSO called to retrieve type for building PDF. Such call of function must be removed.
4456
 *  Instead this function must be called when adding a line to get the array of localtax and type, and then
4457
 *  provide it to the function calcul_price_total.
4458
 *
4459
 *  @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.
4460
 *  @param	int		    $local              Number of localtax (1 or 2, or 0 to return 1 & 2)
4461
 *  @param	Societe	    $buyer         		Company object
4462
 *  @param	Societe	    $seller        		Company object
4463
 *  @param  int         $firstparamisid     1 if first param is ID into table instead of Rate+code (use this if you can)
4464
 *  @return	array    	    				array(localtax_type1(1-6/0 if not found), rate localtax1, localtax_type2, rate localtax2, accountancycodecust, accountancycodesupp)
4465
 *  @see getTaxesFromId
4466
 */
4467
function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
4468
{
4469
	global $db, $mysoc;
4470
4471
	dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
4472
4473
	// Search local taxes
4474
	$sql  = "SELECT t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy";
4475
	$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
4476
	if ($firstparamisid) $sql.= " WHERE t.rowid = ".(int) $vatrate;
4477
	else
4478
	{
4479
		$vatratecleaned = $vatrate;
4480
		$vatratecode = '';
4481
		if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
4482
		{
4483
			$vatratecleaned = $reg[1];
4484
			$vatratecode = $reg[2];
4485
		}
4486
4487
		$sql.=", ".MAIN_DB_PREFIX."c_country as c";
4488
		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 ??
4489
		else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
4490
		$sql.= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4491
		if ($vatratecode) $sql.= " AND t.code = '".$vatratecode."'";
4492
	}
4493
4494
	$resql=$db->query($sql);
4495
	if ($resql)
4496
	{
4497
		$obj = $db->fetch_object($resql);
4498
		if ($local == 1)
4499
		{
4500
			if (! isOnlyOneLocalTax(1))
4501
			{
4502
				return array($obj->localtax1_type, get_localtax($vatrate, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4503
			}
4504
			else
4505
			{
4506
				return array($obj->localtax1_type, $obj->localtax1,$obj->accountancy_code_sell, $obj->accountancy_code_buy);
4507
			}
4508
		}
4509
		elseif ($local == 2)
4510
		{
4511
			if (! isOnlyOneLocalTax(2))
4512
			{
4513
				return array($obj->localtax2_type, get_localtax($vatrate, $local, $buyer, $seller),$obj->accountancy_code_sell, $obj->accountancy_code_buy);
4514
			}
4515
			else
4516
			{
4517
				return array($obj->localtax2_type, $obj->localtax2,$obj->accountancy_code_sell, $obj->accountancy_code_buy);
4518
			}
4519
		}
4520
		else
4521
		{
4522
			if(! isOnlyOneLocalTax(1))
4523
			{
4524
				if(! isOnlyOneLocalTax(2))
4525
				{
4526
					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);
4527
				}
4528
				else
4529
				{
4530
					return array($obj->localtax1_type, get_localtax($vatrate, 1, $buyer, $seller), $obj->localtax2_type, $obj->localtax2, $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4531
				}
4532
			}
4533
			else
4534
			{
4535
				if(! isOnlyOneLocalTax(2))
4536
				{
4537
					return array($obj->localtax1_type, $obj->localtax1, $obj->localtax2_type, get_localtax($vatrate, 2, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4538
				}
4539
				else
4540
				{
4541
					return array($obj->localtax1_type, $obj->localtax1, $obj->localtax2_type, $obj->localtax2, $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4542
				}
4543
			}
4544
		}
4545
	}
4546
4547
	return 0;
4548
}
4549
4550
/**
4551
 *	Return vat rate of a product in a particular selling country or default country vat if product is unknown
4552
 *  Function called by get_default_tva
4553
 *
4554
 *  @param	int			$idprod          	Id of product or 0 if not a predefined product
4555
 *  @param  Societe		$thirdparty_seller  Thirdparty with a ->country_code defined (FR, US, IT, ...)
4556
 *	@param	int			$idprodfournprice	Id product_fournisseur_price (for "supplier" order/invoice)
4557
 *  @return float|string   				    Vat rate to use with format 5.0 or '5.0 (XXX)'
4558
 *  @see get_product_localtax_for_country
4559
 */
4560
function get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice=0)
4561
{
4562
	global $db,$conf,$mysoc;
4563
4564
	require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4565
4566
	$ret=0;
4567
	$found=0;
4568
4569
	if ($idprod > 0)
4570
	{
4571
		// Load product
4572
		$product=new Product($db);
4573
		$result=$product->fetch($idprod);
4574
4575
		if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
4576
		{
4577
			if ($idprodfournprice > 0)     // We want vat for product for a "supplier" order or invoice
4578
			{
4579
				$product->get_buyprice($idprodfournprice,0,0,0);
4580
				$ret=$product->vatrate_supplier;
4581
				if ($product->default_vat_code) $ret.=' ('.$product->default_vat_code.')';
4582
			}
4583
			else
4584
			{
4585
				$ret=$product->tva_tx;    // Default vat of product we defined
4586
				if ($product->default_vat_code) $ret.=' ('.$product->default_vat_code.')';
4587
			}
4588
			$found=1;
4589
		}
4590
		else
4591
		{
4592
			// TODO Read default product vat according to countrycode and product. Vat for couple countrycode/product is a feature not implemeted yet.
4593
			// May be usefull/required if hidden option SERVICE_ARE_ECOMMERCE_200238EC is on
4594
		}
4595
	}
4596
4597
	if (! $found)
4598
	{
4599
		if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS))
4600
		{
4601
			// If vat of product for the country not found or not defined, we return the first higher vat of country.
4602
			$sql = "SELECT taux as vat_rate";
4603
			$sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4604
			$sql.= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
4605
			$sql.= " ORDER BY t.taux DESC, t.code ASC, t.recuperableonly ASC";
4606
			$sql.= $db->plimit(1);
4607
4608
			$resql=$db->query($sql);
4609
			if ($resql)
4610
			{
4611
				$obj=$db->fetch_object($resql);
4612
				if ($obj)
4613
				{
4614
					$ret=$obj->vat_rate;
4615
				}
4616
				$db->free($sql);
4617
			}
4618
			else dol_print_error($db);
4619
		}
4620
		else $ret=$conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;    // Forced value if autodetect fails
4621
	}
4622
4623
	dol_syslog("get_product_vat_for_country: ret=".$ret);
4624
	return $ret;
4625
}
4626
4627
/**
4628
 *	Return localtax vat rate of a product in a particular selling country or default country vat if product is unknown
4629
 *
4630
 *  @param	int		$idprod         		Id of product
4631
 *  @param  int		$local          		1 for localtax1, 2 for localtax 2
4632
 *  @param  Societe	$thirdparty_seller    	Thirdparty with a ->country_code defined (FR, US, IT, ...)
4633
 *  @return int             				<0 if KO, Vat rate if OK
4634
 *  @see get_product_vat_for_country
4635
 */
4636
function get_product_localtax_for_country($idprod, $local, $thirdparty_seller)
4637
{
4638
	global $db,$mysoc;
4639
4640
	if (! class_exists('Product')) {
4641
		require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4642
	}
4643
4644
	$ret=0;
4645
	$found=0;
4646
4647
	if ($idprod > 0)
4648
	{
4649
		// Load product
4650
		$product=new Product($db);
4651
		$result=$product->fetch($idprod);
4652
4653
		if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
4654
		{
4655
			/* Not defined yet, so we don't use this
4656
			if ($local==1) $ret=$product->localtax1_tx;
4657
			elseif ($local==2) $ret=$product->localtax2_tx;
4658
			$found=1;
4659
			*/
4660
		}
4661
		else
4662
		{
4663
			// TODO Read default product vat according to countrycode and product
4664
4665
4666
		}
4667
	}
4668
4669
	if (! $found)
4670
	{
4671
		// If vat of product for the country not found or not defined, we return higher vat of country.
4672
		$sql = "SELECT taux as vat_rate, localtax1, localtax2";
4673
		$sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4674
		$sql.= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
4675
		$sql.= " ORDER BY t.taux DESC, t.recuperableonly ASC";
4676
		$sql.= $db->plimit(1);
4677
4678
		$resql=$db->query($sql);
4679
		if ($resql)
4680
		{
4681
			$obj=$db->fetch_object($resql);
4682
			if ($obj)
4683
			{
4684
				if ($local==1) $ret=$obj->localtax1;
4685
				elseif ($local==2) $ret=$obj->localtax2;
4686
			}
4687
		}
4688
		else dol_print_error($db);
4689
	}
4690
4691
	dol_syslog("get_product_localtax_for_country: ret=".$ret);
4692
	return $ret;
4693
}
4694
4695
/**
4696
 *	Function that return vat rate of a product line (according to seller, buyer and product vat rate)
4697
 *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
4698
 *	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
4699
 *	 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.
4700
 *	 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
4701
 *	 Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise avec num TVA) intra alors TVA par defaut=0. Fin de regle
4702
 *	 Sinon TVA proposee par defaut=0. Fin de regle.
4703
 *
4704
 *	@param	Societe		$thirdparty_seller    	Objet societe vendeuse
4705
 *	@param  Societe		$thirdparty_buyer   	Objet societe acheteuse
4706
 *	@param  int			$idprod					Id product
4707
 *	@param	int			$idprodfournprice		Id product_fournisseur_price (for supplier order/invoice)
4708
 *	@return float|string   				      	Vat rate to use with format 5.0 or '5.0 (XXX)', -1 if we can't guess it
4709
 *  @see get_default_npr, get_default_localtax
4710
 */
4711
function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
4712
{
4713
	global $conf;
4714
4715
	require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
4716
4717
	// Note: possible values for tva_assuj are 0/1 or franchise/reel
4718
	$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;
4719
4720
	$seller_country_code = $thirdparty_seller->country_code;
4721
	$seller_in_cee = isInEEC($thirdparty_seller);
4722
4723
	$buyer_country_code = $thirdparty_buyer->country_code;
4724
	$buyer_in_cee = isInEEC($thirdparty_buyer);
4725
4726
	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:''));
4727
4728
	// 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)
4729
	// we use the buyer VAT.
4730
	if (! empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC))
4731
	{
4732
		if ($seller_in_cee && $buyer_in_cee && ! $thirdparty_buyer->isACompany())
4733
		{
4734
			//print 'VATRULE 0';
4735
			return get_product_vat_for_country($idprod,$thirdparty_buyer,$idprodfournprice);
4736
		}
4737
	}
4738
4739
	// If seller does not use VAT
4740
	if (! $seller_use_vat)
4741
	{
4742
		//print 'VATRULE 1';
4743
		return 0;
4744
	}
4745
4746
	// 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.
4747
4748
	// Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
4749
	if (($seller_country_code == $buyer_country_code)
4750
	|| (in_array($seller_country_code,array('FR,MC')) && in_array($buyer_country_code,array('FR','MC')))) // Warning ->country_code not always defined
4751
	{
4752
		//print 'VATRULE 2';
4753
		return get_product_vat_for_country($idprod,$thirdparty_seller,$idprodfournprice);
4754
	}
4755
4756
	// 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.
4757
	// Not supported
4758
4759
	// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
4760
	// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
4761
	if (($seller_in_cee && $buyer_in_cee))
4762
	{
4763
		$isacompany=$thirdparty_buyer->isACompany();
4764
		if ($isacompany)
4765
		{
4766
			//print 'VATRULE 3';
4767
			return 0;
4768
		}
4769
		else
4770
		{
4771
			//print 'VATRULE 4';
4772
			return get_product_vat_for_country($idprod,$thirdparty_seller,$idprodfournprice);
4773
		}
4774
	}
4775
4776
	// Sinon la TVA proposee par defaut=0. Fin de regle.
4777
	// Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
4778
	//print 'VATRULE 5';
4779
	return 0;
4780
}
4781
4782
4783
/**
4784
 *	Fonction qui renvoie si tva doit etre tva percue recuperable
4785
 *
4786
 *	@param	Societe		$thirdparty_seller    	Thirdparty seller
4787
 *	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
4788
 *  @param  int			$idprod                 Id product
4789
 *  @param	int			$idprodfournprice		Id supplier price for product
4790
 *	@return float       			        	0 or 1
4791
 *  @see get_default_tva, get_default_localtax
4792
 */
4793
function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
4794
{
4795
	global $db;
4796
4797
	if ($idprodfournprice > 0)
4798
	{
4799
		if (! class_exists('ProductFournisseur'))
4800
			require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
4801
		$prodprice = new ProductFournisseur($db);
4802
		$prodprice->fetch_product_fournisseur_price($idprodfournprice);
4803
		return $prodprice->fourn_tva_npr;
4804
	}
4805
	elseif ($idprod > 0)
4806
	{
4807
		if (! class_exists('Product'))
4808
			require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4809
		$prod = new Product($db);
4810
		$prod->fetch($idprod);
4811
		return $prod->tva_npr;
4812
	}
4813
4814
	return 0;
4815
}
4816
4817
/**
4818
 *	Function that return localtax of a product line (according to seller, buyer and product vat rate)
4819
 *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
4820
 *	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
4821
 *	 Sinon TVA proposee par defaut=0. Fin de regle.
4822
 *
4823
 *	@param	Societe		$thirdparty_seller    	Thirdparty seller
4824
 *	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
4825
 *  @param	int			$local					Localtax to process (1 or 2)
4826
 *	@param  int			$idprod					Id product
4827
 *	@return integer        				       	localtax, -1 si ne peut etre determine
4828
 *  @see get_default_tva, get_default_npr
4829
 */
4830
function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
4831
{
4832
	global $mysoc;
4833
4834
	if (!is_object($thirdparty_seller)) return -1;
4835
	if (!is_object($thirdparty_buyer)) return -1;
4836
4837
	if ($local==1) // Localtax 1
4838
	{
4839
		if ($mysoc->country_code == 'ES')
4840
		{
4841
			if (is_numeric($thirdparty_buyer->localtax1_assuj) && ! $thirdparty_buyer->localtax1_assuj) return 0;
4842
		}
4843
		else
4844
		{
4845
			// Si vendeur non assujeti a Localtax1, localtax1 par default=0
4846
			if (is_numeric($thirdparty_seller->localtax1_assuj) && ! $thirdparty_seller->localtax1_assuj) return 0;
4847
			if (! is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj=='localtax1off') return 0;
4848
		}
4849
	}
4850
	elseif ($local==2) //I Localtax 2
4851
	{
4852
		// Si vendeur non assujeti a Localtax2, localtax2 par default=0
4853
		if (is_numeric($thirdparty_seller->localtax2_assuj) && ! $thirdparty_seller->localtax2_assuj) return 0;
4854
		if (! is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj=='localtax2off') return 0;
4855
	}
4856
4857
	if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code)
4858
	{
4859
		return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
4860
	}
4861
4862
	return 0;
4863
}
4864
4865
/**
4866
 *	Return yes or no in current language
4867
 *
4868
 *	@param	string	$yesno			Value to test (1, 'yes', 'true' or 0, 'no', 'false')
4869
 *	@param	integer	$case			1=Yes/No, 0=yes/no, 2=Disabled checkbox, 3=Disabled checkbox + Yes/No
4870
 *	@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.
4871
 *	@return	string					HTML string
4872
 */
4873
function yn($yesno, $case=1, $color=0)
4874
{
4875
	global $langs;
4876
	$result='unknown'; $classname='';
4877
	if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') 	// A mettre avant test sur no a cause du == 0
4878
	{
4879
		$result=$langs->trans('yes');
4880
		if ($case == 1 || $case == 3) $result=$langs->trans("Yes");
4881
		if ($case == 2) $result='<input type="checkbox" value="1" checked disabled>';
4882
		if ($case == 3) $result='<input type="checkbox" value="1" checked disabled> '.$result;
4883
4884
		$classname='ok';
4885
	}
4886
	elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false')
4887
	{
4888
		$result=$langs->trans("no");
4889
		if ($case == 1 || $case == 3) $result=$langs->trans("No");
4890
		if ($case == 2) $result='<input type="checkbox" value="0" disabled>';
4891
		if ($case == 3) $result='<input type="checkbox" value="0" disabled> '.$result;
4892
4893
		if ($color == 2) $classname='ok';
4894
		else $classname='error';
4895
	}
4896
	if ($color) return '<font class="'.$classname.'">'.$result.'</font>';
4897
	return $result;
4898
}
4899
4900
4901
/**
4902
 *	Return a path to have a the directory according to object where files are stored.
4903
 *  New usage:       $conf->module->multidir_output[$object->entity].'/'.get_exdir(0, 0, 0, 1, $object, $modulepart)
4904
 *         or:       $conf->module->dir_output.'/'.get_exdir(0, 0, 0, 1, $object, $modulepart)     if multidir_output not defined.
4905
 *  Example our with new usage:       $object is invoice -> 'INYYMM-ABCD'
4906
 *  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/"
4907
 *
4908
 *	@param	string	$num            Id of object (deprecated, $object will be used in future)
4909
 *	@param  int		$level		    Level of subdirs to return (1, 2 or 3 levels). (deprecated, global option will be used in future)
4910
 * 	@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)
4911
 *  @param  int		$withoutslash   0=With slash at end (except if '/', we return ''), 1=without slash at end
4912
 *  @param	Object	$object			Object
4913
 *  @param	string	$modulepart		Type of object ('invoice_supplier, 'donation', 'invoice', ...')
4914
 *  @return	string					Dir to use ending. Example '' or '1/' or '1/2/'
4915
 */
4916
function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart)
4917
{
4918
	global $conf;
4919
4920
	$path = '';
4921
4922
	$arrayforoldpath=array('cheque','user','category','holiday','supplier_invoice','invoice_supplier','mailing','supplier_payment');
4923
	if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) $arrayforoldpath[]='product';
4924
	if (! empty($level) && in_array($modulepart, $arrayforoldpath))
4925
	{
4926
		// This part should be removed once all code is using "get_exdir" to forge path, with all parameters provided.
4927
		if (empty($alpha)) $num = preg_replace('/([^0-9])/i','',$num);
4928
		else $num = preg_replace('/^.*\-/i','',$num);
4929
		$num = substr("000".$num, -$level);
4930
		if ($level == 1) $path = substr($num,0,1);
4931
		if ($level == 2) $path = substr($num,1,1).'/'.substr($num,0,1);
4932
		if ($level == 3) $path = substr($num,2,1).'/'.substr($num,1,1).'/'.substr($num,0,1);
4933
	}
4934
	else
4935
	{
4936
		// TODO
4937
		// We will enhance here a common way of forging path for document storage
4938
		// Here, object->id, object->ref and modulepart are required.
4939
		if (in_array($modulepart, array('thirdparty','contact','member','propal','proposal','commande','order','facture','invoice','shipment')))
4940
		{
4941
			$path=($object->ref?$object->ref:$object->id);
4942
		}
4943
	}
4944
4945
	if (empty($withoutslash) && ! empty($path)) $path.='/';
4946
4947
	return $path;
4948
}
4949
4950
/**
4951
 *	Creation of a directory (this can create recursive subdir)
4952
 *
4953
 *	@param	string	$dir		Directory to create (Separator must be '/'. Example: '/mydir/mysubdir')
4954
 *	@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)
4955
 *  @param	int		$newmask	Mask for new file (Defaults to $conf->global->MAIN_UMASK or 0755 if unavailable). Example: '0444'
4956
 *	@return int         		< 0 if KO, 0 = already exists, > 0 if OK
4957
 */
4958
function dol_mkdir($dir, $dataroot='', $newmask=null)
4959
{
4960
	global $conf;
4961
4962
	dol_syslog("functions.lib::dol_mkdir: dir=".$dir,LOG_INFO);
4963
4964
	$dir_osencoded=dol_osencode($dir);
4965
	if (@is_dir($dir_osencoded)) return 0;
4966
4967
	$nberr=0;
4968
	$nbcreated=0;
4969
4970
	$ccdir='';
4971
	if (! empty($dataroot)) {
4972
		// Remove data root from loop
4973
		$dir = str_replace($dataroot.'/', '', $dir);
4974
		$ccdir = $dataroot.'/';
4975
	}
4976
4977
	$cdir = explode("/", $dir);
4978
	$num=count($cdir);
4979
	for ($i = 0; $i < $num; $i++)
4980
	{
4981
		if ($i > 0) $ccdir .= '/'.$cdir[$i];
4982
		else $ccdir .= $cdir[$i];
4983
		if (preg_match("/^.:$/",$ccdir,$regs)) continue;	// Si chemin Windows incomplet, on poursuit par rep suivant
4984
4985
		// Attention, le is_dir() peut echouer bien que le rep existe.
4986
		// (ex selon config de open_basedir)
4987
		if ($ccdir)
4988
		{
4989
			$ccdir_osencoded=dol_osencode($ccdir);
4990
			if (! @is_dir($ccdir_osencoded))
4991
			{
4992
				dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.",LOG_DEBUG);
4993
4994
				umask(0);
4995
				$dirmaskdec=octdec($newmask);
4996
				if (empty($newmask)) {
4997
					$dirmaskdec = empty( $conf->global->MAIN_UMASK ) ? octdec( '0755' ) : octdec( $conf->global->MAIN_UMASK );
4998
				}
4999
				$dirmaskdec |= octdec('0111');  // Set x bit required for directories
5000
				if (! @mkdir($ccdir_osencoded, $dirmaskdec))
5001
				{
5002
					// Si le is_dir a renvoye une fausse info, alors on passe ici.
5003
					dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.",LOG_WARNING);
5004
					$nberr++;
5005
				}
5006
				else
5007
				{
5008
					dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created",LOG_DEBUG);
5009
					$nberr=0;	// On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
5010
					$nbcreated++;
5011
				}
5012
			}
5013
			else
5014
			{
5015
				$nberr=0;	// On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
5016
			}
5017
		}
5018
	}
5019
	return ($nberr ? -$nberr : $nbcreated);
5020
}
5021
5022
5023
/**
5024
 *	Return picto saying a field is required
5025
 *
5026
 *	@return  string		Chaine avec picto obligatoire
5027
 */
5028
function picto_required()
5029
{
5030
	return '<span class="fieldrequired">*</span>';
5031
}
5032
5033
5034
/**
5035
 *	Clean a string from all HTML tags and entities.
5036
 *  This function differs from strip_tags because:
5037
 *  - <br> are replace with \n
5038
 *  - if entities are found, they are decoded before the strip
5039
 *  - you can decide to convert line feed into spaces
5040
 *
5041
 *	@param	string	$stringtoclean		String to clean
5042
 *	@param	integer	$removelinefeed		1=Replace also new lines by a space, 0=Only last one are removed
5043
 *  @param  string	$pagecodeto      	Encoding of input/output string
5044
 *	@return string	    				String cleaned
5045
 *
5046
 * 	@see	dol_escape_htmltag strip_tags
5047
 */
5048
function dol_string_nohtmltag($stringtoclean,$removelinefeed=1,$pagecodeto='UTF-8')
5049
{
5050
	// TODO Try to replace with  strip_tags($stringtoclean)
5051
	$pattern = "/<[^<>]+>/";
5052
	$stringtoclean = preg_replace('/<br[^>]*>/', "\n", $stringtoclean);
5053
	$temp = dol_html_entity_decode($stringtoclean,ENT_COMPAT,$pagecodeto);
5054
5055
	// Exemple of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
5056
	$temp = preg_replace($pattern,"",$temp);    // pass 1
5057
	// $temp after pass 1: <a href="/myurl" title="A title">0000-021
5058
	$temp = preg_replace($pattern,"",$temp);    // pass 2
5059
	// $temp after pass 2: 0000-021
5060
5061
	// Supprime aussi les retours
5062
	if ($removelinefeed) $temp=str_replace(array("\r\n","\r","\n")," ",$temp);
5063
5064
	// et les espaces doubles
5065
	while(strpos($temp,"  "))
5066
	{
5067
		$temp = str_replace("  "," ",$temp);
5068
	}
5069
5070
	return trim($temp);
5071
}
5072
5073
5074
/**
5075
 * Return first line of text. Cut will depends if content is HTML or not.
5076
 *
5077
 * @param 	string	$text		Input text
5078
 * @param	int		$nboflines  Nb of lines to get (default is 1 = first line only)
5079
 * @return	string				Output text
5080
 * @see dol_nboflines_bis, dol_string_nohtmltag, dol_escape_htmltag
5081
 */
5082
function dolGetFirstLineOfText($text, $nboflines=1)
5083
{
5084
	if ($nboflines == 1)
5085
	{
5086
		if (dol_textishtml($text))
5087
		{
5088
			$firstline=preg_replace('/<br[^>]*>.*$/s','',$text);		// The s pattern modifier means the . can match newline characters
5089
			$firstline=preg_replace('/<div[^>]*>.*$/s','',$firstline);	// The s pattern modifier means the . can match newline characters
5090
5091
		}
5092
		else
5093
		{
5094
			$firstline=preg_replace('/[\n\r].*/','',$text);
5095
		}
5096
		return $firstline.((strlen($firstline) != strlen($text))?'...':'');
5097
	}
5098
	else
5099
	{
5100
		$ishtml=0;
5101
		if (dol_textishtml($text))
5102
		{
5103
			$text=preg_replace('/\n/','',$text);
5104
			$ishtml=1;
5105
			$repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5106
		}
5107
		else
5108
		{
5109
			$repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5110
		}
5111
5112
		$text = strtr($text, $repTable);
5113
		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...
5114
		else $pattern = '/(<br[^>]*>)/U';							// /U is to have UNGREEDY regex to limit to one html tag.
5115
		$a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5116
5117
		$firstline='';
5118
		$i=0;
5119
		$nba = count($a);	// 2x nb of lines in $a because $a contains also a line for each new line separator
5120
		while (($i < $nba) && ($i < ($nboflines * 2)))
5121
		{
5122
			if ($i % 2 == 0) $firstline .= $a[$i];
5123
			elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) $firstline .= ($ishtml?"<br>\n":"\n");
5124
			$i++;
5125
		}
5126
		unset($a);
5127
		return $firstline.(($i < $nba)?'...':'');
5128
	}
5129
}
5130
5131
5132
/**
5133
 * Replace CRLF in string with a HTML BR tag
5134
 *
5135
 * @param	string	$stringtoencode		String to encode
5136
 * @param	int     $nl2brmode			0=Adding br before \n, 1=Replacing \n by br
5137
 * @param   bool	$forxml             false=Use <br>, true=Use <br />
5138
 * @return	string						String encoded
5139
 * @see dol_nboflines, dolGetFirstLineOfText
5140
 */
5141
function dol_nl2br($stringtoencode,$nl2brmode=0,$forxml=false)
5142
{
5143
	if (!$nl2brmode) {
5144
		return nl2br($stringtoencode, $forxml);
5145
	} else {
5146
		$ret=preg_replace('/(\r\n|\r|\n)/i', ($forxml?'<br />':'<br>'), $stringtoencode);
5147
		return $ret;
5148
	}
5149
}
5150
5151
5152
/**
5153
 *	This function is called to encode a string into a HTML string but differs from htmlentities because
5154
 * 	a detection is done before to see if text is already HTML or not. Also, all entities but &,<,> are converted.
5155
 *  This permits to encode special chars to entities with no double encoding for already encoded HTML strings.
5156
 * 	This function also remove last EOL or BR if $removelasteolbr=1 (default).
5157
 *  For PDF usage, you can show text by 2 ways:
5158
 *              - writeHTMLCell -> param must be encoded into HTML.
5159
 *              - MultiCell -> param must not be encoded into HTML.
5160
 *              Because writeHTMLCell convert also \n into <br>, if function
5161
 *              is used to build PDF, nl2brmode must be 1.
5162
 *
5163
 *	@param	string	$stringtoencode		String to encode
5164
 *	@param	int		$nl2brmode			0=Adding br before \n, 1=Replacing \n by br (for use with FPDF writeHTMLCell function for example)
5165
 *  @param  string	$pagecodefrom       Pagecode stringtoencode is encoded
5166
 *  @param	int		$removelasteolbr	1=Remove last br or lasts \n (default), 0=Do nothing
5167
 *  @return	string						String encoded
5168
 */
5169
function dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
5170
{
5171
	$newstring=$stringtoencode;
5172
	if (dol_textishtml($stringtoencode))	// Check if text is already HTML or not
5173
	{
5174
		$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.
5175
		if ($removelasteolbr) $newstring=preg_replace('/<br>$/i','',$newstring);	// Remove last <br> (remove only last one)
5176
		$newstring=strtr($newstring,array('&'=>'__and__','<'=>'__lt__','>'=>'__gt__','"'=>'__dquot__'));
5177
		$newstring=dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom);	// Make entity encoding
5178
		$newstring=strtr($newstring,array('__and__'=>'&','__lt__'=>'<','__gt__'=>'>','__dquot__'=>'"'));
5179
	}
5180
	else
5181
	{
5182
		if ($removelasteolbr) $newstring=preg_replace('/(\r\n|\r|\n)$/i','',$newstring);	// Remove last \n (may remove several)
5183
		$newstring=dol_nl2br(dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom),$nl2brmode);
5184
	}
5185
	// Other substitutions that htmlentities does not do
5186
	//$newstring=str_replace(chr(128),'&euro;',$newstring);	// 128 = 0x80. Not in html entity table.     // Seems useles with TCPDF. Make bug with UTF8 languages
5187
	return $newstring;
5188
}
5189
5190
/**
5191
 *	This function is called to decode a HTML string (it decodes entities and br tags)
5192
 *
5193
 *	@param	string	$stringtodecode		String to decode
5194
 *	@param	string	$pagecodeto			Page code for result
5195
 *	@return	string						String decoded
5196
 */
5197
function dol_htmlentitiesbr_decode($stringtodecode,$pagecodeto='UTF-8')
5198
{
5199
	$ret=dol_html_entity_decode($stringtodecode,ENT_COMPAT,$pagecodeto);
5200
	$ret=preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i',"<br>",$ret);
5201
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i',"\r\n",$ret);
5202
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i',"\n",$ret);
5203
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i',"\n",$ret);
5204
	return $ret;
5205
}
5206
5207
/**
5208
 *	This function remove all ending \n and br at end
5209
 *
5210
 *	@param	string	$stringtodecode		String to decode
5211
 *	@return	string						String decoded
5212
 */
5213
function dol_htmlcleanlastbr($stringtodecode)
5214
{
5215
	$ret=preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i',"",$stringtodecode);
5216
	return $ret;
5217
}
5218
5219
/**
5220
 * Replace html_entity_decode functions to manage errors
5221
 *
5222
 * @param   string	$a		Operand a
5223
 * @param   string	$b		Operand b (ENT_QUOTES=convert simple and double quotes)
5224
 * @param   string	$c		Operand c
5225
 * @return  string			String decoded
5226
 */
5227
function dol_html_entity_decode($a,$b,$c='UTF-8')
5228
{
5229
	return html_entity_decode($a,$b,$c);
5230
}
5231
5232
/**
5233
 * Replace htmlentities functions.
5234
 * Goal of this function is to be sure to have default values of htmlentities that match what we need.
5235
 *
5236
 * @param   string  $string         The input string to encode
5237
 * @param   int     $flags          Flags (see PHP doc above)
5238
 * @param   string  $encoding       Encoding page code
5239
 * @param   bool    $double_encode  When double_encode is turned off, PHP will not encode existing html entities
5240
 * @return  string  $ret            Encoded string
5241
 */
5242
function dol_htmlentities($string, $flags=null, $encoding='UTF-8', $double_encode=false)
5243
{
5244
	return htmlentities($string, $flags, $encoding, $double_encode);
5245
}
5246
5247
/**
5248
 *	Check if a string is a correct iso string
5249
 *	If not, it will we considered not HTML encoded even if it is by FPDF.
5250
 *	Example, if string contains euro symbol that has ascii code 128
5251
 *
5252
 *	@param	string	$s      String to check
5253
 *	@return	int     		0 if bad iso, 1 if good iso
5254
 */
5255
function dol_string_is_good_iso($s)
5256
{
5257
	$len=dol_strlen($s);
5258
	$ok=1;
5259
	for($scursor=0;$scursor<$len;$scursor++)
5260
	{
5261
		$ordchar=ord($s{$scursor});
5262
		//print $scursor.'-'.$ordchar.'<br>';
5263
		if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) { $ok=0; break; }
5264
		if ($ordchar > 126 && $ordchar < 160) { $ok=0; break; }
5265
	}
5266
	return $ok;
5267
}
5268
5269
5270
/**
5271
 *	Return nb of lines of a clear text
5272
 *
5273
 *	@param	string	$s			String to check
5274
 * 	@param	int     $maxchar	Not yet used
5275
 *	@return	int					Number of lines
5276
 *  @see	dol_nboflines_bis, dolGetFirstLineOfText
5277
 */
5278
function dol_nboflines($s,$maxchar=0)
5279
{
5280
	if ($s == '') return 0;
5281
	$arraystring=explode("\n",$s);
5282
	$nb=count($arraystring);
5283
5284
	return $nb;
5285
}
5286
5287
5288
/**
5289
 *	Return nb of lines of a formated text with \n and <br> (WARNING: string must not have mixed \n and br separators)
5290
 *
5291
 *	@param	string	$text      		Text
5292
 *	@param	int		$maxlinesize  	Largeur de ligne en caracteres (ou 0 si pas de limite - defaut)
5293
 * 	@param	string	$charset		Give the charset used to encode the $text variable in memory.
5294
 *	@return int						Number of lines
5295
 *	@see	dol_nboflines, dolGetFirstLineOfText
5296
 */
5297
function dol_nboflines_bis($text,$maxlinesize=0,$charset='UTF-8')
5298
{
5299
	$repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5300
	if (dol_textishtml($text)) $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5301
5302
	$text = strtr($text, $repTable);
5303
	if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; }	// /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
5304
	else $pattern = '/(<br[^>]*>)/U';							// /U is to have UNGREEDY regex to limit to one html tag.
5305
	$a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5306
5307
	$nblines = (int) floor((count($a)+1)/2);
5308
	// count possible auto line breaks
5309
	if($maxlinesize)
5310
	{
5311
		foreach ($a as $line)
5312
		{
5313
			if (dol_strlen($line)>$maxlinesize)
5314
			{
5315
				//$line_dec = html_entity_decode(strip_tags($line));
5316
				$line_dec = html_entity_decode($line);
5317
				if(dol_strlen($line_dec)>$maxlinesize)
5318
				{
5319
					$line_dec=wordwrap($line_dec,$maxlinesize,'\n',true);
5320
					$nblines+=substr_count($line_dec,'\n');
5321
				}
5322
			}
5323
		}
5324
	}
5325
5326
	unset($a);
5327
	return $nblines;
5328
}
5329
5330
/**
5331
 *	 Same function than microtime in PHP 5 but compatible with PHP4
5332
 *
5333
 * @return		float		Time (millisecondes) with microsecondes in decimal part
5334
 * @deprecated Dolibarr does not support PHP4, you should use native function
5335
 * @see microtime()
5336
 */
5337
function dol_microtime_float()
5338
{
5339
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
5340
5341
	return microtime(true);
5342
}
5343
5344
/**
5345
 *	Return if a text is a html content
5346
 *
5347
 *	@param	string	$msg		Content to check
5348
 *	@param	int		$option		0=Full detection, 1=Fast check
5349
 *	@return	boolean				true/false
5350
 *	@see	dol_concatdesc
5351
 */
5352
function dol_textishtml($msg,$option=0)
5353
{
5354
	if ($option == 1)
5355
	{
5356
		if (preg_match('/<html/i',$msg))				return true;
5357
		elseif (preg_match('/<body/i',$msg))			return true;
5358
		elseif (preg_match('/<br/i',$msg))				return true;
5359
		return false;
5360
	}
5361
	else
5362
	{
5363
		if (preg_match('/<html/i',$msg))				return true;
5364
		elseif (preg_match('/<body/i',$msg))			return true;
5365
		elseif (preg_match('/<(b|em|i|u)>/i',$msg))		return true;
5366
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i',$msg)) 	  return true;
5367
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*>/i',$msg)) return true;
5368
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/>/i',$msg)) return true;
5369
		elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i',$msg)) return true;	// must accept <img src="http://example.com/aaa.png" />
5370
		elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i',$msg)) return true;	// must accept <a href="http://example.com/aaa.png" />
5371
		elseif (preg_match('/<h[0-9]>/i',$msg))			return true;
5372
		elseif (preg_match('/&[A-Z0-9]{1,6};/i',$msg))	return true;    // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
5373
		elseif (preg_match('/&#[0-9]{2,3};/i',$msg))	return true;    // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
5374
		return false;
5375
	}
5376
}
5377
5378
/**
5379
 *  Concat 2 descriptions with a new line between them (second operand after first one with appropriate new line separator)
5380
 *  text1 html + text2 html => text1 + '<br>' + text2
5381
 *  text1 html + text2 txt  => text1 + '<br>' + dol_nl2br(text2)
5382
 *  text1 txt  + text2 html => dol_nl2br(text1) + '<br>' + text2
5383
 *  text1 txt  + text2 txt  => text1 + '\n' + text2
5384
 *
5385
 *  @param	string	$text1		Text 1
5386
 *  @param	string	$text2		Text 2
5387
 *  @param  bool	$forxml     false=Use <br>, true=Use <br />
5388
 *  @return	string				Text 1 + new line + Text2
5389
 *  @see    dol_textishtml
5390
 */
5391
function dol_concatdesc($text1,$text2,$forxml=false)
5392
{
5393
	$ret='';
5394
	$ret.= (! dol_textishtml($text1) && dol_textishtml($text2))?dol_nl2br($text1, 0, $forxml):$text1;
5395
	$ret.= (! empty($text1) && ! empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2))?($forxml?"<br \>\n":"<br>\n") : "\n") : "";
5396
	$ret.= (dol_textishtml($text1) && ! dol_textishtml($text2))?dol_nl2br($text2, 0, $forxml):$text2;
5397
	return $ret;
5398
}
5399
5400
5401
/**
5402
 * Return array of possible common substitutions. This includes several families like: 'system', 'mycompany', 'object', 'objectamount', 'date', 'user'
5403
 *
5404
 * @param	Translate	$outputlangs	Output language
5405
 * @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)
5406
 * @param   array       $exclude        Array of family keys we want to exclude. For example array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...)
5407
 * @param   Object      $object         Object for keys on object
5408
 * @return	array						Array of substitutions
5409
 * @see setSubstitFromObject
5410
 */
5411
function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
5412
{
5413
	global $db, $conf, $mysoc, $user;
5414
5415
	$substitutionarray=array();
5416
5417
	if (empty($exclude) || ! in_array('system', $exclude))
5418
	{
5419
		$substitutionarray['__(AnyTranslationKey)__']=$outputlangs->trans('TranslationOfKey');
5420
		$substitutionarray['__[AnyConstantKey]__']=$outputlangs->trans('ValueOfConstant');
5421
		$substitutionarray['__DOL_MAIN_URL_ROOT__']=DOL_MAIN_URL_ROOT;
5422
	}
5423
	if ((empty($exclude) || ! in_array('mycompany', $exclude)) && is_object($mysoc))
5424
	{
5425
		$substitutionarray=array_merge($substitutionarray, array(
5426
			'__MYCOMPANY_NAME__'    => $mysoc->name,
5427
			'__MYCOMPANY_EMAIL__'   => $mysoc->email,
5428
			'__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
5429
			'__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
5430
			'__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
5431
			'__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
5432
			'__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
5433
			'__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
5434
			'__MYCOMPANY_CAPITAL__' => $mysoc->capital,
5435
			'__MYCOMPANY_FULLADDRESS__' => $mysoc->getFullAddress(1, ', '),
5436
			'__MYCOMPANY_ADDRESS__' => $mysoc->address,
5437
			'__MYCOMPANY_ZIP__'     => $mysoc->zip,
5438
			'__MYCOMPANY_TOWN__'    => $mysoc->town,
5439
			'__MYCOMPANY_COUNTRY__'    => $mysoc->country,
5440
			'__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id
5441
		));
5442
	}
5443
	if (($onlykey || is_object($object)) && (empty($exclude) || ! in_array('object', $exclude)))
5444
	{
5445
		if ($onlykey)
5446
		{
5447
			$substitutionarray['__ID__'] = '__ID__';
5448
			$substitutionarray['__REF__'] = '__REF__';
5449
			$substitutionarray['__REFCLIENT__'] = '__REFCLIENT__';
5450
			$substitutionarray['__REFSUPPLIER__'] = '__REFSUPPLIER__';
5451
			$substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
5452
5453
			$substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
5454
			$substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
5455
5456
			if (is_object($object) && $object->element == 'shipping')
5457
			{
5458
				$substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
5459
				$substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
5460
				$substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
5461
			}
5462
			$substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
5463
			$substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
5464
			$substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
5465
5466
			$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
5467
			$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
5468
			$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
5469
			$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
5470
5471
			$substitutionarray['__ONLINE_PAYMENT_URL__'] = 'LinkToPayOnlineIfApplicable';
5472
			$substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
5473
			$substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
5474
			$substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
5475
			$substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
5476
			$substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a a service';
5477
5478
			if (is_object($object) && $object->element == 'shipping')
5479
			{
5480
				$substitutionarray['__SHIPPINGTRACKNUM__']='Shipping tacking number';
5481
				$substitutionarray['__SHIPPINGTRACKNUMURL__']='Shipping tracking url';
5482
			}
5483
		}
5484
		else
5485
		{
5486
			$substitutionarray['__ID__'] = $object->id;
5487
			$substitutionarray['__REF__'] = $object->ref;
5488
			$substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : ''));
5489
			$substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : '');
5490
5491
			// TODO USe this ?
5492
			$msgishtml = 0;
5493
5494
			$birthday = dol_print_date($object->birth,'day');
5495
5496
			if (method_exists($object, 'getCivilityLabel')) $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
5497
			$substitutionarray['__MEMBER_FIRSTNAME__']=$msgishtml?dol_htmlentitiesbr($object->firstname):$object->firstname;
5498
			$substitutionarray['__MEMBER_LASTNAME__']=$msgishtml?dol_htmlentitiesbr($object->lastname):$object->lastname;
5499
			if (method_exists($object, 'getFullName')) $substitutionarray['__MEMBER_FULLNAME__']=$msgishtml?dol_htmlentitiesbr($object->getFullName($outputlangs)):$object->getFullName($outputlangs);
5500
			$substitutionarray['__MEMBER_COMPANY__']=$msgishtml?dol_htmlentitiesbr($object->societe):$object->societe;
5501
			$substitutionarray['__MEMBER_ADDRESS__']=$msgishtml?dol_htmlentitiesbr($object->address):$object->address;
5502
			$substitutionarray['__MEMBER_ZIP__']=$msgishtml?dol_htmlentitiesbr($object->zip):$object->zip;
5503
			$substitutionarray['__MEMBER_TOWN__']=$msgishtml?dol_htmlentitiesbr($object->town):$object->town;
5504
			$substitutionarray['__MEMBER_COUNTRY__']=$msgishtml?dol_htmlentitiesbr($object->country):$object->country;
5505
			$substitutionarray['__MEMBER_EMAIL__']=$msgishtml?dol_htmlentitiesbr($object->email):$object->email;
5506
			$substitutionarray['__MEMBER_BIRTH__']=$msgishtml?dol_htmlentitiesbr($birthday):$birthday;
5507
			$substitutionarray['__MEMBER_PHOTO__']=$msgishtml?dol_htmlentitiesbr($object->photo):$object->photo;
5508
			$substitutionarray['__MEMBER_LOGIN__']=$msgishtml?dol_htmlentitiesbr($object->login):$object->login;
5509
			$substitutionarray['__MEMBER_PASSWORD__']=$msgishtml?dol_htmlentitiesbr($object->pass):$object->pass;
5510
			$substitutionarray['__MEMBER_PHONE__']=$msgishtml?dol_htmlentitiesbr($object->phone):$object->phone;
5511
			$substitutionarray['__MEMBER_PHONEPRO__']=$msgishtml?dol_htmlentitiesbr($object->phone_perso):$object->phone_perso;
5512
			$substitutionarray['__MEMBER_PHONEMOBILE__']=$msgishtml?dol_htmlentitiesbr($object->phone_mobile):$object->phone_mobile;
5513
5514
			if (is_object($object) && $object->element == 'societe')
5515
			{
5516
				$substitutionarray['__THIRDPARTY_ID__'] = (is_object($object)?$object->id:'');
5517
				$substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object)?$object->name:'');
5518
			}
5519
			elseif (is_object($object->thirdparty) && $object->thirdparty->id > 0)
5520
			{
5521
				$substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty)?$object->thirdparty->id:'');
5522
				$substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty)?$object->thirdparty->name:'');
5523
			}
5524
5525
			if (is_object($object->projet) && $object->projet->id > 0)
5526
			{
5527
				$substitutionarray['__PROJECT_ID__'] = (is_object($object->projet)?$object->projet->id:'');
5528
				$substitutionarray['__PROJECT_REF__'] = (is_object($object->projet)?$object->projet->ref:'');
5529
				$substitutionarray['__PROJECT_NAME__'] = (is_object($object->projet)?$object->projet->title:'');
5530
			}
5531
5532
			if (is_object($object) && $object->element == 'shipping')
5533
			{
5534
				$substitutionarray['__SHIPPINGTRACKNUM__']=$object->tracking_number;
5535
				$substitutionarray['__SHIPPINGTRACKNUMURL__']=$object->tracking_url;
5536
			}
5537
5538
			if (is_object($object) && $object->element == 'contrat' && is_array($object->lines))
5539
			{
5540
				$dateplannedstart='';
5541
				$datenextexpiration='';
5542
				foreach($object->lines as $line)
5543
				{
5544
					if ($line->date_ouverture_prevue > $dateplannedstart) $dateplannedstart = $line->date_ouverture_prevue;
5545
					if ($line->statut == 4 && $line->date_fin_prevue && (! $datenextexpiration || $line->date_fin_prevue < $datenextexpiration)) $datenextexpiration = $line->date_fin_prevue;
5546
				}
5547
				$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'dayrfc');
5548
				$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
5549
				$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'dayrfc');
5550
				$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
5551
			}
5552
5553
			// Create dynamic tags for __EXTRAFIELD_FIELD__
5554
			if ($object->table_element && $object->id > 0)
5555
			{
5556
				$extrafieldstmp = new ExtraFields($db);
5557
				$extralabels = $extrafieldstmp->fetch_name_optionals_label($object->table_element, true);
5558
				$object->fetch_optionals($object->id, $extralabels);
5559
				foreach ($extrafieldstmp->attribute_label as $key => $label) {
5560
					$substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = $object->array_options['options_' . $key];
5561
				}
5562
			}
5563
5564
			$substitutionarray['__ONLINE_PAYMENT_URL__'] = 'TODO';
5565
		}
5566
	}
5567
	if (empty($exclude) || ! in_array('objectamount', $exclude))
5568
	{
5569
		$substitutionarray['__DATE_YMD__']        = is_object($object)?(isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : '') : '';
5570
		$substitutionarray['__DATE_DUE_YMD__']    = is_object($object)?(isset($object->date_lim_reglement)? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : '') : '';
5571
		$substitutionarray['__AMOUNT__']          = is_object($object)?$object->total_ttc:'';
5572
		$substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object)?$object->total_ht:'';
5573
		$substitutionarray['__AMOUNT_VAT__']      = is_object($object)?($object->total_vat?$object->total_vat:$object->total_tva):'';
5574
 		if ($onlykey != 2 || $mysoc->useLocalTax(1)) $substitutionarray['__AMOUNT_TAX2__']     = is_object($object)?($object->total_localtax1?$object->total_localtax1:$object->total_localtax1):'';
5575
		if ($onlykey != 2 || $mysoc->useLocalTax(2)) $substitutionarray['__AMOUNT_TAX3__']     = is_object($object)?($object->total_localtax2?$object->total_localtax2:$object->total_localtax2):'';
5576
5577
		/* TODO Add key for multicurrency
5578
    	$substitutionarray['__AMOUNT_FORMATED__']          = is_object($object)?price($object->total_ttc, 0, $outputlangs, 0, 0, -1, $conf->currency_code):'';
5579
		$substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object)?price($object->total_ht, 0, $outputlangs, 0, 0, -1, $conf->currency_code):'';
5580
        $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)):'';
5581
		*/
5582
		// For backward compatibility
5583
		if ($onlykey != 2)
5584
		{
5585
			$substitutionarray['__TOTAL_TTC__']    = is_object($object)?$object->total_ttc:'';
5586
			$substitutionarray['__TOTAL_HT__']     = is_object($object)?$object->total_ht:'';
5587
			$substitutionarray['__TOTAL_VAT__']    = is_object($object)?($object->total_vat?$object->total_vat:$object->total_tva):'';
5588
		}
5589
	}
5590
5591
	if (empty($exclude) || ! in_array('date', $exclude))
5592
	{
5593
		include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
5594
5595
		$tmp=dol_getdate(dol_now(), true);
5596
		$tmp2=dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
5597
		$tmp3=dol_get_prev_month($tmp['mday'], $tmp['mon'], $tmp['year']);
5598
		$tmp4=dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
5599
		$tmp5=dol_get_next_month($tmp['mday'], $tmp['mon'], $tmp['year']);
5600
5601
		$substitutionarray=array_merge($substitutionarray, array(
5602
			'__DAY__' => (string) $tmp['mday'],
5603
			'__MONTH__' => (string) $tmp['mon'],
5604
			'__YEAR__' => (string) $tmp['year'],
5605
			'__PREVIOUS_DAY__' => (string) $tmp2['day'],
5606
			'__PREVIOUS_MONTH__' => (string) $tmp3['month'],
5607
			'__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
5608
			'__NEXT_DAY__' => (string) $tmp4['day'],
5609
			'__NEXT_MONTH__' => (string) $tmp5['month'],
5610
			'__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
5611
		));
5612
	}
5613
5614
	if (empty($exclude) || ! in_array('user', $exclude))
5615
	{
5616
		// Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
5617
		// this will also replace var found into content of signature
5618
		$signature = $user->signature;
5619
		$substitutionarray=array_merge($substitutionarray, array(
5620
			'__USER_SIGNATURE__' => (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '')
5621
		)
5622
			);
5623
		// For backward compatibility
5624
		if ($onlykey != 2)
5625
		{
5626
			$substitutionarray['__SIGNATURE__'] = (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '');
5627
		}
5628
5629
		$substitutionarray=array_merge($substitutionarray, array(
5630
			'__USER_ID__' => (string) $user->id,
5631
			'__USER_LOGIN__' => (string) $user->login,
5632
			'__USER_LASTNAME__' => (string) $user->lastname,
5633
			'__USER_FIRSTNAME__' => (string) $user->firstname,
5634
			'__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
5635
			'__USER_SUPERVISOR_ID__' => (string) $user->fk_user,
5636
			'__USER_REMOTE_IP__' => (string) $_SERVER['REMOTE_ADDR']
5637
			)
5638
		);
5639
	}
5640
	if (! empty($conf->multicompany->enabled))
5641
	{
5642
		$substitutionarray=array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
5643
	}
5644
5645
	return $substitutionarray;
5646
}
5647
5648
/**
5649
 *  Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newval).
5650
 *  Texts like __(TranslationKey|langfile)__ and __[ConstantKey]__ are also replaced.
5651
 *  Example of usage:
5652
 *  $substitutionarray = getCommonSubstitutionArray($langs, 0, null, $thirdparty);
5653
 *  complete_substitutions_array($substitutionarray, $langs, $thirdparty);
5654
 *  $mesg = make_substitutions($mesg, $substitutionarray, $langs);
5655
 *
5656
 *  @param	string		$text	      			Source string in which we must do substitution
5657
 *  @param  array		$substitutionarray		Array with key->val to substitute. Example: array('__MYKEY__' => 'MyVal', ...)
5658
 *  @param	Translate	$outputlangs			Output language
5659
 * 	@return string  		    				Output string after substitutions
5660
 *  @see	complete_substitutions_array
5661
 */
5662
function make_substitutions($text, $substitutionarray, $outputlangs=null)
5663
{
5664
	global $conf, $langs;
5665
5666
	if (! is_array($substitutionarray)) return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
5667
5668
	if (empty($outputlangs)) $outputlangs=$langs;
5669
5670
	// Make substitution for language keys
5671
	if (is_object($outputlangs))
5672
	{
5673
		while (preg_match('/__\(([^\)]+)\)__/', $text, $reg))
5674
		{
5675
			$msgishtml = 0;
5676
			if (dol_textishtml($text,1)) $msgishtml = 1;
5677
5678
			// If key is __(TranslationKey|langfile)__, then force load of langfile.lang
5679
			$tmp=explode('|',$reg[1]);
5680
			if (! empty($tmp[1])) $outputlangs->load($tmp[1]);
5681
5682
			$text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $msgishtml?dol_htmlentitiesbr($outputlangs->transnoentitiesnoconv($reg[1])):$outputlangs->transnoentitiesnoconv($reg[1]), $text);
5683
		}
5684
	}
5685
5686
	// Make substitution for constant keys. Must be after the substitution of translation, so if text of translation contains a constant,
5687
	// it is also converted.
5688
	while (preg_match('/__\[([^\]]+)\]__/', $text, $reg))
5689
	{
5690
		$msgishtml = 0;
5691
		if (dol_textishtml($text,1)) $msgishtml = 1;
5692
5693
		$keyfound = $reg[1];
5694
		$newval=empty($conf->global->$keyfound)?'':$conf->global->$keyfound;
5695
		$text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml?dol_htmlentitiesbr($newval):$newval, $text);
5696
	}
5697
5698
	// Make substitition for array $substitutionarray
5699
	foreach ($substitutionarray as $key => $value)
5700
	{
5701
		if ($key == '__SIGNATURE__' && (! empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) $value='';		// Protection
5702
		if ($key == '__USER_SIGNATURE__' && (! empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) $value='';	// Protection
5703
5704
		$text=str_replace("$key","$value",$text);	// We must keep the " to work when value is 123.5 for example
5705
	}
5706
5707
	return $text;
5708
}
5709
5710
/**
5711
 *  Complete the $substitutionarray with more entries coming from external module that had set the "substitutions=1" into module_part array.
5712
 *  In this case, method completesubstitutionarray provided by module is called.
5713
 *
5714
 *  @param  array		$substitutionarray		Array substitution old value => new value value
5715
 *  @param  Translate	$outputlangs            Output language
5716
 *  @param  Object		$object                 Source object
5717
 *  @param  mixed		$parameters       		Add more parameters (useful to pass product lines)
5718
 *  @param  string      $callfunc               What is the name of the custom function that will be called? (default: completesubstitutionarray)
5719
 *  @return	void
5720
 *  @see 	make_substitutions
5721
 */
5722
function complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
5723
{
5724
	global $conf,$user;
5725
5726
	require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5727
5728
	// Add a substitution key for each extrafields, using key __EXTRA_XXX__
5729
	// TODO Remove this. Already available into the getCommonSubstitutionArray used to build the substitution array.
5730
	/*if (is_object($object) && is_array($object->array_options))
5731
	{
5732
		foreach($object->array_options as $key => $val)
5733
		{
5734
			$keyshort=preg_replace('/^(options|extra)_/','',$key);
5735
			$substitutionarray['__EXTRAFIELD_'.$keyshort.'__']=$val;
5736
			// For backward compatibiliy
5737
			$substitutionarray['%EXTRA_'.$keyshort.'%']=$val;
5738
		}
5739
	}*/
5740
5741
	// Check if there is external substitution to do, requested by plugins
5742
	$dirsubstitutions=array_merge(array(),(array) $conf->modules_parts['substitutions']);
5743
5744
	foreach($dirsubstitutions as $reldir)
5745
	{
5746
		$dir=dol_buildpath($reldir,0);
5747
5748
		// Check if directory exists
5749
		if (! dol_is_dir($dir)) continue;
5750
5751
		$substitfiles=dol_dir_list($dir,'files',0,'functions_');
5752
		foreach($substitfiles as $substitfile)
5753
		{
5754
			if (preg_match('/functions_(.*)\.lib\.php/i',$substitfile['name'],$reg))
5755
			{
5756
				$module=$reg[1];
5757
5758
				dol_syslog("Library functions_".$substitfile['name']." found into ".$dir);
5759
				// Include the user's functions file
5760
				require_once $dir.$substitfile['name'];
5761
				// Call the user's function, and only if it is defined
5762
				$function_name=$module."_".$callfunc;
5763
				if (function_exists($function_name)) $function_name($substitutionarray,$outputlangs,$object,$parameters);
5764
			}
5765
		}
5766
	}
5767
}
5768
5769
/**
5770
 *    Format output for start and end date
5771
 *
5772
 *    @param	int	$date_start    Start date
5773
 *    @param    int	$date_end      End date
5774
 *    @param    string		$format        Output format
5775
 *    @param	Translate	$outputlangs   Output language
5776
 *    @return	void
5777
 */
5778
function print_date_range($date_start,$date_end,$format = '',$outputlangs='')
5779
{
5780
	print get_date_range($date_start,$date_end,$format,$outputlangs);
5781
}
5782
5783
/**
5784
 *    Format output for start and end date
5785
 *
5786
 *    @param	int			$date_start    		Start date
5787
 *    @param    int			$date_end      		End date
5788
 *    @param    string		$format        		Output format
5789
 *    @param	Translate	$outputlangs   		Output language
5790
 *    @param	integer		$withparenthesis	1=Add parenthesis, 0=non parenthesis
5791
 *    @return	string							String
5792
 */
5793
function get_date_range($date_start,$date_end,$format = '',$outputlangs='', $withparenthesis=1)
5794
{
5795
	global $langs;
5796
5797
	$out='';
5798
5799
	if (! is_object($outputlangs)) $outputlangs=$langs;
5800
5801
	if ($date_start && $date_end)
5802
	{
5803
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFromTo',dol_print_date($date_start, $format, false, $outputlangs),dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
5804
	}
5805
	if ($date_start && ! $date_end)
5806
	{
5807
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFrom',dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis?')':'');
5808
	}
5809
	if (! $date_start && $date_end)
5810
	{
5811
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateUntil',dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
5812
	}
5813
5814
	return $out;
5815
}
5816
5817
/**
5818
 * Return firstname and lastname in correct order
5819
 *
5820
 * @param	string	$firstname		Firstname
5821
 * @param	string	$lastname		Lastname
5822
 * @param	int		$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname
5823
 * @return	string					Firstname + lastname or Lastname + firstname
5824
 */
5825
function dolGetFirstLastname($firstname,$lastname,$nameorder=-1)
5826
{
5827
	global $conf;
5828
5829
	$ret='';
5830
	// If order not defined, we use the setup
5831
	if ($nameorder < 0) $nameorder=(empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION));
5832
	if ($nameorder && ((string) $nameorder != '2'))
5833
	{
5834
		$ret.=$firstname;
5835
		if ($firstname && $lastname) $ret.=' ';
5836
		$ret.=$lastname;
5837
	}
5838
	else if ($nameorder == 2)
5839
	{
5840
	   $ret.=$firstname;
5841
	}
5842
	else
5843
	{
5844
		$ret.=$lastname;
5845
		if ($firstname && $lastname) $ret.=' ';
5846
		$ret.=$firstname;
5847
	}
5848
	return $ret;
5849
}
5850
5851
5852
/**
5853
 *	Set event message in dol_events session object. Will be output by calling dol_htmloutput_events.
5854
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
5855
 *  Note: Prefer to use setEventMessages instead.
5856
 *
5857
 *	@param	mixed	$mesgs			Message string or array
5858
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
5859
 *  @return	void
5860
 *  @see	dol_htmloutput_events
5861
 */
5862
function setEventMessage($mesgs, $style='mesgs')
5863
{
5864
	//dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);		This is not deprecated, it is used by setEventMessages function
5865
	if (! is_array($mesgs))		// If mesgs is a string
5866
	{
5867
		if ($mesgs) $_SESSION['dol_events'][$style][] = $mesgs;
5868
	}
5869
	else						// If mesgs is an array
5870
	{
5871
		foreach($mesgs as $mesg)
5872
		{
5873
			if ($mesg) $_SESSION['dol_events'][$style][] = $mesg;
5874
		}
5875
	}
5876
}
5877
5878
/**
5879
 *	Set event messages in dol_events session object. Will be output by calling dol_htmloutput_events.
5880
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
5881
 *
5882
 *	@param	string	$mesg			Message string
5883
 *	@param	array	$mesgs			Message array
5884
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
5885
 *  @return	void
5886
 *  @see	dol_htmloutput_events
5887
 */
5888
function setEventMessages($mesg, $mesgs, $style='mesgs')
5889
{
5890
	if (! in_array((string) $style, array('mesgs','warnings','errors'))) dol_print_error('','Bad parameter style='.$style.' for setEventMessages');
5891
	if (empty($mesgs)) setEventMessage($mesg, $style);
5892
	else
5893
	{
5894
		if (! empty($mesg) && ! in_array($mesg, $mesgs)) setEventMessage($mesg, $style);	// Add message string if not already into array
5895
		setEventMessage($mesgs, $style);
5896
	}
5897
}
5898
5899
/**
5900
 *	Print formated messages to output (Used to show messages on html output).
5901
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function, so there is
5902
 *  no need to call it explicitely.
5903
 *
5904
 *  @param	int		$disabledoutputofmessages	Clear all messages stored into session without diplaying them
5905
 *  @return	void
5906
 *  @see    									dol_htmloutput_mesg
5907
 */
5908
function dol_htmloutput_events($disabledoutputofmessages=0)
5909
{
5910
	// Show mesgs
5911
	if (isset($_SESSION['dol_events']['mesgs'])) {
5912
		if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
5913
		unset($_SESSION['dol_events']['mesgs']);
5914
	}
5915
5916
	// Show errors
5917
	if (isset($_SESSION['dol_events']['errors'])) {
5918
		if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
5919
		unset($_SESSION['dol_events']['errors']);
5920
	}
5921
5922
	// Show warnings
5923
	if (isset($_SESSION['dol_events']['warnings'])) {
5924
		if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
5925
		unset($_SESSION['dol_events']['warnings']);
5926
	}
5927
}
5928
5929
/**
5930
 *	Get formated messages to output (Used to show messages on html output).
5931
 *  This include also the translation of the message key.
5932
 *
5933
 *	@param	string		$mesgstring		Message string or message key
5934
 *	@param	string[]	$mesgarray      Array of message strings or message keys
5935
 *  @param  string		$style          Style of message output ('ok' or 'error')
5936
 *  @param  int			$keepembedded   Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
5937
 *	@return	string						Return html output
5938
 *
5939
 *  @see    dol_print_error
5940
 *  @see    dol_htmloutput_errors
5941
 *  @see    setEventMessages
5942
 */
5943
function get_htmloutput_mesg($mesgstring='',$mesgarray='', $style='ok', $keepembedded=0)
5944
{
5945
	global $conf, $langs;
5946
5947
	$ret=0; $return='';
5948
	$out='';
5949
	$divstart=$divend='';
5950
5951
	// If inline message with no format, we add it.
5952
	if ((empty($conf->use_javascript_ajax) || ! empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && ! preg_match('/<div class=".*">/i',$out))
5953
	{
5954
		$divstart='<div class="'.$style.' clearboth">';
5955
		$divend='</div>';
5956
	}
5957
5958
	if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring)
5959
	{
5960
		$langs->load("errors");
5961
		$out.=$divstart;
5962
		if (is_array($mesgarray) && count($mesgarray))
5963
		{
5964
			foreach($mesgarray as $message)
5965
			{
5966
				$ret++;
5967
				$out.= $langs->trans($message);
5968
				if ($ret < count($mesgarray)) $out.= "<br>\n";
5969
			}
5970
		}
5971
		if ($mesgstring)
5972
		{
5973
			$langs->load("errors");
5974
			$ret++;
5975
			$out.= $langs->trans($mesgstring);
5976
		}
5977
		$out.=$divend;
5978
	}
5979
5980
	if ($out)
5981
	{
5982
		if (! empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded))
5983
		{
5984
			$return = '<script type="text/javascript">
5985
					$(document).ready(function() {
5986
						var block = '.(! empty($conf->global->MAIN_USE_JQUERY_BLOCKUI)?"true":"false").'
5987
						if (block) {
5988
							$.dolEventValid("","'.dol_escape_js($out).'");
5989
						} else {
5990
							/* jnotify(message, preset of message type, keepmessage) */
5991
							$.jnotify("'.dol_escape_js($out).'",
5992
							"'.($style=="ok" ? 3000 : $style).'",
5993
							'.($style=="ok" ? "false" : "true").',
5994
							{ remove: function (){} } );
5995
						}
5996
					});
5997
				</script>';
5998
		}
5999
		else
6000
		{
6001
			$return = $out;
6002
		}
6003
	}
6004
6005
	return $return;
6006
}
6007
6008
/**
6009
 *  Get formated error messages to output (Used to show messages on html output).
6010
 *
6011
 *  @param	string	$mesgstring         Error message
6012
 *  @param  array	$mesgarray          Error messages array
6013
 *  @param  int		$keepembedded       Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
6014
 *  @return string                		Return html output
6015
 *
6016
 *  @see    dol_print_error
6017
 *  @see    dol_htmloutput_mesg
6018
 */
6019
function get_htmloutput_errors($mesgstring='', $mesgarray='', $keepembedded=0)
6020
{
6021
	return get_htmloutput_mesg($mesgstring, $mesgarray,'error',$keepembedded);
6022
}
6023
6024
/**
6025
 *	Print formated messages to output (Used to show messages on html output).
6026
 *
6027
 *	@param	string		$mesgstring		Message string or message key
6028
 *	@param	string[]	$mesgarray      Array of message strings or message keys
6029
 *  @param  string      $style          Which style to use ('ok', 'warning', 'error')
6030
 *  @param  int         $keepembedded   Set to 1 if message must be kept embedded into its html place (this disable jnotify)
6031
 *  @return	void
6032
 *
6033
 *  @see    dol_print_error
6034
 *  @see    dol_htmloutput_errors
6035
 *  @see    setEventMessages
6036
 */
6037
function dol_htmloutput_mesg($mesgstring='',$mesgarray='', $style='ok', $keepembedded=0)
6038
{
6039
	if (empty($mesgstring) && (! is_array($mesgarray) || count($mesgarray) == 0)) return;
6040
6041
	$iserror=0;
6042
	$iswarning=0;
6043
	if (is_array($mesgarray))
6044
	{
6045
		foreach($mesgarray as $val)
6046
		{
6047
			if ($val && preg_match('/class="error"/i',$val)) { $iserror++; break; }
6048
			if ($val && preg_match('/class="warning"/i',$val)) { $iswarning++; break; }
6049
		}
6050
	}
6051
	else if ($mesgstring && preg_match('/class="error"/i',$mesgstring)) $iserror++;
6052
	else if ($mesgstring && preg_match('/class="warning"/i',$mesgstring)) $iswarning++;
6053
	if ($style=='error') $iserror++;
6054
	if ($style=='warning') $iswarning++;
6055
6056
	if ($iserror || $iswarning)
6057
	{
6058
		// Remove div from texts
6059
		$mesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$mesgstring);
6060
		$mesgstring=preg_replace('/<div class="(error|warning)">/','',$mesgstring);
6061
		$mesgstring=preg_replace('/<\/div>/','',$mesgstring);
6062
		// Remove div from texts array
6063
		if (is_array($mesgarray))
6064
		{
6065
			$newmesgarray=array();
6066
			foreach($mesgarray as $val)
6067
			{
6068
				$tmpmesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$val);
6069
				$tmpmesgstring=preg_replace('/<div class="(error|warning)">/','',$tmpmesgstring);
6070
				$tmpmesgstring=preg_replace('/<\/div>/','',$tmpmesgstring);
6071
				$newmesgarray[]=$tmpmesgstring;
6072
			}
6073
			$mesgarray=$newmesgarray;
6074
		}
6075
		print get_htmloutput_mesg($mesgstring,$mesgarray,($iserror?'error':'warning'),$keepembedded);
6076
	}
6077
	else print get_htmloutput_mesg($mesgstring,$mesgarray,'ok',$keepembedded);
6078
}
6079
6080
/**
6081
 *  Print formated error messages to output (Used to show messages on html output).
6082
 *
6083
 *  @param	string	$mesgstring          Error message
6084
 *  @param  array	$mesgarray           Error messages array
6085
 *  @param  int		$keepembedded        Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
6086
 *  @return	void
6087
 *
6088
 *  @see    dol_print_error
6089
 *  @see    dol_htmloutput_mesg
6090
 */
6091
function dol_htmloutput_errors($mesgstring='', $mesgarray='', $keepembedded=0)
6092
{
6093
	dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
6094
}
6095
6096
/**
6097
 * 	Advanced sort array by second index function, which produces ascending (default)
6098
 *  or descending output and uses optionally natural case insensitive sorting (which
6099
 *  can be optionally case sensitive as well).
6100
 *
6101
 *  @param      array		$array      		Array to sort (array of array('key','otherkey1','otherkey2'...))
6102
 *  @param      string		$index				Key in array to use for sorting criteria
6103
 *  @param      int			$order				Sort order ('asc' or 'desc')
6104
 *  @param      int			$natsort			1=use "natural" sort (natsort), 0=use "standard" sort (asort)
6105
 *  @param      int			$case_sensitive		1=sort is case sensitive, 0=not case sensitive
6106
 *  @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.
6107
 *  @return     array							Sorted array
6108
 */
6109
function dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
6110
{
6111
	// Clean parameters
6112
	$order=strtolower($order);
6113
6114
	$sizearray=count($array);
6115
	if (is_array($array) && $sizearray>0)
6116
	{
6117
		$temp = array();
6118
		foreach(array_keys($array) as $key) $temp[$key]=$array[$key][$index];
6119
6120
		if (!$natsort) ($order=='asc') ? asort($temp) : arsort($temp);
6121
		else
6122
		{
6123
			($case_sensitive) ? natsort($temp) : natcasesort($temp);
6124
			if($order!='asc') $temp=array_reverse($temp,TRUE);
6125
		}
6126
6127
		$sorted = array();
6128
6129
		foreach(array_keys($temp) as $key)
6130
		{
6131
			(is_numeric($key) && empty($keepindex)) ? $sorted[]=$array[$key] : $sorted[$key]=$array[$key];
6132
		}
6133
6134
		return $sorted;
6135
	}
6136
	return $array;
6137
}
6138
6139
6140
/**
6141
 *      Check if a string is in UTF8
6142
 *
6143
 *      @param	string	$str        String to check
6144
 * 		@return	boolean				True if string is UTF8 or ISO compatible with UTF8, False if not (ISO with special char or Binary)
6145
 */
6146
function utf8_check($str)
6147
{
6148
	// We must use here a binary strlen function (so not dol_strlen)
6149
	$strLength = dol_strlen($str);
6150
	for ($i=0; $i<$strLength; $i++)
6151
	{
6152
		if (ord($str[$i]) < 0x80) continue; // 0bbbbbbb
6153
		elseif ((ord($str[$i]) & 0xE0) == 0xC0) $n=1; // 110bbbbb
6154
		elseif ((ord($str[$i]) & 0xF0) == 0xE0) $n=2; // 1110bbbb
6155
		elseif ((ord($str[$i]) & 0xF8) == 0xF0) $n=3; // 11110bbb
6156
		elseif ((ord($str[$i]) & 0xFC) == 0xF8) $n=4; // 111110bb
6157
		elseif ((ord($str[$i]) & 0xFE) == 0xFC) $n=5; // 1111110b
6158
		else return false; // Does not match any model
6159
		for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ?
6160
			if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
6161
			return false;
6162
		}
6163
	}
6164
	return true;
6165
}
6166
6167
6168
/**
6169
 *      Return a string encoded into OS filesystem encoding. This function is used to define
6170
 * 	    value to pass to filesystem PHP functions.
6171
 *
6172
 *      @param	string	$str        String to encode (UTF-8)
6173
 * 		@return	string				Encoded string (UTF-8, ISO-8859-1)
6174
 */
6175
function dol_osencode($str)
6176
{
6177
	global $conf;
6178
6179
	$tmp=ini_get("unicode.filesystem_encoding");						// Disponible avec PHP 6.0
6180
	if (empty($tmp) && ! empty($_SERVER["WINDIR"])) $tmp='iso-8859-1';	// By default for windows
6181
	if (empty($tmp)) $tmp='utf-8';										// By default for other
6182
	if (! empty($conf->global->MAIN_FILESYSTEM_ENCODING)) $tmp=$conf->global->MAIN_FILESYSTEM_ENCODING;
6183
6184
	if ($tmp == 'iso-8859-1') return utf8_decode($str);
6185
	return $str;
6186
}
6187
6188
6189
/**
6190
 *      Return an id or code from a code or id.
6191
 *      Store also Code-Id into a cache to speed up next request on same key.
6192
 *
6193
 * 		@param	DoliDB	$db				Database handler
6194
 * 		@param	string	$key			Code or Id to get Id or Code
6195
 * 		@param	string	$tablename		Table name without prefix
6196
 * 		@param	string	$fieldkey		Field to search the key into
6197
 * 		@param	string	$fieldid		Field to get
6198
 *      @param  int		$entityfilter	Filter by entity
6199
 *      @return int						<0 if KO, Id of code if OK
6200
 *      @see $langs->getLabelFromKey
6201
 */
6202
function dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0)
6203
{
6204
	global $cache_codes;
6205
6206
	// If key empty
6207
	if ($key == '') return '';
6208
6209
	// Check in cache
6210
	if (isset($cache_codes[$tablename][$key]))	// Can be defined to 0 or ''
6211
	{
6212
		return $cache_codes[$tablename][$key];   // Found in cache
6213
	}
6214
6215
	$sql = "SELECT ".$fieldid." as valuetoget";
6216
	$sql.= " FROM ".MAIN_DB_PREFIX.$tablename;
6217
	$sql.= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
6218
	if (! empty($entityfilter))
6219
		$sql.= " AND entity IN (" . getEntity($tablename) . ")";
6220
6221
	dol_syslog('dol_getIdFromCode', LOG_DEBUG);
6222
	$resql = $db->query($sql);
6223
	if ($resql)
6224
	{
6225
		$obj = $db->fetch_object($resql);
6226
		if ($obj) $cache_codes[$tablename][$key]=$obj->valuetoget;
6227
		else $cache_codes[$tablename][$key]='';
6228
		$db->free($resql);
6229
		return $cache_codes[$tablename][$key];
6230
	}
6231
	else
6232
	{
6233
		return -1;
6234
	}
6235
}
6236
6237
/**
6238
 * Verify if condition in string is ok or not
6239
 *
6240
 * @param 	string		$strRights		String with condition to check
6241
 * @return 	boolean						True or False. Return true if strRights is ''
6242
 */
6243
function verifCond($strRights)
6244
{
6245
	global $user,$conf,$langs;
6246
	global $leftmenu;
6247
	global $rights;    // To export to dol_eval function
6248
6249
	//print $strRights."<br>\n";
6250
	$rights = true;
6251
	if ($strRights != '')
6252
	{
6253
		//$tab_rights = explode('&&', $strRights);
6254
		//$i = 0;
6255
		//while (($i < count($tab_rights)) && ($rights == true)) {
6256
		$str = 'if(!(' . $strRights . ')) { $rights = false; }';
6257
		dol_eval($str);
6258
		//	$i++;
6259
		//}
6260
	}
6261
	return $rights;
6262
}
6263
6264
/**
6265
 * Replace eval function to add more security.
6266
 * This function is called by verifCond() or trans() and transnoentitiesnoconv().
6267
 *
6268
 * @param 	string	$s				String to evaluate
6269
 * @param	int		$returnvalue	0=No return (used to execute eval($a=something)). 1=Value of eval is returned (used to eval($something)).
6270
 * @param   int     $hideerrors     1=Hide errors
6271
 * @return	mixed					Nothing or return of eval
6272
 */
6273
function dol_eval($s, $returnvalue=0, $hideerrors=1)
6274
{
6275
	// Only global variables can be changed by eval function and returned to caller
6276
	global $db, $langs, $user, $conf;
6277
	global $mainmenu, $leftmenu;
6278
	global $rights;
6279
	global $object;
6280
	global $mysoc;
6281
6282
	global $obj;       // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
6283
	global $soc;       // For backward compatibility
6284
6285
	//print $s."<br>\n";
6286
	if ($returnvalue)
6287
	{
6288
		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...
6289
		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...
6290
	}
6291
	else
6292
	{
6293
		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...
6294
		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...
6295
	}
6296
}
6297
6298
/**
6299
 * Return if var element is ok
6300
 *
6301
 * @param   string      $element    Variable to check
6302
 * @return  boolean                 Return true of variable is not empty
6303
 */
6304
function dol_validElement($element)
6305
{
6306
	return (trim($element) != '');
6307
}
6308
6309
/**
6310
 * 	Return img flag of country for a language code or country code
6311
 *
6312
 * 	@param	string	$codelang	Language code (en_IN, fr_CA...) or Country code (IN, FR)
6313
 *  @param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
6314
 * 	@return	string				HTML img string with flag.
6315
 */
6316
function picto_from_langcode($codelang, $moreatt = '')
6317
{
6318
	global $langs;
6319
6320
	if (empty($codelang)) return '';
6321
6322
	if (empty($codelang)) return '';
6323
6324
	if ($codelang == 'auto')
6325
	{
6326
		return '<span class="fa fa-globe"></span>';
6327
	}
6328
6329
	$langtocountryflag = array(
6330
		'ar_AR' => '',
6331
		'ca_ES' => 'catalonia',
6332
		'da_DA' => 'dk',
6333
		'fr_CA' => 'mq',
6334
		'sv_SV' => 'se'
6335
	);
6336
6337
	if (isset($langtocountryflag[$codelang])) $flagImage = $langtocountryflag[$codelang];
6338
	else
6339
	{
6340
		$tmparray = explode('_', $codelang);
6341
		$flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
6342
	}
6343
6344
	return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt);
6345
}
6346
6347
/**
6348
 *  Complete or removed entries into a head array (used to build tabs).
6349
 *  For example, with value added by external modules. Such values are declared into $conf->modules_parts['tab'].
6350
 *  Or by change using hook completeTabsHead
6351
 *
6352
 *  @param	Conf			$conf           Object conf
6353
 *  @param  Translate		$langs          Object langs
6354
 *  @param  object|null		$object         Object object
6355
 *  @param  array			$head          	Object head
6356
 *  @param  int				$h				New position to fill
6357
 *  @param  string			$type           Value for object where objectvalue can be
6358
 *                              			'thirdparty'       to add a tab in third party view
6359
 *		                        	      	'intervention'     to add a tab in intervention view
6360
 *     		                    	     	'supplier_order'   to add a tab in supplier order view
6361
 *          		            	        'supplier_invoice' to add a tab in supplier invoice view
6362
 *                  		    	        'invoice'          to add a tab in customer invoice view
6363
 *                          			    'order'            to add a tab in customer order view
6364
 *                          				'contract'		   to add a tabl in contract view
6365
 *                      			        'product'          to add a tab in product view
6366
 *                              			'propal'           to add a tab in propal view
6367
 *                              			'user'             to add a tab in user view
6368
 *                              			'group'            to add a tab in group view
6369
 * 		        	               	     	'member'           to add a tab in fundation member view
6370
 *      		                        	'categories_x'	   to add a tab in category view ('x': type of category (0=product, 1=supplier, 2=customer, 3=member)
6371
 *      									'ecm'			   to add a tab for another ecm view
6372
 *                                          'stock'            to add a tab for warehouse view
6373
 *  @param  string		$mode  	        	'add' to complete head, 'remove' to remove entries
6374
 *	@return	void
6375
 */
6376
function complete_head_from_modules($conf,$langs,$object,&$head,&$h,$type,$mode='add')
6377
{
6378
	global $hookmanager;
6379
6380
	if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type]))
6381
	{
6382
		foreach ($conf->modules_parts['tabs'][$type] as $value)
6383
		{
6384
			$values=explode(':',$value);
6385
6386
			if ($mode == 'add' && ! preg_match('/^\-/',$values[1]))
6387
			{
6388
				if (count($values) == 6)       // new declaration with permissions:  $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
6389
				{
6390
					if ($values[0] != $type) continue;
6391
6392
					if (verifCond($values[4]))
6393
					{
6394
						if ($values[3]) $langs->load($values[3]);
6395
						if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
6396
						{
6397
							$substitutionarray=array();
6398
							complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
6399
							$label=make_substitutions($reg[1], $substitutionarray);
6400
						}
6401
						else $label=$langs->trans($values[2]);
6402
6403
						$head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && ! empty($object->id))?$object->id:''), $values[5]), 1);
6404
						$head[$h][1] = $label;
6405
						$head[$h][2] = str_replace('+','',$values[1]);
6406
						$h++;
6407
					}
6408
				}
6409
				else if (count($values) == 5)       // deprecated
6410
				{
6411
					dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
6412
6413
					if ($values[0] != $type) continue;
6414
					if ($values[3]) $langs->load($values[3]);
6415
					if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
6416
					{
6417
						$substitutionarray=array();
6418
						complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
6419
						$label=make_substitutions($reg[1], $substitutionarray);
6420
					}
6421
					else $label=$langs->trans($values[2]);
6422
6423
					$head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && ! empty($object->id))?$object->id:''), $values[4]), 1);
6424
					$head[$h][1] = $label;
6425
					$head[$h][2] = str_replace('+','',$values[1]);
6426
					$h++;
6427
				}
6428
			}
6429
			else if ($mode == 'remove' && preg_match('/^\-/',$values[1]))
6430
			{
6431
				if ($values[0] != $type) continue;
6432
				$tabname=str_replace('-','',$values[1]);
6433
				foreach($head as $key => $val)
6434
				{
6435
					$condition = (! empty($values[3]) ? verifCond($values[3]) : 1);
6436
					if ($head[$key][2]==$tabname && $condition)
6437
					{
6438
						unset($head[$key]);
6439
						break;
6440
					}
6441
				}
6442
			}
6443
		}
6444
	}
6445
6446
	// No need to make a return $head. Var is modified as a reference
6447
	if (! empty($hookmanager))
6448
	{
6449
		$parameters=array('object' => $object, 'mode' => $mode, 'head'=>$head);
6450
		$reshook=$hookmanager->executeHooks('completeTabsHead',$parameters);
6451
		if ($reshook > 0)
6452
		{
6453
			$head = $hookmanager->resArray;
6454
		}
6455
	}
6456
}
6457
6458
/**
6459
 * Print common footer :
6460
 * 		conf->global->MAIN_HTML_FOOTER
6461
 *      js for switch of menu hider
6462
 * 		js for conf->global->MAIN_GOOGLE_AN_ID
6463
 * 		js for conf->global->MAIN_SHOW_TUNING_INFO or $_SERVER["MAIN_SHOW_TUNING_INFO"]
6464
 * 		js for conf->logbuffer
6465
 *
6466
 * @param	string	$zone	'private' (for private pages) or 'public' (for public pages)
6467
 * @return	void
6468
 */
6469
function printCommonFooter($zone='private')
6470
{
6471
	global $conf, $hookmanager;
6472
	global $micro_start_time;
6473
6474
	if ($zone == 'private') print "\n".'<!-- Common footer for private page -->'."\n";
6475
	else print "\n".'<!-- Common footer for public page -->'."\n";
6476
6477
	$parameters=array();
6478
	$reshook=$hookmanager->executeHooks('printCommonFooter',$parameters);    // Note that $action and $object may have been modified by some hooks
6479
	if (empty($reshook))
6480
	{
6481
		if (! empty($conf->global->MAIN_HTML_FOOTER)) print $conf->global->MAIN_HTML_FOOTER."\n";
6482
6483
		print "\n";
6484
		if (! empty($conf->use_javascript_ajax))
6485
		{
6486
			print '<script type="text/javascript" language="javascript">'."\n";
6487
6488
			if ($zone == 'private' && empty($conf->dol_use_jmobile))
6489
			{
6490
				print "\n";
6491
				print '/* JS CODE TO ENABLE to enable handler to switch left menu page (menuhider) */'."\n";
6492
				print 'jQuery(".menuhider").click(function() {';
6493
				print '  console.log("We click on .menuhider");'."\n";
6494
				//print "  $('.side-nav').animate({width:'toggle'},200);\n";     // OK with eldy theme but not with md
6495
				print "  $('.side-nav').toggle()\n";
6496
				print "  $('.login_block').toggle()\n";
6497
				print '});'."\n";
6498
			}
6499
6500
			// Google Analytics (need Google module)
6501
			if (! empty($conf->google->enabled) && ! empty($conf->global->MAIN_GOOGLE_AN_ID))
6502
			{
6503
				if (($conf->dol_use_jmobile != 4))
6504
				{
6505
					print "\n";
6506
					print "/* JS CODE TO ENABLE for google analtics tag */\n";
6507
					print '  var _gaq = _gaq || [];'."\n";
6508
					print '  _gaq.push([\'_setAccount\', \''.$conf->global->MAIN_GOOGLE_AN_ID.'\']);'."\n";
6509
					print '  _gaq.push([\'_trackPageview\']);'."\n";
6510
					print ''."\n";
6511
					print '  (function() {'."\n";
6512
					print '    var ga = document.createElement(\'script\'); ga.type = \'text/javascript\'; ga.async = true;'."\n";
6513
					print '    ga.src = (\'https:\' == document.location.protocol ? \'https://ssl\' : \'http://www\') + \'.google-analytics.com/ga.js\';'."\n";
6514
					print '    var s = document.getElementsByTagName(\'script\')[0]; s.parentNode.insertBefore(ga, s);'."\n";
6515
					print '  })();'."\n";
6516
				}
6517
			}
6518
6519
			// End of tuning
6520
			if (! empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || ! empty($conf->global->MAIN_SHOW_TUNING_INFO))
6521
			{
6522
				print "\n";
6523
				print "/* JS CODE TO ENABLE to add memory info */\n";
6524
				print 'window.console && console.log("';
6525
				if (! empty($conf->global->MEMCACHED_SERVER)) print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
6526
				print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED)?$conf->global->MAIN_OPTIMIZE_SPEED:'off');
6527
				if (! empty($micro_start_time))   // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
6528
				{
6529
					$micro_end_time = microtime(true);
6530
					print ' - Build time: '.ceil(1000*($micro_end_time-$micro_start_time)).' ms';
6531
				}
6532
				if (function_exists("memory_get_usage"))
6533
				{
6534
					print ' - Mem: '.memory_get_usage();
6535
				}
6536
				if (function_exists("xdebug_memory_usage"))
6537
				{
6538
					print ' - XDebug time: '.ceil(1000*xdebug_time_index()).' ms';
6539
					print ' - XDebug mem: '.xdebug_memory_usage();
6540
					print ' - XDebug mem peak: '.xdebug_peak_memory_usage();
6541
				}
6542
				if (function_exists("zend_loader_file_encoded"))
6543
				{
6544
					print ' - Zend encoded file: '.(zend_loader_file_encoded()?'yes':'no');
6545
				}
6546
				print '");'."\n";
6547
			}
6548
6549
			print "\n".'</script>'."\n";
6550
		}
6551
6552
		// Add Xdebug coverage of code
6553
		if (defined('XDEBUGCOVERAGE'))
6554
		{
6555
			print_r(xdebug_get_code_coverage());
6556
		}
6557
6558
		// If there is some logs in buffer to show
6559
		if (count($conf->logbuffer))
6560
		{
6561
			print "\n";
6562
			print "<!-- Start of log output\n";
6563
			//print '<div class="hidden">'."\n";
6564
			foreach($conf->logbuffer as $logline)
6565
			{
6566
				print $logline."<br>\n";
6567
			}
6568
			//print '</div>'."\n";
6569
			print "End of log output -->\n";
6570
		}
6571
	}
6572
}
6573
6574
/**
6575
 * Split a string with 2 keys into key array.
6576
 * For example: "A=1;B=2;C=2" is exploded into array('A'=>1,'B'=>2,'C'=>3)
6577
 *
6578
 * @param 	string	$string		String to explode
6579
 * @param 	string	$delimiter	Delimiter between each couple of data
6580
 * @param 	string	$kv			Delimiter between key and value
6581
 * @return	array				Array of data exploded
6582
 */
6583
function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
6584
{
6585
	if ($a = explode($delimiter, $string))
6586
	{
6587
		$ka = array();
6588
		foreach ($a as $s) { // each part
6589
			if ($s) {
6590
				if ($pos = strpos($s, $kv)) { // key/value delimiter
6591
					$ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
6592
				} else { // key delimiter not found
6593
					$ka[] = trim($s);
6594
				}
6595
			}
6596
		}
6597
		return $ka;
6598
	}
6599
	return array();
6600
}
6601
6602
6603
/**
6604
 * Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag)
6605
 *
6606
 * @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.
6607
 * @return	string				HTML code to set focus
6608
 */
6609
function dol_set_focus($selector)
6610
{
6611
	print "\n".'<!-- Set focus onto a specific field -->'."\n";
6612
	print '<script type="text/javascript" language="javascript">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
6613
}
6614
6615
6616
/**
6617
 * Return getmypid() or random PID when function is disabled
6618
 * Some web hosts disable this php function for security reasons
6619
 * and sometimes we can't redeclare function
6620
 *
6621
 * @return	int
6622
 */
6623
function dol_getmypid()
6624
{
6625
	if (! function_exists('getmypid')) {
6626
		return mt_rand(1,32768);
6627
	} else {
6628
		return getmypid();
6629
	}
6630
}
6631
6632
6633
/**
6634
 * Generate natural SQL search string for a criteria (this criteria can be tested on one or several fields)
6635
 *
6636
 * @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")
6637
 * @param 	string 			$value 		The value to look for.
6638
 *                          		    If param $mode is 0, can contains several keywords separated with a space or |
6639
 *                                         like "keyword1 keyword2" = We want record field like keyword1 AND field like keyword2
6640
 *                                         or like "keyword1|keyword2" = We want record field like keyword1 OR field like keyword2
6641
 *                             			If param $mode is 1, can contains an operator <, > or = like "<10" or ">=100.5 < 1000"
6642
 *                             			If param $mode is 2, can contains a list of int id separated by comma like "1,3,4"
6643
 *                             			If param $mode is 3, can contains a list of string separated by comma like "a,b,c"
6644
 * @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')
6645
 * @param	integer			$nofirstand	1=Do not output the first 'AND'
6646
 * @return 	string 			$res 		The statement to append to the SQL query
6647
 */
6648
function natural_search($fields, $value, $mode=0, $nofirstand=0)
6649
{
6650
	global $db,$langs;
6651
6652
	$value=trim($value);
6653
6654
	if ($mode == 0)
6655
	{
6656
		$value=preg_replace('/\*/','%',$value);	// Replace * with %
6657
	}
6658
	if ($mode == 1)
6659
	{
6660
		$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
6661
	}
6662
6663
	$value = preg_replace('/\s*\|\s*/','|', $value);
6664
6665
	$crits = explode(' ', $value);
6666
	$res = '';
6667
	if (! is_array($fields)) $fields = array($fields);
6668
6669
	$nboffields = count($fields);
6670
	$end2 = count($crits);
6671
	$j = 0;
6672
	foreach ($crits as $crit)
6673
	{
6674
		$i = 0; $i2 = 0;
6675
		$newres = '';
6676
		foreach ($fields as $field)
6677
		{
6678
			if ($mode == 1)
6679
			{
6680
				$operator='=';
6681
				$newcrit = preg_replace('/([<>=]+)/','',trim($crit));
6682
6683
				preg_match('/([<>=]+)/',trim($crit), $reg);
6684
				if ($reg[1])
6685
				{
6686
					$operator = $reg[1];
6687
				}
6688
				if ($newcrit != '')
6689
				{
6690
					$numnewcrit = price2num($newcrit);
6691
					if (is_numeric($numnewcrit))
6692
					{
6693
						$newres .= ($i2 > 0 ? ' OR ' : '') . $field . ' '.$operator.' '.$numnewcrit;
6694
					}
6695
					else
6696
					{
6697
						$newres .= ($i2 > 0 ? ' OR ' : '') . '1 = 2';	// force false
6698
					}
6699
					$i2++;	// a criteria was added to string
6700
				}
6701
			}
6702
			else if ($mode == 2)
6703
			{
6704
				$newres .= ($i2 > 0 ? ' OR ' : '') . $field . " IN (" . $db->escape(trim($crit)) . ")";
6705
				$i2++;	// a criteria was added to string
6706
			}
6707
			else if ($mode == 3)
6708
			{
6709
				$tmparray=explode(',',trim($crit));
6710
				if (count($tmparray))
6711
				{
6712
					$listofcodes='';
6713
					foreach($tmparray as $val)
6714
					{
6715
						if ($val)
6716
						{
6717
							$listofcodes.=($listofcodes?',':'');
6718
							$listofcodes.="'".$db->escape(trim($val))."'";
6719
						}
6720
					}
6721
					$newres .= ($i2 > 0 ? ' OR ' : '') . $field . " IN (" . $listofcodes . ")";
6722
					$i2++;	// a criteria was added to string
6723
				}
6724
			}
6725
			else    // $mode=0
6726
			{
6727
				$textcrit = '';
6728
				$tmpcrits = explode('|',$crit);
6729
				$i3 = 0;
6730
				foreach($tmpcrits as $tmpcrit)
6731
				{
6732
					$newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
6733
6734
					if (preg_match('/\.(id|rowid)$/', $field))	// Special case for rowid that is sometimes a ref so used as a search field
6735
					{
6736
						$newres .= $field . " = " . (is_numeric(trim($tmpcrit))?trim($tmpcrit):'0');
6737
					}
6738
					else
6739
					{
6740
						$newres .= $field . " LIKE '";
6741
6742
						$tmpcrit=trim($tmpcrit);
6743
						$tmpcrit2=$tmpcrit;
6744
						$tmpbefore='%'; $tmpafter='%';
6745
						if (preg_match('/^[\^\$]/', $tmpcrit))
6746
						{
6747
							$tmpbefore='';
6748
							$tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
6749
						}
6750
						if (preg_match('/[\^\$]$/', $tmpcrit))
6751
						{
6752
							$tmpafter='';
6753
							$tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
6754
						}
6755
						$newres .= $tmpbefore;
6756
						$newres .= $db->escape($tmpcrit2);
6757
						$newres .= $tmpafter;
6758
						$newres .= "'";
6759
						if ($tmpcrit2 == '')
6760
						{
6761
							$newres .= ' OR ' . $field . " IS NULL";
6762
						}
6763
					}
6764
6765
					$i3++;
6766
				}
6767
				$i2++;	// a criteria was added to string
6768
			}
6769
			$i++;
6770
		}
6771
		if ($newres) $res = $res . ($res ? ' AND ' : '') . ($i2 > 1 ? '(' : '') .$newres . ($i2 > 1 ? ')' : '');
6772
		$j++;
6773
	}
6774
	$res = ($nofirstand?"":" AND ")."(" . $res . ")";
6775
	//print 'xx'.$res.'yy';
6776
	return $res;
6777
}
6778
6779
/**
6780
 * Return string with full Url
6781
 *
6782
 * @param   Object	$object		Object
6783
 * @return	string				Url string
6784
 */
6785
function showDirectDownloadLink($object)
6786
{
6787
	global $conf, $langs;
6788
6789
	$out='';
6790
	$url = $object->getLastMainDocLink($object->element);
6791
6792
	if ($url)
6793
	{
6794
		$out.= img_picto('','object_globe.png').' '.$langs->trans("DirectDownloadLink").'<br>';
6795
		$out.= '<input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'">';
6796
		$out.= ajax_autoselect("directdownloadlink", 0);
6797
	}
6798
	return $out;
6799
}
6800
6801
/**
6802
 * Return the filename of file to get the thumbs
6803
 *
6804
 * @param   string  $file           Original filename (full or relative path)
6805
 * @param   string  $extName        Extension to differenciate thumb file name ('', '_small', '_mini')
6806
 * @param   string  $extImgTarget   Force image extension for thumbs. Use '' to keep same extension than original image (default).
6807
 * @return  string                  New file name (full or relative path, including the thumbs/)
6808
 */
6809
function getImageFileNameForSize($file, $extName, $extImgTarget='')
6810
{
6811
	$dirName = dirname($file);
6812
	if ($dirName == '.') $dirName='';
6813
6814
	$fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp)$/i','',$file);	// We remove extension, whatever is its case
6815
	$fileName = basename($fileName);
6816
6817
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpg$/i',$file)?'.jpg':'');
6818
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpeg$/i',$file)?'.jpeg':'');
6819
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.gif$/i',$file)?'.gif':'');
6820
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.png$/i',$file)?'.png':'');
6821
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.bmp$/i',$file)?'.bmp':'');
6822
6823
	if (! $extImgTarget) return $file;
6824
6825
	$subdir='';
6826
	if ($extName) $subdir = 'thumbs/';
6827
6828
	return ($dirName?$dirName.'/':'').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
6829
}
6830
6831
6832
/**
6833
 * Return URL we can use for advanced preview links
6834
 *
6835
 * @param   string    $modulepart     propal, facture, facture_fourn, ...
6836
 * @param   string    $relativepath   Relative path of docs.
6837
 * @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)
6838
 * @param	string	  $param		  More param on http links
6839
 * @return  string|array              Output string with href link or array with all components of link
6840
 */
6841
function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
6842
{
6843
	global $conf, $langs;
6844
6845
	if (empty($conf->use_javascript_ajax)) return '';
6846
6847
	$mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'svg+xml');
6848
	//$mime_preview[]='vnd.oasis.opendocument.presentation';
6849
	//$mime_preview[]='archive';
6850
	$num_mime = array_search(dol_mimetype($relativepath, '', 1), $mime_preview);
6851
6852
	if ($alldata == 1)
6853
	{
6854
		if ($num_mime !== false) return array('target'=>'_blank', 'css'=>'documentpreview', 'url'=>DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&attachment=0&file='.urlencode($relativepath), 'mime'=>dol_mimetype($relativepath), );
6855
		else return array();
6856
	}
6857
6858
	// old behavior
6859
	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')).'\')';
6860
	else return '';
6861
}
6862
6863
6864
/**
6865
 * Make content of an input box selected when we click into input field.
6866
 *
6867
 * @param string	$htmlname	Id of html object
6868
 * @param string	$addlink	Add a 'link to' after
6869
 */
6870
function ajax_autoselect($htmlname, $addlink='')
6871
{
6872
	global $langs;
6873
	$out = '<script type="text/javascript">
6874
               jQuery(document).ready(function () {
6875
				    jQuery("#'.$htmlname.'").click(function() { jQuery(this).select(); } );
6876
				});
6877
		    </script>';
6878
	if ($addlink) $out.=' <a href="'.$addlink.'" target="_blank">'.$langs->trans("Link").'</a>';
6879
	return $out;
6880
}
6881
6882
6883
/**
6884
 *	Return mime type of a file
6885
 *
6886
 *	@param	string	$file		Filename we looking for MIME type
6887
 *  @param  string	$default    Default mime type if extension not found in known list
6888
 * 	@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
6889
 *	@return string 		    	Return a mime type family (text/xxx, application/xxx, image/xxx, audio, video, archive)
6890
 *  @see    image_format_supported (images.lib.php)
6891
 */
6892
function dol_mimetype($file, $default='application/octet-stream', $mode=0)
6893
{
6894
	$mime=$default;
6895
	$imgmime='other.png';
6896
	$famime='file-o';
6897
	$srclang='';
6898
6899
	$tmpfile=preg_replace('/\.noexe$/','',$file);
6900
6901
	// Text files
6902
	if (preg_match('/\.txt$/i',$tmpfile))         			   { $mime='text/plain';    $imgmime='text.png'; $famime='file-text-o'; }
6903
	if (preg_match('/\.rtx$/i',$tmpfile))                      { $mime='text/richtext'; $imgmime='text.png'; $famime='file-text-o'; }
6904
	if (preg_match('/\.csv$/i',$tmpfile))					   { $mime='text/csv';      $imgmime='text.png'; $famime='file-text-o'; }
6905
	if (preg_match('/\.tsv$/i',$tmpfile))					   { $mime='text/tab-separated-values'; $imgmime='text.png'; $famime='file-text-o'; }
6906
	if (preg_match('/\.(cf|conf|log)$/i',$tmpfile))            { $mime='text/plain';    $imgmime='text.png'; $famime='file-text-o'; }
6907
	if (preg_match('/\.ini$/i',$tmpfile))                      { $mime='text/plain';    $imgmime='text.png'; $srclang='ini'; $famime='file-text-o'; }
6908
	if (preg_match('/\.css$/i',$tmpfile))                      { $mime='text/css';      $imgmime='css.png'; $srclang='css'; $famime='file-text-o'; }
6909
	// Certificate files
6910
	if (preg_match('/\.(crt|cer|key|pub)$/i',$tmpfile))        { $mime='text/plain';    $imgmime='text.png'; $famime='file-text-o'; }
6911
	// HTML/XML
6912
	if (preg_match('/\.(html|htm|shtml)$/i',$tmpfile))         { $mime='text/html';     $imgmime='html.png'; $srclang='html'; $famime='file-text-o'; }
6913
	if (preg_match('/\.(xml|xhtml)$/i',$tmpfile))              { $mime='text/xml';      $imgmime='other.png'; $srclang='xml'; $famime='file-text-o'; }
6914
	// Languages
6915
	if (preg_match('/\.bas$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='bas';  $famime='file-code-o'; }
6916
	if (preg_match('/\.(c)$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='c';  $famime='file-code-o'; }
6917
	if (preg_match('/\.(cpp)$/i',$tmpfile))                    { $mime='text/plain'; $imgmime='text.png'; $srclang='cpp';  $famime='file-code-o'; }
6918
	if (preg_match('/\.(h)$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='h';  $famime='file-code-o'; }
6919
	if (preg_match('/\.(java|jsp)$/i',$tmpfile))               { $mime='text/plain'; $imgmime='text.png'; $srclang='java';  $famime='file-code-o'; }
6920
	if (preg_match('/\.php([0-9]{1})?$/i',$tmpfile))           { $mime='text/plain'; $imgmime='php.png'; $srclang='php';  $famime='file-code-o'; }
6921
	if (preg_match('/\.phtml$/i',$tmpfile))                    { $mime='text/plain'; $imgmime='php.png'; $srclang='php';  $famime='file-code-o'; }
6922
	if (preg_match('/\.(pl|pm)$/i',$tmpfile))                  { $mime='text/plain'; $imgmime='pl.png'; $srclang='perl';  $famime='file-code-o'; }
6923
	if (preg_match('/\.sql$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='sql';  $famime='file-code-o'; }
6924
	if (preg_match('/\.js$/i',$tmpfile))                       { $mime='text/x-javascript'; $imgmime='jscript.png'; $srclang='js';  $famime='file-code-o'; }
6925
	// Open office
6926
	if (preg_match('/\.odp$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.presentation'; $imgmime='ooffice.png'; $famime='file-powerpoint-o'; }
6927
	if (preg_match('/\.ods$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.spreadsheet';  $imgmime='ooffice.png'; $famime='file-excel-o'; }
6928
	if (preg_match('/\.odt$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.text';         $imgmime='ooffice.png'; $famime='file-word-o'; }
6929
	// MS Office
6930
	if (preg_match('/\.mdb$/i',$tmpfile))					   { $mime='application/msaccess'; $imgmime='mdb.png'; $famime='file-o'; }
6931
	if (preg_match('/\.doc(x|m)?$/i',$tmpfile))				   { $mime='application/msword'; $imgmime='doc.png'; $famime='file-word-o'; }
6932
	if (preg_match('/\.dot(x|m)?$/i',$tmpfile))				   { $mime='application/msword'; $imgmime='doc.png'; $famime='file-word-o'; }
6933
	if (preg_match('/\.xlt(x)?$/i',$tmpfile))				   { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; $famime='file-excel-o'; }
6934
	if (preg_match('/\.xla(m)?$/i',$tmpfile))				   { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; $famime='file-excel-o'; }
6935
	if (preg_match('/\.xls$/i',$tmpfile))			           { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; $famime='file-excel-o'; }
6936
	if (preg_match('/\.xls(b|m|x)$/i',$tmpfile))			   { $mime='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; $imgmime='xls.png'; $famime='file-excel-o'; }
6937
	if (preg_match('/\.pps(m|x)?$/i',$tmpfile))				   { $mime='application/vnd.ms-powerpoint'; $imgmime='ppt.png'; $famime='file-powerpoint-o'; }
6938
	if (preg_match('/\.ppt(m|x)?$/i',$tmpfile))				   { $mime='application/x-mspowerpoint'; $imgmime='ppt.png'; $famime='file-powerpoint-o'; }
6939
	// Other
6940
	if (preg_match('/\.pdf$/i',$tmpfile))                      { $mime='application/pdf'; $imgmime='pdf.png'; $famime='file-pdf-o'; }
6941
	// Scripts
6942
	if (preg_match('/\.bat$/i',$tmpfile))                      { $mime='text/x-bat';  $imgmime='script.png'; $srclang='dos'; $famime='file-code-o'; }
6943
	if (preg_match('/\.sh$/i',$tmpfile))                       { $mime='text/x-sh';   $imgmime='script.png'; $srclang='bash'; $famime='file-code-o'; }
6944
	if (preg_match('/\.ksh$/i',$tmpfile))                      { $mime='text/x-ksh';  $imgmime='script.png'; $srclang='bash'; $famime='file-code-o'; }
6945
	if (preg_match('/\.bash$/i',$tmpfile))                     { $mime='text/x-bash'; $imgmime='script.png'; $srclang='bash'; $famime='file-code-o'; }
6946
	// Images
6947
	if (preg_match('/\.ico$/i',$tmpfile))                      { $mime='image/x-icon'; $imgmime='image.png'; $famime='file-image-o'; }
6948
	if (preg_match('/\.(jpg|jpeg)$/i',$tmpfile))			   { $mime='image/jpeg';   $imgmime='image.png'; $famime='file-image-o'; }
6949
	if (preg_match('/\.png$/i',$tmpfile))					   { $mime='image/png';    $imgmime='image.png'; $famime='file-image-o'; }
6950
	if (preg_match('/\.gif$/i',$tmpfile))					   { $mime='image/gif';    $imgmime='image.png'; $famime='file-image-o'; }
6951
	if (preg_match('/\.bmp$/i',$tmpfile))					   { $mime='image/bmp';    $imgmime='image.png'; $famime='file-image-o'; }
6952
	if (preg_match('/\.(tif|tiff)$/i',$tmpfile))			   { $mime='image/tiff';   $imgmime='image.png'; $famime='file-image-o'; }
6953
	if (preg_match('/\.svg$/i',$tmpfile))					   { $mime='image/svg+xml';$imgmime='image.png'; $famime='file-image-o'; }
6954
	// Calendar
6955
	if (preg_match('/\.vcs$/i',$tmpfile))					   { $mime='text/calendar'; $imgmime='other.png'; $famime='file-text-o'; }
6956
	if (preg_match('/\.ics$/i',$tmpfile))					   { $mime='text/calendar'; $imgmime='other.png'; $famime='file-text-o'; }
6957
	// Other
6958
	if (preg_match('/\.torrent$/i',$tmpfile))				   { $mime='application/x-bittorrent'; $imgmime='other.png'; $famime='file-o'; }
6959
	// Audio
6960
	if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i',$tmpfile)) { $mime='audio'; $imgmime='audio.png'; $famime='file-audio-o'; }
6961
	// Video
6962
	if (preg_match('/\.ogv$/i',$tmpfile))                      { $mime='video/ogg';       $imgmime='video.png'; $famime='file-video-o'; }
6963
	if (preg_match('/\.webm$/i',$tmpfile))                     { $mime='video/webm';      $imgmime='video.png'; $famime='file-video-o'; }
6964
	if (preg_match('/\.avi$/i',$tmpfile))                      { $mime='video/x-msvideo'; $imgmime='video.png'; $famime='file-video-o'; }
6965
	if (preg_match('/\.divx$/i',$tmpfile))                     { $mime='video/divx';      $imgmime='video.png'; $famime='file-video-o'; }
6966
	if (preg_match('/\.xvid$/i',$tmpfile))                     { $mime='video/xvid';      $imgmime='video.png'; $famime='file-video-o'; }
6967
	if (preg_match('/\.(wmv|mpg|mpeg)$/i',$tmpfile))           { $mime='video';           $imgmime='video.png'; $famime='file-video-o'; }
6968
	// Archive
6969
	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, ...
6970
	// Exe
6971
	if (preg_match('/\.(exe|com)$/i',$tmpfile))                { $mime='application/octet-stream'; $imgmime='other.png'; $famime='file-o'; }
6972
	// Lib
6973
	if (preg_match('/\.(dll|lib|o|so|a)$/i',$tmpfile))         { $mime='library'; $imgmime='library.png'; $famime='file-o'; }
6974
	// Err
6975
	if (preg_match('/\.err$/i',$tmpfile))                      { $mime='error'; $imgmime='error.png'; $famime='file-text-o'; }
6976
6977
	// Return string
6978
	if ($mode == 1)
6979
	{
6980
		$tmp=explode('/',$mime);
6981
		return (! empty($tmp[1])?$tmp[1]:$tmp[0]);
6982
	}
6983
	if ($mode == 2)
6984
	{
6985
		return $imgmime;
6986
	}
6987
	if ($mode == 3)
6988
	{
6989
		return $srclang;
6990
	}
6991
	if ($mode == 4)
6992
	{
6993
		return $famime;
6994
	}
6995
	return $mime;
6996
}
6997
6998
/**
6999
 * Return value from dictionary
7000
 *
7001
 * @param string	$tablename		name of dictionary
7002
 * @param string	$field			the value to return
7003
 * @param int		$id				id of line
7004
 * @param bool		$checkentity	add filter on entity
7005
 * @param string	$rowidfield		name of the column rowid
7006
 */
7007
function getDictvalue($tablename, $field, $id, $checkentity=false, $rowidfield='rowid')
7008
{
7009
	global $dictvalues,$db,$langs;
7010
7011
	if (!isset($dictvalues[$tablename]))
7012
	{
7013
		$dictvalues[$tablename] = array();
7014
		$sql = 'SELECT * FROM '.$tablename.' WHERE 1';
7015
		if ($checkentity) $sql.= ' entity IN (0,'.getEntity('').')';
7016
7017
		$resql = $db->query($sql);
7018
		if ($resql)
7019
		{
7020
			while ($obj = $db->fetch_object($resql))
7021
			{
7022
				$dictvalues[$tablename][$obj->{$rowidfield}] = $obj;
7023
			}
7024
		}
7025
		else
7026
		{
7027
			dol_print_error($db);
7028
		}
7029
	}
7030
7031
	if (!empty($dictvalues[$tablename][$id])) return $dictvalues[$tablename][$id]->{$field}; // Found
7032
	else // Not found
7033
	{
7034
		if ($id > 0) return $id;
7035
		return '';
7036
	}
7037
}
7038
7039
/**
7040
 *	Return true if the color is light
7041
 *
7042
 *  @param	string	$stringcolor		String with hex (FFFFFF) or comma RGB ('255,255,255')
7043
 *  @return	int							-1 : Error with argument passed |0 : color is dark | 1 : color is light
7044
 */
7045
function colorIsLight($stringcolor)
7046
{
7047
	$res = -1;
7048
	if (!empty($stringcolor))
7049
	{
7050
		$res = 0;
7051
		$tmp=explode(',', $stringcolor);
7052
		if (count($tmp) > 1)   // This is a comma RGB ('255','255','255')
7053
		{
7054
			$r = $tmp[0];
7055
			$g = $tmp[1];
7056
			$b = $tmp[2];
7057
		}
7058
		else
7059
		{
7060
			$hexr=$stringcolor[0].$stringcolor[1];
7061
			$hexg=$stringcolor[2].$stringcolor[3];
7062
			$hexb=$stringcolor[4].$stringcolor[5];
7063
			$r = hexdec($hexr);
7064
			$g = hexdec($hexg);
7065
			$b = hexdec($hexb);
7066
		}
7067
		$bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0;    // HSL algorithm
7068
		if ($bright > 0.6) $res = 1;
7069
	}
7070
	return $res;
7071
}
7072
7073
/**
7074
 * Function to test if an entry is enabled or not
7075
 *
7076
 * @param	string		$type_user					0=We test for internal user, 1=We test for external user
7077
 * @param	array		$menuentry					Array for feature entry to test
7078
 * @param	array		$listofmodulesforexternal	Array with list of modules allowed to external users
7079
 * @return	int										0=Hide, 1=Show, 2=Show gray
7080
 */
7081
function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
7082
{
7083
	global $conf;
7084
7085
	//print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
7086
	//print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
7087
	if (empty($menuentry['enabled'])) return 0;	// Entry disabled by condition
7088
	if ($type_user && $menuentry['module'])
7089
	{
7090
		$tmploops=explode('|',$menuentry['module']);
7091
		$found=0;
7092
		foreach($tmploops as $tmploop)
7093
		{
7094
			if (in_array($tmploop, $listofmodulesforexternal)) {
7095
				$found++; break;
7096
			}
7097
		}
7098
		if (! $found) return 0;	// Entry is for menus all excluded to external users
7099
	}
7100
	if (! $menuentry['perms'] && $type_user) return 0; 											// No permissions and user is external
7101
	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
7102
	if (! $menuentry['perms']) return 2;															// No permissions and user is external
7103
	return 1;
7104
}
7105