Completed
Branch develop (f1b0bd)
by
unknown
34:52
created

functions.lib.php ➔ get_default_tva()   F

Complexity

Conditions 20
Paths 130

Size

Total Lines 76

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
nc 130
nop 4
dl 0
loc 76
rs 3.9166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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