Passed
Branch develop (7b8a20)
by
unknown
32:48
created

dolIsAllowedForPreview()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 5
nop 1
dl 0
loc 16
rs 10
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2000-2007	Rodolphe Quiedeville		<[email protected]>
3
 * Copyright (C) 2003		Jean-Louis Bergamo			<[email protected]>
4
 * Copyright (C) 2004-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-2019	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
 * Copyright (C) 2018-2020  Frédéric France             <[email protected]>
17
 * Copyright (C) 2019       Thibault Foucart            <[email protected]>
18
 * Copyright (C) 2020       Open-Dsi         			<[email protected]>
19
 *
20
 * This program is free software; you can redistribute it and/or modify
21
 * it under the terms of the GNU General Public License as published by
22
 * the Free Software Foundation; either version 3 of the License, or
23
 * (at your option) any later version.
24
 *
25
 * This program is distributed in the hope that it will be useful,
26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28
 * GNU General Public License for more details.
29
 *
30
 * You should have received a copy of the GNU General Public License
31
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
32
 * or see https://www.gnu.org/
33
 */
34
35
/**
36
 *	\file			htdocs/core/lib/functions.lib.php
37
 *	\brief			A set of functions for Dolibarr
38
 *					This file contains all frequently used functions.
39
 */
40
41
include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
42
43
44
/**
45
 * Return a DoliDB instance (database handler).
46
 *
47
 * @param   string	$type		Type of database (mysql, pgsql...)
48
 * @param	string	$host		Address of database server
49
 * @param	string	$user		Authorized username
50
 * @param	string	$pass		Password
51
 * @param	string	$name		Name of database
52
 * @param	int		$port		Port of database server
53
 * @return	DoliDB				A DoliDB instance
54
 */
55
function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
56
{
57
	require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
58
59
	$class = 'DoliDB'.ucfirst($type);
60
	$dolidb = new $class($type, $host, $user, $pass, $name, $port);
61
	return $dolidb;
62
}
63
64
/**
65
 * 	Get list of entity id to use.
66
 *
67
 * 	@param	string	$element		Current element
68
 *									'societe', 'socpeople', 'actioncomm', 'agenda', 'resource',
69
 *									'product', 'productprice', 'stock', 'bom', 'mo',
70
 *									'propal', 'supplier_proposal', 'invoice', 'supplier_invoice', 'payment_various',
71
 *									'categorie', 'bank_account', 'bank_account', 'adherent', 'user',
72
 *									'commande', 'supplier_order', 'expedition', 'intervention', 'survey',
73
 *									'contract', 'tax', 'expensereport', 'holiday', 'multicurrency', 'project',
74
 *									'email_template', 'event', 'donation'
75
 *									'c_paiement', 'c_payment_term', ...
76
 * 	@param	int		$shared			0=Return id of current entity only,
77
 * 									1=Return id of current entity + shared entities (default)
78
 *  @param	object	$currentobject	Current object if needed
79
 * 	@return	mixed					Entity id(s) to use ( eg. entity IN ('.getEntity(elementname).')' )
80
 */
81
function getEntity($element, $shared = 1, $currentobject = null)
82
{
83
	global $conf, $mc;
84
85
	if (is_object($mc))
86
	{
87
		return $mc->getEntity($element, $shared, $currentobject);
88
	} else {
89
		$out = '';
90
		$addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values');
91
		if (in_array($element, $addzero)) $out .= '0,';
92
		$out .= $conf->entity;
93
		return $out;
94
	}
95
}
96
97
/**
98
 * 	Set entity id to use when to create an object
99
 *
100
 * 	@param	object	$currentobject	Current object
101
 * 	@return	mixed					Entity id to use ( eg. entity = '.setEntity($object) )
102
 */
103
function setEntity($currentobject)
104
{
105
	global $conf, $mc;
106
107
	if (is_object($mc) && method_exists($mc, 'setEntity'))
108
	{
109
		return $mc->setEntity($currentobject);
110
	} else {
111
		return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
112
	}
113
}
114
115
/**
116
 * Return information about user browser
117
 *
118
 * Returns array with the following format:
119
 * array(
120
 *  'browsername' => Browser name (firefox|chrome|iceweasel|epiphany|safari|opera|ie|unknown)
121
 *  'browserversion' => Browser version. Empty if unknown
122
 *  'browseros' => Set with mobile OS (android|blackberry|ios|palm|symbian|webos|maemo|windows|unknown)
123
 *  'layout' => (tablet|phone|classic)
124
 *  'phone' => empty if not mobile, (android|blackberry|ios|palm|unknown) if mobile
125
 *  'tablet' => true/false
126
 * )
127
 *
128
 * @param string $user_agent Content of $_SERVER["HTTP_USER_AGENT"] variable
129
 * @return	array Check function documentation
130
 */
131
function getBrowserInfo($user_agent)
132
{
133
	include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
134
135
	$name = 'unknown';
136
	$version = '';
137
	$os = 'unknown';
138
	$phone = '';
139
140
	$detectmobile = new Mobile_Detect(null, $user_agent);
141
	$tablet = $detectmobile->isTablet();
142
143
	if ($detectmobile->isMobile()) {
144
		$phone = 'unknown';
145
146
		// If phone/smartphone, we set phone os name.
147
		if ($detectmobile->is('AndroidOS')) {
148
			$os = $phone = 'android';
149
		} elseif ($detectmobile->is('BlackBerryOS')) {
150
			$os = $phone = 'blackberry';
151
		} elseif ($detectmobile->is('iOS')) {
152
			$os = 'ios';
153
			$phone = 'iphone';
154
		} elseif ($detectmobile->is('PalmOS')) {
155
			$os = $phone = 'palm';
156
		} elseif ($detectmobile->is('SymbianOS')) {
157
			$os = 'symbian';
158
		} elseif ($detectmobile->is('webOS')) {
159
			$os = 'webos';
160
		} elseif ($detectmobile->is('MaemoOS')) {
161
			$os = 'maemo';
162
		} elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
163
			$os = 'windows';
164
		}
165
	}
166
167
	// OS
168
	if (preg_match('/linux/i', $user_agent)) { $os = 'linux'; } elseif (preg_match('/macintosh/i', $user_agent)) { $os = 'macintosh'; } elseif (preg_match('/windows/i', $user_agent)) { $os = 'windows'; }
169
170
	// Name
171
	if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'firefox'; $version = $reg[2]; } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'edge'; $version = $reg[2]; } 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
172
	elseif (preg_match('/chrome/i', $user_agent, $reg)) { $name = 'chrome'; } elseif (preg_match('/iceweasel/i', $user_agent)) { $name = 'iceweasel'; } elseif (preg_match('/epiphany/i', $user_agent)) { $name = 'epiphany'; } 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.
173
	elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'opera'; $version = $reg[2]; } 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
174
	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
175
	elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) { $name = 'lynxlinks'; $version = $reg[4]; }
176
177
	if ($tablet) {
178
		$layout = 'tablet';
179
	} elseif ($phone) {
180
		$layout = 'phone';
181
	} else {
182
		$layout = 'classic';
183
	}
184
185
	return array(
186
		'browsername' => $name,
187
		'browserversion' => $version,
188
		'browseros' => $os,
189
		'layout' => $layout,
190
		'phone' => $phone,
191
		'tablet' => $tablet
192
	);
193
}
194
195
/**
196
 *  Function called at end of web php process
197
 *
198
 *  @return	void
199
 */
200
function dol_shutdown()
201
{
202
	global $conf, $user, $langs, $db;
203
	$disconnectdone = false; $depth = 0;
204
	if (is_object($db) && !empty($db->connected)) { $depth = $db->transaction_opened; $disconnectdone = $db->close(); }
205
	dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ?LOG_WARNING:LOG_INFO));
206
}
207
208
/**
209
 * Return true if we are in a context of submitting a parameter
210
 *
211
 * @param 	string	$paramname		Name or parameter to test
212
 * @return 	boolean					True if we have just submit a POST or GET request with the parameter provided (even if param is empty)
213
 */
214
function GETPOSTISSET($paramname)
215
{
216
	$isset = 0;
217
218
	$relativepathstring = $_SERVER["PHP_SELF"];
219
	// Clean $relativepathstring
220
	if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
221
	$relativepathstring = preg_replace('/^\//', '', $relativepathstring);
222
	$relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
223
	//var_dump($relativepathstring);
224
	//var_dump($user->default_values);
225
226
	// Code for search criteria persistence.
227
	// Retrieve values if restore_lastsearch_values
228
	if (!empty($_GET['restore_lastsearch_values']))        // Use $_GET here and not GETPOST
229
	{
230
		if (!empty($_SESSION['lastsearch_values_'.$relativepathstring]))	// If there is saved values
231
		{
232
			$tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
233
			if (is_array($tmp))
234
			{
235
				foreach ($tmp as $key => $val)
236
				{
237
					if ($key == $paramname)	// We are on the requested parameter
238
					{
239
						$isset = 1;
240
						break;
241
					}
242
				}
243
			}
244
		}
245
		// If there is saved contextpage, page or limit
246
		if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring]))
247
		{
248
			$isset = 1;
249
		} elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring]))
250
		{
251
			$isset = 1;
252
		} elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring]))
253
		{
254
			$isset = 1;
255
		}
256
	} else {
257
		$isset = (isset($_POST[$paramname]) || isset($_GET[$paramname]));
258
	}
259
260
	return $isset;
261
}
262
263
/**
264
 *  Return value of a param into GET or POST supervariable.
265
 *  Use the property $user->default_values[path]['creatform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder']
266
 *  Note: The property $user->default_values is loaded by main.php when loading the user.
267
 *
268
 *  @param  string  $paramname   Name of parameter to found
269
 *  @param  string  $check	     Type of check
270
 *                               ''=no check (deprecated)
271
 *                               'none'=no check (only for param that should have very rich content)
272
 *                               'int'=check it's numeric (integer or float)
273
 *                               'intcomma'=check it's integer+comma ('1,2,3,4...')
274
 *                               'alpha'=check it's text and sign
275
 *                               'aZ'=check it's a-z only
276
 *                               'aZ09'=check it's simple alpha string (recommended for keys)
277
 *                               'array'=check it's array
278
 *                               'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string)
279
 *                               'nohtml', 'alphanohtml'=check there is no html content
280
 *                               'restricthtml'=check html content is restricted to some tags only
281
 *                               'custom'= custom filter specify $filter and $options)
282
 *  @param	int		$method	     Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get)
283
 *  @param  int     $filter      Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails)
284
 *  @param  mixed   $options     Options to pass to filter_var when $check is set to 'custom'
285
 *  @param	string	$noreplace	 Force disable of replacement of __xxx__ strings.
286
 *  @return string|array         Value found (string or array), or '' if check fails
287
 */
288
function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
289
{
290
	global $mysoc, $user, $conf;
291
292
	if (empty($paramname)) return 'BadFirstParameterForGETPOST';
293
	if (empty($check))
294
	{
295
		dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
296
		// Enable this line to know who call the GETPOST with '' $check parameter.
297
		//var_dump(debug_backtrace()[0]);
298
	}
299
300
	if (empty($method)) $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
301
	elseif ($method == 1) $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
302
	elseif ($method == 2) $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
303
	elseif ($method == 3) $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
304
	else return 'BadThirdParameterForGETPOST';
305
306
	if (empty($method) || $method == 3 || $method == 4)
307
	{
308
		$relativepathstring = $_SERVER["PHP_SELF"];
309
		// Clean $relativepathstring
310
		if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
311
		$relativepathstring = preg_replace('/^\//', '', $relativepathstring);
312
		$relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
313
		//var_dump($relativepathstring);
314
		//var_dump($user->default_values);
315
316
		// Code for search criteria persistence.
317
		// Retrieve values if restore_lastsearch_values
318
		if (!empty($_GET['restore_lastsearch_values']))        // Use $_GET here and not GETPOST
319
		{
320
			if (!empty($_SESSION['lastsearch_values_'.$relativepathstring]))	// If there is saved values
321
			{
322
				$tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
323
				if (is_array($tmp))
324
				{
325
					foreach ($tmp as $key => $val)
326
					{
327
						if ($key == $paramname)	// We are on the requested parameter
328
						{
329
							$out = $val;
330
							break;
331
						}
332
					}
333
				}
334
			}
335
			// If there is saved contextpage, page or limit
336
			if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring]))
337
			{
338
				$out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
339
			} elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring]))
340
			{
341
				$out = $_SESSION['lastsearch_page_'.$relativepathstring];
342
			} elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring]))
343
			{
344
				$out = $_SESSION['lastsearch_limit_'.$relativepathstring];
345
			}
346
		} // Else, retreive default values if we are not doing a sort
347
		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
348
		{
349
			if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname]))
350
			{
351
				// Search default value from $object->field
352
				global $object;
353
				if (is_object($object) && isset($object->fields[$paramname]['default']))
354
				{
355
					$out = $object->fields[$paramname]['default'];
356
				}
357
			}
358
			if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES))
359
			{
360
				if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname]))
361
				{
362
					// Now search in setup to overwrite default values
363
					if (!empty($user->default_values))		// $user->default_values defined from menu 'Setup - Default values'
364
					{
365
						if (isset($user->default_values[$relativepathstring]['createform']))
366
						{
367
							foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval)
368
							{
369
								$qualified = 0;
370
								if ($defkey != '_noquery_')
371
								{
372
									$tmpqueryarraytohave = explode('&', $defkey);
373
									$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
374
									$foundintru = 0;
375
									foreach ($tmpqueryarraytohave as $tmpquerytohave)
376
									{
377
										if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru = 1;
378
									}
379
									if (!$foundintru) $qualified = 1;
380
									//var_dump($defkey.'-'.$qualified);
381
								} else $qualified = 1;
382
383
								if ($qualified)
384
								{
385
									//var_dump($user->default_values[$relativepathstring][$defkey]['createform']);
386
									if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname]))
387
									{
388
										$out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
389
										break;
390
									}
391
								}
392
							}
393
						}
394
					}
395
				} // Management of default search_filters and sort order
396
				//elseif (preg_match('/list.php$/', $_SERVER["PHP_SELF"]) && ! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
397
				elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname]))
398
				{
399
					if (!empty($user->default_values))		// $user->default_values defined from menu 'Setup - Default values'
400
					{
401
						//var_dump($user->default_values[$relativepathstring]);
402
						if ($paramname == 'sortfield' || $paramname == 'sortorder')			// Sorted on which fields ? ASC or DESC ?
403
						{
404
							if (isset($user->default_values[$relativepathstring]['sortorder']))	// Even if paramname is sortfield, data are stored into ['sortorder...']
405
							{
406
								foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval)
407
								{
408
									$qualified = 0;
409
									if ($defkey != '_noquery_')
410
									{
411
										$tmpqueryarraytohave = explode('&', $defkey);
412
										$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
413
										$foundintru = 0;
414
										foreach ($tmpqueryarraytohave as $tmpquerytohave)
415
										{
416
											if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru = 1;
417
										}
418
										if (!$foundintru) $qualified = 1;
419
										//var_dump($defkey.'-'.$qualified);
420
									} else $qualified = 1;
421
422
									if ($qualified)
423
									{
424
										$forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
425
										foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val)
426
										{
427
											if ($out) $out .= ', ';
428
											if ($paramname == 'sortfield')
429
											{
430
												$out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
431
											}
432
											if ($paramname == 'sortorder')
433
											{
434
												$out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
435
											}
436
										}
437
										//break;	// No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
438
									}
439
								}
440
							}
441
						} elseif (isset($user->default_values[$relativepathstring]['filters']))
442
						{
443
							foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval)	// $defkey is a querystring like 'a=b&c=d', $defval is key of user
444
							{
445
								$qualified = 0;
446
								if ($defkey != '_noquery_')
447
								{
448
									$tmpqueryarraytohave = explode('&', $defkey);
449
									$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
450
									$foundintru = 0;
451
									foreach ($tmpqueryarraytohave as $tmpquerytohave)
452
									{
453
										if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru = 1;
454
									}
455
									if (!$foundintru) $qualified = 1;
456
									//var_dump($defkey.'-'.$qualified);
457
								} else $qualified = 1;
458
459
								if ($qualified)
460
								{
461
									if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all']))
462
									{
463
										// We made a search from quick search menu, do we still use default filter ?
464
										if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH))
465
										{
466
											$forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
467
											$out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
468
										}
469
									} else {
470
										$forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
471
										$out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
472
									}
473
									break;
474
								}
475
							}
476
						}
477
					}
478
				}
479
			}
480
		}
481
	}
482
483
	// Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters)
484
	// Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
485
	// 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.
486
	if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace))
487
	{
488
		$reg = array();
489
		$maxloop = 20; $loopnb = 0; // Protection against infinite loop
490
		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.
491
		{
492
			$loopnb++; $newout = '';
493
494
			if ($reg[1] == 'DAY') {
495
				$tmp = dol_getdate(dol_now(), true);
496
				$newout = $tmp['mday'];
497
			} elseif ($reg[1] == 'MONTH') {
498
				$tmp = dol_getdate(dol_now(), true);
499
				$newout = $tmp['mon'];
500
			} elseif ($reg[1] == 'YEAR') {
501
				$tmp = dol_getdate(dol_now(), true);
502
				$newout = $tmp['year'];
503
			} elseif ($reg[1] == 'PREVIOUS_DAY') {
504
				$tmp = dol_getdate(dol_now(), true);
505
				$tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
506
				$newout = $tmp2['day'];
507
			} elseif ($reg[1] == 'PREVIOUS_MONTH') {
508
				$tmp = dol_getdate(dol_now(), true);
509
				$tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
510
				$newout = $tmp2['month'];
511
			} elseif ($reg[1] == 'PREVIOUS_YEAR') {
512
				$tmp = dol_getdate(dol_now(), true);
513
				$newout = ($tmp['year'] - 1);
514
			} elseif ($reg[1] == 'NEXT_DAY') {
515
				$tmp = dol_getdate(dol_now(), true);
516
				$tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
517
				$newout = $tmp2['day'];
518
			} elseif ($reg[1] == 'NEXT_MONTH') {
519
				$tmp = dol_getdate(dol_now(), true);
520
				$tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
521
				$newout = $tmp2['month'];
522
			} elseif ($reg[1] == 'NEXT_YEAR') {
523
				$tmp = dol_getdate(dol_now(), true);
524
				$newout = ($tmp['year'] + 1);
525
			} elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
526
				$newout = $mysoc->country_id;
527
			} elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
528
				$newout = $user->id;
529
			} elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
530
				$newout = $user->fk_user;
531
			} elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
532
				$newout = $conf->entity;
533
			} else {
534
				$newout = ''; // Key not found, we replace with empty string
535
			}
536
			//var_dump('__'.$reg[1].'__ -> '.$newout);
537
			$out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
538
		}
539
	}
540
541
	// Check is done after replacement
542
	switch ($check)
543
	{
544
		case 'none':
545
			break;
546
		case 'int':    // Check param is a numeric value (integer but also float or hexadecimal)
547
			if (!is_numeric($out)) { $out = ''; }
548
			break;
549
		case 'intcomma':
550
			if (preg_match('/[^0-9,-]+/i', $out)) $out = '';
551
			break;
552
		case 'alpha':
553
			if (!is_array($out)) {
554
				// '"' is dangerous because param in url can close the href= or src= and add javascript functions.
555
				// '../' is dangerous because it allows dir transversals
556
				$out = str_replace(array('"', '../'), '', trim($out));
557
			}
558
			break;
559
		case 'san_alpha':
560
			$out = filter_var($out, FILTER_SANITIZE_STRING);
561
			break;
562
		case 'aZ':
563
			if (!is_array($out))
564
			{
565
				$out = trim($out);
566
				if (preg_match('/[^a-z]+/i', $out)) $out = '';
567
			}
568
			break;
569
		case 'aZ09':
570
			if (!is_array($out))
571
			{
572
				$out = trim($out);
573
				if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) $out = '';
574
			}
575
			break;
576
		case 'aZ09comma':		// great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh
577
			if (!is_array($out))
578
			{
579
				$out = trim($out);
580
				if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) $out = '';
581
			}
582
			break;
583
		case 'array':
584
			if (!is_array($out) || empty($out)) $out = array();
585
			break;
586
		case 'nohtml':
587
			$out = dol_string_nohtmltag($out, 0);
588
			break;
589
		case 'alphanohtml':	// Recommended for most scalar parameters and search parameters
590
			if (!is_array($out))
591
			{
592
				// '"' is dangerous because param in url can close the href= or src= and add javascript functions.
593
				// '../' is dangerous because it allows dir transversals
594
				$out = str_replace(array('"', '../'), '', trim($out));
595
				$out = dol_string_nohtmltag($out, 0);
596
			}
597
			break;
598
		case 'restricthtml':		// Recommended for most html textarea
599
			$out = dol_string_onlythesehtmltags($out, 0);
600
			break;
601
		case 'custom':
602
			if (empty($filter)) return 'BadFourthParameterForGETPOST';
603
			$out = filter_var($out, $filter, $options);
604
			break;
605
	}
606
607
	// Code for search criteria persistence.
608
	// Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
609
	if (empty($method) || $method == 3 || $method == 4)
610
	{
611
		if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield')))
612
		{
613
			//var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
614
615
			// We save search key only if $out not empty that means:
616
			// - posted value not empty, or
617
			// - 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).
618
619
			if ($out != '')		// $out = '0' or 'abc', it is a search criteria to keep
620
			{
621
				$user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $relativepathstring does not seem to be defined for all execution paths leading up to this point.
Loading history...
622
			}
623
		}
624
	}
625
626
	return $out;
627
}
628
629
630
if (!function_exists('dol_getprefix'))
631
{
632
	/**
633
	 *  Return a prefix to use for this Dolibarr instance, for session/cookie names or email id.
634
	 *  The prefix is unique for instance and avoid conflict between multi-instances, even when having two instances with same root dir
635
	 *  or two instances in same virtual servers.
636
	 *
637
	 *  @param  string  $mode                   '' (prefix for session name) or 'email' (prefix for email id)
638
	 *  @return	string                          A calculated prefix
639
	 */
640
	function dol_getprefix($mode = '')
641
	{
642
		global $conf;
643
644
		// If prefix is for email
645
		if ($mode == 'email')
646
		{
647
			if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID))	// If MAIL_PREFIX_FOR_EMAIL_ID is set (a value initialized with a random value is recommended)
648
			{
649
				if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
650
				elseif (isset($_SERVER["SERVER_NAME"])) return $_SERVER["SERVER_NAME"];
651
			}
652
653
			// The recommended value (may be not defined for old versions)
654
			if (!empty($conf->file->instance_unique_id)) return $conf->file->instance_unique_id;
655
656
			// For backward compatibility
657
			return dol_hash(DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3');
658
		}
659
660
		// The recommended value (may be not defined for old versions)
661
		if (!empty($conf->file->instance_unique_id)) return $conf->file->instance_unique_id;
662
663
		// For backward compatibility
664
		if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"]))
665
		{
666
			return dol_hash($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3');
667
		}
668
669
		return dol_hash(DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3');
670
	}
671
}
672
673
/**
674
 *	Make an include_once using default root and alternate root if it fails.
675
 *  To link to a core file, use include(DOL_DOCUMENT_ROOT.'/pathtofile')
676
 *  To link to a module file from a module file, use include './mymodulefile';
677
 *  To link to a module file from a core file, then this function can be used (call by hook / trigger / speciales pages)
678
 *
679
 * 	@param	string	$relpath	Relative path to file (Ie: mydir/myfile, ../myfile, ...)
680
 * 	@param	string	$classname	Class name (deprecated)
681
 *  @return bool                True if load is a success, False if it fails
682
 */
683
function dol_include_once($relpath, $classname = '')
684
{
685
	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']
686
687
	$fullpath = dol_buildpath($relpath);
688
689
	if (!file_exists($fullpath)) {
690
		dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
691
		return false;
692
	}
693
694
	if (!empty($classname) && !class_exists($classname)) {
695
		return include $fullpath;
696
	} else {
697
		return include_once $fullpath;
698
	}
699
}
700
701
702
/**
703
 *	Return path of url or filesystem. Can check into alternate dir or alternate dir + main dir depending on value of $returnemptyifnotfound.
704
 *
705
 * 	@param	string	$path						Relative path to file (if mode=0) or relative url (if mode=1). Ie: mydir/myfile, ../myfile
706
 *  @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)
707
 *  @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)
708
 *  											1:If $type==0 and if file was not found into alternate dir, return empty string
709
 *  											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
710
 *  @return string								Full filesystem path (if path=0) or '' if file not found, Full url path (if mode=1)
711
 */
712
function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
713
{
714
	global $conf;
715
716
	$path = preg_replace('/^\//', '', $path);
717
718
	if (empty($type))	// For a filesystem path
719
	{
720
		$res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
721
		if (is_array($conf->file->dol_document_root))
722
		{
723
			foreach ($conf->file->dol_document_root as $key => $dirroot)	// ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
724
			{
725
				if ($key == 'main')
726
				{
727
					continue;
728
				}
729
				if (file_exists($dirroot.'/'.$path))
730
				{
731
					$res = $dirroot.'/'.$path;
732
					return $res;
733
				}
734
			}
735
		}
736
		if ($returnemptyifnotfound)								// Not found into alternate dir
737
		{
738
			if ($returnemptyifnotfound == 1 || !file_exists($res)) return '';
739
		}
740
	} else // For an url path
741
	{
742
		// We try to get local path of file on filesystem from url
743
		// Note that trying to know if a file on disk exist by forging path on disk from url
744
		// works only for some web server and some setup. This is bugged when
745
		// using proxy, rewriting, virtual path, etc...
746
		$res = '';
747
		if ($type == 1) $res = DOL_URL_ROOT.'/'.$path; // Standard value
748
		if ($type == 2) $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
749
		if ($type == 3) $res = DOL_URL_ROOT.'/'.$path;
750
751
		foreach ($conf->file->dol_document_root as $key => $dirroot)	// ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
752
		{
753
			if ($key == 'main')
754
			{
755
				if ($type == 3)
756
				{
757
					global $dolibarr_main_url_root;
758
759
					// Define $urlwithroot
760
					$urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
761
					$urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
762
					//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
763
764
					$res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
765
				}
766
				continue;
767
			}
768
			preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
769
			if (!empty($regs[1]))
770
			{
771
				//print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
772
				if (file_exists($dirroot.'/'.$regs[1]))
773
				{
774
					if ($type == 1)
775
					{
776
						$res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
777
					}
778
					if ($type == 2)
779
					{
780
						$res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
781
					}
782
					if ($type == 3)
783
					{
784
						global $dolibarr_main_url_root;
785
786
						// Define $urlwithroot
787
						$urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
788
						$urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
789
						//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
790
791
						$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
792
					}
793
					break;
794
				}
795
			}
796
		}
797
	}
798
799
	return $res;
800
}
801
802
/**
803
 *	Create a clone of instance of object (new instance with same value for properties)
804
 *  With native = 0: Property that are reference are also new object (full isolation clone). This means $this->db of new object is not valid.
805
 *  With native = 1: Use PHP clone. Property that are reference are same pointer. This means $this->db of new object is still valid but point to same this->db than original object.
806
 *
807
 * 	@param	object	$object		Object to clone
808
 *  @param	int		$native		0=Full isolation method, 1=Native PHP method
809
 *	@return object				Clone object
810
 *  @see https://php.net/manual/language.oop5.cloning.php
811
 */
812
function dol_clone($object, $native = 0)
813
{
814
	if (empty($native))
815
	{
816
		$myclone = unserialize(serialize($object));
817
	} else {
818
		$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)
819
	}
820
821
	return $myclone;
822
}
823
824
/**
825
 *	Optimize a size for some browsers (phone, smarphone, ...)
826
 *
827
 * 	@param	int		$size		Size we want
828
 * 	@param	string	$type		Type of optimizing:
829
 * 								'' = function used to define a size for truncation
830
 * 								'width' = function is used to define a width
831
 *	@return int					New size after optimizing
832
 */
833
function dol_size($size, $type = '')
834
{
835
	global $conf;
836
	if (empty($conf->dol_optimize_smallscreen)) return $size;
837
	if ($type == 'width' && $size > 250) return 250;
838
	else return 10;
839
}
840
841
842
/**
843
 *	Clean a string to use it as a file name
844
 *
845
 *	@param	string	$str            String to clean
846
 * 	@param	string	$newstr			String to replace bad chars with
847
 *  @param	int	    $unaccent		1=Remove also accent (default), 0 do not remove them
848
 *	@return string          		String cleaned (a-zA-Z_)
849
 *
850
 * 	@see        	dol_string_nospecial(), dol_string_unaccent(), dol_sanitizePathName()
851
 */
852
function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
853
{
854
	// List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
855
	// Char '>' '<' '|' '$' and ';' are special chars for shells.
856
	// Char '/' and '\' are file delimiters.
857
	$filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';');
858
	return dol_string_nospecial($unaccent ?dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
859
}
860
861
/**
862
 *	Clean a string to use it as a path name
863
 *
864
 *	@param	string	$str            String to clean
865
 * 	@param	string	$newstr			String to replace bad chars with
866
 *  @param	int	    $unaccent		1=Remove also accent (default), 0 do not remove them
867
 *	@return string          		String cleaned (a-zA-Z_)
868
 *
869
 * 	@see        	dol_string_nospecial(), dol_string_unaccent(), dol_sanitizeFileName()
870
 */
871
function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
872
{
873
	$filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°');
874
	return dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
875
}
876
877
/**
878
 *	Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName
879
 *
880
 *	@param	string	$str			String to clean
881
 *	@return string   	       		Cleaned string
882
 *
883
 * 	@see    		dol_sanitizeFilename(), dol_string_nospecial()
884
 */
885
function dol_string_unaccent($str)
886
{
887
	if (utf8_check($str))
888
	{
889
		// See http://www.utf8-chartable.de/
890
		$string = rawurlencode($str);
891
		$replacements = array(
892
		'%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
893
		'%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
894
		'%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
895
		'%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
896
		'%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
897
		'%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
898
		'%C3%A7' => 'c',
899
		'%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
900
		'%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
901
		'%C3%B1' => 'n',
902
		'%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
903
		'%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
904
		'%C3%BF' => 'y'
905
		);
906
		$string = strtr($string, $replacements);
907
		return rawurldecode($string);
908
	} else {
909
		// See http://www.ascii-code.com/
910
		$string = strtr(
911
			$str,
912
			"\xC0\xC1\xC2\xC3\xC4\xC5\xC7
913
			\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
914
			\xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
915
			\xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
916
			\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
917
			\xF9\xFA\xFB\xFC\xFD\xFF",
918
			"AAAAAAC
919
			EEEEIIIIDN
920
			OOOOOUUUY
921
			aaaaaaceeee
922
			iiiidnooooo
923
			uuuuyy"
924
		);
925
		$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"));
926
		return $string;
927
	}
928
}
929
930
/**
931
 *	Clean a string from all punctuation characters to use it as a ref or login.
932
 *  This is a more complete function than dol_sanitizeFileName.
933
 *
934
 *	@param	string	$str            	String to clean
935
 * 	@param	string	$newstr				String to replace forbidden chars with
936
 *  @param  array	$badcharstoreplace  List of forbidden characters
937
 * 	@return string          			Cleaned string
938
 *
939
 * 	@see    		dol_sanitizeFilename(), dol_string_unaccent()
940
 */
941
function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '')
942
{
943
	$forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°'); // more complete than dol_sanitizeFileName
944
	$forbidden_chars_to_remove = array();
945
	if (is_array($badcharstoreplace)) $forbidden_chars_to_replace = $badcharstoreplace;
946
	//$forbidden_chars_to_remove=array("(",")");
947
948
	return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
949
}
950
951
952
/**
953
 *  Returns text escaped for inclusion into javascript code
954
 *
955
 *  @param      string		$stringtoescape		String to escape
956
 *  @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 \
957
 *  @param		int		$noescapebackslashn	0=Escape also \n. 1=Do not escape \n.
958
 *  @return     string     		 				Escaped string. Both ' and " are escaped into ' if they are escaped.
959
 */
960
function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
961
{
962
	// escape quotes and backslashes, newlines, etc.
963
	$substitjs = array("&#039;"=>"\\'", "\r"=>'\\r');
964
	//$substitjs['</']='<\/';	// We removed this. Should be useless.
965
	if (empty($noescapebackslashn)) { $substitjs["\n"] = '\\n'; $substitjs['\\'] = '\\\\'; }
966
	if (empty($mode)) { $substitjs["'"] = "\\'"; $substitjs['"'] = "\\'"; } elseif ($mode == 1) $substitjs["'"] = "\\'";
967
	elseif ($mode == 2) { $substitjs['"'] = '\\"'; } elseif ($mode == 3) { $substitjs["'"] = "\\'"; $substitjs['"'] = "\\\""; }
968
	return strtr($stringtoescape, $substitjs);
969
}
970
971
/**
972
 *  Returns text escaped for inclusion into javascript code
973
 *
974
 *  @param      string		$stringtoescape		String to escape
975
 *  @return     string     		 				Escaped string for json content.
976
 */
977
function dol_escape_json($stringtoescape)
978
{
979
	return str_replace('"', '\"', $stringtoescape);
980
}
981
982
/**
983
 *  Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
984
 *
985
 *  @param      string		$stringtoescape			String to escape
986
 *  @param		int			$keepb					1=Preserve b tags (otherwise, remove them)
987
 *  @param      int         $keepn              	1=Preserve \r\n strings (otherwise, replace them with escaped value). Set to 1 when escaping for a <textarea>.
988
 *  @param		string		$keepmoretags			'' or 'common' or list of tags
989
 *  @param		int			$escapeonlyhtmltags		1=Escape only html tags, not the special chars like accents.
990
 *  @return     string     				 			Escaped string
991
 *  @see		dol_string_nohtmltag(), dol_string_nospecial(), dol_string_unaccent()
992
 */
993
function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $keepmoretags = '', $escapeonlyhtmltags = 0)
994
{
995
	if ($keepmoretags == 'common') $keepmoretags = 'html,body,a,em,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody';
996
	// TODO Implement $keepmoretags
997
998
	// escape quotes and backslashes, newlines, etc.
999
	if ($escapeonlyhtmltags) {
1000
		$tmp = htmlspecialchars_decode($stringtoescape, ENT_COMPAT);
1001
	} else {
1002
		$tmp = html_entity_decode($stringtoescape, ENT_COMPAT, 'UTF-8');
1003
	}
1004
	if (!$keepb) $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>''));
1005
	if (!$keepn) $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n'));
1006
	if ($escapeonlyhtmltags) {
1007
		return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1008
	} else {
1009
		return htmlentities($tmp, ENT_COMPAT, 'UTF-8');
1010
	}
1011
}
1012
1013
1014
/**
1015
 * Convert a string to lower. Never use strtolower because it does not works with UTF8 strings.
1016
 *
1017
 * @param 	string		$utf8_string		String to encode
1018
 * @return 	string							String converted
1019
 */
1020
function dol_strtolower($utf8_string)
1021
{
1022
	return mb_strtolower($utf8_string, "UTF-8");
1023
}
1024
1025
/**
1026
 * Convert a string to upper. Never use strtolower because it does not works with UTF8 strings.
1027
 *
1028
 * @param 	string		$utf8_string		String to encode
1029
 * @return 	string							String converted
1030
 */
1031
function dol_strtoupper($utf8_string)
1032
{
1033
	return mb_strtoupper($utf8_string, "UTF-8");
1034
}
1035
1036
1037
/**
1038
 *	Write log message into outputs. Possible outputs can be:
1039
 *	SYSLOG_HANDLERS = ["mod_syslog_file"]  		file name is then defined by SYSLOG_FILE
1040
 *	SYSLOG_HANDLERS = ["mod_syslog_syslog"]  	facility is then defined by SYSLOG_FACILITY
1041
 *  Warning, syslog functions are bugged on Windows, generating memory protection faults. To solve
1042
 *  this, use logging to files instead of syslog (see setup of module).
1043
 *  Note: If constant 'SYSLOG_FILE_NO_ERROR' defined, we never output any error message when writing to log fails.
1044
 *  Note: You can get log message into html sources by adding parameter &logtohtml=1 (constant MAIN_LOGTOHTML must be set)
1045
 *  This function works only if syslog module is enabled.
1046
 * 	This must not use any call to other function calling dol_syslog (avoid infinite loop).
1047
 *
1048
 * 	@param  string		$message				Line to log. ''=Show nothing
1049
 *  @param  int			$level					Log level
1050
 *												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
1051
 *												On Linux   LOG_ERR=3, LOG_WARNING=4, LOG_INFO=6, LOG_DEBUG=7
1052
 *  @param	int			$ident					1=Increase ident of 1, -1=Decrease ident of 1
1053
 *  @param	string		$suffixinfilename		When output is a file, append this suffix into default log filename.
1054
 *  @param	string		$restricttologhandler	Force output of log only to this log handler
1055
 *  @param	array|null	$logcontext				If defined, an array with extra informations (can be used by some log handlers)
1056
 *  @return	void
1057
 */
1058
function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
1059
{
1060
	global $conf, $user, $debugbar;
1061
1062
	// If syslog module enabled
1063
	if (empty($conf->syslog->enabled)) return;
1064
1065
	// Check if we are into execution of code of a website
1066
	if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
1067
		global $website, $websitekey;
1068
		if (is_object($website) && !empty($website->ref)) $suffixinfilename .= '_website_'.$website->ref;
1069
		elseif (!empty($websitekey)) $suffixinfilename .= '_website_'.$websitekey;
1070
	}
1071
1072
	if ($ident < 0)
1073
	{
1074
		foreach ($conf->loghandlers as $loghandlerinstance)
1075
		{
1076
			$loghandlerinstance->setIdent($ident);
1077
		}
1078
	}
1079
1080
	if (!empty($message))
1081
	{
1082
		// Test log level
1083
		$logLevels = array(LOG_EMERG=>'EMERG', LOG_ALERT=>'ALERT', LOG_CRIT=>'CRITICAL', LOG_ERR=>'ERR', LOG_WARNING=>'WARN', LOG_NOTICE=>'NOTICE', LOG_INFO=>'INFO', LOG_DEBUG=>'DEBUG');
1084
		if (!array_key_exists($level, $logLevels))
1085
		{
1086
			throw new Exception('Incorrect log level');
1087
		}
1088
		if ($level > $conf->global->SYSLOG_LEVEL) return;
1089
1090
		$message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1091
1092
		// If adding log inside HTML page is required
1093
		if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML))
1094
			|| (!empty($user->rights->debugbar->read) && is_object($debugbar)))
1095
		{
1096
			$conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message;
1097
		}
1098
1099
		//TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1100
		// If html log tag enabled and url parameter log defined, we show output log on HTML comments
1101
		if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"]))
1102
		{
1103
			print "\n\n<!-- Log start\n";
1104
			print $message."\n";
1105
			print "Log end -->\n";
1106
		}
1107
1108
		$data = array(
1109
			'message' => $message,
1110
			'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
1111
			'level' => $level,
1112
			'user' => ((is_object($user) && $user->id) ? $user->login : false),
1113
			'ip' => false
1114
		);
1115
1116
		$remoteip = getUserRemoteIP(); // Get ip when page run on a web server
1117
		if (!empty($remoteip)) {
1118
			$data['ip'] = $remoteip;
1119
			// This is when server run behind a reverse proxy
1120
			if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
1121
			elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
1122
		} // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1123
		elseif (!empty($_SERVER['SERVER_ADDR'])) $data['ip'] = $_SERVER['SERVER_ADDR'];
1124
		// 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).
1125
		elseif (!empty($_SERVER['COMPUTERNAME'])) $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']);
1126
		// 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).
1127
		elseif (!empty($_SERVER['LOGNAME'])) $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1128
		// Loop on each log handler and send output
1129
		foreach ($conf->loghandlers as $loghandlerinstance)
1130
		{
1131
			if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) continue;
1132
			$loghandlerinstance->export($data, $suffixinfilename);
1133
		}
1134
		unset($data);
1135
	}
1136
1137
	if ($ident > 0)
1138
	{
1139
		foreach ($conf->loghandlers as $loghandlerinstance)
1140
		{
1141
			$loghandlerinstance->setIdent($ident);
1142
		}
1143
	}
1144
}
1145
1146
1147
/**
1148
 *	Show tab header of a card
1149
 *
1150
 *	@param	array	$links				Array of tabs. Currently initialized by calling a function xxx_admin_prepare_head
1151
 *	@param	string	$active     		Active tab name (document', 'info', 'ldap', ....)
1152
 *	@param  string	$title      		Title
1153
 *	@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), -2=Add tab header with no seaparation under tab (to start a tab just after)
1154
 * 	@param	string	$picto				Add a picto on tab title
1155
 *	@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.
1156
 *  @param	string	$morehtmlright		Add more html content on right of tabs title
1157
 *  @param	string	$morecss			More Css
1158
 *  @param	int		$limittoshow		Limit number of tabs to show. Use 0 to use automatic default value.
1159
 *  @param	string	$moretabssuffix		A suffix to use when you have several dol_get_fiche_head() in same page
1160
 * 	@return	void
1161
 */
1162
function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1163
{
1164
	print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
1165
}
1166
1167
/**
1168
 *  Show tabs of a record
1169
 *
1170
 *	@param	array	$links				Array of tabs
1171
 *	@param	string	$active     		Active tab name
1172
 *	@param  string	$title      		Title
1173
 *	@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), -2=Add tab header with no seaparation under tab (to start a tab just after)
1174
 * 	@param	string	$picto				Add a picto on tab title
1175
 *	@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.
1176
 *  @param	string	$morehtmlright		Add more html content on right of tabs title
1177
 *  @param	string	$morecss			More Css
1178
 *  @param	int		$limittoshow		Limit number of tabs to show. Use 0 to use automatic default value.
1179
 *  @param	string	$moretabssuffix		A suffix to use when you have several dol_get_fiche_head() in same page
1180
 * 	@return	string
1181
 */
1182
function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1183
{
1184
	global $conf, $langs, $hookmanager;
1185
1186
	// Show title
1187
	$showtitle = 1;
1188
	if (!empty($conf->dol_optimize_smallscreen)) $showtitle = 0;
1189
1190
	$out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
1191
1192
	if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1193
		$out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
1194
	}
1195
1196
	// Show right part
1197
	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.
1198
1199
	// Show title
1200
	if (!empty($title) && $showtitle)
1201
	{
1202
		$limittitle = 30;
1203
		$out .= '<a class="tabTitle">';
1204
		if ($picto && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) $out .= img_picto($title, ($pictoisfullpath ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
1205
		$out .= '<span class="tabTitleText">'.dol_trunc($title, $limittitle).'</span>';
1206
		$out .= '</a>';
1207
	}
1208
1209
	// Show tabs
1210
1211
	// Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
1212
	$maxkey = -1;
1213
	if (is_array($links) && !empty($links))
1214
	{
1215
		$keys = array_keys($links);
1216
		if (count($keys)) $maxkey = max($keys);
1217
	}
1218
1219
	// Show tabs
1220
	// if =0 we don't use the feature
1221
	if (empty($limittoshow)) {
1222
		$limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
1223
	}
1224
	if (!empty($conf->dol_optimize_smallscreen)) $limittoshow = 2;
1225
1226
	$displaytab = 0;
1227
	$nbintab = 0;
1228
	$popuptab = 0;
1229
	$outmore = '';
1230
	for ($i = 0; $i <= $maxkey; $i++)
1231
	{
1232
		if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1233
			// If active tab is already present
1234
			if ($i >= $limittoshow) $limittoshow--;
1235
		}
1236
	}
1237
1238
	for ($i = 0; $i <= $maxkey; $i++)
1239
	{
1240
		if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1241
			$isactive = true;
1242
		} else {
1243
			$isactive = false;
1244
		}
1245
1246
		if ($i < $limittoshow || $isactive)
1247
		{
1248
			$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]).' -->';
1249
			if (isset($links[$i][2]) && $links[$i][2] == 'image')
1250
			{
1251
				if (!empty($links[$i][0]))
1252
				{
1253
					$out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1254
				} else {
1255
					$out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1256
				}
1257
			} elseif (!empty($links[$i][1]))
1258
			{
1259
				//print "x $i $active ".$links[$i][2]." z";
1260
				if ($isactive)
1261
				{
1262
					$out .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="tabactive tab inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
1263
					$out .= $links[$i][1];
1264
					$out .= '</a>'."\n";
1265
				} else {
1266
					$out .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="tabunactive tab inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
1267
					$out .= $links[$i][1];
1268
					$out .= '</a>'."\n";
1269
				}
1270
			}
1271
			$out .= '</div>';
1272
		} else {
1273
			// The popup with the other tabs
1274
			if (!$popuptab)
1275
			{
1276
				$popuptab = 1;
1277
				$outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
1278
			}
1279
			$outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
1280
			if (isset($links[$i][2]) && $links[$i][2] == 'image')
1281
			{
1282
				if (!empty($links[$i][0]))
1283
					$outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1284
				else $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1285
			} elseif (!empty($links[$i][1]))
1286
			{
1287
				$outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
1288
				$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.
1289
				$outmore .= '</a>'."\n";
1290
			}
1291
			$outmore .= '</div>';
1292
1293
			$nbintab++;
1294
		}
1295
		$displaytab = $i;
1296
	}
1297
	if ($popuptab) $outmore .= '</div>';
1298
1299
	if ($popuptab)	// If there is some tabs not shown
1300
	{
1301
		$left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
1302
		$right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
1303
1304
		$tabsname = $moretabssuffix;
1305
		if (empty($tabsname)) { $tabsname = str_replace("@", "", $picto); }
1306
		$out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem">';
1307
		$out .= '<a href="#" class="tab moretab inline-block tabunactive reposition">'.$langs->trans("More").'... ('.$nbintab.')</a>';
1308
		$out .= '<div id="moretabsList'.$tabsname.'" style="position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
1309
		$out .= $outmore;
1310
		$out .= '</div>';
1311
		$out .= '<div></div>';
1312
		$out .= "</div>\n";
1313
1314
		$out .= "<script>";
1315
		$out .= "$('#moretabs".$tabsname."').mouseenter( function() { console.log('mouseenter ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','auto');});";
1316
		$out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
1317
		$out .= "</script>";
1318
	}
1319
1320
	if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1321
		$out .= "</div>\n";
1322
	}
1323
1324
	if (!$notab || $notab == -1 || $notab == -2) $out .= "\n".'<div class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ' tabBarWithBottom')).'">'."\n";
1325
1326
	$parameters = array('tabname' => $active, 'out' => $out);
1327
	$reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
1328
	if ($reshook > 0)
1329
	{
1330
		$out = $hookmanager->resPrint;
1331
	}
1332
1333
	return $out;
1334
}
1335
1336
/**
1337
 *  Show tab footer of a card
1338
 *
1339
 *  @param	int		$notab       -1 or 0=Add tab footer, 1=no tab footer
1340
 *  @return	void
1341
 */
1342
function dol_fiche_end($notab = 0)
1343
{
1344
	print dol_get_fiche_end($notab);
1345
}
1346
1347
/**
1348
 *	Return tab footer of a card
1349
 *
1350
 *	@param  int		$notab		-1 or 0=Add tab footer, 1=no tab footer
1351
 *  @return	string
1352
 */
1353
function dol_get_fiche_end($notab = 0)
1354
{
1355
	if (!$notab || $notab == -1) return "\n</div>\n";
1356
	else return '';
1357
}
1358
1359
/**
1360
 *  Show tab footer of a card.
1361
 *  Note: $object->next_prev_filter can be set to restrict select to find next or previous record by $form->showrefnav.
1362
 *
1363
 *  @param	Object	$object			Object to show
1364
 *  @param	string	$paramid   		Name of parameter to use to name the id into the URL next/previous link
1365
 *  @param	string	$morehtml  		More html content to output just before the nav bar
1366
 *  @param	int		$shownav	  	Show Condition (navigation is shown if value is 1)
1367
 *  @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.
1368
 *  @param	string	$fieldref   	Nom du champ objet ref (object->ref) a utiliser pour select next et previous
1369
 *  @param	string	$morehtmlref  	More html to show after ref
1370
 *  @param	string	$moreparam  	More param to add in nav link url.
1371
 *	@param	int		$nodbprefix		Do not include DB prefix to forge table name
1372
 *	@param	string	$morehtmlleft	More html code to show before ref
1373
 *	@param	string	$morehtmlstatus	More html code to show under navigation arrows
1374
 *  @param  int     $onlybanner     Put this to 1, if the card will contains only a banner (this add css 'arearefnobottom' on div)
1375
 *	@param	string	$morehtmlright	More html code to show before navigation arrows
1376
 *  @return	void
1377
 */
1378
function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
1379
{
1380
	global $conf, $form, $user, $langs;
1381
1382
	$error = 0;
1383
1384
	$maxvisiblephotos = 1;
1385
	$showimage = 1;
1386
	$entity = (empty($object->entity) ? $conf->entity : $object->entity);
1387
	$showbarcode = empty($conf->barcode->enabled) ? 0 : ($object->barcode ? 1 : 0);
1388
	if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) $showbarcode = 0;
1389
	$modulepart = 'unknown';
1390
1391
	if ($object->element == 'societe')         $modulepart = 'societe';
1392
	if ($object->element == 'contact')         $modulepart = 'contact';
1393
	if ($object->element == 'member')          $modulepart = 'memberphoto';
1394
	if ($object->element == 'user')            $modulepart = 'userphoto';
1395
	if ($object->element == 'product')         $modulepart = 'product';
1396
	if ($object->element == 'ticket')          $modulepart = 'ticket';
1397
1398
	if (class_exists("Imagick"))
1399
	{
1400
		if ($object->element == 'propal')            $modulepart = 'propal';
1401
		if ($object->element == 'commande')          $modulepart = 'commande';
1402
		if ($object->element == 'facture')           $modulepart = 'facture';
1403
		if ($object->element == 'fichinter')         $modulepart = 'ficheinter';
1404
		if ($object->element == 'contrat')           $modulepart = 'contract';
1405
		if ($object->element == 'supplier_proposal') $modulepart = 'supplier_proposal';
1406
		if ($object->element == 'order_supplier')    $modulepart = 'supplier_order';
1407
		if ($object->element == 'invoice_supplier')  $modulepart = 'supplier_invoice';
1408
		if ($object->element == 'expensereport')     $modulepart = 'expensereport';
1409
	}
1410
1411
	if ($object->element == 'product')
1412
	{
1413
		$width = 80; $cssclass = 'photoref';
1414
		$showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
1415
		$maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5);
1416
		if ($conf->browser->layout == 'phone') $maxvisiblephotos = 1;
1417
		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>';
1418
		else {
1419
			if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
1420
				$nophoto = '';
1421
				$morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
1422
			} else {    // Show no photo link
1423
				$nophoto = '/public/theme/common/nophoto.png';
1424
				$morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
1425
			}
1426
		}
1427
	} elseif ($object->element == 'ticket') {
1428
		$width = 80; $cssclass = 'photoref';
1429
		$showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
1430
		$maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2);
1431
		if ($conf->browser->layout == 'phone') $maxvisiblephotos = 1;
1432
		if ($showimage)
1433
		{
1434
			$showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
1435
			if ($object->nbphoto > 0)
1436
			{
1437
				$morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
1438
			} else {
1439
				$showimage = 0;
1440
			}
1441
		}
1442
		if (!$showimage)
1443
		{
1444
			if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) {
1445
				$nophoto = '';
1446
				$morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
1447
			} else {    // Show no photo link
1448
				$nophoto = '/public/theme/common/nophoto.png';
1449
				$morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo" border="0"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
1450
			}
1451
		}
1452
	} else {
1453
		if ($showimage)
1454
		{
1455
			if ($modulepart != 'unknown')
1456
			{
1457
				$phototoshow = '';
1458
				// Check if a preview file is available
1459
				if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick"))
1460
				{
1461
					$objectref = dol_sanitizeFileName($object->ref);
1462
					$dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
1463
					if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice')))
1464
					{
1465
						$subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
1466
						$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
1467
					} else {
1468
						$subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
1469
					}
1470
					if (empty($subdir)) $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
1471
1472
					$filepath = $dir_output.$subdir."/";
1473
1474
					$filepdf = $filepath.$objectref.".pdf";
1475
					$relativepath = $subdir.'/'.$objectref.'.pdf';
1476
1477
					// Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
1478
					$fileimage = $filepdf.'_preview.png';
1479
					$relativepathimage = $relativepath.'_preview.png';
1480
1481
					$pdfexists = file_exists($filepdf);
1482
1483
					// If PDF file exists
1484
					if ($pdfexists)
1485
					{
1486
						// Conversion du PDF en image png si fichier png non existant
1487
						if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf)))
1488
						{
1489
							if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS))		// If you experience trouble with pdf thumb generation and imagick, you can disable here.
1490
							{
1491
								include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1492
								$ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
1493
								if ($ret < 0) $error++;
1494
							}
1495
						}
1496
					}
1497
1498
					if ($pdfexists && !$error)
1499
					{
1500
						$heightforphotref = 70;
1501
						if (!empty($conf->dol_optimize_smallscreen)) $heightforphotref = 60;
1502
						// If the preview file is found
1503
						if (file_exists($fileimage))
1504
						{
1505
							$phototoshow = '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
1506
							$phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
1507
							$phototoshow .= '</div></div>';
1508
						}
1509
					}
1510
				} elseif (!$phototoshow)
1511
				{
1512
					$phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photoref', 'small', 1, 0, $maxvisiblephotos);
1513
				}
1514
1515
				if ($phototoshow)
1516
				{
1517
					$morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
1518
					$morehtmlleft .= $phototoshow;
1519
					$morehtmlleft .= '</div>';
1520
				}
1521
			}
1522
1523
			if (!$phototoshow)      // Show No photo link (picto of object)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $phototoshow does not seem to be defined for all execution paths leading up to this point.
Loading history...
1524
			{
1525
				$morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
1526
				if ($object->element == 'action')
1527
				{
1528
					$width = 80;
1529
					$cssclass = 'photorefcenter';
1530
					$nophoto = img_picto('No photo', 'title_agenda');
1531
				} else {
1532
					$width = 14; $cssclass = 'photorefcenter';
1533
					$picto = $object->picto;
1534
					if ($object->element == 'project' && !$object->public) $picto = 'project'; // instead of projectpub
1535
					$nophoto = img_picto('No photo', 'object_'.$picto);
1536
				}
1537
				$morehtmlleft .= '<!-- No photo to show -->';
1538
				$morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
1539
				$morehtmlleft .= $nophoto;
1540
				$morehtmlleft .= '</div></div>';
1541
1542
				$morehtmlleft .= '</div>';
1543
			}
1544
		}
1545
	}
1546
1547
	if ($showbarcode) $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object).'</div>';
1548
1549
	if ($object->element == 'societe')
1550
	{
1551
		if (!empty($conf->use_javascript_ajax) && $user->rights->societe->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE))
1552
		{
1553
		   	$morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
1554
		} else {
1555
			$morehtmlstatus .= $object->getLibStatut(6);
1556
		}
1557
	} elseif ($object->element == 'product')
1558
	{
1559
		//$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
1560
		if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1561
			$morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
1562
		} else {
1563
			$morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
1564
		}
1565
		$morehtmlstatus .= ' &nbsp; ';
1566
		//$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
1567
		if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1568
			$morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
1569
		} else {
1570
			$morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
1571
		}
1572
	} elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan')))
1573
	{
1574
		$tmptxt = $object->getLibStatut(6, $object->totalpaye);
1575
		if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) $tmptxt = $object->getLibStatut(5, $object->totalpaye);
1576
		$morehtmlstatus .= $tmptxt;
1577
	} elseif ($object->element == 'contrat' || $object->element == 'contract')
1578
	{
1579
		if ($object->statut == 0) $morehtmlstatus .= $object->getLibStatut(5);
1580
		else $morehtmlstatus .= $object->getLibStatut(4);
1581
	} elseif ($object->element == 'facturerec')
1582
	{
1583
		if ($object->frequency == 0) $morehtmlstatus .= $object->getLibStatut(2);
1584
		else $morehtmlstatus .= $object->getLibStatut(5);
1585
	} elseif ($object->element == 'project_task')
1586
	{
1587
		$object->fk_statut = 1;
1588
		if ($object->progress > 0) $object->fk_statut = 2;
1589
		if ($object->progress >= 100) $object->fk_statut = 3;
1590
		$tmptxt = $object->getLibStatut(5);
1591
		$morehtmlstatus .= $tmptxt; // No status on task
1592
	} else { // Generic case
1593
		$tmptxt = $object->getLibStatut(6);
1594
		if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) $tmptxt = $object->getLibStatut(5);
1595
		$morehtmlstatus .= $tmptxt;
1596
	}
1597
1598
	// Add if object was dispatched "into accountancy"
1599
	if (!empty($conf->accounting->enabled) && in_array($object->element, array('bank', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various')))
1600
	{
1601
		if (method_exists($object, 'getVentilExportCompta'))
1602
		{
1603
			$accounted = $object->getVentilExportCompta();
1604
			$langs->load("accountancy");
1605
			$morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
1606
		}
1607
	}
1608
1609
	// Add alias for thirdparty
1610
	if (!empty($object->name_alias)) $morehtmlref .= '<div class="refidno">'.$object->name_alias.'</div>';
1611
1612
	// Add label
1613
	if (in_array($object->element, array('product', 'bank_account', 'project_task')))
1614
	{
1615
		if (!empty($object->label)) $morehtmlref .= '<div class="refidno">'.$object->label.'</div>';
1616
	}
1617
1618
	if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files')))
1619
	{
1620
		$moreaddress = $object->getBannerAddress('refaddress', $object);
1621
		if ($moreaddress) {
1622
			$morehtmlref .= '<div class="refidno">';
1623
			$morehtmlref .= $moreaddress;
1624
			$morehtmlref .= '</div>';
1625
		}
1626
	}
1627
	if (!empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && ($conf->global->MAIN_SHOW_TECHNICAL_ID == '1' || preg_match('/'.preg_quote($object->element, '/').'/i', $conf->global->MAIN_SHOW_TECHNICAL_ID)) && !empty($object->id))
1628
	{
1629
		$morehtmlref .= '<div style="clear: both;"></div><div class="refidno">';
1630
		$morehtmlref .= $langs->trans("TechnicalID").': '.$object->id;
1631
		$morehtmlref .= '</div>';
1632
	}
1633
1634
	print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
1635
	print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
1636
	print '</div>';
1637
	print '<div class="underrefbanner clearboth"></div>';
1638
}
1639
1640
/**
1641
 * Show a string with the label tag dedicated to the HTML edit field.
1642
 *
1643
 * @param	string	$langkey		Translation key
1644
 * @param 	string	$fieldkey		Key of the html select field the text refers to
1645
 * @param	int		$fieldrequired	1=Field is mandatory
1646
 * @return string
1647
 * @deprecated Form::editfieldkey
1648
 */
1649
function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
1650
{
1651
	global $langs;
1652
	$ret = '';
1653
	if ($fieldrequired) $ret .= '<span class="fieldrequired">';
1654
	$ret .= '<label for="'.$fieldkey.'">';
1655
	$ret .= $langs->trans($langkey);
1656
	$ret .= '</label>';
1657
	if ($fieldrequired) $ret .= '</span>';
1658
	return $ret;
1659
}
1660
1661
/**
1662
 * Return string to add class property on html element with pair/impair.
1663
 *
1664
 * @param	string	$var			0 or 1
1665
 * @param	string	$moreclass		More class to add
1666
 * @return	string					String to add class onto HTML element
1667
 */
1668
function dol_bc($var, $moreclass = '')
1669
{
1670
	global $bc;
1671
	$ret = ' '.$bc[$var];
1672
	if ($moreclass) $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
1673
	return $ret;
1674
}
1675
1676
/**
1677
 *      Return a formated address (part address/zip/town/state) according to country rules.
1678
 *      See https://en.wikipedia.org/wiki/Address
1679
 *
1680
 *      @param  Object		$object			A company or contact object
1681
 * 	    @param	int			$withcountry	1=Add country into address string
1682
 *      @param	string		$sep			Separator to use to build string
1683
 *      @param	Translate	$outputlangs	Object lang that contains language for text translation.
1684
 *      @param	int			$mode			0=Standard output, 1=Remove address
1685
 *  	@param	string		$extralangcode	User extralanguage $langcode as values for address, town
1686
 *      @return string						Formated string
1687
 *      @see dol_print_address()
1688
 */
1689
function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '')
1690
{
1691
	global $conf, $langs;
1692
1693
	$ret = '';
1694
	$countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
1695
1696
	// See format of addresses on https://en.wikipedia.org/wiki/Address
1697
	// Address
1698
	if (empty($mode)) {
1699
		$ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : $object->address);
1700
	}
1701
	// Zip/Town/State
1702
	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
1703
	{
1704
		$town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : $object->town);
1705
		$ret .= ($ret ? $sep : '').$town;
1706
		if ($object->state)
1707
		{
1708
			$ret .= ($ret ? ", " : '').$object->state;
1709
		}
1710
		if ($object->zip) $ret .= ($ret ? ", " : '').$object->zip;
1711
	} elseif (in_array($object->country_code, array('GB', 'UK'))) // UK: title firstname name \n address lines \n town state \n zip \n country
1712
	{
1713
		$town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : $object->town);
1714
		$ret .= ($ret ? $sep : '').$town;
1715
		if ($object->state)
1716
		{
1717
			$ret .= ($ret ? ", " : '').$object->state;
1718
		}
1719
		if ($object->zip) $ret .= ($ret ? $sep : '').$object->zip;
1720
	} elseif (in_array($object->country_code, array('ES', 'TR'))) // ES: title firstname name \n address lines \n zip town \n state \n country
1721
	{
1722
		$ret .= ($ret ? $sep : '').$object->zip;
1723
		$town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : $object->town);
1724
		$ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
1725
		if ($object->state)
1726
		{
1727
			$ret .= "\n".$object->state;
1728
		}
1729
	} elseif (in_array($object->country_code, array('IT'))) // IT: tile firstname name\n address lines \n zip (Code Departement) \n country
1730
	{
1731
		$ret .= ($ret ? $sep : '').$object->zip;
1732
		$town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : $object->town);
1733
		$ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
1734
		$ret .= ($object->state_code ? (' '.($object->state_code)) : '');
1735
	} else // Other: title firstname name \n address lines \n zip town \n country
1736
	{
1737
		$town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : $object->town);
1738
		$ret .= $object->zip ? (($ret ? $sep : '').$object->zip) : '';
1739
		$ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
1740
		if ($object->state && in_array($object->country_code, $countriesusingstate))
1741
		{
1742
			$ret .= ($ret ? ", " : '').$object->state;
1743
		}
1744
	}
1745
	if (!is_object($outputlangs)) $outputlangs = $langs;
1746
	if ($withcountry)
1747
	{
1748
		$langs->load("dict");
1749
		$ret .= ($object->country_code ? ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)) : '');
1750
	}
1751
1752
	return $ret;
1753
}
1754
1755
1756
1757
/**
1758
 *	Format a string.
1759
 *
1760
 *	@param	string	$fmt		Format of strftime function (http://php.net/manual/fr/function.strftime.php)
1761
 *  @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)
1762
 *  @param	int		$is_gmt		See comment of timestamp parameter
1763
 *	@return	string				A formatted string
1764
 */
1765
function dol_strftime($fmt, $ts = false, $is_gmt = false)
1766
{
1767
	if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
1768
		return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts);
1769
	} else return 'Error date into a not supported range';
1770
}
1771
1772
/**
1773
 *	Output date in a string format according to outputlangs (or langs if not defined).
1774
 * 	Return charset is always UTF-8, except if encodetoouput is defined. In this case charset is output charset
1775
 *
1776
 *	@param	int			$time			GM Timestamps date
1777
 *	@param	string		$format      	Output date format (tag of strftime function)
1778
 *										"%d %b %Y",
1779
 *										"%d/%m/%Y %H:%M",
1780
 *										"%d/%m/%Y %H:%M:%S",
1781
 *                                      "%B"=Long text of month, "%A"=Long text of day, "%b"=Short text of month, "%a"=Short text of day
1782
 *										"day", "daytext", "dayhour", "dayhourldap", "dayhourtext", "dayrfc", "dayhourrfc", "...reduceformat"
1783
 * 	@param	string		$tzoutput		true or 'gmt' => string is for Greenwich location
1784
 * 										false or 'tzserver' => output string is for local PHP server TZ usage
1785
 * 										'tzuser' => output string is for user TZ (current browser TZ with current dst) => In a future, we should have same behaviour than 'tzuserrel'
1786
 *                                      'tzuserrel' => output string is for user TZ (current browser TZ with dst or not, depending on date position) (TODO not implemented yet)
1787
 *	@param	Translate	$outputlangs	Object lang that contains language for text translation.
1788
 *  @param  boolean		$encodetooutput false=no convert into output pagecode
1789
 * 	@return string      				Formated date or '' if time is null
1790
 *
1791
 *  @see        dol_mktime(), dol_stringtotime(), dol_getdate()
1792
 */
1793
function dol_print_date($time, $format = '', $tzoutput = 'tzserver', $outputlangs = '', $encodetooutput = false)
1794
{
1795
	global $conf, $langs;
1796
1797
	// Clean parameters
1798
	$to_gmt = false;
1799
	$offsettz = $offsetdst = 0;
1800
	if ($tzoutput)
1801
	{
1802
		$to_gmt = true; // For backward compatibility
1803
		if (is_string($tzoutput))
1804
		{
1805
			if ($tzoutput == 'tzserver')
1806
			{
1807
				$to_gmt = false;
1808
				$offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
1809
				$offsettz = 0;
1810
				$offsetdst = 0;
1811
			} elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel')
1812
			{
1813
				$to_gmt = true;
1814
				$offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
1815
				$offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
1816
				$offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
1817
			}
1818
		}
1819
	}
1820
	if (!is_object($outputlangs)) $outputlangs = $langs;
1821
	if (!$format) $format = 'daytextshort';
1822
	$reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0;
1823
	$formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
1824
	if ($formatwithoutreduce != $format) { $format = $formatwithoutreduce; $reduceformat = 1; }  // so format 'dayreduceformat' is processed like day
1825
1826
	// Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
1827
	// TODO Add format daysmallyear and dayhoursmallyear
1828
	if ($format == 'day')				$format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
1829
	elseif ($format == 'hour')			$format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
1830
	elseif ($format == 'hourduration')	$format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
1831
	elseif ($format == 'daytext')			 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
1832
	elseif ($format == 'daytextshort')	$format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
1833
	elseif ($format == 'dayhour')			 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
1834
	elseif ($format == 'dayhoursec')		 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
1835
	elseif ($format == 'dayhourtext')		 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
1836
	elseif ($format == 'dayhourtextshort') $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
1837
	// Format not sensitive to language
1838
	elseif ($format == 'dayhourlog')		 $format = '%Y%m%d%H%M%S';
1839
	elseif ($format == 'dayhourldap')		 $format = '%Y%m%d%H%M%SZ';
1840
	elseif ($format == 'dayhourxcard')	$format = '%Y%m%dT%H%M%SZ';
1841
	elseif ($format == 'dayxcard')	 	$format = '%Y%m%d';
1842
	elseif ($format == 'dayrfc')			 $format = '%Y-%m-%d'; // DATE_RFC3339
1843
	elseif ($format == 'dayhourrfc')		 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
1844
	elseif ($format == 'standard')		$format = '%Y-%m-%d %H:%M:%S';
1845
1846
	if ($reduceformat)
1847
	{
1848
		$format = str_replace('%Y', '%y', $format);
1849
		$format = str_replace('yyyy', 'yy', $format);
1850
	}
1851
1852
	// If date undefined or "", we return ""
1853
	if (dol_strlen($time) == 0) return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
1854
1855
	// Clean format
1856
	if (preg_match('/%b/i', $format))		// There is some text to translate
1857
	{
1858
		// We inhibate translation to text made by strftime functions. We will use trans instead later.
1859
		$format = str_replace('%b', '__b__', $format);
1860
		$format = str_replace('%B', '__B__', $format);
1861
	}
1862
	if (preg_match('/%a/i', $format))		// There is some text to translate
1863
	{
1864
		// We inhibate translation to text made by strftime functions. We will use trans instead later.
1865
		$format = str_replace('%a', '__a__', $format);
1866
		$format = str_replace('%A', '__A__', $format);
1867
	}
1868
1869
1870
	// Analyze date
1871
	$reg = array();
1872
	if (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
1873
	{
1874
		dol_print_error("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]);
1875
		return '';
1876
	} elseif (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i', $time, $reg))    // Still available to solve problems in extrafields of type date
1877
	{
1878
		// This part of code should not be used.
1879
		dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING);
1880
		//if (function_exists('debug_print_backtrace')) debug_print_backtrace();
1881
		// Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
1882
		$syear	= (!empty($reg[1]) ? $reg[1] : '');
1883
		$smonth = (!empty($reg[2]) ? $reg[2] : '');
1884
		$sday	= (!empty($reg[3]) ? $reg[3] : '');
1885
		$shour	= (!empty($reg[4]) ? $reg[4] : '');
1886
		$smin	= (!empty($reg[5]) ? $reg[5] : '');
1887
		$ssec	= (!empty($reg[6]) ? $reg[6] : '');
1888
1889
		$time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
1890
		$ret = adodb_strftime($format, $time + $offsettz + $offsetdst, $to_gmt);
1891
	} else {
1892
		// Date is a timestamps
1893
		if ($time < 100000000000)	// Protection against bad date values
1894
		{
1895
			$timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1896
1897
			$ret = adodb_strftime($format, $timetouse, $to_gmt);
1898
		} else $ret = 'Bad value '.$time.' for date';
1899
	}
1900
1901
	if (preg_match('/__b__/i', $format))
1902
	{
1903
		$timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1904
1905
		// Here ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs.
1906
		$month = adodb_strftime('%m', $timetouse);
1907
		$month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
1908
		if ($encodetooutput)
1909
		{
1910
			$monthtext = $outputlangs->transnoentities('Month'.$month);
1911
			$monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
1912
		} else {
1913
			$monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
1914
			$monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
1915
		}
1916
		//print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
1917
		$ret = str_replace('__b__', $monthtextshort, $ret);
1918
		$ret = str_replace('__B__', $monthtext, $ret);
1919
		//print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
1920
		//return $ret;
1921
	}
1922
	if (preg_match('/__a__/i', $format))
1923
	{
1924
		$timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1925
1926
		$w = adodb_strftime('%w', $timetouse); // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1927
		$dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
1928
		$ret = str_replace('__A__', $dayweek, $ret);
1929
		$ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
1930
	}
1931
1932
	return $ret;
1933
}
1934
1935
1936
/**
1937
 *  Return an array with locale date info.
1938
 *  PHP getdate is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
1939
 *  WARNING: This function always use PHP server timezone to return locale informations !!!
1940
 *  Usage must be avoid.
1941
 *  FIXME: Replace this with PHP date function and a parameter $gm
1942
 *
1943
 *	@param	int			$timestamp      Timestamp
1944
 *	@param	boolean		$fast           Fast mode
1945
 *	@return	array						Array of informations
1946
 *										If no fast mode:
1947
 *										'seconds' => $secs,
1948
 *										'minutes' => $min,
1949
 *										'hours' => $hour,
1950
 *										'mday' => $day,
1951
 *										'wday' => $dow,		0=sunday, 6=saturday
1952
 *										'mon' => $month,
1953
 *										'year' => $year,
1954
 *										'yday' => floor($secsInYear/$_day_power),
1955
 *										'weekday' => gmdate('l',$_day_power*(3+$dow)),
1956
 *										'month' => gmdate('F',mktime(0,0,0,$month,2,1971)),
1957
 *										If fast mode:
1958
 *										'seconds' => $secs,
1959
 *										'minutes' => $min,
1960
 *										'hours' => $hour,
1961
 *										'mday' => $day,
1962
 *										'mon' => $month,
1963
 *										'year' => $year,
1964
 *										'yday' => floor($secsInYear/$_day_power),
1965
 *										'leap' => $leaf,
1966
 *										'ndays' => $ndays
1967
 * 	@see 								dol_print_date(), dol_stringtotime(), dol_mktime()
1968
 */
1969
function dol_getdate($timestamp, $fast = false)
1970
{
1971
	global $conf;
1972
1973
	$usealternatemethod = false;
1974
	if ($timestamp <= 0) $usealternatemethod = true; // <= 1970
1975
	if ($timestamp >= 2145913200) $usealternatemethod = true; // >= 2038
1976
1977
	if ($usealternatemethod)
1978
	{
1979
		$arrayinfo = adodb_getdate($timestamp, $fast);
1980
	} else {
1981
		$arrayinfo = getdate($timestamp);
1982
	}
1983
1984
	return $arrayinfo;
1985
}
1986
1987
/**
1988
 *	Return a timestamp date built from detailed informations (by default a local PHP server timestamp)
1989
 * 	Replace function mktime not available under Windows if year < 1970
1990
 *	PHP mktime is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
1991
 *
1992
 * 	@param	int			$hour			Hour	(can be -1 for undefined)
1993
 *	@param	int			$minute			Minute	(can be -1 for undefined)
1994
 *	@param	int			$second			Second	(can be -1 for undefined)
1995
 *	@param	int			$month			Month (1 to 12)
1996
 *	@param	int			$day			Day (1 to 31)
1997
 *	@param	int			$year			Year
1998
 *	@param	mixed		$gm				True or 1 or 'gmt'=Input informations are GMT values
1999
 *										False or 0 or 'server' = local to server TZ
2000
 *										'user' = local to user TZ
2001
 *										'tz,TimeZone' = use specified timezone
2002
 *	@param	int			$check			0=No check on parameters (Can use day 32, etc...)
2003
 *	@return	int|string					Date as a timestamp, '' or false if error
2004
 * 	@see 								dol_print_date(), dol_stringtotime(), dol_getdate()
2005
 */
2006
function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = false, $check = 1)
2007
{
2008
	global $conf;
2009
	//print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
2010
2011
	// Clean parameters
2012
	if ($hour == -1 || empty($hour)) $hour = 0;
2013
	if ($minute == -1 || empty($minute)) $minute = 0;
2014
	if ($second == -1 || empty($second)) $second = 0;
2015
2016
	// Check parameters
2017
	if ($check)
2018
	{
2019
		if (!$month || !$day)  return '';
2020
		if ($day > 31) return '';
2021
		if ($month > 12) return '';
2022
		if ($hour < 0 || $hour > 24) return '';
2023
		if ($minute < 0 || $minute > 60) return '';
2024
		if ($second < 0 || $second > 60) return '';
2025
	}
2026
2027
	if (empty($gm) || $gm === 'server')
2028
	{
2029
		$default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
2030
		$localtz = new DateTimeZone($default_timezone);
2031
	} elseif ($gm === 'user')
2032
	{
2033
		// We use dol_tz_string first because it is more reliable.
2034
		$default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
2035
		try {
2036
			$localtz = new DateTimeZone($default_timezone);
2037
		} catch (Exception $e)
2038
		{
2039
			dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
2040
			$default_timezone = @date_default_timezone_get();
2041
		}
2042
	} elseif (strrpos($gm, "tz,") !== false)
2043
	{
2044
		$timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
2045
		try {
2046
			$localtz = new DateTimeZone($timezone);
2047
		} catch (Exception $e)
2048
		{
2049
			dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
2050
		}
2051
	}
2052
2053
	if (empty($localtz)) {
2054
		$localtz = new DateTimeZone('UTC');
2055
	}
2056
	//var_dump($localtz);
2057
	//var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
2058
	$dt = new DateTime(null, $localtz);
2059
	$dt->setDate((int) $year, (int) $month, (int) $day);
2060
	$dt->setTime((int) $hour, (int) $minute, (int) $second);
2061
	$date = $dt->getTimestamp(); // should include daylight saving time
2062
	//var_dump($date);
2063
	return $date;
2064
}
2065
2066
2067
/**
2068
 *  Return date for now. In most cases, we use this function without parameters (that means GMT time).
2069
 *
2070
 *  @param	string		$mode	'gmt' => we return GMT timestamp,
2071
 * 								'tzserver' => we add the PHP server timezone
2072
 *  							'tzref' => we add the company timezone
2073
 * 								'tzuser' => we add the user timezone
2074
 *	@return int   $date	Timestamp
2075
 */
2076
function dol_now($mode = 'gmt')
2077
{
2078
	$ret = 0;
2079
2080
	if ($mode == 'gmt') $ret = time(); // Time for now at greenwich.
2081
	elseif ($mode == 'tzserver')		// Time for now with PHP server timezone added
2082
	{
2083
		require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2084
		$tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
2085
		$ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
2086
	} /*elseif ($mode == 'tzref')				// Time for now with parent company timezone is added
2087
	{
2088
		require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2089
		$tzsecond=getParentCompanyTimeZoneInt();    // Contains tz+dayling saving time
2090
		$ret=dol_now('gmt')+($tzsecond*3600);
2091
	}*/
2092
	elseif ($mode == 'tzuser')				// Time for now with user timezone added
2093
	{
2094
		//print 'time: '.time();
2095
		$offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
2096
		$offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
2097
		$ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
2098
	}
2099
2100
	return $ret;
2101
}
2102
2103
2104
/**
2105
 * Return string with formated size
2106
 *
2107
 * @param	int		$size		Size to print
2108
 * @param	int		$shortvalue	Tell if we want long value to use another unit (Ex: 1.5Kb instead of 1500b)
2109
 * @param	int		$shortunit	Use short label of size unit (for example 'b' instead of 'bytes')
2110
 * @return	string				Link
2111
 */
2112
function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
2113
{
2114
	global $conf, $langs;
2115
	$level = 1024;
2116
2117
	if (!empty($conf->dol_optimize_smallscreen)) $shortunit = 1;
2118
2119
	// Set value text
2120
	if (empty($shortvalue) || $size < ($level * 10))
2121
	{
2122
		$ret = $size;
2123
		$textunitshort = $langs->trans("b");
2124
		$textunitlong = $langs->trans("Bytes");
2125
	} else {
2126
		$ret = round($size / $level, 0);
2127
		$textunitshort = $langs->trans("Kb");
2128
		$textunitlong = $langs->trans("KiloBytes");
2129
	}
2130
	// Use long or short text unit
2131
	if (empty($shortunit)) { $ret .= ' '.$textunitlong; } else { $ret .= ' '.$textunitshort; }
2132
2133
	return $ret;
2134
}
2135
2136
/**
2137
 * Show Url link
2138
 *
2139
 * @param	string		$url		Url to show
2140
 * @param	string		$target		Target for link
2141
 * @param	int			$max		Max number of characters to show
2142
 * @param	int			$withpicto	With picto
2143
 * @return	string					HTML Link
2144
 */
2145
function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0)
2146
{
2147
	global $langs;
2148
2149
	if (empty($url)) return '';
2150
2151
	$link = '<a href="';
2152
	if (!preg_match('/^http/i', $url)) $link .= 'http://';
2153
	$link .= $url;
2154
	$link .= '"';
2155
	if ($target) $link .= ' target="'.$target.'"';
2156
	$link .= '>';
2157
	if (!preg_match('/^http/i', $url)) $link .= 'http://';
2158
	$link .= dol_trunc($url, $max);
2159
	$link .= '</a>';
2160
	return '<div class="nospan float" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>';
2161
}
2162
2163
/**
2164
 * Show EMail link
2165
 *
2166
 * @param	string		$email			EMail to show (only email, without 'Name of recipient' before)
2167
 * @param 	int			$cid 			Id of contact if known
2168
 * @param 	int			$socid 			Id of third party if known
2169
 * @param 	int			$addlink		0=no link, 1=email has a html email link (+ link to create action if constant AGENDA_ADDACTIONFOREMAIL is on)
2170
 * @param	int			$max			Max number of characters to show
2171
 * @param	int			$showinvalid	1=Show warning if syntax email is wrong
2172
 * @param	int			$withpicto		Show picto
2173
 * @return	string						HTML Link
2174
 */
2175
function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
2176
{
2177
	global $conf, $user, $langs, $hookmanager;
2178
2179
	$newemail = $email;
2180
2181
	if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) $withpicto = 0;
2182
2183
	if (empty($email)) return '&nbsp;';
2184
2185
	if (!empty($addlink))
2186
	{
2187
		$newemail = '<a style="text-overflow: ellipsis;" href="';
2188
		if (!preg_match('/^mailto:/i', $email)) $newemail .= 'mailto:';
2189
		$newemail .= $email;
2190
		$newemail .= '">';
2191
		$newemail .= dol_trunc($email, $max);
2192
		$newemail .= '</a>';
2193
		if ($showinvalid && !isValidEmail($email))
2194
		{
2195
			$langs->load("errors");
2196
			$newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
2197
		}
2198
2199
		if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2200
		{
2201
			$type = 'AC_EMAIL'; $link = '';
2202
			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>';
2203
			if ($link) $newemail = '<div>'.$newemail.' '.$link.'</div>';
2204
		}
2205
	} else {
2206
		if ($showinvalid && !isValidEmail($email))
2207
		{
2208
			$langs->load("errors");
2209
			$newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
2210
		}
2211
	}
2212
2213
	//$rep = '<div class="nospan" style="margin-right: 10px">';
2214
	$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, 'object_email.png').' ' : '').$newemail;
2215
	//$rep .= '</div>';
2216
	if ($hookmanager) {
2217
		$parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
2218
		$reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
2219
		if ($reshook > 0) {
2220
			$rep = '';
2221
		}
2222
		$rep .= $hookmanager->resPrint;
2223
	}
2224
2225
	return $rep;
2226
}
2227
2228
/**
2229
 * Get array of social network dictionary
2230
 *
2231
 * @return  array       Array of Social Networks Dictionary
2232
 */
2233
function getArrayOfSocialNetworks()
2234
{
2235
	global $conf, $db;
2236
	$sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
2237
	$sql .= " WHERE entity=".$conf->entity;
2238
	$socialnetworks = array();
2239
	$resql = $db->query($sql);
2240
	if ($resql) {
2241
		while ($obj = $db->fetch_object($resql)) {
2242
			$socialnetworks[$obj->code] = array(
2243
				'rowid' => $obj->rowid,
2244
				'label' => $obj->label,
2245
				'url' => $obj->url,
2246
				'icon' => $obj->icon,
2247
				'active' => $obj->active,
2248
			);
2249
		}
2250
	}
2251
	return $socialnetworks;
2252
}
2253
2254
/**
2255
 * Show social network link
2256
 *
2257
 * @param	string		$value			Skype to show (only skype, without 'Name of recipient' before)
2258
 * @param	int 		$cid 			Id of contact if known
2259
 * @param	int 		$socid 			Id of third party if known
2260
 * @param	string 		$type			'skype','facebook',...
2261
 * @return	string						HTML Link
2262
 */
2263
function dol_print_socialnetworks($value, $cid, $socid, $type)
2264
{
2265
	global $conf, $user, $langs;
2266
2267
	$htmllink = $value;
2268
2269
	if (empty($value)) return '&nbsp;';
2270
2271
	if (!empty($type))
2272
	{
2273
		$htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
2274
		$htmllink .= img_picto($langs->trans(strtoupper($type)), $type.'.png', '', false, 0, 0, '', 'paddingright', 0);
2275
		$htmllink .= $value;
2276
		if ($type == 'skype')
2277
		{
2278
			$htmllink .= '&nbsp;';
2279
			$htmllink .= '<a href="skype:';
2280
			$htmllink .= $value;
2281
			$htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.$langs->trans("Call").'&nbsp;'.$value.'">';
2282
			$htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
2283
			$htmllink .= '</a><a href="skype:';
2284
			$htmllink .= $value;
2285
			$htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.$langs->trans("Chat").'&nbsp;'.$value.'">';
2286
			$htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
2287
			$htmllink .= '</a>';
2288
		}
2289
		if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create && $type == 'skype')
2290
		{
2291
			$addlink = 'AC_SKYPE';
2292
			$link = '';
2293
			if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) $link = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.$addlink.'&amp;contactid='.$cid.'&amp;socid='.$socid.'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
2294
			$htmllink .= ($link ? ' '.$link : '');
2295
		}
2296
		$htmllink .= '</div>';
2297
	} else {
2298
		$langs->load("errors");
2299
		$htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
2300
	}
2301
	return $htmllink;
2302
}
2303
2304
/**
2305
 * 	Format phone numbers according to country
2306
 *
2307
 * 	@param  string  $phone          Phone number to format
2308
 * 	@param  string  $countrycode    Country code to use for formatting
2309
 * 	@param 	int		$cid 		    Id of contact if known
2310
 * 	@param 	int		$socid          Id of third party if known
2311
 * 	@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)
2312
 * 	@param 	string	$separ 		    Separation between numbers for a better visibility example : xx.xx.xx.xx.xx
2313
 *  @param	string  $withpicto      Show picto
2314
 *  @param	string	$titlealt	    Text to show on alt
2315
 *  @param  int     $adddivfloat    Add div float around phone.
2316
 * 	@return string 				    Formated phone number
2317
 */
2318
function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
2319
{
2320
	global $conf, $user, $langs, $mysoc, $hookmanager;
2321
2322
	// Clean phone parameter
2323
	$phone = preg_replace("/[\s.-]/", "", trim($phone));
2324
	if (empty($phone)) { return ''; }
2325
	if ($conf->global->MAIN_PHONE_SEPAR) $separ = $conf->global->MAIN_PHONE_SEPAR;
2326
	if (empty($countrycode)) $countrycode = $mysoc->country_code;
2327
2328
	// Short format for small screens
2329
	if ($conf->dol_optimize_smallscreen) $separ = '';
2330
2331
	$newphone = $phone;
2332
	if (strtoupper($countrycode) == "FR")
2333
	{
2334
		// France
2335
		if (dol_strlen($phone) == 10) {
2336
			$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);
2337
		} elseif (dol_strlen($phone) == 7)
2338
		{
2339
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
2340
		} elseif (dol_strlen($phone) == 9)
2341
		{
2342
			$newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
2343
		} elseif (dol_strlen($phone) == 11)
2344
		{
2345
			$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);
2346
		} elseif (dol_strlen($phone) == 12)
2347
		{
2348
			$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);
2349
		}
2350
	} elseif (strtoupper($countrycode) == "CA")
2351
	{
2352
		if (dol_strlen($phone) == 10) {
2353
			$newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
2354
		}
2355
	} elseif (strtoupper($countrycode) == "PT")
2356
	{//Portugal
2357
		if (dol_strlen($phone) == 13)
2358
		{//ex: +351_ABC_DEF_GHI
2359
			$newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
2360
		}
2361
	} elseif (strtoupper($countrycode) == "SR")
2362
	{//Suriname
2363
		if (dol_strlen($phone) == 10)
2364
		{//ex: +597_ABC_DEF
2365
			$newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
2366
		} elseif (dol_strlen($phone) == 11)
2367
		{//ex: +597_ABC_DEFG
2368
			$newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
2369
		}
2370
	} elseif (strtoupper($countrycode) == "DE")
2371
	{//Allemagne
2372
		if (dol_strlen($phone) == 14)
2373
		{//ex:  +49_ABCD_EFGH_IJK
2374
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
2375
		} elseif (dol_strlen($phone) == 13)
2376
		{//ex: +49_ABC_DEFG_HIJ
2377
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
2378
		}
2379
	} elseif (strtoupper($countrycode) == "ES")
2380
	{//Espagne
2381
		if (dol_strlen($phone) == 12)
2382
		{//ex:  +34_ABC_DEF_GHI
2383
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
2384
		}
2385
	} elseif (strtoupper($countrycode) == "BF")
2386
	{// Burkina Faso
2387
		if (dol_strlen($phone) == 12)
2388
		{//ex :  +22 A BC_DE_FG_HI
2389
			$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);
2390
		}
2391
	} elseif (strtoupper($countrycode) == "RO")
2392
	{// Roumanie
2393
		if (dol_strlen($phone) == 12)
2394
		{//ex :  +40 AB_CDE_FG_HI
2395
			$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);
2396
		}
2397
	} elseif (strtoupper($countrycode) == "TR")
2398
	{//Turquie
2399
		if (dol_strlen($phone) == 13)
2400
		{//ex :  +90 ABC_DEF_GHIJ
2401
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
2402
		}
2403
	} elseif (strtoupper($countrycode) == "US")
2404
	{//Etat-Unis
2405
		if (dol_strlen($phone) == 12)
2406
		{//ex: +1 ABC_DEF_GHIJ
2407
			$newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
2408
		}
2409
	} elseif (strtoupper($countrycode) == "MX")
2410
	{//Mexique
2411
		if (dol_strlen($phone) == 12)
2412
		{//ex: +52 ABCD_EFG_HI
2413
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
2414
		} elseif (dol_strlen($phone) == 11)
2415
		{//ex: +52 AB_CD_EF_GH
2416
			$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);
2417
		} elseif (dol_strlen($phone) == 13)
2418
		{//ex: +52 ABC_DEF_GHIJ
2419
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
2420
		}
2421
	} elseif (strtoupper($countrycode) == "ML")
2422
	{//Mali
2423
		if (dol_strlen($phone) == 12)
2424
		{//ex: +223 AB_CD_EF_GH
2425
			$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);
2426
		}
2427
	} elseif (strtoupper($countrycode) == "TH")
2428
	{//Thaïlande
2429
		if (dol_strlen($phone) == 11)
2430
		{//ex: +66_ABC_DE_FGH
2431
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
2432
		} elseif (dol_strlen($phone) == 12)
2433
		{//ex: +66_A_BCD_EF_GHI
2434
			$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);
2435
		}
2436
	} elseif (strtoupper($countrycode) == "MU")
2437
	{
2438
		//Maurice
2439
		if (dol_strlen($phone) == 11)
2440
		{//ex: +230_ABC_DE_FG
2441
			$newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
2442
		} elseif (dol_strlen($phone) == 12)
2443
		{//ex: +230_ABCD_EF_GH
2444
			$newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
2445
		}
2446
	} elseif (strtoupper($countrycode) == "ZA")
2447
	{//Afrique du sud
2448
		if (dol_strlen($phone) == 12)
2449
		{//ex: +27_AB_CDE_FG_HI
2450
			$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);
2451
		}
2452
	} elseif (strtoupper($countrycode) == "SY")
2453
	{//Syrie
2454
		if (dol_strlen($phone) == 12)
2455
		{//ex: +963_AB_CD_EF_GH
2456
			$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);
2457
		} elseif (dol_strlen($phone) == 13)
2458
		{//ex: +963_AB_CD_EF_GHI
2459
			$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);
2460
		}
2461
	} elseif (strtoupper($countrycode) == "AE")
2462
	{//Emirats Arabes Unis
2463
		if (dol_strlen($phone) == 12)
2464
		{//ex: +971_ABC_DEF_GH
2465
			$newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
2466
		} elseif (dol_strlen($phone) == 13)
2467
		{//ex: +971_ABC_DEF_GHI
2468
			$newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
2469
		} elseif (dol_strlen($phone) == 14)
2470
		{//ex: +971_ABC_DEF_GHIK
2471
			$newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
2472
		}
2473
	} elseif (strtoupper($countrycode) == "DZ")
2474
	{//Algérie
2475
		if (dol_strlen($phone) == 13)
2476
		{//ex: +213_ABC_DEF_GHI
2477
			$newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
2478
		}
2479
	} elseif (strtoupper($countrycode) == "BE")
2480
	{//Belgique
2481
		if (dol_strlen($phone) == 11)
2482
		{//ex: +32_ABC_DE_FGH
2483
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
2484
		} elseif (dol_strlen($phone) == 12)
2485
		{//ex: +32_ABC_DEF_GHI
2486
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
2487
		}
2488
	} elseif (strtoupper($countrycode) == "PF")
2489
	{//Polynésie française
2490
		if (dol_strlen($phone) == 12)
2491
		{//ex: +689_AB_CD_EF_GH
2492
			$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);
2493
		}
2494
	} elseif (strtoupper($countrycode) == "CO")
2495
	{//Colombie
2496
		if (dol_strlen($phone) == 13)
2497
		{//ex: +57_ABC_DEF_GH_IJ
2498
			$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);
2499
		}
2500
	} elseif (strtoupper($countrycode) == "JO")
2501
	{//Jordanie
2502
		if (dol_strlen($phone) == 12)
2503
		{//ex: +962_A_BCD_EF_GH
2504
			$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);
2505
		}
2506
	} elseif (strtoupper($countrycode) == "JM")
2507
	{//Jamaïque
2508
		if (dol_strlen($newphone) == 12)
2509
		{//ex: +1867_ABC_DEFG
2510
			$newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
2511
		}
2512
	} elseif (strtoupper($countrycode) == "MG")
2513
	{//Madagascar
2514
		if (dol_strlen($phone) == 13)
2515
		{//ex: +261_AB_CD_EF_GHI
2516
			$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);
2517
		}
2518
	} elseif (strtoupper($countrycode) == "GB")
2519
	{//Royaume uni
2520
		if (dol_strlen($phone) == 13)
2521
		{//ex: +44_ABCD_EFG_HIJ
2522
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
2523
		}
2524
	} elseif (strtoupper($countrycode) == "CH")
2525
	{//Suisse
2526
		if (dol_strlen($phone) == 12)
2527
		{//ex: +41_AB_CDE_FG_HI
2528
			$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);
2529
		} elseif (dol_strlen($phone) == 15)
2530
		{// +41_AB_CDE_FGH_IJKL
2531
			$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);
2532
		}
2533
	} elseif (strtoupper($countrycode) == "TN")
2534
	{//Tunisie
2535
		if (dol_strlen($phone) == 12)
2536
		{//ex: +216_AB_CDE_FGH
2537
			$newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
2538
		}
2539
	} elseif (strtoupper($countrycode) == "GF")
2540
	{//Guyane francaise
2541
		if (dol_strlen($phone) == 13)
2542
		{//ex: +594_ABC_DE_FG_HI  (ABC=594 de nouveau)
2543
			$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);
2544
		}
2545
	} elseif (strtoupper($countrycode) == "GP")
2546
	{//Guadeloupe
2547
		if (dol_strlen($phone) == 13)
2548
		{//ex: +590_ABC_DE_FG_HI  (ABC=590 de nouveau)
2549
			$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);
2550
		}
2551
	} elseif (strtoupper($countrycode) == "MQ")
2552
	{//Martinique
2553
		if (dol_strlen($phone) == 13)
2554
		{//ex: +596_ABC_DE_FG_HI  (ABC=596 de nouveau)
2555
			$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);
2556
		}
2557
	} elseif (strtoupper($countrycode) == "IT")
2558
	{//Italie
2559
		if (dol_strlen($phone) == 12)
2560
		{//ex: +39_ABC_DEF_GHI
2561
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
2562
		} elseif (dol_strlen($phone) == 13)
2563
		{//ex: +39_ABC_DEF_GH_IJ
2564
			$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);
2565
		}
2566
	} elseif (strtoupper($countrycode) == "AU")
2567
	{
2568
		//Australie
2569
		if (dol_strlen($phone) == 12)
2570
		{
2571
			//ex: +61_A_BCDE_FGHI
2572
			$newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
2573
		}
2574
	}
2575
	if (!empty($addlink))	// Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
2576
	{
2577
		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
2578
		{
2579
			$newphoneform = $newphone;
2580
			$newphone = '<a href="tel:'.$phone.'"';
2581
			$newphone .= '>'.$newphoneform.'</a>';
2582
		} elseif (!empty($conf->clicktodial->enabled) && $addlink == 'AC_TEL')		// If click to dial, we use click to dial url
2583
		{
2584
			if (empty($user->clicktodial_loaded)) $user->fetch_clicktodial();
2585
2586
			// Define urlmask
2587
			$urlmask = 'ErrorClickToDialModuleNotConfigured';
2588
			if (!empty($conf->global->CLICKTODIAL_URL)) $urlmask = $conf->global->CLICKTODIAL_URL;
2589
			if (!empty($user->clicktodial_url)) $urlmask = $user->clicktodial_url;
2590
2591
			$clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : '');
2592
			$clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : '');
2593
			$clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : '');
2594
			// This line is for backward compatibility
2595
			$url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
2596
			// Thoose lines are for substitution
2597
			$substitarray = array('__PHONEFROM__'=>$clicktodial_poste,
2598
								'__PHONETO__'=>urlencode($phone),
2599
								'__LOGIN__'=>$clicktodial_login,
2600
								'__PASS__'=>$clicktodial_password);
2601
			$url = make_substitutions($url, $substitarray);
2602
			$newphonesav = $newphone;
2603
			$newphone = '<a href="'.$url.'"';
2604
			if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) $newphone .= ' target="_blank"';
2605
			$newphone .= '>'.$newphonesav.'</a>';
2606
		}
2607
2608
		//if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2609
		if (!empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2610
		{
2611
			$type = 'AC_TEL'; $link = '';
2612
			if ($addlink == 'AC_FAX') $type = 'AC_FAX';
2613
			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>';
2614
			if ($link) $newphone = '<div>'.$newphone.' '.$link.'</div>';
2615
		}
2616
	}
2617
2618
	if (empty($titlealt))
2619
	{
2620
		$titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
2621
	}
2622
	$rep = '';
2623
2624
	if ($hookmanager) {
2625
		$parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
2626
		$reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
2627
		$rep .= $hookmanager->resPrint;
2628
	}
2629
	if (empty($reshook))
2630
	{
2631
		$picto = '';
2632
		if ($withpicto) {
2633
			if ($withpicto == 'fax') {
2634
				$picto = 'phoning_fax';
2635
			} elseif ($withpicto == 'phone') {
2636
				$picto = 'phoning';
2637
			} elseif ($withpicto == 'mobile') {
2638
				$picto = 'phoning_mobile';
2639
			} else {
2640
				$picto = '';
2641
			}
2642
		}
2643
		if ($adddivfloat) $rep .= '<div class="nospan float" style="margin-right: 10px">';
2644
		else $rep .= '<span style="margin-right: 10px;">';
2645
		$rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone;
2646
		if ($adddivfloat) $rep .= '</div>';
2647
		else $rep .= '</span>';
2648
	}
2649
2650
	return $rep;
2651
}
2652
2653
/**
2654
 * 	Return an IP formated to be shown on screen
2655
 *
2656
 * 	@param	string	$ip			IP
2657
 * 	@param	int		$mode		0=return IP + country/flag, 1=return only country/flag, 2=return only IP
2658
 * 	@return string 				Formated IP, with country if GeoIP module is enabled
2659
 */
2660
function dol_print_ip($ip, $mode = 0)
2661
{
2662
	global $conf, $langs;
2663
2664
	$ret = '';
2665
2666
	if (empty($mode)) $ret .= $ip;
2667
2668
	if ($mode != 2)
2669
	{
2670
		$countrycode = dolGetCountryCodeFromIp($ip);
2671
		if ($countrycode)	// If success, countrycode is us, fr, ...
2672
		{
2673
			if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png'))
2674
			{
2675
				$ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
2676
			} else $ret .= ' ('.$countrycode.')';
2677
		} else {
2678
			// Nothing
2679
		}
2680
	}
2681
2682
	return $ret;
2683
}
2684
2685
/**
2686
 * Return the IP of remote user.
2687
 * Take HTTP_X_FORWARDED_FOR (defined when using proxy)
2688
 * Then HTTP_CLIENT_IP if defined (rare)
2689
 * Then REMOTE_ADDR (no way to be modified by user but may be wrong if user is using a proxy)
2690
 *
2691
 * @return	string		Ip of remote user.
2692
 */
2693
function getUserRemoteIP()
2694
{
2695
	$ip = empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? (empty($_SERVER['HTTP_CLIENT_IP']) ? (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']) : $_SERVER['HTTP_CLIENT_IP']) : $_SERVER['HTTP_X_FORWARDED_FOR'];
2696
	return $ip;
2697
}
2698
2699
/**
2700
 * 	Return a country code from IP. Empty string if not found.
2701
 *
2702
 * 	@param	string	$ip			IP
2703
 * 	@return string 				Country code ('us', 'fr', ...)
2704
 */
2705
function dolGetCountryCodeFromIp($ip)
2706
{
2707
	global $conf;
2708
2709
	$countrycode = '';
2710
2711
	if (!empty($conf->geoipmaxmind->enabled))
2712
	{
2713
		$datafile = $conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
2714
		//$ip='24.24.24.24';
2715
		//$datafile='/usr/share/GeoIP/GeoIP.dat';    Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
2716
		include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
2717
		$geoip = new DolGeoIP('country', $datafile);
2718
		//print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
2719
		$countrycode = $geoip->getCountryCodeFromIP($ip);
2720
	}
2721
2722
	return $countrycode;
2723
}
2724
2725
2726
/**
2727
 *  Return country code for current user.
2728
 *  If software is used inside a local network, detection may fails (we need a public ip)
2729
 *
2730
 *  @return     string      Country code (fr, es, it, us, ...)
2731
 */
2732
function dol_user_country()
2733
{
2734
	global $conf, $langs, $user;
2735
2736
	//$ret=$user->xxx;
2737
	$ret = '';
2738
	if (!empty($conf->geoipmaxmind->enabled))
2739
	{
2740
		$ip = getUserRemoteIP();
2741
		$datafile = $conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
2742
		//$ip='24.24.24.24';
2743
		//$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
2744
		include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
2745
		$geoip = new DolGeoIP('country', $datafile);
2746
		$countrycode = $geoip->getCountryCodeFromIP($ip);
2747
		$ret = $countrycode;
2748
	}
2749
	return $ret;
2750
}
2751
2752
/**
2753
 *  Format address string
2754
 *
2755
 *  @param	string	$address    Address string, already formatted with dol_format_address()
2756
 *  @param  int		$htmlid     Html ID (for example 'gmap')
2757
 *  @param  int		$element    'thirdparty'|'contact'|'member'|'other'
2758
 *  @param  int		$id         Id of object
2759
 *  @param	int		$noprint	No output. Result is the function return
2760
 *  @param  string  $charfornl  Char to use instead of nl2br. '' means we use a standad nl2br.
2761
 *  @return string|void			Nothing if noprint is 0, formatted address if noprint is 1
2762
 *  @see dol_format_address()
2763
 */
2764
function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
2765
{
2766
	global $conf, $user, $langs, $hookmanager;
2767
2768
	$out = '';
2769
2770
	if ($address)
2771
	{
2772
		if ($hookmanager) {
2773
			$parameters = array('element' => $element, 'id' => $id);
2774
			$reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
2775
			$out .= $hookmanager->resPrint;
2776
		}
2777
		if (empty($reshook))
2778
		{
2779
			if (empty($charfornl)) $out .= nl2br($address);
2780
			else $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
2781
2782
			// TODO Remove this block, we can add this using the hook now
2783
			$showgmap = $showomap = 0;
2784
			if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) $showgmap = 1;
2785
			if ($element == 'contact' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) $showgmap = 1;
2786
			if ($element == 'member' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) $showgmap = 1;
2787
			if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) $showomap = 1;
2788
			if ($element == 'contact' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) $showomap = 1;
2789
			if ($element == 'member' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) $showomap = 1;
2790
			if ($showgmap)
2791
			{
2792
				$url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
2793
				$out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
2794
			}
2795
			if ($showomap)
2796
			{
2797
				$url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
2798
				$out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
2799
			}
2800
		}
2801
	}
2802
	if ($noprint) return $out;
2803
	else print $out;
2804
}
2805
2806
2807
/**
2808
 *	Return true if email syntax is ok
2809
 *
2810
 *	@param	    string		$address    			email (Ex: "[email protected]", "John Do <[email protected]>")
2811
 *  @param		int			$acceptsupervisorkey	If 1, the special string '__SUPERVISOREMAIL__' is also accepted as valid
2812
 *	@return     boolean     						true if email syntax is OK, false if KO or empty string
2813
 */
2814
function isValidEmail($address, $acceptsupervisorkey = 0)
2815
{
2816
	if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') return true;
2817
	if (filter_var($address, FILTER_VALIDATE_EMAIL)) return true;
2818
2819
	return false;
2820
}
2821
2822
/**
2823
 *	Return if the domain name has a valid MX record.
2824
 *  WARNING: This need function idn_to_ascii, checkdnsrr and getmxrr
2825
 *
2826
 *	@param	    string		$domain	    			Domain name (Ex: "yahoo.com", "yhaoo.com", "dolibarr.fr")
2827
 *	@return     int     							-1 if error (function not available), 0=Not valid, 1=Valid
2828
 */
2829
function isValidMXRecord($domain)
2830
{
2831
	if (function_exists('idn_to_ascii') && function_exists('checkdnsrr'))
2832
	{
2833
		if (!checkdnsrr(idn_to_ascii($domain), 'MX'))
2834
		{
2835
			return 0;
2836
		}
2837
		if (function_exists('getmxrr'))
2838
		{
2839
			$mxhosts = array();
2840
			$weight = array();
2841
			getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
2842
			if (count($mxhosts) > 1) return 1;
2843
			if (count($mxhosts) == 1 && !empty($mxhosts[0])) return 1;
2844
2845
			return 0;
2846
		}
2847
	}
2848
	return -1;
2849
}
2850
2851
/**
2852
 *  Return true if phone number syntax is ok
2853
 *  TODO Decide what to do with this
2854
 *
2855
 *  @param	string		$phone		phone (Ex: "0601010101")
2856
 *  @return boolean     			true if phone syntax is OK, false if KO or empty string
2857
 */
2858
function isValidPhone($phone)
2859
{
2860
	return true;
2861
}
2862
2863
2864
/**
2865
 * Make a strlen call. Works even if mbstring module not enabled
2866
 *
2867
 * @param   string		$string				String to calculate length
2868
 * @param   string		$stringencoding		Encoding of string
2869
 * @return  int								Length of string
2870
 */
2871
function dol_strlen($string, $stringencoding = 'UTF-8')
2872
{
2873
	if (function_exists('mb_strlen')) return mb_strlen($string, $stringencoding);
2874
	else return strlen($string);
2875
}
2876
2877
/**
2878
 * Make a substring. Works even if mbstring module is not enabled for better compatibility.
2879
 *
2880
 * @param	string	$string				String to scan
2881
 * @param	string	$start				Start position
2882
 * @param	int		$length				Length (in nb of characters or nb of bytes depending on trunconbytes param)
2883
 * @param   string	$stringencoding		Page code used for input string encoding
2884
 * @param	int		$trunconbytes		1=Length is max of bytes instead of max of characters
2885
 * @return  string						substring
2886
 */
2887
function dol_substr($string, $start, $length, $stringencoding = '', $trunconbytes = 0)
2888
{
2889
	global $langs;
2890
2891
	if (empty($stringencoding)) $stringencoding = $langs->charset_output;
2892
2893
	$ret = '';
2894
	if (empty($trunconbytes))
2895
	{
2896
		if (function_exists('mb_substr'))
2897
		{
2898
			$ret = mb_substr($string, $start, $length, $stringencoding);
2899
		} else {
2900
			$ret = substr($string, $start, $length);
2901
		}
2902
	} else {
2903
		if (function_exists('mb_strcut'))
2904
		{
2905
			$ret = mb_strcut($string, $start, $length, $stringencoding);
2906
		} else {
2907
			$ret = substr($string, $start, $length);
2908
		}
2909
	}
2910
	return $ret;
2911
}
2912
2913
2914
/**
2915
 *	Truncate a string to a particular length adding '...' if string larger than length.
2916
 * 	If length = max length+1, we do no truncate to avoid having just 1 char replaced with '...'.
2917
 *  MAIN_DISABLE_TRUNC=1 can disable all truncings
2918
 *
2919
 *	@param	string	$string				String to truncate
2920
 *	@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 ...)
2921
 *	@param	string	$trunc				Where to trunc: right, left, middle (size must be a 2 power), wrap
2922
 * 	@param	string	$stringencoding		Tell what is source string encoding
2923
 *  @param	int		$nodot				Truncation do not add ... after truncation. So it's an exact truncation.
2924
 *  @param  int     $display            Trunc is used to display data and can be changed for small screen. TODO Remove this param (must be dealt with CSS)
2925
 *	@return string						Truncated string. WARNING: length is never higher than $size if $nodot is set, but can be 3 chars higher otherwise.
2926
 */
2927
function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
2928
{
2929
	global $conf;
2930
2931
	if ($size == 0 || !empty($conf->global->MAIN_DISABLE_TRUNC)) return $string;
2932
2933
	if (empty($stringencoding)) $stringencoding = 'UTF-8';
2934
	// reduce for small screen
2935
	if ($conf->dol_optimize_smallscreen == 1 && $display == 1) $size = round($size / 3);
2936
2937
	// We go always here
2938
	if ($trunc == 'right')
2939
	{
2940
		$newstring = dol_textishtml($string) ?dol_string_nohtmltag($string, 1) : $string;
2941
		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 ...
2942
		return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '...');
2943
		else //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
2944
		return $string;
2945
	} elseif ($trunc == 'middle')
2946
	{
2947
		$newstring = dol_textishtml($string) ?dol_string_nohtmltag($string, 1) : $string;
2948
		if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1))
2949
		{
2950
			$size1 = round($size / 2);
2951
			$size2 = round($size / 2);
2952
			return dol_substr($newstring, 0, $size1, $stringencoding).'...'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
2953
		} else return $string;
2954
	} elseif ($trunc == 'left')
2955
	{
2956
		$newstring = dol_textishtml($string) ?dol_string_nohtmltag($string, 1) : $string;
2957
		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 ...
2958
		return '...'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
2959
		else return $string;
2960
	} elseif ($trunc == 'wrap')
2961
	{
2962
		$newstring = dol_textishtml($string) ?dol_string_nohtmltag($string, 1) : $string;
2963
		if (dol_strlen($newstring, $stringencoding) > ($size + 1))
2964
		return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
2965
		else return $string;
2966
	} else return 'BadParam3CallingDolTrunc';
2967
}
2968
2969
/**
2970
 *	Show picto whatever it's its name (generic function)
2971
 *
2972
 *	@param      string		$titlealt         		Text on title tag for tooltip. Not used if param notitle is set to 1.
2973
 *	@param      string		$picto       			Name of image file to show ('filenew', ...)
2974
 *													If no extension provided, we use '.png'. Image must be stored into theme/xxx/img directory.
2975
 *                                  				Example: picto.png                  if picto.png is stored into htdocs/theme/mytheme/img
2976
 *                                  				Example: picto.png@mymodule         if picto.png is stored into htdocs/mymodule/img
2977
 *                                  				Example: /mydir/mysubdir/picto.png  if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1)
2978
 *	@param		string		$moreatt				Add more attribute on img tag (For example 'style="float: right"')
2979
 *	@param		boolean|int	$pictoisfullpath		If true or 1, image path is a full path
2980
 *	@param		int			$srconly				Return only content of the src attribute of img.
2981
 *  @param		int			$notitle				1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip.
2982
 *  @param		string		$alt					Force alt for bind people
2983
 *  @param		string		$morecss				Add more class css on img tag (For example 'myclascss').
2984
 *  @param		string		$marginleftonlyshort	1 = Add a short left margin on picto, 2 = Add a larger left margin on picto, 0 = No margin left. Works for fontawesome picto only.
2985
 *  @return     string       				    	Return img tag
2986
 *  @see        img_object(), img_picto_common()
2987
 */
2988
function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
2989
{
2990
	global $conf, $langs;
2991
2992
	// We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
2993
	$url = DOL_URL_ROOT;
2994
	$theme = $conf->theme;
2995
	$path = 'theme/'.$theme;
2996
2997
	// Define fullpathpicto to use into src
2998
	if ($pictoisfullpath) {
2999
		// Clean parameters
3000
		if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
3001
			$picto .= '.png';
3002
		}
3003
		$fullpathpicto = $picto;
3004
		$reg = array();
3005
		if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
3006
			$morecss .= ($morecss ? ' ' : '').$reg[1];
3007
			$moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
3008
		}
3009
	} else {
3010
		$pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
3011
		if (empty($srconly) && in_array($pictowithouttext, array(
3012
				'1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
3013
				'accountancy', 'account', 'accountline', 'action', 'add', 'address', 'bank_account', 'barcode', 'bank', 'bill', 'bookmark', 'bom', 'building',
3014
				'cash-register', 'category', 'check', 'clock', 'close_title', 'company', 'contact', 'contract', 'cubes',
3015
				'delete', 'dolly', 'dollyrevert', 'edit', 'ellipsis-h', 'email', 'external-link-alt', 'external-link-square-alt',
3016
				'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'folder', 'folder-open', 'globe', 'globe-americas', 'grip', 'grip_title',
3017
				'help', 'holiday',
3018
				'intervention', 'label', 'language', 'list', 'listlight', 'lot',
3019
				'map-marker-alt', 'money-bill-alt', 'mrp', 'note',
3020
				'object_accounting', 'object_account', 'object_accountline', 'object_action', 'object_barcode', 'object_bill', 'object_billa', 'object_billd', 'object_bom',
3021
				'object_category', 'object_conversation', 'object_bookmark', 'object_bug', 'object_dolly', 'object_dollyrevert', 'object_generic', 'object_folder',
3022
				'object_list-alt', 'object_calendar', 'object_calendarweek', 'object_calendarmonth', 'object_calendarday', 'object_calendarperuser',
3023
				'object_cash-register', 'object_company', 'object_contact', 'object_contract', 'object_donation', 'object_dynamicprice',
3024
				'object_globe', 'object_holiday', 'object_hrm', 'object_invoice', 'object_intervention', 'object_label',
3025
				'object_margin', 'object_money-bill-alt', 'object_multicurrency', 'object_order', 'object_payment',
3026
				'object_lot', 'object_mrp', 'object_payment', 'object_product', 'object_propal',
3027
				'object_other', 'object_paragraph', 'object_poll', 'object_printer', 'object_project', 'object_projectpub', 'object_propal', 'object_resource', 'object_rss', 'object_projecttask',
3028
				'object_recruitmentjobposition', 'object_recruitmentcandidature',
3029
				'object_shipment', 'object_share-alt', 'object_supplier_invoice', 'object_supplier_invoicea', 'object_supplier_invoiced', 'object_supplier_order', 'object_supplier_proposal', 'object_service', 'object_stock',
3030
				'object_technic', 'object_ticket', 'object_trip', 'object_user', 'object_group', 'object_member',
3031
				'object_phoning', 'object_phoning_mobile', 'object_phoning_fax', 'object_email', 'object_website',
3032
				'off', 'on', 'order',
3033
				'paiment', 'play', 'playdisabled', 'poll', 'printer', 'product', 'propal', 'projecttask', 'stock', 'resize', 'service', 'stats', 'trip',
3034
				'setup', 'share-alt', 'sign-out', 'split', 'stripe-s', 'switch_off', 'switch_on', 'tools', 'unlink', 'uparrow', 'user', 'vcard', 'wrench',
3035
				'jabber', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
3036
				'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
3037
				'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
3038
				'payment', 'pencil-ruler', 'preview', 'project', 'projectpub', 'refresh', 'supplier_invoice', 'ticket',
3039
				'error', 'warning',
3040
				'recruitmentcandidature', 'recruitmentjobposition', 'resource',
3041
				'supplier_proposal', 'supplier_order', 'supplier_invoice',
3042
				'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda'
3043
			)
3044
		)) {
3045
			$fakey = $pictowithouttext;
3046
			$facolor = ''; $fasize = '';
3047
			$fa = 'fas';
3048
			if (in_array($pictowithouttext, array('clock', 'object_generic', 'note', 'off', 'on', 'object_bookmark', 'bookmark', 'vcard'))) {
3049
				$fa = 'far';
3050
			}
3051
			if (in_array($pictowithouttext, array('black-tie', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
3052
				$fa = 'fab';
3053
			}
3054
3055
			$pictowithouttext = str_replace('object_', '', $pictowithouttext);
3056
3057
			$arrayconvpictotofa = array(
3058
				'account'=>'university', 'accountline'=>'receipt', 'accountancy'=>'money-check-alt', 'action'=>'calendar-alt', 'add'=>'plus-circle', 'address'=> 'address-book',
3059
				'bank_account'=>'university', 'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'supplier_invoicea'=>'file-excel', 'billd'=>'file-medical', 'supplier_invoiced'=>'file-medical', 'bom'=>'cubes',
3060
				'company'=>'building', 'contact'=>'address-book', 'contract'=>'suitcase', 'conversation'=>'comments', 'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd',
3061
				'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins',
3062
				'accounting'=>'chart-line', 'category'=>'tag', 'dollyrevert'=>'dolly',
3063
				'hrm'=>'user-tie', 'margin'=>'calculator', 'members'=>'users', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode',
3064
				'email'=>'at',
3065
				'edit'=>'pencil-alt', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle',
3066
				'generic'=>'file', 'holiday'=>'umbrella-beach', 'label'=>'layer-group',
3067
				'member'=>'users', 'mrp'=>'cubes', 'trip'=>'wallet', 'group'=>'users',
3068
				'sign-out'=>'sign-out-alt',
3069
				'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star', 'bookmark'=>'star',
3070
				'bank'=>'university', 'close_title'=>'window-close', 'delete'=>'trash', 'edit'=>'pencil-alt', 'filter'=>'filter',
3071
				'list-alt'=>'list-alt', 'calendar'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarmonth'=>'calendar-alt', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table',
3072
				'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice',
3073
				'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle',
3074
				'other'=>'square',
3075
				'playdisabled'=>'play', 'poll'=>'check-double', 'preview'=>'binoculars', 'project'=>'sitemap', 'projectpub'=>'sitemap', 'projecttask'=>'tasks', 'propal'=>'file-signature',
3076
				'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge',
3077
				'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature',
3078
				'payment'=>'money-check-alt', 'phoning'=>'phone', 'phoning_mobile'=>'mobile-alt', 'phoning_fax'=>'fax', 'printer'=>'print', 'product'=>'cube', 'service'=>'concierge-bell',
3079
				'refresh'=>'redo', 'resource'=>'laptop-house',
3080
				'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'supplier_invoice'=>'file-invoice-dollar', 'technic'=>'cogs', 'ticket'=>'ticket-alt',
3081
				'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach',
3082
				'title_agenda'=>'calendar-alt',
3083
				'uparrow'=>'mail-forward', 'vcard'=>'address-card',
3084
				'jabber'=>'comment-o',
3085
				'website'=>'globe-americas'
3086
			);
3087
			if ($pictowithouttext == 'off') {
3088
				$fakey = 'fa-square';
3089
				$fasize = '1.3em';
3090
			} elseif ($pictowithouttext == 'on') {
3091
				$fakey = 'fa-check-square';
3092
				$fasize = '1.3em';
3093
			} elseif ($pictowithouttext == 'listlight') {
3094
				$fakey = 'fa-download';
3095
				$marginleftonlyshort = 1;
3096
			} elseif ($pictowithouttext == 'printer') {
3097
				$fakey = 'fa-print';
3098
				$fasize = '1.2em';
3099
			} elseif ($pictowithouttext == 'note') {
3100
				$fakey = 'fa-sticky-note';
3101
				$marginleftonlyshort = 1;
3102
			} elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
3103
				$convertarray = array('1uparrow'=>'caret-up', '1downarrow'=>'caret-down', '1leftarrow'=>'caret-left', '1rightarrow'=>'caret-right', '1uparrow_selected'=>'caret-up', '1downarrow_selected'=>'caret-down', '1leftarrow_selected'=>'caret-left', '1rightarrow_selected'=>'caret-right');
3104
				$fakey = 'fa-'.$convertarray[$pictowithouttext];
3105
				if (preg_match('/selected/', $pictowithouttext)) $facolor = '#888';
3106
				$marginleftonlyshort = 1;
3107
			} elseif (!empty($arrayconvpictotofa[$pictowithouttext]))
3108
			{
3109
				$fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
3110
			} else {
3111
				$fakey = 'fa-'.$pictowithouttext;
3112
			}
3113
3114
			// Define $marginleftonlyshort
3115
			$arrayconvpictotomarginleftonly = array(
3116
				'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
3117
				'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_off',
3118
				'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
3119
			);
3120
			if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
3121
				$marginleftonlyshort = 0;
3122
			}
3123
3124
			// Add CSS
3125
			$arrayconvpictotomorcess = array(
3126
				'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account',
3127
				'bank_account'=>'bg-infobox-bank_account',
3128
				'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billd'=>'infobox-commande',
3129
				'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'conversation'=>'infobox-contrat',
3130
				'donation'=>'infobox-commande', 'dollyrevert'=>'flip', 'ecm'=>'infobox-action',
3131
				'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat',
3132
				'multicurrency'=>'infobox-bank_account',
3133
				'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account',
3134
				'order'=>'infobox-commande',
3135
				'user'=>'infobox-adherent', 'users'=>'infobox-adherent',
3136
				'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4',
3137
				'holiday'=>'infobox-holiday', 'invoice'=>'infobox-commande',
3138
				'payment'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'project'=>'infobox-project', 'projecttask'=>'infobox-project', 'propal'=>'infobox-propal',
3139
				'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent',
3140
				'resource'=>'infobox-action',
3141
				'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier',
3142
				'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal',
3143
				'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action',
3144
				//'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
3145
				'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode'
3146
			);
3147
			if (!empty($arrayconvpictotomorcess[$pictowithouttext])) {
3148
				$morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
3149
			}
3150
3151
			// Define $color
3152
			$arrayconvpictotocolor = array(
3153
				'address'=>'#aaa', 'building'=>'#aaa', 'bom'=>'#a69944',
3154
				'companies'=>'#aaa', 'company'=>'#aaa', 'contact'=>'#37a', 'dynamicprice'=>'#a69944',
3155
				'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999',
3156
				'dolly'=>'#a69944', 'dollyrevert'=>'#a69944', 'lot'=>'#a69944',
3157
				'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'stock'=>'#a69944',
3158
				'other'=>'#ddd',
3159
				'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'resize'=>'#444', 'rss'=>'#cba',
3160
				'shipment'=>'#a69944', 'stats'=>'#444', 'switch_off'=>'#999', 'uparrow'=>'#555', 'globe-americas'=>'#aaa',
3161
				'website'=>'#304'
3162
			);
3163
			if (isset($arrayconvpictotocolor[$pictowithouttext])) {
3164
				$facolor = $arrayconvpictotocolor[$pictowithouttext];
3165
			}
3166
3167
			// This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
3168
			// class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
3169
			$reg = array();
3170
			if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
3171
				$morecss .= ($morecss ? ' ' : '').$reg[1];
3172
				$moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
3173
			}
3174
			if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
3175
				$morestyle = ' '.$reg[1];
3176
				$moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
3177
			}
3178
			$moreatt = trim($moreatt);
3179
3180
			$enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
3181
			$enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $morestyle does not seem to be defined for all execution paths leading up to this point.
Loading history...
3182
			if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3183
				$enabledisablehtml .= $titlealt;
3184
			}
3185
			$enabledisablehtml .= '</span>';
3186
3187
			return $enabledisablehtml;
3188
		}
3189
3190
		if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) {
3191
			$path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module
3192
		} elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) {
3193
			$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
3194
		} elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
3195
			$path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
3196
		}
3197
3198
		// If we ask an image into $url/$mymodule/img (instead of default path)
3199
		$regs = array();
3200
		if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
3201
			$picto = $regs[1];
3202
			$path = $regs[2]; // $path is $mymodule
3203
		}
3204
3205
		// Clean parameters
3206
		if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
3207
			$picto .= '.png';
3208
		}
3209
		// If alt path are defined, define url where img file is, according to physical path
3210
		// ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
3211
		foreach ($conf->file->dol_document_root as $type => $dirroot) {
3212
			if ($type == 'main') {
3213
				continue;
3214
			}
3215
			// This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
3216
			if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
3217
				$url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
3218
				break;
3219
			}
3220
		}
3221
3222
		// $url is '' or '/custom', $path is current theme or
3223
		$fullpathpicto = $url.'/'.$path.'/img/'.$picto;
3224
	}
3225
3226
	if ($srconly) {
3227
		return $fullpathpicto;
3228
	}
3229
		// tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
3230
	return '<img src="'.$fullpathpicto.'" alt="'.dol_escape_htmltag($alt).'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt.($morecss ? ' class="'.$morecss.'"' : '') : ' class="inline-block'.($morecss ? ' '.$morecss : '').'"').'>'; // Alt is used for accessibility, title for popup
3231
}
3232
3233
/**
3234
 *	Show a picto called object_picto (generic function)
3235
 *
3236
 *	@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.
3237
 *	@param	string	$picto				Name of image to show object_picto (example: user, group, action, bill, contract, propal, product, ...)
3238
 *										For external modules use imagename@mymodule to search into directory "img" of module.
3239
 *	@param	string	$moreatt			Add more attribute on img tag (ie: class="datecallink")
3240
 *	@param	int		$pictoisfullpath	If 1, image path is a full path
3241
 *	@param	int		$srconly			Return only content of the src attribute of img.
3242
 *  @param	int		$notitle			1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip.
3243
 *	@return	string						Return img tag
3244
 *	@see	img_picto(), img_picto_common()
3245
 */
3246
function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0)
3247
{
3248
	if (strpos($picto, '^') === 0) return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
3249
	else return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
3250
}
3251
3252
/**
3253
 *	Show weather picto
3254
 *
3255
 *	@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.
3256
 *	@param      string|int	$picto       		Name of image file to show (If no extension provided, we use '.png'). Image must be stored into htdocs/theme/common directory. Or level of meteo image (0-4).
3257
 *	@param		string		$moreatt			Add more attribute on img tag
3258
 *	@param		int			$pictoisfullpath	If 1, image path is a full path
3259
 *  @param      string      $morecss            More CSS
3260
 *	@return     string      					Return img tag
3261
 *  @see        img_object(), img_picto()
3262
 */
3263
function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
3264
{
3265
	global $conf;
3266
3267
	if (is_numeric($picto)) {
3268
		//$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
3269
		//$picto = $leveltopicto[$picto];
3270
		return '<i class="fa fa-weather-level'.$picto.'"></i>';
3271
	} elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
3272
		$picto .= '.png';
3273
	}
3274
3275
	$path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
3276
3277
	return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
3278
}
3279
3280
/**
3281
 *	Show picto (generic function)
3282
 *
3283
 *	@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.
3284
 *	@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.
3285
 *	@param		string		$moreatt			Add more attribute on img tag
3286
 *	@param		int			$pictoisfullpath	If 1, image path is a full path
3287
 *	@return     string      					Return img tag
3288
 *  @see        img_object(), img_picto()
3289
 */
3290
function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0)
3291
{
3292
	global $conf;
3293
3294
	if (!preg_match('/(\.png|\.gif)$/i', $picto)) $picto .= '.png';
3295
3296
	if ($pictoisfullpath) $path = $picto;
3297
	else {
3298
		$path = DOL_URL_ROOT.'/theme/common/'.$picto;
3299
3300
		if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS))
3301
		{
3302
			$themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
3303
3304
			if (file_exists($themepath)) $path = $themepath;
3305
		}
3306
	}
3307
3308
	return img_picto($titlealt, $path, $moreatt, 1);
3309
}
3310
3311
/**
3312
 *	Show logo action
3313
 *
3314
 *	@param	string		$titlealt       Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3315
 *	@param  string		$numaction   	Action id or code to show
3316
 *	@param 	string		$picto      	Name of image file to show ('filenew', ...)
3317
 *                                      If no extension provided, we use '.png'. Image must be stored into theme/xxx/img directory.
3318
 *                                      Example: picto.png                  if picto.png is stored into htdocs/theme/mytheme/img
3319
 *                                      Example: picto.png@mymodule         if picto.png is stored into htdocs/mymodule/img
3320
 *                                      Example: /mydir/mysubdir/picto.png  if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1)
3321
 *	@return string      				Return an img tag
3322
 */
3323
function img_action($titlealt, $numaction, $picto = '')
3324
{
3325
	global $langs;
3326
3327
	if (empty($titlealt) || $titlealt == 'default')
3328
	{
3329
		if ($numaction == '-1' || $numaction == 'ST_NO') {
3330
			$numaction = -1;
3331
			$titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
3332
		} elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
3333
			$numaction = 0;
3334
			$titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
3335
		} elseif ($numaction == '1' || $numaction == 'ST_TODO') {
3336
			$numaction = 1;
3337
			$titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
3338
		} elseif ($numaction == '2' || $numaction == 'ST_PEND') {
3339
			$numaction = 2;
3340
			$titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
3341
		} elseif ($numaction == '3' || $numaction == 'ST_DONE') {
3342
			$numaction = 3;
3343
			$titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
3344
		} else {
3345
			$titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
3346
			$numaction = 0;
3347
		}
3348
	}
3349
	if (!is_numeric($numaction)) $numaction = 0;
3350
3351
	return img_picto($titlealt, !empty($picto) ? $picto : 'stcomm'.$numaction.'.png');
3352
}
3353
3354
/**
3355
 *  Show pdf logo
3356
 *
3357
 *  @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.
3358
 *  @param  int		    $size       Taille de l'icone : 3 = 16x16px , 2 = 14x14px
3359
 *  @return string      			Retourne tag img
3360
 */
3361
function img_pdf($titlealt = 'default', $size = 3)
3362
{
3363
	global $langs;
3364
3365
	if ($titlealt == 'default') $titlealt = $langs->trans('Show');
3366
3367
	return img_picto($titlealt, 'pdf'.$size.'.png');
3368
}
3369
3370
/**
3371
 *	Show logo +
3372
 *
3373
 *	@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.
3374
 *	@param  string	$other      Add more attributes on img
3375
 *	@return string      		Return tag img
3376
 */
3377
function img_edit_add($titlealt = 'default', $other = '')
3378
{
3379
	global $langs;
3380
3381
	if ($titlealt == 'default') $titlealt = $langs->trans('Add');
3382
3383
	return img_picto($titlealt, 'edit_add.png', $other);
3384
}
3385
/**
3386
 *	Show logo -
3387
 *
3388
 *	@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.
3389
 *	@param  string	$other      Add more attributes on img
3390
 *	@return string      		Return tag img
3391
 */
3392
function img_edit_remove($titlealt = 'default', $other = '')
3393
{
3394
	global $langs;
3395
3396
	if ($titlealt == 'default') $titlealt = $langs->trans('Remove');
3397
3398
	return img_picto($titlealt, 'edit_remove.png', $other);
3399
}
3400
3401
/**
3402
 *	Show logo editer/modifier fiche
3403
 *
3404
 *	@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.
3405
 *	@param  integer	$float      If you have to put the style "float: right"
3406
 *	@param  string	$other		Add more attributes on img
3407
 *	@return string      		Return tag img
3408
 */
3409
function img_edit($titlealt = 'default', $float = 0, $other = '')
3410
{
3411
	global $langs;
3412
3413
	if ($titlealt == 'default') $titlealt = $langs->trans('Modify');
3414
3415
	return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
3416
}
3417
3418
/**
3419
 *	Show logo view card
3420
 *
3421
 *	@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.
3422
 *	@param  integer	$float      If you have to put the style "float: right"
3423
 *	@param  string	$other		Add more attributes on img
3424
 *	@return string      		Return tag img
3425
 */
3426
function img_view($titlealt = 'default', $float = 0, $other = '')
3427
{
3428
	global $langs;
3429
3430
	if ($titlealt == 'default') $titlealt = $langs->trans('View');
3431
3432
	$moreatt = ($float ? 'style="float: right" ' : '').$other;
3433
3434
	return img_picto($titlealt, 'view.png', $moreatt);
3435
}
3436
3437
/**
3438
 *  Show delete logo
3439
 *
3440
 *  @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.
3441
 *	@param  string	$other      Add more attributes on img
3442
 *  @param	string	$morecss	More CSS
3443
 *  @return string      		Retourne tag img
3444
 */
3445
function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
3446
{
3447
	global $langs;
3448
3449
	if ($titlealt == 'default') $titlealt = $langs->trans('Delete');
3450
3451
	return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss);
3452
}
3453
3454
/**
3455
 *  Show printer logo
3456
 *
3457
 *  @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.
3458
 *  @param  string  $other      Add more attributes on img
3459
 *  @return string              Retourne tag img
3460
 */
3461
function img_printer($titlealt = "default", $other = '')
3462
{
3463
	global $langs;
3464
	if ($titlealt == "default") $titlealt = $langs->trans("Print");
3465
	return img_picto($titlealt, 'printer.png', $other);
3466
}
3467
3468
/**
3469
 *  Show split logo
3470
 *
3471
 *  @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.
3472
 *	@param  string	$other      Add more attributes on img
3473
 *  @return string      		Retourne tag img
3474
 */
3475
function img_split($titlealt = 'default', $other = 'class="pictosplit"')
3476
{
3477
	global $langs;
3478
3479
	if ($titlealt == 'default') $titlealt = $langs->trans('Split');
3480
3481
	return img_picto($titlealt, 'split.png', $other);
3482
}
3483
3484
/**
3485
 *	Show help logo with cursor "?"
3486
 *
3487
 * 	@param	int              	$usehelpcursor		1=Use help cursor, 2=Use click pointer cursor, 0=No specific cursor
3488
 * 	@param	int|string	        $usealttitle		Text to use as alt title
3489
 * 	@return string            	           			Return tag img
3490
 */
3491
function img_help($usehelpcursor = 1, $usealttitle = 1)
3492
{
3493
	global $langs;
3494
3495
	if ($usealttitle)
3496
	{
3497
		if (is_string($usealttitle)) $usealttitle = dol_escape_htmltag($usealttitle);
3498
		else $usealttitle = $langs->trans('Info');
3499
	}
3500
3501
	return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
3502
}
3503
3504
/**
3505
 *	Show info logo
3506
 *
3507
 *	@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.
3508
 *	@return string      		Return img tag
3509
 */
3510
function img_info($titlealt = 'default')
3511
{
3512
	global $langs;
3513
3514
	if ($titlealt == 'default') $titlealt = $langs->trans('Informations');
3515
3516
	return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
3517
}
3518
3519
/**
3520
 *	Show warning logo
3521
 *
3522
 *	@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.
3523
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"'). If 1, add float: right. Can't be "class" attribute.
3524
 *  @param	string  $morecss	Add more CSS
3525
 *	@return string      		Return img tag
3526
 */
3527
function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
3528
{
3529
	global $langs;
3530
3531
	if ($titlealt == 'default') $titlealt = $langs->trans('Warning');
3532
3533
	//return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
3534
	return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
3535
}
3536
3537
/**
3538
 *  Show error logo
3539
 *
3540
 *	@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.
3541
 *	@return string      		Return img tag
3542
 */
3543
function img_error($titlealt = 'default')
3544
{
3545
	global $langs;
3546
3547
	if ($titlealt == 'default') $titlealt = $langs->trans('Error');
3548
3549
	return img_picto($titlealt, 'error.png');
3550
}
3551
3552
/**
3553
 *	Show next logo
3554
 *
3555
 *	@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.
3556
*	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3557
 *	@return string      		Return img tag
3558
 */
3559
function img_next($titlealt = 'default', $moreatt = '')
3560
{
3561
	global $langs;
3562
3563
	if ($titlealt == 'default') $titlealt = $langs->trans('Next');
3564
3565
	//return img_picto($titlealt, 'next.png', $moreatt);
3566
	return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
3567
}
3568
3569
/**
3570
 *	Show previous logo
3571
 *
3572
 *	@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.
3573
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3574
 *	@return string      		Return img tag
3575
 */
3576
function img_previous($titlealt = 'default', $moreatt = '')
3577
{
3578
	global $langs;
3579
3580
	if ($titlealt == 'default') $titlealt = $langs->trans('Previous');
3581
3582
	//return img_picto($titlealt, 'previous.png', $moreatt);
3583
	return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
3584
}
3585
3586
/**
3587
 *	Show down arrow logo
3588
 *
3589
 *	@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.
3590
 *	@param  int		$selected   Selected
3591
 *  @param	string	$moreclass	Add more CSS classes
3592
 *	@return string      		Return img tag
3593
 */
3594
function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
3595
{
3596
	global $langs;
3597
3598
	if ($titlealt == 'default') $titlealt = $langs->trans('Down');
3599
3600
	return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
3601
}
3602
3603
/**
3604
 *	Show top arrow logo
3605
 *
3606
 *	@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.
3607
 *	@param  int		$selected	Selected
3608
 *  @param	string	$moreclass	Add more CSS classes
3609
 *	@return string      		Return img tag
3610
 */
3611
function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
3612
{
3613
	global $langs;
3614
3615
	if ($titlealt == 'default') $titlealt = $langs->trans('Up');
3616
3617
	return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
3618
}
3619
3620
/**
3621
 *	Show left arrow logo
3622
 *
3623
 *	@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.
3624
 *	@param  int		$selected	Selected
3625
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3626
 *	@return string      		Return img tag
3627
 */
3628
function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
3629
{
3630
	global $langs;
3631
3632
	if ($titlealt == 'default') {
3633
		$titlealt = $langs->trans('Left');
3634
	}
3635
3636
	return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
3637
}
3638
3639
/**
3640
 *	Show right arrow logo
3641
 *
3642
 *	@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.
3643
 *	@param  int		$selected	Selected
3644
 *	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3645
 *	@return string      		Return img tag
3646
 */
3647
function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
3648
{
3649
	global $langs;
3650
3651
	if ($titlealt == 'default') $titlealt = $langs->trans('Right');
3652
3653
	return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
3654
}
3655
3656
/**
3657
 *	Show tick logo if allowed
3658
 *
3659
 *	@param	string	$allow		Allow
3660
 *	@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.
3661
 *	@return string      		Return img tag
3662
 */
3663
function img_allow($allow, $titlealt = 'default')
3664
{
3665
	global $langs;
3666
3667
	if ($titlealt == 'default') $titlealt = $langs->trans('Active');
3668
3669
	if ($allow == 1) return img_picto($titlealt, 'tick.png');
3670
3671
	return '-';
3672
}
3673
3674
/**
3675
 *	Return image of a credit card according to its brand name
3676
 *
3677
 *	@param  string	$brand		Brand name of credit card
3678
 *  @param  string	$morecss	More CSS
3679
 *	@return string     			Return img tag
3680
 */
3681
function img_credit_card($brand, $morecss = null)
3682
{
3683
	if (is_null($morecss)) $morecss = 'fa-2x';
3684
3685
	if ($brand == 'visa' || $brand == 'Visa') {
3686
		$brand = 'cc-visa';
3687
	} elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
3688
		$brand = 'cc-mastercard';
3689
	} elseif ($brand == 'amex' || $brand == 'American Express') {
3690
		$brand = 'cc-amex';
3691
	} elseif ($brand == 'discover' || $brand == 'Discover') {
3692
		$brand = 'cc-discover';
3693
	} elseif ($brand == 'jcb' || $brand == 'JCB') {
3694
		$brand = 'cc-jcb';
3695
	} elseif ($brand == 'diners' || $brand == 'Diners club') {
3696
		$brand = 'cc-diners-club';
3697
	} elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
3698
		$brand = 'credit-card';
3699
	}
3700
3701
	return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
3702
}
3703
3704
/**
3705
 *	Show MIME img of a file
3706
 *
3707
 *	@param	string	$file		Filename
3708
 * 	@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.
3709
 *  @param	string	$morecss	More css
3710
 *	@return string     			Return img tag
3711
 */
3712
function img_mime($file, $titlealt = '', $morecss = '')
3713
{
3714
	require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3715
3716
	$mimetype = dol_mimetype($file, '', 1);
3717
	$mimeimg = dol_mimetype($file, '', 2);
3718
	$mimefa = dol_mimetype($file, '', 4);
3719
3720
	if (empty($titlealt)) $titlealt = 'Mime type: '.$mimetype;
3721
3722
	//return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
3723
	return '<i class="fa fa-'.$mimefa.' paddingright"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
3724
}
3725
3726
3727
/**
3728
 *  Show search logo
3729
 *
3730
 *  @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.
3731
 *	@param  string	$other      Add more attributes on img
3732
 *  @return string      		Retourne tag img
3733
 */
3734
function img_search($titlealt = 'default', $other = '')
3735
{
3736
	global $conf, $langs;
3737
3738
	if ($titlealt == 'default') $titlealt = $langs->trans('Search');
3739
3740
	$img = img_picto($titlealt, 'search.png', $other, false, 1);
3741
3742
	$input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
3743
	$input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
3744
3745
	return $input;
3746
}
3747
3748
/**
3749
 *  Show search logo
3750
 *
3751
 *  @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.
3752
 *	@param  string	$other      Add more attributes on img
3753
 *  @return string      		Retourne tag img
3754
 */
3755
function img_searchclear($titlealt = 'default', $other = '')
3756
{
3757
	global $conf, $langs;
3758
3759
	if ($titlealt == 'default') $titlealt = $langs->trans('Search');
3760
3761
	$img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
3762
3763
	$input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
3764
	$input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
3765
3766
	return $input;
3767
}
3768
3769
/**
3770
 *	Show information for admin users or standard users
3771
 *
3772
 *	@param	string	$text				Text info
3773
 *	@param  integer	$infoonimgalt		Info is shown only on alt of star picto, otherwise it is show on output after the star picto
3774
 *	@param	int		$nodiv				No div
3775
 *  @param  string  $admin      	    '1'=Info for admin users. '0'=Info for standard users (change only the look), 'error','xxx'=Other
3776
 *  @param	string	$morecss			More CSS ('', 'warning', 'error')
3777
 *  @param	string	$textfordropdown	Show a text to click to dropdown the info box.
3778
 *	@return	string						String with info text
3779
 */
3780
function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = '', $textfordropdown = '')
3781
{
3782
	global $conf, $langs;
3783
3784
	if ($infoonimgalt)
3785
	{
3786
		$result = img_picto($text, 'info', 'class="hideonsmartphone'.($morecss ? ' '.$morecss : '').'"');
3787
	} else {
3788
		if (empty($conf->use_javascript_ajax)) $textfordropdown = '';
3789
3790
		$class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
3791
		$result = ($nodiv ? '' : '<div class="'.$class.' hideonsmartphone'.($morecss ? ' '.$morecss : '').($textfordropdown ? ' hidden' : '').'">').'<span class="fa fa-info-circle" title="'.dol_escape_htmltag($admin ? $langs->trans('InfoAdmin') : $langs->trans('Note')).'"></span> '.$text.($nodiv ? '' : '</div>');
3792
3793
		if ($textfordropdown) {
3794
			$tmpresult .= '<span class="'.$class.'text opacitymedium">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tmpresult does not exist. Did you maybe mean $result?
Loading history...
3795
			$tmpresult .= '<script type="text/javascript" language="javascript">
3796
				jQuery(document).ready(function() {
3797
					jQuery(".'.$class.'text").click(function() {
3798
						console.log("toggle text");
3799
						jQuery(".'.$class.'").toggle();
3800
					});
3801
				});
3802
				</script>';
3803
3804
			$result = $tmpresult.$result;
3805
		}
3806
	}
3807
3808
	return $result;
3809
}
3810
3811
3812
/**
3813
 *  Displays error message system with all the information to facilitate the diagnosis and the escalation of the bugs.
3814
 *  This function must be called when a blocking technical error is encountered.
3815
 *  However, one must try to call it only within php pages, classes must return their error through their property "error".
3816
 *
3817
 *	@param	 	DoliDB	$db      	Database handler
3818
 *	@param  	mixed	$error		String or array of errors strings to show
3819
 *  @param		array	$errors		Array of errors
3820
 *	@return 	void
3821
 *  @see    	dol_htmloutput_errors()
3822
 */
3823
function dol_print_error($db = '', $error = '', $errors = null)
3824
{
3825
	global $conf, $langs, $argv;
3826
	global $dolibarr_main_prod;
3827
3828
	$out = '';
3829
	$syslog = '';
3830
3831
	// Si erreur intervenue avant chargement langue
3832
	if (!$langs)
3833
	{
3834
		require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
3835
		$langs = new Translate('', $conf);
3836
		$langs->load("main");
3837
	}
3838
	// Load translation files required by the page
3839
	$langs->loadLangs(array('main', 'errors'));
3840
3841
	if ($_SERVER['DOCUMENT_ROOT'])    // Mode web
3842
	{
3843
		$out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
3844
		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";
3845
		$out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
3846
3847
		$out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
3848
		$out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
3849
		if (isset($conf->global->MAIN_FEATURES_LEVEL)) $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".$conf->global->MAIN_FEATURES_LEVEL."<br>\n";
3850
		if (function_exists("phpversion"))
3851
		{
3852
			$out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
3853
		}
3854
		$out .= "<b>".$langs->trans("Server").":</b> ".$_SERVER["SERVER_SOFTWARE"]."<br>\n";
3855
		if (function_exists("php_uname"))
3856
		{
3857
			$out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
3858
		}
3859
		$out .= "<b>".$langs->trans("UserAgent").":</b> ".dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT, 'UTF-8')."<br>\n";
3860
		$out .= "<br>\n";
3861
		$out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT, 'UTF-8')."<br>\n";
3862
		$out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ?dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT, 'UTF-8') : '')."<br>\n";
3863
		$out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? $conf->standard_menu : '')."<br>\n";
3864
		$out .= "<br>\n";
3865
		$syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
3866
		$syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
3867
	} else // Mode CLI
3868
	{
3869
		$out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
3870
		$syslog .= "pid=".dol_getmypid();
3871
	}
3872
3873
	if (!empty($conf->modules))
3874
	{
3875
		$out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n";
3876
	}
3877
3878
	if (is_object($db))
3879
	{
3880
		if ($_SERVER['DOCUMENT_ROOT'])  // Mode web
3881
		{
3882
			$out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
3883
			$out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror() ?dol_escape_htmltag($db->lastqueryerror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
3884
			$out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ?dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
3885
			$out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ?dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
3886
			$out .= "<br>\n";
3887
		} else // Mode CLI
3888
		{
3889
			// No dol_escape_htmltag for output, we are in CLI mode
3890
			$out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
3891
			$out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
3892
			$out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
3893
			$out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
3894
		}
3895
		$syslog .= ", sql=".$db->lastquery();
3896
		$syslog .= ", db_error=".$db->lasterror();
3897
	}
3898
3899
	if ($error || $errors)
3900
	{
3901
		$langs->load("errors");
3902
3903
		// Merge all into $errors array
3904
		if (is_array($error) && is_array($errors)) $errors = array_merge($error, $errors);
3905
		elseif (is_array($error)) $errors = $error;
3906
		elseif (is_array($errors)) $errors = array_merge(array($error), $errors);
3907
		else $errors = array_merge(array($error));
3908
3909
		foreach ($errors as $msg)
3910
		{
3911
			if (empty($msg)) continue;
3912
			if ($_SERVER['DOCUMENT_ROOT'])  // Mode web
3913
			{
3914
				$out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
3915
			} else // Mode CLI
3916
			{
3917
				$out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
3918
			}
3919
			$syslog .= ", msg=".$msg;
3920
		}
3921
	}
3922
	if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file'))
3923
	{
3924
		xdebug_print_function_stack();
3925
		$out .= '<b>XDebug informations:</b>'."<br>\n";
3926
		$out .= 'File: '.xdebug_call_file()."<br>\n";
3927
		$out .= 'Line: '.xdebug_call_line()."<br>\n";
3928
		$out .= 'Function: '.xdebug_call_function()."<br>\n";
3929
		$out .= "<br>\n";
3930
	}
3931
3932
	if (empty($dolibarr_main_prod)) print $out;
3933
	else {
3934
		// This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
3935
		print 'This website or feature is currently temporarly not available or failed after a technical error.<br><br>This may be due to a maintenance operation. Current status of operation are on next line...<br><br>'."\n";
3936
		$langs->load("errors");
3937
		print $langs->trans("DolibarrHasDetectedError").'. ';
3938
		print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
3939
		define("MAIN_CORE_ERROR", 1);
3940
	}
3941
	//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.';
3942
	dol_syslog("Error ".$syslog, LOG_ERR);
3943
}
3944
3945
/**
3946
 * Show a public email and error code to contact if technical error
3947
 *
3948
 * @param	string	$prefixcode		Prefix of public error code
3949
 * @param   string  $errormessage   Complete error message
3950
 * @param	array	$errormessages	Array of error messages
3951
 * @param	string	$morecss		More css
3952
 * @param	string	$email			Email
3953
 * @return	void
3954
 */
3955
function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
3956
{
3957
	global $langs, $conf;
3958
3959
	if (empty($email)) $email = $conf->global->MAIN_INFO_SOCIETE_MAIL;
3960
3961
	$langs->load("errors");
3962
	$now = dol_now();
3963
3964
	print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
3965
	print $langs->trans("ErrorContactEMail", $email, $prefixcode.dol_print_date($now, '%Y%m%d%H%M%S'));
3966
	if ($errormessage) print '<br><br>'.$errormessage;
3967
	if (is_array($errormessages) && count($errormessages))
3968
	{
3969
		foreach ($errormessages as $mesgtoshow)
3970
		{
3971
			print '<br><br>'.$mesgtoshow;
3972
		}
3973
	}
3974
	print '</div></div>';
3975
}
3976
3977
/**
3978
 *	Show title line of an array
3979
 *
3980
 *	@param	string	$name        Label of field
3981
 *	@param	string	$file        Url used when we click on sort picto
3982
 *	@param	string	$field       Field to use for new sorting
3983
 *	@param	string	$begin       ("" by defaut)
3984
 *	@param	string	$moreparam   Add more parameters on sort url links ("" by default)
3985
 *	@param  string	$moreattrib  Options of attribute td ("" by defaut, example: 'align="center"')
3986
 *	@param  string	$sortfield   Current field used to sort
3987
 *	@param  string	$sortorder   Current sort order
3988
 *  @param	string	$prefix		 Prefix for css. Use space after prefix to add your own CSS tag.
3989
 *  @param	string	$tooltip	 Tooltip
3990
 *	@return	void
3991
 */
3992
function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "")
3993
{
3994
	print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip);
3995
}
3996
3997
/**
3998
 *	Get title line of an array
3999
 *
4000
 *	@param	string	$name        		Translation key of field
4001
 *	@param	int		$thead		 		0=To use with standard table format, 1=To use inside <thead><tr>, 2=To use with <div>
4002
 *	@param	string	$file        		Url used when we click on sort picto
4003
 *	@param	string	$field       		Field to use for new sorting. Empty if this field is not sortable. Example "t.abc" or "t.abc,t.def"
4004
 *	@param	string	$begin       		("" by defaut)
4005
 *	@param	string	$moreparam   		Add more parameters on sort url links ("" by default)
4006
 *	@param  string	$moreattrib  		Add more attributes on th ("" by defaut, example: 'align="center"'). To add more css class, use param $prefix.
4007
 *	@param  string	$sortfield   		Current field used to sort (Ex: 'd.datep,d.id')
4008
 *	@param  string	$sortorder   		Current sort order (Ex: 'asc,desc')
4009
 *  @param	string	$prefix		 		Prefix for css. Use space after prefix to add your own CSS tag, for example 'mycss '.
4010
 *  @param	string	$disablesortlink	1=Disable sort link
4011
 *  @param	string	$tooltip	 		Tooltip
4012
 *	@return	string
4013
 */
4014
function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '')
4015
{
4016
	global $conf, $langs, $form;
4017
	//print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
4018
4019
	if ($moreattrib == 'class="right"') $prefix .= 'right '; // For backward compatibility
4020
4021
	$sortorder = strtoupper($sortorder);
4022
	$out = '';
4023
	$sortimg = '';
4024
4025
	$tag = 'th';
4026
	if ($thead == 2) $tag = 'div';
4027
4028
	$tmpsortfield = explode(',', $sortfield);
4029
	$sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
4030
	$tmpfield = explode(',', $field);
4031
	$field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
4032
4033
	if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE)) {
4034
		$prefix = 'wrapcolumntitle '.$prefix;
4035
	}
4036
	//var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
4037
	// If field is used as sort criteria we use a specific css class liste_titre_sel
4038
	// Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
4039
	if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
4040
		$out .= '<'.$tag.' class="'.$prefix.'liste_titre_sel" '.$moreattrib;
4041
		$out .= (($field && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && preg_match('/^[a-zA-Z_0-9\s\.\-:]*$/', $name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
4042
		$out .= '>';
4043
	} else {
4044
		$out .= '<'.$tag.' class="'.$prefix.'liste_titre" '.$moreattrib;
4045
		$out .= (($field && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && preg_match('/^[a-zA-Z_0-9\s\.\-:]*$/', $name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
4046
		$out .= '>';
4047
	}
4048
4049
	if (empty($thead) && $field && empty($disablesortlink))    // If this is a sort field
4050
	{
4051
		$options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', $moreparam);
4052
		$options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
4053
		$options = preg_replace('/&+/i', '&', $options);
4054
		if (!preg_match('/^&/', $options)) $options = '&'.$options;
4055
4056
		$sortordertouseinlink = '';
4057
		if ($field1 != $sortfield1) // We are on another field than current sorted field
4058
		{
4059
			if (preg_match('/^DESC/i', $sortorder))
4060
			{
4061
				$sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
4062
			} else // We reverse the var $sortordertouseinlink
4063
			{
4064
				$sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
4065
			}
4066
		} else // We are on field that is the first current sorting criteria
4067
		{
4068
			if (preg_match('/^ASC/i', $sortorder))	// We reverse the var $sortordertouseinlink
4069
			{
4070
				$sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
4071
			} else {
4072
				$sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
4073
			}
4074
		}
4075
		$sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
4076
		$out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
4077
		//$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
4078
		$out .= '>';
4079
	}
4080
4081
	if ($tooltip) $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tooltip));
4082
	else $out .= $langs->trans($name);
4083
4084
	if (empty($thead) && $field && empty($disablesortlink))    // If this is a sort field
4085
	{
4086
		$out .= '</a>';
4087
	}
4088
4089
	if (empty($thead) && $field)    // If this is a sort field
4090
	{
4091
		$options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', $moreparam);
4092
		$options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
4093
		$options = preg_replace('/&+/i', '&', $options);
4094
		if (!preg_match('/^&/', $options)) $options = '&'.$options;
4095
4096
		if (!$sortorder || $field1 != $sortfield1)
4097
		{
4098
			//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
4099
			//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
4100
		} else {
4101
			if (preg_match('/^DESC/', $sortorder)) {
4102
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
4103
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
4104
				$sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingleft').'</span>';
4105
			}
4106
			if (preg_match('/^ASC/', $sortorder)) {
4107
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
4108
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
4109
				$sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingleft').'</span>';
4110
			}
4111
		}
4112
	}
4113
4114
	$out .= $sortimg;
4115
4116
	$out .= '</'.$tag.'>';
4117
4118
	return $out;
4119
}
4120
4121
/**
4122
 *	Show a title.
4123
 *
4124
 *	@param	string	$title			Title to show
4125
 *	@return	string					Title to show
4126
 *  @deprecated						Use load_fiche_titre instead
4127
 *  @see load_fiche_titre()
4128
 */
4129
function print_titre($title)
4130
{
4131
	dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
4132
4133
	print '<div class="titre">'.$title.'</div>';
4134
}
4135
4136
/**
4137
 *	Show a title with picto
4138
 *
4139
 *	@param	string	$title				Title to show
4140
 *	@param	string	$mesg				Added message to show on right
4141
 *	@param	string	$picto				Icon to use before title (should be a 32x32 transparent png file)
4142
 *	@param	int		$pictoisfullpath	1=Icon name is a full absolute url of image
4143
 * 	@param	int		$id					To force an id on html objects
4144
 * 	@return	void
4145
 *  @deprecated Use print load_fiche_titre instead
4146
 */
4147
function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
4148
{
4149
	print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
4150
}
4151
4152
/**
4153
 *	Load a title with picto
4154
 *
4155
 *	@param	string	$titre				Title to show
4156
 *	@param	string	$morehtmlright		Added message to show on right
4157
 *	@param	string	$picto				Icon to use before title (should be a 32x32 transparent png file)
4158
 *	@param	int		$pictoisfullpath	1=Icon name is a full absolute url of image
4159
 * 	@param	string	$id					To force an id on html objects
4160
 *  @param  string  $morecssontable     More css on table
4161
 *	@param	string	$morehtmlcenter		Added message to show on center
4162
 * 	@return	string
4163
 *  @see print_barre_liste()
4164
 */
4165
function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
4166
{
4167
	global $conf;
4168
4169
	$return = '';
4170
4171
	if ($picto == 'setup') $picto = 'generic';
4172
4173
	$return .= "\n";
4174
	$return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
4175
	$return .= '<tr class="titre">';
4176
	if ($picto) $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
4177
	$return .= '<td class="nobordernopadding valignmiddle col-title">';
4178
	$return .= '<div class="titre inline-block">'.$titre.'</div>';
4179
	$return .= '</td>';
4180
	if (dol_strlen($morehtmlcenter))
4181
	{
4182
		$return .= '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>';
4183
	}
4184
	if (dol_strlen($morehtmlright))
4185
	{
4186
		$return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle">'.$morehtmlright.'</td>';
4187
	}
4188
	$return .= '</tr></table>'."\n";
4189
4190
	return $return;
4191
}
4192
4193
/**
4194
 *	Print a title with navigation controls for pagination
4195
 *
4196
 *	@param	string	    $titre				Title to show (required)
4197
 *	@param	int   	    $page				Numero of page to show in navigation links (required)
4198
 *	@param	string	    $file				Url of page (required)
4199
 *	@param	string	    $options         	More parameters for links ('' by default, does not include sortfield neither sortorder). Value must be 'urlencoded' before calling function.
4200
 *	@param	string    	$sortfield       	Field to sort on ('' by default)
4201
 *	@param	string	    $sortorder       	Order to sort ('' by default)
4202
 *	@param	string	    $morehtmlcenter     String in the middle ('' by default). We often find here string $massaction comming from $form->selectMassAction()
4203
 *	@param	int		    $num				Number of records found by select with limit+1
4204
 *	@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.
4205
 *	@param	string	    $picto				Icon to use before title (should be a 32x32 transparent png file)
4206
 *	@param	int		    $pictoisfullpath	1=Icon name is a full absolute url of image
4207
 *  @param	string	    $morehtmlright		More html to show (after arrows)
4208
 *  @param  string      $morecss            More css to the table
4209
 *  @param  int         $limit              Max number of lines (-1 = use default, 0 = no limit, > 0 = limit).
4210
 *  @param  int         $hideselectlimit    Force to hide select limit
4211
 *  @param  int         $hidenavigation     Force to hide all navigation tools
4212
 *  @param  int			$pagenavastextinput 1=Do not suggest list of pages to navigate but suggest the page number into an input field.
4213
 *  @param	string		$morehtmlrightbeforearrow	More html to show (before arrows)
4214
 *	@return	void
4215
 */
4216
function print_barre_liste($titre, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'generic', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $hideselectlimit = 0, $hidenavigation = 0, $pagenavastextinput = 0, $morehtmlrightbeforearrow = '')
4217
{
4218
	global $conf, $langs;
4219
4220
	$savlimit = $limit;
4221
	$savtotalnboflines = $totalnboflines;
4222
	$totalnboflines = abs($totalnboflines);
4223
4224
	if ($picto == 'setup') $picto = 'title_setup.png';
4225
	if (($conf->browser->name == 'ie') && $picto == 'generic') $picto = 'title.gif';
4226
	if ($limit < 0) $limit = $conf->liste_limit;
4227
	if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0)))
4228
	{
4229
		$nextpage = 1;
4230
	} else {
4231
		$nextpage = 0;
4232
	}
4233
	//print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage;
4234
4235
	print "\n";
4236
	print "<!-- Begin title '".$titre."' -->\n";
4237
	print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
4238
4239
	// Left
4240
4241
	if ($picto && $titre) print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
4242
	print '<td class="nobordernopadding valignmiddle col-title">';
4243
	print '<div class="titre inline-block">'.$titre;
4244
	if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>';
4245
	print '</div></td>';
4246
4247
	// Center
4248
	if ($morehtmlcenter)
4249
	{
4250
		print '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>';
4251
	}
4252
4253
	// Right
4254
	print '<td class="nobordernopadding valignmiddle right">';
4255
	if ($sortfield) $options .= "&sortfield=".urlencode($sortfield);
4256
	if ($sortorder) $options .= "&sortorder=".urlencode($sortorder);
4257
	// Show navigation bar
4258
	$pagelist = '';
4259
	if ($savlimit != 0 && ($page > 0 || $num > $limit))
4260
	{
4261
		if ($totalnboflines)	// If we know total nb of lines
4262
		{
4263
			// Define nb of extra page links before and after selected page + ... + first or last
4264
			$maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
4265
4266
			if ($limit > 0) $nbpages = ceil($totalnboflines / $limit);
4267
			else $nbpages = 1;
4268
			$cpt = ($page - $maxnbofpage);
4269
			if ($cpt < 0) { $cpt = 0; }
4270
4271
			if ($cpt >= 1)
4272
			{
4273
				if (empty($pagenavastextinput)) {
4274
					$pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>';
4275
					if ($cpt > 2) $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
4276
					elseif ($cpt == 2) $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>';
4277
				}
4278
			}
4279
4280
			do {
4281
				if ($pagenavastextinput) {
4282
					if ($cpt == $page)
4283
					{
4284
						$pagelist .= '<li class="pagination"><input type="text" class="width25 center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
4285
						$pagelist .= '/';
4286
						//if (($cpt + 1) < $nbpages) $pagelist .= '/';
4287
					}
4288
				} else {
4289
					if ($cpt == $page)
4290
					{
4291
						$pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
4292
					} else {
4293
						$pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
4294
					}
4295
				}
4296
				$cpt++;
4297
			} while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
4298
4299
			if (empty($pagenavastextinput)) {
4300
				if ($cpt < $nbpages)
4301
				{
4302
					if ($cpt < $nbpages - 2) $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
4303
					elseif ($cpt == $nbpages - 2) $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
4304
					$pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
4305
				}
4306
			} else {
4307
				//var_dump($page.' '.$cpt.' '.$nbpages);
4308
				//if (($page + 1) < $nbpages) {
4309
					$pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
4310
				//}
4311
			}
4312
		} else {
4313
			$pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
4314
		}
4315
	}
4316
4317
	if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
4318
		print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $hideselectlimit, $morehtmlrightbeforearrow); // output the div and ul for previous/last completed with page numbers into $pagelist
4319
	}
4320
4321
	// js to autoselect page field on focus
4322
	if ($pagenavastextinput) {
4323
		print ajax_autoselect('.pageplusone');
4324
	}
4325
4326
	print '</td>';
4327
4328
	print '</tr></table>'."\n";
4329
	print "<!-- End title -->\n\n";
4330
}
4331
4332
/**
4333
 *	Function to show navigation arrows into lists
4334
 *
4335
 *	@param	int				$page				Number of page
4336
 *	@param	string			$file				Page URL (in most cases provided with $_SERVER["PHP_SELF"])
4337
 *	@param	string			$options         	Other url parameters to propagate ("" by default, may include sortfield and sortorder)
4338
 *	@param	integer			$nextpage	    	Do we show a next page button
4339
 *	@param	string			$betweenarrows		HTML content to show between arrows. MUST contains '<li> </li>' tags or '<li><span> </span></li>'.
4340
 *  @param	string			$afterarrows		HTML content to show after arrows. Must NOT contains '<li> </li>' tags.
4341
 *  @param  int             $limit              Max nb of record to show  (-1 = no combo with limit, 0 = no limit, > 0 = limit)
4342
 *	@param	int		        $totalnboflines		Total number of records/lines for all pages (if known)
4343
 *  @param  int             $hideselectlimit    Force to hide select limit
4344
 *  @param	string			$beforearrows		HTML content to show before arrows. Must NOT contains '<li> </li>' tags.
4345
 *	@return	void
4346
 */
4347
function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '')
4348
{
4349
	global $conf, $langs;
4350
4351
	print '<div class="pagination"><ul>';
4352
	if ($beforearrows)
4353
	{
4354
		print '<li class="paginationbeforearrows">';
4355
		print $beforearrows;
4356
		print '</li>';
4357
	}
4358
	if ((int) $limit > 0 && empty($hideselectlimit))
4359
	{
4360
		$pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000,5000:5000';
4361
		//$pagesizechoices.=',0:'.$langs->trans("All");     // Not yet supported
4362
		//$pagesizechoices.=',2:2';
4363
		if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES;
4364
4365
		print '<li class="pagination">';
4366
		print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
4367
		$tmpchoice = explode(',', $pagesizechoices);
4368
		$tmpkey = $limit.':'.$limit;
4369
		if (!in_array($tmpkey, $tmpchoice)) $tmpchoice[] = $tmpkey;
4370
		$tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
4371
		if (!in_array($tmpkey, $tmpchoice)) $tmpchoice[] = $tmpkey;
4372
		asort($tmpchoice, SORT_NUMERIC);
4373
		foreach ($tmpchoice as $val)
4374
		{
4375
			$selected = '';
4376
			$tmp = explode(':', $val);
4377
			$key = $tmp[0];
4378
			$val = $tmp[1];
4379
			if ($key != '' && $val != '')
4380
			{
4381
				if ((int) $key == (int) $limit)
4382
				{
4383
					$selected = ' selected="selected"';
4384
				}
4385
				print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
4386
			}
4387
		}
4388
		print '</select>';
4389
		if ($conf->use_javascript_ajax)
4390
		{
4391
			print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
4392
            		<script>
4393
                	jQuery(document).ready(function () {
4394
            	  		jQuery(".selectlimit").change(function() {
4395
                            console.log("Change limit. Send submit");
4396
                            $(this).parents(\'form:first\').submit();
4397
            	  		});
4398
                	});
4399
            		</script>
4400
                ';
4401
		}
4402
		print '</li>';
4403
	}
4404
	if ($page > 0)
4405
	{
4406
		print '<li class="pagination paginationpage paginationpageleft"><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>';
4407
	}
4408
	if ($betweenarrows)
4409
	{
4410
		print '<!--<div class="betweenarrows nowraponall inline-block">-->';
4411
		print $betweenarrows;
4412
		print '<!--</div>-->';
4413
	}
4414
	if ($nextpage > 0)
4415
	{
4416
		print '<li class="pagination paginationpage paginationpageright"><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>';
4417
	}
4418
	if ($afterarrows)
4419
	{
4420
		print '<li class="paginationafterarrows">';
4421
		print $afterarrows;
4422
		print '</li>';
4423
	}
4424
	print '</ul></div>'."\n";
4425
}
4426
4427
4428
/**
4429
 *	Return a string with VAT rate label formated for view output
4430
 *	Used into pdf and HTML pages
4431
 *
4432
 *	@param	string	$rate			Rate value to format ('19.6', '19,6', '19.6%', '19,6%', '19.6 (CODEX)', ...)
4433
 *  @param	boolean	$addpercent		Add a percent % sign in output
4434
 *	@param	int		$info_bits		Miscellaneous information on vat (0=Default, 1=French NPR vat)
4435
 *	@param	int		$usestarfornpr	-1=Never show, 0 or 1=Use '*' for NPR vat rates
4436
 *  @return	string					String with formated amounts ('19,6' or '19,6%' or '8.5% (NPR)' or '8.5% *' or '19,6 (CODEX)')
4437
 */
4438
function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0)
4439
{
4440
	$morelabel = '';
4441
4442
	if (preg_match('/%/', $rate))
4443
	{
4444
		$rate = str_replace('%', '', $rate);
4445
		$addpercent = true;
4446
	}
4447
	if (preg_match('/\((.*)\)/', $rate, $reg))
4448
	{
4449
		$morelabel = ' ('.$reg[1].')';
4450
		$rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
4451
	}
4452
	if (preg_match('/\*/', $rate))
4453
	{
4454
		$rate = str_replace('*', '', $rate);
4455
		$info_bits |= 1;
4456
	}
4457
4458
	// If rate is '9/9/9' we don't change it.  If rate is '9.000' we apply price()
4459
	if (!preg_match('/\//', $rate)) $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
4460
	else {
4461
		// TODO Split on / and output with a price2num to have clean numbers without ton of 000.
4462
		$ret = $rate.($addpercent ? '%' : '');
4463
	}
4464
	if (($info_bits & 1) && $usestarfornpr >= 0) $ret .= ' *';
4465
	$ret .= $morelabel;
4466
	return $ret;
4467
}
4468
4469
4470
/**
4471
 *		Function to format a value into an amount for visual output
4472
 *		Function used into PDF and HTML pages
4473
 *
4474
 *		@param	float		$amount			Amount to format
4475
 *		@param	integer		$form			Type of format, HTML or not (not by default)
4476
 *		@param	Translate	$outlangs		Object langs for output
4477
 *		@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.
4478
 *		@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)
4479
 *		@param	int			$forcerounding	Force the number of decimal to forcerounding decimal (-1=do not force)
4480
 *		@param	string		$currency_code	To add currency symbol (''=add nothing, 'auto'=Use default currency, 'XXX'=add currency symbols for XXX currency)
4481
 *		@return	string						Chaine avec montant formate
4482
 *
4483
 *		@see	price2num()					Revert function of price
4484
 */
4485
function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
4486
{
4487
	global $langs, $conf;
4488
4489
	// Clean parameters
4490
	if (empty($amount)) $amount = 0; // To have a numeric value if amount not defined or = ''
4491
	$amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
4492
	if ($rounding < 0) $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT);
4493
	$nbdecimal = $rounding;
4494
4495
	// Output separators by default (french)
4496
	$dec = ','; $thousand = ' ';
4497
4498
	// If $outlangs not forced, we use use language
4499
	if (!is_object($outlangs)) $outlangs = $langs;
4500
4501
	if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal")  $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
4502
	if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
4503
	if ($thousand == 'None') $thousand = '';
4504
	elseif ($thousand == 'Space') $thousand = ' ';
4505
	//print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
4506
4507
	//print "amount=".$amount."-";
4508
	$amount = str_replace(',', '.', $amount); // should be useless
4509
	//print $amount."-";
4510
	$datas = explode('.', $amount);
4511
	$decpart = isset($datas[1]) ? $datas[1] : '';
4512
	$decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
4513
	//print "decpart=".$decpart."<br>";
4514
	$end = '';
4515
4516
	// We increase nbdecimal if there is more decimal than asked (to not loose information)
4517
	if (dol_strlen($decpart) > $nbdecimal) $nbdecimal = dol_strlen($decpart);
4518
	// Si on depasse max
4519
	if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN)
4520
	{
4521
		$nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN;
4522
		if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN))
4523
		{
4524
			// Si un affichage est tronque, on montre des ...
4525
			$end = '...';
4526
		}
4527
	}
4528
4529
	// If force rounding
4530
	if ($forcerounding >= 0) $nbdecimal = $forcerounding;
4531
4532
	// Format number
4533
	$output = number_format($amount, $nbdecimal, $dec, $thousand);
4534
	if ($form)
4535
	{
4536
		$output = preg_replace('/\s/', '&nbsp;', $output);
4537
		$output = preg_replace('/\'/', '&#039;', $output);
4538
	}
4539
	// Add symbol of currency if requested
4540
	$cursymbolbefore = $cursymbolafter = '';
4541
	if ($currency_code)
4542
	{
4543
		if ($currency_code == 'auto') $currency_code = $conf->currency;
4544
4545
		$listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD');
4546
		$listoflanguagesbefore = array('nl_NL');
4547
		if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore))
4548
		{
4549
			$cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
4550
		} else {
4551
			$tmpcur = $outlangs->getCurrencySymbol($currency_code);
4552
			$cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
4553
		}
4554
	}
4555
	$output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
4556
4557
	return $output;
4558
}
4559
4560
/**
4561
 *	Function that return a number with universal decimal format (decimal separator is '.') from an amount typed by a user.
4562
 *	Function to use on each input amount before any numeric test or database insert. A better name for this function
4563
 *  should be roundtext2num().
4564
 *
4565
 *	@param	float	$amount			Amount to convert/clean or round
4566
 *	@param	string	$rounding		''=No rounding
4567
 * 									'MU'=Round to Max unit price (MAIN_MAX_DECIMALS_UNIT)
4568
 *									'MT'=Round to Max for totals with Tax (MAIN_MAX_DECIMALS_TOT)
4569
 *									'MS'=Round to Max for stock quantity (MAIN_MAX_DECIMALS_STOCK)
4570
 *                                  'CR'=Currency rate
4571
 *									Numeric = Nb of digits for rounding
4572
 * 	@param	int		$alreadysqlnb	Put 1 if you know that content is already universal format number
4573
 *	@return	string					Amount with universal numeric format (Example: '99.99999').
4574
 *									If conversion fails, it return text unchanged if $rounding = '' or '0' if $rounding is defined.
4575
 *									If amount is null or '', it returns '' if $rounding = '' or '0' if $rounding is defined..
4576
 *
4577
 *	@see    price()					Opposite function of price2num
4578
 */
4579
function price2num($amount, $rounding = '', $alreadysqlnb = 0)
4580
{
4581
	global $langs, $conf;
4582
4583
	// Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
4584
	// Numbers must be '1234.56'
4585
	// Decimal delimiter for PHP and database SQL requests must be '.'
4586
	$dec = ','; $thousand = ' ';
4587
	if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal")  $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
4588
	if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
4589
	if ($thousand == 'None') $thousand = '';
4590
	elseif ($thousand == 'Space') $thousand = ' ';
4591
	//print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
4592
4593
	// Convert value to universal number format (no thousand separator, '.' as decimal separator)
4594
	if ($alreadysqlnb != 1)	// If not a PHP number or unknown, we change format
4595
	{
4596
		//print 'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
4597
4598
		// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
4599
		// to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
4600
		if (is_numeric($amount))
4601
		{
4602
			// We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
4603
			$temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
4604
			$temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
4605
			$nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
4606
			$amount = number_format($amount, $nbofdec, $dec, $thousand);
4607
		}
4608
		//print "QQ".$amount.'<br>';
4609
4610
		// Now make replace (the main goal of function)
4611
		if ($thousand != ',' && $thousand != '.') $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
4612
		$amount = str_replace(' ', '', $amount); // To avoid spaces
4613
		$amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
4614
		$amount = str_replace($dec, '.', $amount);
4615
	}
4616
4617
	// Now, make a rounding if required
4618
	if ($rounding)
4619
	{
4620
		$nbofdectoround = '';
4621
		if ($rounding == 'MU')     $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT;
4622
		elseif ($rounding == 'MT') $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT;
4623
		elseif ($rounding == 'MS') $nbofdectoround = empty($conf->global->MAIN_MAX_DECIMALS_STOCK) ? 5 : $conf->global->MAIN_MAX_DECIMALS_STOCK;
4624
		elseif ($rounding == 'CR') $nbofdectoround = 8;
4625
		elseif (is_numeric($rounding))  $nbofdectoround = $rounding;
4626
		//print "RR".$amount.' - '.$nbofdectoround.'<br>';
4627
		if (dol_strlen($nbofdectoround)) $amount = round($amount, $nbofdectoround); // $nbofdectoround can be 0.
4628
		else return 'ErrorBadParameterProvidedToFunction';
4629
		//print 'SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
4630
4631
		// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
4632
		// to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
4633
		if (is_numeric($amount))
4634
		{
4635
			// We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
4636
			$temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
4637
			$temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
4638
			$nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
4639
			$amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
4640
		}
4641
		//print "TT".$amount.'<br>';
4642
4643
		// Always make replace because each math function (like round) replace
4644
		// with local values and we want a number that has a SQL string format x.y
4645
		if ($thousand != ',' && $thousand != '.') $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
4646
		$amount = str_replace(' ', '', $amount); // To avoid spaces
4647
		$amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
4648
		$amount = str_replace($dec, '.', $amount);
4649
	}
4650
4651
	return $amount;
4652
}
4653
4654
/**
4655
 * Output a dimension with best unit
4656
 *
4657
 * @param   float       $dimension      Dimension
4658
 * @param   int         $unit           Unit scale of dimension (Example: 0=kg, -3=g, -6=mg, 98=ounce, 99=pound, ...)
4659
 * @param   string      $type           'weight', 'volume', ...
4660
 * @param   Translate   $outputlangs    Translate language object
4661
 * @param   int         $round          -1 = non rounding, x = number of decimal
4662
 * @param   string      $forceunitoutput    'no' or numeric (-3, -6, ...) compared to $unit (In most case, this value is value defined into $conf->global->MAIN_WEIGHT_DEFAULT_UNIT)
4663
 * @return  string                      String to show dimensions
4664
 */
4665
function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no')
4666
{
4667
	require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
4668
4669
	if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6))
4670
	{
4671
		$dimension = $dimension * 1000000;
4672
		$unit = $unit - 6;
4673
	} elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3))
4674
	{
4675
		$dimension = $dimension * 1000;
4676
		$unit = $unit - 3;
4677
	} elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6))
4678
	{
4679
		$dimension = $dimension / 1000000;
4680
		$unit = $unit + 6;
4681
	} elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3))
4682
	{
4683
		$dimension = $dimension / 1000;
4684
		$unit = $unit + 3;
4685
	}
4686
	// Special case when we want output unit into pound or ounce
4687
	/* TODO
4688
	if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
4689
	{
4690
	    $dimension = // convert dimension from standard unit into ounce or pound
4691
	    $unit = $forceunitoutput;
4692
	}
4693
	if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
4694
	{
4695
	    $dimension = // convert dimension from standard unit into ounce or pound
4696
	    $unit = $forceunitoutput;
4697
	}*/
4698
4699
	$ret = price($dimension, 0, $outputlangs, 0, 0, $round).' '.measuringUnitString(0, $type, $unit);
4700
4701
	return $ret;
4702
}
4703
4704
4705
/**
4706
 *	Return localtax rate for a particular vat, when selling a product with vat $vatrate, from a $thirdparty_buyer to a $thirdparty_seller
4707
 *  Note: This function applies same rules than get_default_tva
4708
 *
4709
 * 	@param	float		$vatrate		        Vat rate. Can be '8.5' or '8.5 (VATCODEX)' for example
4710
 * 	@param  int			$local		         	Local tax to search and return (1 or 2 return only tax rate 1 or tax rate 2)
4711
 *  @param  Societe		$thirdparty_buyer    	Object of buying third party
4712
 *  @param	Societe		$thirdparty_seller		Object of selling third party ($mysoc if not defined)
4713
 *  @param	int			$vatnpr					If vat rate is NPR or not
4714
 * 	@return	mixed			   					0 if not found, localtax rate if found
4715
 *  @see get_default_tva()
4716
 */
4717
function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
4718
{
4719
	global $db, $conf, $mysoc;
4720
4721
	if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) $thirdparty_seller = $mysoc;
4722
4723
	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);
4724
4725
	$vatratecleaned = $vatrate;
4726
	$reg = array();
4727
	if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
4728
	{
4729
		$vatratecleaned = trim($reg[1]);
4730
		$vatratecode = $reg[2];
4731
	}
4732
4733
	/*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
4734
	{
4735
		return 0;
4736
	}*/
4737
4738
	// Some test to guess with no need to make database access
4739
	if ($mysoc->country_code == 'ES') // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
4740
	{
4741
		if ($local == 1)
4742
		{
4743
			if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") return 0;
4744
			if ($thirdparty_seller->id == $mysoc->id)
4745
			{
4746
				if (!$thirdparty_buyer->localtax1_assuj) return 0;
4747
			} else {
4748
				if (!$thirdparty_seller->localtax1_assuj) return 0;
4749
			}
4750
		}
4751
4752
		if ($local == 2)
4753
		{
4754
			//if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
4755
			if (!$mysoc->localtax2_assuj) return 0; // If main vat is 0, IRPF may be different than 0.
4756
			if ($thirdparty_seller->id == $mysoc->id)
4757
			{
4758
				if (!$thirdparty_buyer->localtax2_assuj) return 0;
4759
			} else {
4760
				if (!$thirdparty_seller->localtax2_assuj) return 0;
4761
			}
4762
		}
4763
	} else {
4764
		if ($local == 1 && !$thirdparty_seller->localtax1_assuj) return 0;
4765
		if ($local == 2 && !$thirdparty_seller->localtax2_assuj) return 0;
4766
	}
4767
4768
	// For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
4769
	if (in_array($mysoc->country_code, array('ES')))
4770
	{
4771
		$conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
4772
	}
4773
4774
	// Search local taxes
4775
	if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY))
4776
	{
4777
		if ($local == 1)
4778
		{
4779
			if ($thirdparty_seller != $mysoc)
4780
			{
4781
				if (!isOnlyOneLocalTax($local))  // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
4782
				{
4783
					return $thirdparty_seller->localtax1_value;
4784
				}
4785
			} else // i am the seller
4786
			{
4787
				if (!isOnlyOneLocalTax($local))  // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
4788
				{
4789
					return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
4790
				}
4791
			}
4792
		}
4793
		if ($local == 2)
4794
		{
4795
			if ($thirdparty_seller != $mysoc)
4796
			{
4797
				if (!isOnlyOneLocalTax($local))  // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
4798
				// TODO We should also return value defined on thirdparty only if defined
4799
				{
4800
					return $thirdparty_seller->localtax2_value;
4801
				}
4802
			} else // i am the seller
4803
			{
4804
				if (in_array($mysoc->country_code, array('ES')))
4805
				{
4806
					return $thirdparty_buyer->localtax2_value;
4807
				} else {
4808
					return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
4809
				}
4810
			}
4811
		}
4812
	}
4813
4814
	// By default, search value of local tax on line of common tax
4815
	$sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
4816
   	$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4817
   	$sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$thirdparty_seller->country_code."'";
4818
   	$sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4819
   	if ($vatratecode) $sql .= " AND t.code ='".$vatratecode."'"; // If we have the code, we use it in priority
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $vatratecode does not seem to be defined for all execution paths leading up to this point.
Loading history...
4820
   	else $sql .= " AND t.recuperableonly ='".$vatnpr."'";
4821
   	dol_syslog("get_localtax", LOG_DEBUG);
4822
   	$resql = $db->query($sql);
4823
4824
   	if ($resql)
4825
   	{
4826
   		$obj = $db->fetch_object($resql);
4827
   		if ($local == 1) return $obj->localtax1;
4828
   		elseif ($local == 2) return $obj->localtax2;
4829
	}
4830
4831
	return 0;
4832
}
4833
4834
4835
/**
4836
 * Return true if LocalTax (1 or 2) is unique.
4837
 * Example: If localtax1 is 5 on line with highest common vat rate, return true
4838
 * Example: If localtax1 is 5:8:15 on line with highest common vat rate, return false
4839
 *
4840
 * @param   int 	$local	Local tax to test (1 or 2)
4841
 * @return  boolean 		True if LocalTax have multiple values, False if not
4842
 */
4843
function isOnlyOneLocalTax($local)
4844
{
4845
	$tax = get_localtax_by_third($local);
4846
4847
	$valors = explode(":", $tax);
4848
4849
	if (count($valors) > 1)
4850
	{
4851
		return false;
4852
	} else {
4853
		return true;
4854
	}
4855
}
4856
4857
/**
4858
 * Get values of localtaxes (1 or 2) for company country for the common vat with the highest value
4859
 *
4860
 * @param	int		$local 	LocalTax to get
4861
 * @return	number			Values of localtax
4862
 */
4863
function get_localtax_by_third($local)
4864
{
4865
	global $db, $mysoc;
4866
	$sql = "SELECT t.localtax1, t.localtax2 ";
4867
	$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=t.fk_pays";
4868
	$sql .= " WHERE c.code = '".$mysoc->country_code."' AND t.active = 1 AND t.taux=(";
4869
	$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";
4870
	$sql .= "  WHERE c.code = '".$mysoc->country_code."' AND tt.active = 1";
4871
	$sql .= "  )";
4872
4873
	$resql = $db->query($sql);
4874
	if ($resql)
4875
	{
4876
		$obj = $db->fetch_object($resql);
4877
		if ($local == 1) return $obj->localtax1;
4878
		elseif ($local == 2) return $obj->localtax2;
4879
	}
4880
4881
	return 0;
4882
}
4883
4884
4885
/**
4886
 *  Get vat main information from Id.
4887
 *  You can call getLocalTaxesFromRate after to get other fields.
4888
 *
4889
 *  @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.
4890
 *  @param	Societe	    $buyer         		Company object
4891
 *  @param	Societe	    $seller        		Company object
4892
 *  @param  int         $firstparamisid     1 if first param is id into table (use this if you can)
4893
 *  @return	array       	  				array('rowid'=> , 'code'=> ...)
4894
 *  @see getLocalTaxesFromRate()
4895
 */
4896
function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
4897
{
4898
	global $db, $mysoc;
4899
4900
	dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
4901
4902
	// Search local taxes
4903
	$sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy";
4904
	$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
4905
	if ($firstparamisid) $sql .= " WHERE t.rowid = ".(int) $vatrate;
4906
	else {
4907
		$vatratecleaned = $vatrate;
4908
		$vatratecode = '';
4909
		if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
4910
		{
4911
			$vatratecleaned = $reg[1];
4912
			$vatratecode = $reg[2];
4913
		}
4914
4915
		$sql .= ", ".MAIN_DB_PREFIX."c_country as c";
4916
		/*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 ??
4917
		else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";*/
4918
		$sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
4919
		$sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4920
		if ($vatratecode) $sql .= " AND t.code = '".$vatratecode."'";
4921
	}
4922
4923
	$resql = $db->query($sql);
4924
	if ($resql)
4925
	{
4926
		$obj = $db->fetch_object($resql);
4927
		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);
4928
		else return array();
4929
	} else dol_print_error($db);
4930
4931
	return array();
4932
}
4933
4934
/**
4935
 *  Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
4936
 *  This does not take into account the seller setup if subject to vat or not, only country.
4937
 *  TODO
4938
 *  This function is ALSO called to retrieve type for building PDF. Such call of function must be removed.
4939
 *  Instead this function must be called when adding a line to get the array of localtax and type, and then
4940
 *  provide it to the function calcul_price_total.
4941
 *
4942
 *  @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.
4943
 *  @param	int		    $local              Number of localtax (1 or 2, or 0 to return 1 & 2)
4944
 *  @param	Societe	    $buyer         		Company object
4945
 *  @param	Societe	    $seller        		Company object
4946
 *  @param  int         $firstparamisid     1 if first param is ID into table instead of Rate+code (use this if you can)
4947
 *  @return	array    	    				array(localtax_type1(1-6/0 if not found), rate localtax1, localtax_type2, rate localtax2, accountancycodecust, accountancycodesupp)
4948
 *  @see getTaxesFromId()
4949
 */
4950
function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
4951
{
4952
	global $db, $mysoc;
4953
4954
	dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
4955
4956
	// Search local taxes
4957
	$sql  = "SELECT t.taux as rate, t.code, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy";
4958
	$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
4959
	if ($firstparamisid) $sql .= " WHERE t.rowid = ".(int) $vatrate;
4960
	else {
4961
		$vatratecleaned = $vatrate;
4962
		$vatratecode = '';
4963
		$reg = array();
4964
		if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "x.x (yy)"
4965
		{
4966
			$vatratecleaned = $reg[1];
4967
			$vatratecode = $reg[2];
4968
		}
4969
4970
		$sql .= ", ".MAIN_DB_PREFIX."c_country as c";
4971
		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 ??
4972
		else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
4973
		$sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4974
		if ($vatratecode) $sql .= " AND t.code = '".$vatratecode."'";
4975
	}
4976
4977
	$resql = $db->query($sql);
4978
	if ($resql)
4979
	{
4980
		$obj = $db->fetch_object($resql);
4981
4982
		$vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
4983
4984
		if ($local == 1)
4985
		{
4986
			return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4987
		} elseif ($local == 2)
4988
		{
4989
			return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4990
		} else {
4991
			return array($obj->localtax1_type, get_localtax($vateratestring, 1, $buyer, $seller), $obj->localtax2_type, get_localtax($vateratestring, 2, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4992
		}
4993
	}
4994
4995
	return 0;
4996
}
4997
4998
/**
4999
 *	Return vat rate of a product in a particular selling country or default country vat if product is unknown
5000
 *  Function called by get_default_tva
5001
 *
5002
 *  @param	int			$idprod          	Id of product or 0 if not a predefined product
5003
 *  @param  Societe		$thirdparty_seller  Thirdparty with a ->country_code defined (FR, US, IT, ...)
5004
 *	@param	int			$idprodfournprice	Id product_fournisseur_price (for "supplier" proposal/order/invoice)
5005
 *  @return float|string   				    Vat rate to use with format 5.0 or '5.0 (XXX)'
5006
 *  @see get_product_localtax_for_country()
5007
 */
5008
function get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice = 0)
5009
{
5010
	global $db, $conf, $mysoc;
5011
5012
	require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
5013
5014
	$ret = 0;
5015
	$found = 0;
5016
5017
	if ($idprod > 0)
5018
	{
5019
		// Load product
5020
		$product = new Product($db);
5021
		$result = $product->fetch($idprod);
5022
5023
		if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
5024
		{
5025
			if ($idprodfournprice > 0)     // We want vat for product for a "supplier" object
5026
			{
5027
				$product->get_buyprice($idprodfournprice, 0, 0, 0);
5028
				$ret = $product->vatrate_supplier;
5029
				if ($product->default_vat_code) $ret .= ' ('.$product->default_vat_code.')';
5030
			} else {
5031
				$ret = $product->tva_tx; // Default vat of product we defined
5032
				if ($product->default_vat_code) $ret .= ' ('.$product->default_vat_code.')';
5033
			}
5034
			$found = 1;
5035
		} else {
5036
			// TODO Read default product vat according to countrycode and product. Vat for couple countrycode/product is a feature not implemeted yet.
5037
			// May be usefull/required if hidden option SERVICE_ARE_ECOMMERCE_200238EC is on
5038
		}
5039
	}
5040
5041
	if (!$found)
5042
	{
5043
		if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS))
5044
		{
5045
			// If vat of product for the country not found or not defined, we return the first higher vat of country.
5046
			$sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
5047
			$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
5048
			$sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
5049
			$sql .= " ORDER BY t.taux DESC, t.code ASC, t.recuperableonly ASC";
5050
			$sql .= $db->plimit(1);
5051
5052
			$resql = $db->query($sql);
5053
			if ($resql)
5054
			{
5055
				$obj = $db->fetch_object($resql);
5056
				if ($obj)
5057
				{
5058
					$ret = $obj->vat_rate;
5059
					if ($obj->default_vat_code) $ret .= ' ('.$obj->default_vat_code.')';
5060
				}
5061
				$db->free($sql);
5062
			} else dol_print_error($db);
5063
		} else $ret = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS; // Forced value if autodetect fails
5064
	}
5065
5066
	dol_syslog("get_product_vat_for_country: ret=".$ret);
5067
	return $ret;
5068
}
5069
5070
/**
5071
 *	Return localtax vat rate of a product in a particular selling country or default country vat if product is unknown
5072
 *
5073
 *  @param	int		$idprod         		Id of product
5074
 *  @param  int		$local          		1 for localtax1, 2 for localtax 2
5075
 *  @param  Societe	$thirdparty_seller    	Thirdparty with a ->country_code defined (FR, US, IT, ...)
5076
 *  @return int             				<0 if KO, Vat rate if OK
5077
 *  @see get_product_vat_for_country()
5078
 */
5079
function get_product_localtax_for_country($idprod, $local, $thirdparty_seller)
5080
{
5081
	global $db, $mysoc;
5082
5083
	if (!class_exists('Product')) {
5084
		require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
5085
	}
5086
5087
	$ret = 0;
5088
	$found = 0;
5089
5090
	if ($idprod > 0)
5091
	{
5092
		// Load product
5093
		$product = new Product($db);
5094
		$result = $product->fetch($idprod);
5095
5096
		if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
5097
		{
5098
			/* Not defined yet, so we don't use this
5099
			if ($local==1) $ret=$product->localtax1_tx;
5100
			elseif ($local==2) $ret=$product->localtax2_tx;
5101
			$found=1;
5102
			*/
5103
		} else {
5104
			// TODO Read default product vat according to countrycode and product
5105
		}
5106
	}
5107
5108
	if (!$found)
5109
	{
5110
		// If vat of product for the country not found or not defined, we return higher vat of country.
5111
		$sql = "SELECT taux as vat_rate, localtax1, localtax2";
5112
		$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
5113
		$sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
5114
		$sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
5115
		$sql .= $db->plimit(1);
5116
5117
		$resql = $db->query($sql);
5118
		if ($resql)
5119
		{
5120
			$obj = $db->fetch_object($resql);
5121
			if ($obj)
5122
			{
5123
				if ($local == 1) $ret = $obj->localtax1;
5124
				elseif ($local == 2) $ret = $obj->localtax2;
5125
			}
5126
		} else dol_print_error($db);
5127
	}
5128
5129
	dol_syslog("get_product_localtax_for_country: ret=".$ret);
5130
	return $ret;
5131
}
5132
5133
/**
5134
 *	Function that return vat rate of a product line (according to seller, buyer and product vat rate)
5135
 *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
5136
 *	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
5137
 *	 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.
5138
 *	 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
5139
 *	 Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise avec num TVA) intra alors TVA par defaut=0. Fin de regle
5140
 *	 Sinon TVA proposee par defaut=0. Fin de regle.
5141
 *
5142
 *	@param	Societe		$thirdparty_seller    	Objet societe vendeuse
5143
 *	@param  Societe		$thirdparty_buyer   	Objet societe acheteuse
5144
 *	@param  int			$idprod					Id product
5145
 *	@param	int			$idprodfournprice		Id product_fournisseur_price (for supplier order/invoice)
5146
 *	@return float|string   				      	Vat rate to use with format 5.0 or '5.0 (XXX)', -1 if we can't guess it
5147
 *  @see get_default_npr(), get_default_localtax()
5148
 */
5149
function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
5150
{
5151
	global $conf;
5152
5153
	require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
5154
5155
	// Note: possible values for tva_assuj are 0/1 or franchise/reel
5156
	$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;
5157
5158
	$seller_country_code = $thirdparty_seller->country_code;
5159
	$seller_in_cee = isInEEC($thirdparty_seller);
5160
5161
	$buyer_country_code = $thirdparty_buyer->country_code;
5162
	$buyer_in_cee = isInEEC($thirdparty_buyer);
5163
5164
	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 : ''));
5165
5166
	// 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)
5167
	// we use the buyer VAT.
5168
	if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC))
5169
	{
5170
		if ($seller_in_cee && $buyer_in_cee && !$thirdparty_buyer->isACompany())
5171
		{
5172
			//print 'VATRULE 0';
5173
			return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
5174
		}
5175
	}
5176
5177
	// If seller does not use VAT
5178
	if (!$seller_use_vat)
5179
	{
5180
		//print 'VATRULE 1';
5181
		return 0;
5182
	}
5183
5184
	// 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.
5185
5186
	// Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
5187
	if (($seller_country_code == $buyer_country_code)
5188
	|| (in_array($seller_country_code, array('FR,MC')) && in_array($buyer_country_code, array('FR', 'MC')))) // Warning ->country_code not always defined
5189
	{
5190
		//print 'VATRULE 2';
5191
		return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
5192
	}
5193
5194
	// 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.
5195
	// Not supported
5196
5197
	// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
5198
	// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
5199
	if (($seller_in_cee && $buyer_in_cee))
5200
	{
5201
		$isacompany = $thirdparty_buyer->isACompany();
5202
		if ($isacompany)
5203
		{
5204
			//print 'VATRULE 3';
5205
			return 0;
5206
		} else {
5207
			//print 'VATRULE 4';
5208
			return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
5209
		}
5210
	}
5211
5212
	// Si (vendeur en France et acheteur hors Communaute europeenne et acheteur particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
5213
	if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee) && !$thirdparty_buyer->isACompany()) {
5214
		return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
5215
	}
5216
5217
	// Sinon la TVA proposee par defaut=0. Fin de regle.
5218
	// Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
5219
	//print 'VATRULE 5';
5220
	return 0;
5221
}
5222
5223
5224
/**
5225
 *	Fonction qui renvoie si tva doit etre tva percue recuperable
5226
 *
5227
 *	@param	Societe		$thirdparty_seller    	Thirdparty seller
5228
 *	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
5229
 *  @param  int			$idprod                 Id product
5230
 *  @param	int			$idprodfournprice		Id supplier price for product
5231
 *	@return float       			        	0 or 1
5232
 *  @see get_default_tva(), get_default_localtax()
5233
 */
5234
function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
5235
{
5236
	global $db;
5237
5238
	if ($idprodfournprice > 0)
5239
	{
5240
		if (!class_exists('ProductFournisseur')) {
5241
			require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
5242
		}
5243
		$prodprice = new ProductFournisseur($db);
5244
		$prodprice->fetch_product_fournisseur_price($idprodfournprice);
5245
		return $prodprice->fourn_tva_npr;
5246
	} elseif ($idprod > 0)
5247
	{
5248
		if (!class_exists('Product')) {
5249
			require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
5250
		}
5251
		$prod = new Product($db);
5252
		$prod->fetch($idprod);
5253
		return $prod->tva_npr;
5254
	}
5255
5256
	return 0;
5257
}
5258
5259
/**
5260
 *	Function that return localtax of a product line (according to seller, buyer and product vat rate)
5261
 *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
5262
 *	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
5263
 *	 Sinon TVA proposee par defaut=0. Fin de regle.
5264
 *
5265
 *	@param	Societe		$thirdparty_seller    	Thirdparty seller
5266
 *	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
5267
 *  @param	int			$local					Localtax to process (1 or 2)
5268
 *	@param  int			$idprod					Id product
5269
 *	@return integer        				       	localtax, -1 si ne peut etre determine
5270
 *  @see get_default_tva(), get_default_npr()
5271
 */
5272
function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
5273
{
5274
	global $mysoc;
5275
5276
	if (!is_object($thirdparty_seller)) return -1;
5277
	if (!is_object($thirdparty_buyer)) return -1;
5278
5279
	if ($local == 1) // Localtax 1
5280
	{
5281
		if ($mysoc->country_code == 'ES')
5282
		{
5283
			if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) return 0;
5284
		} else {
5285
			// Si vendeur non assujeti a Localtax1, localtax1 par default=0
5286
			if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) return 0;
5287
			if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') return 0;
5288
		}
5289
	} elseif ($local == 2) //I Localtax 2
5290
	{
5291
		// Si vendeur non assujeti a Localtax2, localtax2 par default=0
5292
		if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) return 0;
5293
		if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') return 0;
5294
	}
5295
5296
	if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code)
5297
	{
5298
		return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
5299
	}
5300
5301
	return 0;
5302
}
5303
5304
/**
5305
 *	Return yes or no in current language
5306
 *
5307
 *	@param	string|int	$yesno			Value to test (1, 'yes', 'true' or 0, 'no', 'false')
5308
 *	@param	integer		$case			1=Yes/No, 0=yes/no, 2=Disabled checkbox, 3=Disabled checkbox + Yes/No
5309
 *	@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.
5310
 *	@return	string						HTML string
5311
 */
5312
function yn($yesno, $case = 1, $color = 0)
5313
{
5314
	global $langs;
5315
	$result = 'unknown'; $classname = '';
5316
	if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') 	// A mettre avant test sur no a cause du == 0
5317
	{
5318
		$result = $langs->trans('yes');
5319
		if ($case == 1 || $case == 3) $result = $langs->trans("Yes");
5320
		if ($case == 2) $result = '<input type="checkbox" value="1" checked disabled>';
5321
		if ($case == 3) $result = '<input type="checkbox" value="1" checked disabled> '.$result;
5322
5323
		$classname = 'ok';
5324
	} elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false')
5325
	{
5326
		$result = $langs->trans("no");
5327
		if ($case == 1 || $case == 3) $result = $langs->trans("No");
5328
		if ($case == 2) $result = '<input type="checkbox" value="0" disabled>';
5329
		if ($case == 3) $result = '<input type="checkbox" value="0" disabled> '.$result;
5330
5331
		if ($color == 2) $classname = 'ok';
5332
		else $classname = 'error';
5333
	}
5334
	if ($color) return '<font class="'.$classname.'">'.$result.'</font>';
5335
	return $result;
5336
}
5337
5338
/**
5339
 *	Return a path to have a the directory according to object where files are stored.
5340
 *  New usage:       $conf->module->multidir_output[$object->entity].'/'.get_exdir(0, 0, 0, 1, $object, $modulepart)
5341
 *         or:       $conf->module->dir_output.'/'.get_exdir(0, 0, 0, 1, $object, $modulepart)     if multidir_output not defined.
5342
 *  Example our with new usage:       $object is invoice -> 'INYYMM-ABCD'
5343
 *  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/"
5344
 *
5345
 *	@param	string	$num            Id of object (deprecated, $object will be used in future)
5346
 *	@param  int		$level		    Level of subdirs to return (1, 2 or 3 levels). (deprecated, global option will be used in future)
5347
 * 	@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)
5348
 *  @param  int		$withoutslash   0=With slash at end (except if '/', we return ''), 1=without slash at end
5349
 *  @param	Object	$object			Object
5350
 *  @param	string	$modulepart		Type of object ('invoice_supplier, 'donation', 'invoice', ...')
5351
 *  @return	string					Dir to use ending. Example '' or '1/' or '1/2/'
5352
 */
5353
function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart)
5354
{
5355
	global $conf;
5356
5357
	$path = '';
5358
5359
	$arrayforoldpath = array('cheque', 'user', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment');
5360
	if (!empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) $arrayforoldpath[] = 'product';
5361
	if (!empty($level) && in_array($modulepart, $arrayforoldpath))
5362
	{
5363
		// This part should be removed once all code is using "get_exdir" to forge path, with all parameters provided.
5364
		if (empty($alpha)) $num = preg_replace('/([^0-9])/i', '', $num);
5365
		else $num = preg_replace('/^.*\-/i', '', $num);
5366
		$num = substr("000".$num, -$level);
5367
		if ($level == 1) $path = substr($num, 0, 1);
5368
		if ($level == 2) $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
5369
		if ($level == 3) $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
5370
	} else {
5371
		// TODO
5372
		// We will enhance here a common way of forging path for document storage
5373
		// Here, object->id, object->ref and modulepart are required.
5374
		//var_dump($modulepart);
5375
		if (in_array($modulepart, array('thirdparty', 'contact', 'member', 'propal', 'proposal', 'commande', 'order', 'facture', 'invoice',
5376
			'supplier_order', 'supplier_proposal', 'shipment', 'contract', 'expensereport', 'ficheinter')))
5377
		{
5378
			$path = ($object->ref ? $object->ref : $object->id);
5379
		}
5380
	}
5381
5382
	if (empty($withoutslash) && !empty($path)) $path .= '/';
5383
5384
	return $path;
5385
}
5386
5387
/**
5388
 *	Creation of a directory (this can create recursive subdir)
5389
 *
5390
 *	@param	string		$dir		Directory to create (Separator must be '/'. Example: '/mydir/mysubdir')
5391
 *	@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)
5392
 *  @param	string|null	$newmask	Mask for new file (Defaults to $conf->global->MAIN_UMASK or 0755 if unavailable). Example: '0444'
5393
 *	@return int         			< 0 if KO, 0 = already exists, > 0 if OK
5394
 */
5395
function dol_mkdir($dir, $dataroot = '', $newmask = null)
5396
{
5397
	global $conf;
5398
5399
	dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
5400
5401
	$dir_osencoded = dol_osencode($dir);
5402
	if (@is_dir($dir_osencoded)) return 0;
5403
5404
	$nberr = 0;
5405
	$nbcreated = 0;
5406
5407
	$ccdir = '';
5408
	if (!empty($dataroot)) {
5409
		// Remove data root from loop
5410
		$dir = str_replace($dataroot.'/', '', $dir);
5411
		$ccdir = $dataroot.'/';
5412
	}
5413
5414
	$cdir = explode("/", $dir);
5415
	$num = count($cdir);
5416
	for ($i = 0; $i < $num; $i++)
5417
	{
5418
		if ($i > 0) $ccdir .= '/'.$cdir[$i];
5419
		else $ccdir .= $cdir[$i];
5420
		if (preg_match("/^.:$/", $ccdir, $regs)) continue; // Si chemin Windows incomplet, on poursuit par rep suivant
5421
5422
		// Attention, le is_dir() peut echouer bien que le rep existe.
5423
		// (ex selon config de open_basedir)
5424
		if ($ccdir)
5425
		{
5426
			$ccdir_osencoded = dol_osencode($ccdir);
5427
			if (!@is_dir($ccdir_osencoded))
5428
			{
5429
				dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
5430
5431
				umask(0);
5432
				$dirmaskdec = octdec($newmask);
5433
				if (empty($newmask)) {
5434
					$dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
5435
				}
5436
				$dirmaskdec |= octdec('0111'); // Set x bit required for directories
5437
				if (!@mkdir($ccdir_osencoded, $dirmaskdec))
5438
				{
5439
					// Si le is_dir a renvoye une fausse info, alors on passe ici.
5440
					dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
5441
					$nberr++;
5442
				} else {
5443
					dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
5444
					$nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
5445
					$nbcreated++;
5446
				}
5447
			} else {
5448
				$nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
5449
			}
5450
		}
5451
	}
5452
	return ($nberr ? -$nberr : $nbcreated);
5453
}
5454
5455
5456
/**
5457
 *	Return picto saying a field is required
5458
 *
5459
 *	@return  string		Chaine avec picto obligatoire
5460
 */
5461
function picto_required()
5462
{
5463
	return '<span class="fieldrequired">*</span>';
5464
}
5465
5466
5467
/**
5468
 *	Clean a string from all HTML tags and entities.
5469
 *  This function differs from strip_tags because:
5470
 *  - <br> are replaced with \n if removelinefeed=0 or 1
5471
 *  - if entities are found, they are decoded BEFORE the strip
5472
 *  - you can decide to convert line feed into a space
5473
 *
5474
 *	@param	string	$stringtoclean		String to clean
5475
 *	@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..."
5476
 *  @param  string	$pagecodeto      	Encoding of input/output string
5477
 *  @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)
5478
 *	@return string	    				String cleaned
5479
 *
5480
 * 	@see	dol_escape_htmltag() strip_tags() dol_string_onlythesehtmltags() dol_string_neverthesehtmltags(), dolStripPhpCode()
5481
 */
5482
function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0)
5483
{
5484
	if ($removelinefeed == 2) $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
5485
	$temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
5486
5487
	if ($strip_tags) {
5488
		$temp = strip_tags($temp);
5489
	} else {
5490
		$pattern = "/<[^<>]+>/";
5491
		// Exemple of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
5492
		$temp = preg_replace($pattern, "", $temp); // pass 1
5493
		// $temp after pass 1: <a href="/myurl" title="A title">0000-021
5494
		$temp = preg_replace($pattern, "", $temp); // pass 2
5495
		// $temp after pass 2: 0000-021
5496
	}
5497
5498
	$temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
5499
5500
	// Supprime aussi les retours
5501
	if ($removelinefeed == 1) $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
5502
5503
	// et les espaces doubles
5504
	while (strpos($temp, "  "))
5505
	{
5506
		$temp = str_replace("  ", " ", $temp);
5507
	}
5508
5509
	return trim($temp);
5510
}
5511
5512
/**
5513
 *	Clean a string to keep only desirable HTML tags.
5514
 *
5515
 *	@param	string	$stringtoclean			String to clean
5516
 *  @param	boolean	$cleanalsosomestyles	Remove absolute/fixed positioning from inline styles
5517
 *  @param	boolean	$removeclassattribute	Remove the class attribute from tags
5518
 *	@return string	    					String cleaned
5519
 *
5520
 * 	@see	dol_escape_htmltag() strip_tags() dol_string_nohtmltag() dol_string_neverthesehtmltags()
5521
 */
5522
function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1)
5523
{
5524
	$allowed_tags = array(
5525
		"html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li", "link",
5526
		"ol", "p", "q", "s", "section", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6"
5527
	);
5528
	$allowed_tags_string = join("><", $allowed_tags);
5529
	$allowed_tags_string = preg_replace('/^>/', '', $allowed_tags_string);
5530
	$allowed_tags_string = preg_replace('/<$/', '', $allowed_tags_string);
5531
	$allowed_tags_string = '<'.$allowed_tags_string.'>';
5532
5533
	if ($cleanalsosomestyles) {
5534
		$stringtoclean = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/i', '', $stringtoclean); // Note: If hacker try to introduce css comment into string to bypass this regex, the string must also be encoded by the dol_htmlentitiesbr during output so it become harmless
5535
	}
5536
	if ($removeclassattribute) {
5537
		$stringtoclean = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $stringtoclean);
5538
	}
5539
5540
	$temp = strip_tags($stringtoclean, $allowed_tags_string);
5541
5542
	return $temp;
5543
}
5544
5545
/**
5546
 *	Clean a string from some undesirable HTML tags.
5547
 *  Note. Not enough secured as dol_string_onlythesehtmltags().
5548
 *
5549
 *	@param	string	$stringtoclean			String to clean
5550
 *  @param	array	$disallowed_tags		Array of tags not allowed
5551
 *  @param	string	$cleanalsosomestyles	Clean also some tags
5552
 *	@return string	    					String cleaned
5553
 *
5554
 * 	@see	dol_escape_htmltag() strip_tags() dol_string_nohtmltag() dol_string_onlythesehtmltags()
5555
 */
5556
function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
5557
{
5558
	$temp = $stringtoclean;
5559
	foreach ($disallowed_tags as $tagtoremove)
5560
	{
5561
		$temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
5562
		$temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
5563
	}
5564
5565
	if ($cleanalsosomestyles) {
5566
		$temp = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/', '', $temp); // Note: If hacker try to introduce css comment into string to avoid this, string should be encoded by the dol_htmlentitiesbr so be harmless
5567
	}
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
 * @param   string  $charset    Charset of $text string (UTF-8 by default)
5579
 * @return	string				Output text
5580
 * @see dol_nboflines_bis(), dol_string_nohtmltag(), dol_escape_htmltag()
5581
 */
5582
function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
5583
{
5584
	if ($nboflines == 1)
5585
	{
5586
		if (dol_textishtml($text))
5587
		{
5588
			$firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
5589
			$firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
5590
		} else {
5591
			$firstline = preg_replace('/[\n\r].*/', '', $text);
5592
		}
5593
		return $firstline.((strlen($firstline) != strlen($text)) ? '...' : '');
5594
	} else {
5595
		$ishtml = 0;
5596
		if (dol_textishtml($text))
5597
		{
5598
			$text = preg_replace('/\n/', '', $text);
5599
			$ishtml = 1;
5600
			$repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5601
		} else {
5602
			$repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5603
		}
5604
5605
		$text = strtr($text, $repTable);
5606
		if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; } // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
5607
		else $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
5608
		$a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5609
5610
		$firstline = '';
5611
		$i = 0;
5612
		$nba = count($a); // 2x nb of lines in $a because $a contains also a line for each new line separator
5613
		while (($i < $nba) && ($i < ($nboflines * 2)))
5614
		{
5615
			if ($i % 2 == 0) $firstline .= $a[$i];
5616
			elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) $firstline .= ($ishtml ? "<br>\n" : "\n");
5617
			$i++;
5618
		}
5619
		unset($a);
5620
		return $firstline.(($i < $nba) ? '...' : '');
5621
	}
5622
}
5623
5624
5625
/**
5626
 * Replace CRLF in string with a HTML BR tag.
5627
 * WARNING: The content after operation contains some HTML tags (the <br>) so be sure to also have encode the special chars of stringtoencode into HTML before.
5628
 *
5629
 * @param	string	$stringtoencode		String to encode
5630
 * @param	int     $nl2brmode			0=Adding br before \n, 1=Replacing \n by br
5631
 * @param   bool	$forxml             false=Use <br>, true=Use <br />
5632
 * @return	string						String encoded
5633
 * @see dol_nboflines(), dolGetFirstLineOfText()
5634
 */
5635
function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
5636
{
5637
	if (!$nl2brmode) {
5638
		return nl2br($stringtoencode, $forxml);
5639
	} else {
5640
		$ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
5641
		return $ret;
5642
	}
5643
}
5644
5645
5646
/**
5647
 *	This function is called to encode a string into a HTML string but differs from htmlentities because
5648
 * 	a detection is done before to see if text is already HTML or not. Also, all entities but &,<,>," are converted.
5649
 *  This permits to encode special chars to entities with no double encoding for already encoded HTML strings.
5650
 * 	This function also remove last EOL or BR if $removelasteolbr=1 (default).
5651
 *  For PDF usage, you can show text by 2 ways:
5652
 *              - writeHTMLCell -> param must be encoded into HTML.
5653
 *              - MultiCell -> param must not be encoded into HTML.
5654
 *              Because writeHTMLCell convert also \n into <br>, if function
5655
 *              is used to build PDF, nl2brmode must be 1.
5656
 *
5657
 *	@param	string	$stringtoencode		String to encode
5658
 *	@param	int		$nl2brmode			0=Adding br before \n, 1=Replacing \n by br (for use with FPDF writeHTMLCell function for example)
5659
 *  @param  string	$pagecodefrom       Pagecode stringtoencode is encoded
5660
 *  @param	int		$removelasteolbr	1=Remove last br or lasts \n (default), 0=Do nothing
5661
 *  @return	string						String encoded
5662
 */
5663
function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
5664
{
5665
	$newstring = $stringtoencode;
5666
	if (dol_textishtml($stringtoencode))	// Check if text is already HTML or not
5667
	{
5668
		$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.
5669
		if ($removelasteolbr) $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
5670
		$newstring = strtr($newstring, array('&'=>'__and__', '<'=>'__lt__', '>'=>'__gt__', '"'=>'__dquot__'));
5671
		$newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
5672
		$newstring = strtr($newstring, array('__and__'=>'&', '__lt__'=>'<', '__gt__'=>'>', '__dquot__'=>'"'));
5673
	} else {
5674
		if ($removelasteolbr) $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
5675
		$newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
5676
	}
5677
	// Other substitutions that htmlentities does not do
5678
	//$newstring=str_replace(chr(128),'&euro;',$newstring);	// 128 = 0x80. Not in html entity table.     // Seems useles with TCPDF. Make bug with UTF8 languages
5679
	return $newstring;
5680
}
5681
5682
/**
5683
 *	This function is called to decode a HTML string (it decodes entities and br tags)
5684
 *
5685
 *	@param	string	$stringtodecode		String to decode
5686
 *	@param	string	$pagecodeto			Page code for result
5687
 *	@return	string						String decoded
5688
 */
5689
function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
5690
{
5691
	$ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT, $pagecodeto);
5692
	$ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
5693
	$ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
5694
	$ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
5695
	$ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
5696
	return $ret;
5697
}
5698
5699
/**
5700
 *	This function remove all ending \n and br at end
5701
 *
5702
 *	@param	string	$stringtodecode		String to decode
5703
 *	@return	string						String decoded
5704
 */
5705
function dol_htmlcleanlastbr($stringtodecode)
5706
{
5707
	$ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $stringtodecode);
5708
	return $ret;
5709
}
5710
5711
/**
5712
 * Replace html_entity_decode functions to manage errors
5713
 *
5714
 * @param   string	$a					Operand a
5715
 * @param   string	$b					Operand b (ENT_QUOTES=convert simple and double quotes)
5716
 * @param   string	$c					Operand c
5717
 * @param	string	$keepsomeentities	Entities but &amp;, <, >, " are not converted.
5718
 * @return  string						String decoded
5719
 */
5720
function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
5721
{
5722
	$newstring = $a;
5723
	if ($keepsomeentities) $newstring = strtr($newstring, array('&amp;'=>'__andamp__', '&lt;'=>'__andlt__', '&gt;'=>'__andgt__', '"'=>'__dquot__'));
5724
	$newstring = html_entity_decode($newstring, $b, $c);
5725
	if ($keepsomeentities) $newstring = strtr($newstring, array('__andamp__'=>'&amp;', '__andlt__'=>'&lt;', '__andgt__'=>'&gt;', '__dquot__'=>'"'));
5726
	return $newstring;
5727
}
5728
5729
/**
5730
 * Replace htmlentities functions.
5731
 * Goal of this function is to be sure to have default values of htmlentities that match what we need.
5732
 *
5733
 * @param   string  $string         The input string to encode
5734
 * @param   int     $flags          Flags (see PHP doc above)
5735
 * @param   string  $encoding       Encoding page code
5736
 * @param   bool    $double_encode  When double_encode is turned off, PHP will not encode existing html entities
5737
 * @return  string  $ret            Encoded string
5738
 */
5739
function dol_htmlentities($string, $flags = null, $encoding = 'UTF-8', $double_encode = false)
5740
{
5741
	return htmlentities($string, $flags, $encoding, $double_encode);
5742
}
5743
5744
/**
5745
 *	Check if a string is a correct iso string
5746
 *	If not, it will we considered not HTML encoded even if it is by FPDF.
5747
 *	Example, if string contains euro symbol that has ascii code 128
5748
 *
5749
 *	@param	string		$s      	String to check
5750
 *  @param	string		$clean		Clean if it is not an ISO. Warning, if file is utf8, you will get a bad formated file.
5751
 *	@return	int|string  	   		0 if bad iso, 1 if good iso, Or the clean string if $clean is 1
5752
 */
5753
function dol_string_is_good_iso($s, $clean = 0)
5754
{
5755
	$len = dol_strlen($s);
5756
	$out = '';
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; } elseif ($ordchar > 126 && $ordchar < 160) { $ok = 0; break; } elseif ($clean) {
5763
			$out .= $s[$scursor];
5764
		}
5765
	}
5766
	if ($clean) return $out;
5767
	return $ok;
5768
}
5769
5770
/**
5771
 *	Return nb of lines of a clear text
5772
 *
5773
 *	@param	string	$s			String to check
5774
 * 	@param	int     $maxchar	Not yet used
5775
 *	@return	int					Number of lines
5776
 *  @see	dol_nboflines_bis(), dolGetFirstLineOfText()
5777
 */
5778
function dol_nboflines($s, $maxchar = 0)
5779
{
5780
	if ($s == '') return 0;
5781
	$arraystring = explode("\n", $s);
5782
	$nb = count($arraystring);
5783
5784
	return $nb;
5785
}
5786
5787
5788
/**
5789
 *	Return nb of lines of a formated text with \n and <br> (WARNING: string must not have mixed \n and br separators)
5790
 *
5791
 *	@param	string	$text      		Text
5792
 *	@param	int		$maxlinesize  	Largeur de ligne en caracteres (ou 0 si pas de limite - defaut)
5793
 * 	@param	string	$charset		Give the charset used to encode the $text variable in memory.
5794
 *	@return int						Number of lines
5795
 *	@see	dol_nboflines(), dolGetFirstLineOfText()
5796
 */
5797
function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
5798
{
5799
	$repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5800
	if (dol_textishtml($text)) $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5801
5802
	$text = strtr($text, $repTable);
5803
	if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; } // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
5804
	else $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
5805
	$a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5806
5807
	$nblines = (int) floor((count($a) + 1) / 2);
5808
	// count possible auto line breaks
5809
	if ($maxlinesize)
5810
	{
5811
		foreach ($a as $line)
5812
		{
5813
			if (dol_strlen($line) > $maxlinesize)
5814
			{
5815
				//$line_dec = html_entity_decode(strip_tags($line));
5816
				$line_dec = html_entity_decode($line);
5817
				if (dol_strlen($line_dec) > $maxlinesize)
5818
				{
5819
					$line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
5820
					$nblines += substr_count($line_dec, '\n');
5821
				}
5822
			}
5823
		}
5824
	}
5825
5826
	unset($a);
5827
	return $nblines;
5828
}
5829
5830
/**
5831
 *	Return if a text is a html content
5832
 *
5833
 *	@param	string	$msg		Content to check
5834
 *	@param	int		$option		0=Full detection, 1=Fast check
5835
 *	@return	boolean				true/false
5836
 *	@see	dol_concatdesc()
5837
 */
5838
function dol_textishtml($msg, $option = 0)
5839
{
5840
	if ($option == 1)
5841
	{
5842
		if (preg_match('/<html/i', $msg))				return true;
5843
		elseif (preg_match('/<body/i', $msg))			return true;
5844
		elseif (preg_match('/<\/textarea/i', $msg))	  return true;
5845
		elseif (preg_match('/<br/i', $msg))				return true;
5846
		return false;
5847
	} else {
5848
		if (preg_match('/<html/i', $msg))				return true;
5849
		elseif (preg_match('/<body/i', $msg))			return true;
5850
		elseif (preg_match('/<\/textarea/i', $msg))	  return true;
5851
		elseif (preg_match('/<(b|em|i|u)>/i', $msg))		return true;
5852
		elseif (preg_match('/<br\/>/i', $msg))	  return true;
5853
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i', $msg)) 	  return true;
5854
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*>/i', $msg)) return true;
5855
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/>/i', $msg)) return true;
5856
		elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) return true; // must accept <img src="http://example.com/aaa.png" />
5857
		elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) return true; // must accept <a href="http://example.com/aaa.png" />
5858
		elseif (preg_match('/<h[0-9]>/i', $msg))			return true;
5859
		elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg))	return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
5860
		elseif (preg_match('/&#[0-9]{2,3};/i', $msg))	return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
5861
5862
		return false;
5863
	}
5864
}
5865
5866
/**
5867
 *  Concat 2 descriptions with a new line between them (second operand after first one with appropriate new line separator)
5868
 *  text1 html + text2 html => text1 + '<br>' + text2
5869
 *  text1 html + text2 txt  => text1 + '<br>' + dol_nl2br(text2)
5870
 *  text1 txt  + text2 html => dol_nl2br(text1) + '<br>' + text2
5871
 *  text1 txt  + text2 txt  => text1 + '\n' + text2
5872
 *
5873
 *  @param  string  $text1          Text 1
5874
 *  @param  string  $text2          Text 2
5875
 *  @param  bool    $forxml         true=Use <br /> instead of <br> if we have to add a br tag
5876
 *  @param  bool    $invert         invert order of description lines (we often use config MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION in this parameter)
5877
 *  @return string                  Text 1 + new line + Text2
5878
 *  @see    dol_textishtml()
5879
 */
5880
function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
5881
{
5882
	if (!empty($invert))
5883
	{
5884
			$tmp = $text1;
5885
			$text1 = $text2;
5886
			$text2 = $tmp;
5887
	}
5888
5889
	$ret = '';
5890
	$ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
5891
	$ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br \>\n" : "<br>\n") : "\n") : "";
5892
	$ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
5893
	return $ret;
5894
}
5895
5896
5897
5898
/**
5899
 * Return array of possible common substitutions. This includes several families like: 'system', 'mycompany', 'object', 'objectamount', 'date', 'user'
5900
 *
5901
 * @param	Translate	$outputlangs	Output language
5902
 * @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)
5903
 * @param   array       $exclude        Array of family keys we want to exclude. For example array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...)
5904
 * @param   Object      $object         Object for keys on object
5905
 * @return	array						Array of substitutions
5906
 * @see setSubstitFromObject()
5907
 */
5908
function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null)
5909
{
5910
	global $db, $conf, $mysoc, $user, $extrafields;
5911
5912
	$substitutionarray = array();
5913
5914
	if (empty($exclude) || !in_array('user', $exclude))
5915
	{
5916
		// Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
5917
		// this will include signature content first and then replace var found into content of signature
5918
		$signature = $user->signature;
5919
		$substitutionarray = array_merge($substitutionarray, array(
5920
			'__USER_SIGNATURE__' => (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '')
5921
		)
5922
			);
5923
5924
		$substitutionarray = array_merge($substitutionarray, array(
5925
		'__USER_ID__' => (string) $user->id,
5926
		'__USER_LOGIN__' => (string) $user->login,
5927
		'__USER_EMAIL__' => (string) $user->email,
5928
		'__USER_LASTNAME__' => (string) $user->lastname,
5929
		'__USER_FIRSTNAME__' => (string) $user->firstname,
5930
		'__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
5931
		'__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
5932
		'__USER_REMOTE_IP__' => (string) getUserRemoteIP()
5933
		)
5934
			);
5935
	}
5936
	if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc))
5937
	{
5938
		$substitutionarray = array_merge($substitutionarray, array(
5939
			'__MYCOMPANY_NAME__'    => $mysoc->name,
5940
			'__MYCOMPANY_EMAIL__'   => $mysoc->email,
5941
			'__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
5942
			'__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
5943
			'__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
5944
			'__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
5945
			'__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
5946
			'__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
5947
			'__MYCOMPANY_CAPITAL__' => $mysoc->capital,
5948
			'__MYCOMPANY_FULLADDRESS__' => $mysoc->getFullAddress(1, ', '),
5949
			'__MYCOMPANY_ADDRESS__' => $mysoc->address,
5950
			'__MYCOMPANY_ZIP__'     => $mysoc->zip,
5951
			'__MYCOMPANY_TOWN__'    => $mysoc->town,
5952
			'__MYCOMPANY_COUNTRY__'    => $mysoc->country,
5953
			'__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
5954
			'__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
5955
			'__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
5956
		));
5957
	}
5958
5959
	if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude)))
5960
	{
5961
		if ($onlykey)
5962
		{
5963
			$substitutionarray['__ID__'] = '__ID__';
5964
			$substitutionarray['__REF__'] = '__REF__';
5965
			$substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
5966
			$substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
5967
			$substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
5968
			$substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
5969
			$substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
5970
5971
			if (!empty($conf->societe->enabled))	// Most objects are concerned
5972
			{
5973
				$substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
5974
				$substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
5975
				$substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
5976
				$substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
5977
				$substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
5978
				$substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
5979
				$substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
5980
				$substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
5981
				$substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
5982
				$substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
5983
				$substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
5984
				$substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
5985
				$substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
5986
				$substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
5987
				$substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
5988
				$substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
5989
				$substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
5990
				$substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
5991
				$substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
5992
				$substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
5993
			}
5994
			if (!empty($conf->adherent->enabled) && (!is_object($object) || $object->element == 'adherent'))
5995
			{
5996
				$substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
5997
				$substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
5998
				$substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
5999
				$substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
6000
				/*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
6001
				$substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
6002
			}
6003
			if (!empty($conf->recruitment->enabled) && (!is_object($object) || $object->element == 'candidature'))
6004
			{
6005
				$substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
6006
				$substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
6007
				$substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
6008
			}
6009
			if (!empty($conf->projet->enabled))		// Most objects
6010
			{
6011
				$substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
6012
				$substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
6013
				$substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
6014
				/*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
6015
				$substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
6016
			}
6017
			if (!empty($conf->contrat->enabled) && (!is_object($object) || $object->element == 'contract'))
6018
			{
6019
				$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
6020
				$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
6021
				$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
6022
				$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
6023
			}
6024
			$substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
6025
			$substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
6026
			$substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
6027
			$substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
6028
			$substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
6029
			$substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
6030
			$substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a a service';
6031
6032
			$substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
6033
			$substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
6034
			$substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
6035
6036
			if (!empty($conf->expedition->enabled) && (!is_object($object) || $object->element == 'shipping'))
6037
			{
6038
				$substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tacking number';
6039
				$substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
6040
			}
6041
		} else {
6042
			$substitutionarray['__ID__'] = $object->id;
6043
			$substitutionarray['__REF__'] = $object->ref;
6044
			$substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
6045
			$substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
6046
			$substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
6047
			$substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
6048
6049
			$substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
6050
6051
			// For backward compatibility
6052
			$substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
6053
			$substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
6054
			$substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
6055
			$substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
6056
			$substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
6057
			$substitutionarray['__SUPPLIER_ORDER_DELAY_DELIVERY__'] = $outputlangs->transnoentities("AvailabilityType".$object->availability_code) != ('AvailabilityType'.$object->availability_code) ? $outputlangs->transnoentities("AvailabilityType".$object->availability_code) : $outputlangs->convToOutputCharset(isset($object->availability) ? $object->availability : '');
6058
6059
			$birthday = dol_print_date($object->birth, 'day');
6060
6061
			if (is_object($object) && $object->element == 'adherent' && $object->id > 0)
6062
			{
6063
				$substitutionarray['__MEMBER_ID__'] = $object->id;
6064
				if (method_exists($object, 'getCivilityLabel')) $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
6065
				$substitutionarray['__MEMBER_FIRSTNAME__'] = $object->firstname;
6066
				$substitutionarray['__MEMBER_LASTNAME__'] = $object->lastname;
6067
				if (method_exists($object, 'getFullName')) $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
6068
				$substitutionarray['__MEMBER_COMPANY__'] = $object->societe;
6069
				$substitutionarray['__MEMBER_ADDRESS__'] = $object->address;
6070
				$substitutionarray['__MEMBER_ZIP__'] = $object->zip;
6071
				$substitutionarray['__MEMBER_TOWN__'] = $object->town;
6072
				$substitutionarray['__MEMBER_COUNTRY__'] = $object->country;
6073
				$substitutionarray['__MEMBER_EMAIL__'] = $object->email;
6074
				$substitutionarray['__MEMBER_BIRTH__'] = $birthday;
6075
				$substitutionarray['__MEMBER_PHOTO__'] = $object->photo;
6076
				$substitutionarray['__MEMBER_LOGIN__'] = $object->login;
6077
				$substitutionarray['__MEMBER_PASSWORD__'] = $object->pass;
6078
				$substitutionarray['__MEMBER_PHONE__'] = $object->phone;
6079
				$substitutionarray['__MEMBER_PHONEPRO__'] = $object->phone_perso;
6080
				$substitutionarray['__MEMBER_PHONEMOBILE__'] = $object->phone_mobile;
6081
				$substitutionarray['__MEMBER_TYPE__'] = $object->type;
6082
				$substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__']       = dol_print_date($object->first_subscription_date, 'dayrfc');
6083
				$substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->first_subscription_date_start, 'dayrfc');
6084
				$substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__']   = dol_print_date($object->first_subscription_date_end, 'dayrfc');
6085
				$substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__']        = dol_print_date($object->last_subscription_date, 'dayrfc');
6086
				$substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__']  = dol_print_date($object->last_subscription_date_start, 'dayrfc');
6087
				$substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__']    = dol_print_date($object->last_subscription_date_end, 'dayrfc');
6088
			}
6089
6090
			if (is_object($object) && $object->element == 'societe') {
6091
				$substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
6092
				$substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
6093
				$substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
6094
				$substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
6095
				$substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
6096
				$substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
6097
				$substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? $object->phone : '');
6098
				$substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? $object->fax : '');
6099
				$substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
6100
				$substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
6101
				$substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
6102
				$substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
6103
				$substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
6104
				$substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
6105
				$substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
6106
				$substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
6107
				$substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
6108
				$substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
6109
				$substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
6110
				$substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
6111
				$substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
6112
				$substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
6113
			} elseif (is_object($object->thirdparty)) {
6114
				$substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
6115
				$substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
6116
				$substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
6117
				$substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
6118
				$substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
6119
				$substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
6120
				$substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? $object->thirdparty->phone : '');
6121
				$substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? $object->thirdparty->fax : '');
6122
				$substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
6123
				$substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
6124
				$substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
6125
				$substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
6126
				$substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
6127
				$substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
6128
				$substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
6129
				$substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
6130
				$substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
6131
				$substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
6132
				$substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
6133
				$substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
6134
				$substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
6135
				$substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
6136
			}
6137
6138
			if (is_object($object) && $object->element == 'recruitmentcandidature') {
6139
				$substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
6140
				$substitutionarray['__CANDIDATE_FIRSTNAME__'] = $object->firstname;
6141
				$substitutionarray['__CANDIDATE_LASTNAME__'] = $object->lastname;
6142
			}
6143
6144
			if (is_object($object->project))
6145
			{
6146
				$substitutionarray['__PROJECT_ID__'] = (is_object($object->project) ? $object->project->id : '');
6147
				$substitutionarray['__PROJECT_REF__'] = (is_object($object->project) ? $object->project->ref : '');
6148
				$substitutionarray['__PROJECT_NAME__'] = (is_object($object->project) ? $object->project->title : '');
6149
			}
6150
			if (is_object($object->projet))	// Deprecated, for backward compatibility
6151
			{
6152
				$substitutionarray['__PROJECT_ID__'] = (is_object($object->projet) ? $object->projet->id : '');
6153
				$substitutionarray['__PROJECT_REF__'] = (is_object($object->projet) ? $object->projet->ref : '');
6154
				$substitutionarray['__PROJECT_NAME__'] = (is_object($object->projet) ? $object->projet->title : '');
6155
			}
6156
6157
			if (is_object($object) && $object->element == 'shipping')
6158
			{
6159
				$substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
6160
				$substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
6161
			}
6162
6163
			if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines))
6164
			{
6165
				$dateplannedstart = '';
6166
				$datenextexpiration = '';
6167
				foreach ($object->lines as $line)
6168
				{
6169
					if ($line->date_ouverture_prevue > $dateplannedstart) $dateplannedstart = $line->date_ouverture_prevue;
6170
					if ($line->statut == 4 && $line->date_fin_prevue && (!$datenextexpiration || $line->date_fin_prevue < $datenextexpiration)) $datenextexpiration = $line->date_fin_prevue;
6171
				}
6172
				$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'dayrfc');
6173
				$substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
6174
				$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'dayrfc');
6175
				$substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
6176
			}
6177
6178
			// Create dynamic tags for __EXTRAFIELD_FIELD__
6179
			if ($object->table_element && $object->id > 0)
6180
			{
6181
				if (!is_object($extrafields)) $extrafields = new ExtraFields($db);
6182
				$extrafields->fetch_name_optionals_label($object->table_element, true);
6183
6184
				if ($object->fetch_optionals() > 0)
0 ignored issues
show
Bug introduced by
The method fetch_optionals() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

6184
				if ($object->/** @scrutinizer ignore-call */ fetch_optionals() > 0)

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
6185
				{
6186
					if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0)
6187
					{
6188
						foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
6189
							$substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
6190
							if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
6191
								$substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
6192
								$substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
6193
								$substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
6194
							} elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
6195
								$datetime = $object->array_options['options_'.$key];
6196
								$substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
6197
								$substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
6198
								$substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
6199
								$substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
6200
							}
6201
						}
6202
					}
6203
				}
6204
			}
6205
6206
			// Complete substitution array with the url to make online payment
6207
			$paymenturl = '';
6208
			if (empty($substitutionarray['__REF__']))
6209
			{
6210
				$paymenturl = '';
6211
			} else {
6212
				// Set the online payment url link into __ONLINE_PAYMENT_URL__ key
6213
				require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
6214
				$outputlangs->loadLangs(array('paypal', 'other'));
6215
				$typeforonlinepayment = 'free';
6216
				if (is_object($object) && $object->element == 'commande') $typeforonlinepayment = 'order';
6217
				if (is_object($object) && $object->element == 'facture')  $typeforonlinepayment = 'invoice';
6218
				if (is_object($object) && $object->element == 'member')   $typeforonlinepayment = 'member';
6219
				$url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__']);
6220
				$paymenturl = $url;
6221
			}
6222
6223
			if ($object->id > 0)
6224
			{
6225
				$substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ?str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
6226
				$substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
6227
6228
				if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal')
6229
				{
6230
					$substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
6231
				} else $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
6232
				if (!empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'commande')
6233
				{
6234
					$substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
6235
				} else $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
6236
				if (!empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'facture')
6237
				{
6238
					$substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
6239
				} else $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
6240
6241
				if (is_object($object) && $object->element == 'propal') $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
6242
				if (is_object($object) && $object->element == 'commande') $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
6243
				if (is_object($object) && $object->element == 'facture') $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
6244
			}
6245
6246
			if (is_object($object) && $object->element == 'action')
6247
			{
6248
				$substitutionarray['__EVENT_LABEL__'] = $object->label;
6249
				$substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, '%A %d %b %Y');
6250
				$substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, '%H:%M:%S');
6251
			}
6252
		}
6253
	}
6254
	if (empty($exclude) || !in_array('objectamount', $exclude))
6255
	{
6256
		$substitutionarray['__DATE_YMD__']        = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
6257
		$substitutionarray['__DATE_DUE_YMD__']    = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
6258
6259
		$substitutionarray['__AMOUNT__']          = is_object($object) ? $object->total_ttc : '';
6260
		$substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
6261
		$substitutionarray['__AMOUNT_VAT__']      = is_object($object) ? ($object->total_vat ? $object->total_vat : $object->total_tva) : '';
6262
		if ($onlykey != 2 || $mysoc->useLocalTax(1)) $substitutionarray['__AMOUNT_TAX2__']     = is_object($object) ? $object->total_localtax1 : '';
6263
		if ($onlykey != 2 || $mysoc->useLocalTax(2)) $substitutionarray['__AMOUNT_TAX3__']     = is_object($object) ? $object->total_localtax2 : '';
6264
6265
		$substitutionarray['__AMOUNT_FORMATED__']          = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, 0, -1, $conf->currency) : null) : '';
6266
		$substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, 0, -1, $conf->currency) : null) : '';
6267
		$substitutionarray['__AMOUNT_VAT_FORMATED__']      = is_object($object) ? ($object->total_vat ? price($object->total_vat, 0, $outputlangs, 0, 0, -1, $conf->currency) : ($object->total_tva ? price($object->total_tva, 0, $outputlangs, 0, 0, -1, $conf->currency) : null)) : '';
6268
		if ($onlykey != 2 || $mysoc->useLocalTax(1)) $substitutionarray['__AMOUNT_TAX2_FORMATED__']     = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, 0, -1, $conf->currency) : null) : '';
6269
		if ($onlykey != 2 || $mysoc->useLocalTax(2)) $substitutionarray['__AMOUNT_TAX3_FORMATED__']     = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, 0, -1, $conf->currency) : null) : '';
6270
6271
		// TODO Add keys for foreign multicurrency
6272
6273
		// For backward compatibility
6274
		if ($onlykey != 2)
6275
		{
6276
			$substitutionarray['__TOTAL_TTC__']    = is_object($object) ? $object->total_ttc : '';
6277
			$substitutionarray['__TOTAL_HT__']     = is_object($object) ? $object->total_ht : '';
6278
			$substitutionarray['__TOTAL_VAT__']    = is_object($object) ? ($object->total_vat ? $object->total_vat : $object->total_tva) : '';
6279
		}
6280
	}
6281
6282
	//var_dump($substitutionarray['__AMOUNT_FORMATED__']);
6283
	if (empty($exclude) || !in_array('date', $exclude))
6284
	{
6285
		include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6286
6287
		$tmp = dol_getdate(dol_now(), true);
6288
		$tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
6289
		$tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
6290
		$tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
6291
		$tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
6292
6293
		$substitutionarray = array_merge($substitutionarray, array(
6294
			'__DAY__' => (string) $tmp['mday'],
6295
			'__DAY_TEXT__' => $outputlangs->trans('Day'.$tmp['wday']), // Monday
6296
			'__DAY_TEXT_SHORT__' => $outputlangs->trans($tmp['weekday'].'Min'), // Mon
6297
			'__DAY_TEXT_MIN__' => $outputlangs->trans('Short'.$tmp['weekday']), // M
6298
			'__MONTH__' => (string) $tmp['mon'],
6299
			'__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
6300
			'__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
6301
			'__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
6302
			'__YEAR__' => (string) $tmp['year'],
6303
			'__PREVIOUS_DAY__' => (string) $tmp2['day'],
6304
			'__PREVIOUS_MONTH__' => (string) $tmp3['month'],
6305
			'__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
6306
			'__NEXT_DAY__' => (string) $tmp4['day'],
6307
			'__NEXT_MONTH__' => (string) $tmp5['month'],
6308
			'__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
6309
		));
6310
	}
6311
6312
	if (!empty($conf->multicompany->enabled))
6313
	{
6314
		$substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
6315
	}
6316
	if (empty($exclude) || !in_array('system', $exclude))
6317
	{
6318
		$substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
6319
		$substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
6320
		$substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
6321
		$substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
6322
	}
6323
6324
	return $substitutionarray;
6325
}
6326
6327
/**
6328
 *  Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newval),
6329
 *  and texts like __(TranslationKey|langfile)__ and __[ConstantKey]__ are also replaced.
6330
 *  Example of usage:
6331
 *  $substitutionarray = getCommonSubstitutionArray($langs, 0, null, $thirdparty);
6332
 *  complete_substitutions_array($substitutionarray, $langs, $thirdparty);
6333
 *  $mesg = make_substitutions($mesg, $substitutionarray, $langs);
6334
 *
6335
 *  @param	string		$text	      			Source string in which we must do substitution
6336
 *  @param  array		$substitutionarray		Array with key->val to substitute. Example: array('__MYKEY__' => 'MyVal', ...)
6337
 *  @param	Translate	$outputlangs			Output language
6338
 * 	@return string  		    				Output string after substitutions
6339
 *  @see	complete_substitutions_array(), getCommonSubstitutionArray()
6340
 */
6341
function make_substitutions($text, $substitutionarray, $outputlangs = null)
6342
{
6343
	global $conf, $langs;
6344
6345
	if (!is_array($substitutionarray)) return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
6346
6347
	if (empty($outputlangs)) $outputlangs = $langs;
6348
6349
	// Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
6350
	if (is_object($outputlangs))
6351
	{
6352
		$reg = array();
6353
		while (preg_match('/__\(([^\)]+)\)__/', $text, $reg))
6354
		{
6355
			$msgishtml = 0;
6356
			if (dol_textishtml($text, 1)) $msgishtml = 1;
6357
6358
			// If key is __(TranslationKey|langfile)__, then force load of langfile.lang
6359
			$tmp = explode('|', $reg[1]);
6360
			if (!empty($tmp[1])) $outputlangs->load($tmp[1]);
6361
6362
			$text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $msgishtml ?dol_htmlentitiesbr($outputlangs->transnoentitiesnoconv($reg[1])) : $outputlangs->transnoentitiesnoconv($reg[1]), $text);
6363
		}
6364
	}
6365
6366
	// Make substitution for constant keys.
6367
	// Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
6368
	$reg = array();
6369
	while (preg_match('/__\[([^\]]+)\]__/', $text, $reg))
6370
	{
6371
		$msgishtml = 0;
6372
		if (dol_textishtml($text, 1)) $msgishtml = 1;
6373
6374
		$keyfound = $reg[1];
6375
		if (preg_match('/(_pass|_pw|password|secret|_key|key$)/i', $keyfound)) $newval = '*****forbidden*****';
6376
		else $newval = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
6377
		$text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ?dol_htmlentitiesbr($newval) : $newval, $text);
6378
	}
6379
6380
	// Make substitition for array $substitutionarray
6381
	foreach ($substitutionarray as $key => $value)
6382
	{
6383
		if (!isset($value)) continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
6384
6385
		if ($key == '__USER_SIGNATURE__' && (!empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) $value = ''; // Protection
6386
6387
		$text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
6388
	}
6389
6390
	return $text;
6391
}
6392
6393
/**
6394
 *  Complete the $substitutionarray with more entries coming from external module that had set the "substitutions=1" into module_part array.
6395
 *  In this case, method completesubstitutionarray provided by module is called.
6396
 *
6397
 *  @param  array		$substitutionarray		Array substitution old value => new value value
6398
 *  @param  Translate	$outputlangs            Output language
6399
 *  @param  Object		$object                 Source object
6400
 *  @param  mixed		$parameters       		Add more parameters (useful to pass product lines)
6401
 *  @param  string      $callfunc               What is the name of the custom function that will be called? (default: completesubstitutionarray)
6402
 *  @return	void
6403
 *  @see 	make_substitutions()
6404
 */
6405
function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
6406
{
6407
	global $conf, $user;
6408
6409
	require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
6410
6411
	// Add a substitution key for each extrafields, using key __EXTRA_XXX__
6412
	// TODO Remove this. Already available into the getCommonSubstitutionArray used to build the substitution array.
6413
	/*if (is_object($object) && is_array($object->array_options))
6414
	{
6415
		foreach($object->array_options as $key => $val)
6416
		{
6417
			$keyshort=preg_replace('/^(options|extra)_/','',$key);
6418
			$substitutionarray['__EXTRAFIELD_'.$keyshort.'__']=$val;
6419
			// For backward compatibiliy
6420
			$substitutionarray['%EXTRA_'.$keyshort.'%']=$val;
6421
		}
6422
	}*/
6423
6424
	// Check if there is external substitution to do, requested by plugins
6425
	$dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
6426
6427
	foreach ($dirsubstitutions as $reldir)
6428
	{
6429
		$dir = dol_buildpath($reldir, 0);
6430
6431
		// Check if directory exists
6432
		if (!dol_is_dir($dir)) continue;
6433
6434
		$substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
6435
		foreach ($substitfiles as $substitfile)
6436
		{
6437
			if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg))
6438
			{
6439
				$module = $reg[1];
6440
6441
				dol_syslog("Library ".$substitfile['name']." found into ".$dir);
6442
				// Include the user's functions file
6443
				require_once $dir.$substitfile['name'];
6444
				// Call the user's function, and only if it is defined
6445
				$function_name = $module."_".$callfunc;
6446
				if (function_exists($function_name)) $function_name($substitutionarray, $outputlangs, $object, $parameters);
6447
			}
6448
		}
6449
	}
6450
}
6451
6452
/**
6453
 *    Format output for start and end date
6454
 *
6455
 *    @param	int	$date_start    Start date
6456
 *    @param    int	$date_end      End date
6457
 *    @param    string		$format        Output format
6458
 *    @param	Translate	$outputlangs   Output language
6459
 *    @return	void
6460
 */
6461
function print_date_range($date_start, $date_end, $format = '', $outputlangs = '')
6462
{
6463
	print get_date_range($date_start, $date_end, $format, $outputlangs);
6464
}
6465
6466
/**
6467
 *    Format output for start and end date
6468
 *
6469
 *    @param	int			$date_start    		Start date
6470
 *    @param    int			$date_end      		End date
6471
 *    @param    string		$format        		Output format
6472
 *    @param	Translate	$outputlangs   		Output language
6473
 *    @param	integer		$withparenthesis	1=Add parenthesis, 0=non parenthesis
6474
 *    @return	string							String
6475
 */
6476
function get_date_range($date_start, $date_end, $format = '', $outputlangs = '', $withparenthesis = 1)
6477
{
6478
	global $langs;
6479
6480
	$out = '';
6481
6482
	if (!is_object($outputlangs)) $outputlangs = $langs;
6483
6484
	if ($date_start && $date_end)
6485
	{
6486
		$out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
6487
	}
6488
	if ($date_start && !$date_end)
6489
	{
6490
		$out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
6491
	}
6492
	if (!$date_start && $date_end)
6493
	{
6494
		$out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
6495
	}
6496
6497
	return $out;
6498
}
6499
6500
/**
6501
 * Return firstname and lastname in correct order
6502
 *
6503
 * @param	string	$firstname		Firstname
6504
 * @param	string	$lastname		Lastname
6505
 * @param	int		$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname, 3=Firstname if defined else lastname
6506
 * @return	string					Firstname + lastname or Lastname + firstname
6507
 */
6508
function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
6509
{
6510
	global $conf;
6511
6512
	$ret = '';
6513
	// If order not defined, we use the setup
6514
	if ($nameorder < 0) $nameorder = (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION) ? 1 : 0);
6515
	if ($nameorder && $nameorder != 2 && $nameorder != 3)
6516
	{
6517
		$ret .= $firstname;
6518
		if ($firstname && $lastname) $ret .= ' ';
6519
		$ret .= $lastname;
6520
	} elseif ($nameorder == 2 || $nameorder == 3)
6521
	{
6522
		$ret .= $firstname;
6523
		if (empty($ret) && $nameorder == 3)
6524
		{
6525
			$ret .= $lastname;
6526
		}
6527
	} else {
6528
		$ret .= $lastname;
6529
		if ($firstname && $lastname) $ret .= ' ';
6530
		$ret .= $firstname;
6531
	}
6532
	return $ret;
6533
}
6534
6535
6536
/**
6537
 *	Set event message in dol_events session object. Will be output by calling dol_htmloutput_events.
6538
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
6539
 *  Note: Prefer to use setEventMessages instead.
6540
 *
6541
 *	@param	mixed	$mesgs			Message string or array
6542
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
6543
 *  @return	void
6544
 *  @see	dol_htmloutput_events()
6545
 */
6546
function setEventMessage($mesgs, $style = 'mesgs')
6547
{
6548
	//dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);		This is not deprecated, it is used by setEventMessages function
6549
	if (!is_array($mesgs))		// If mesgs is a string
6550
	{
6551
		if ($mesgs) $_SESSION['dol_events'][$style][] = $mesgs;
6552
	} else // If mesgs is an array
6553
	{
6554
		foreach ($mesgs as $mesg)
6555
		{
6556
			if ($mesg) $_SESSION['dol_events'][$style][] = $mesg;
6557
		}
6558
	}
6559
}
6560
6561
/**
6562
 *	Set event messages in dol_events session object. Will be output by calling dol_htmloutput_events.
6563
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
6564
 *
6565
 *	@param	string	$mesg			Message string
6566
 *	@param	array	$mesgs			Message array
6567
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
6568
 *  @param	string	$messagekey		A key to be used to allow the feature "Never show this message again"
6569
 *  @return	void
6570
 *  @see	dol_htmloutput_events()
6571
 */
6572
function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '')
6573
{
6574
	if (empty($mesg) && empty($mesgs))
6575
	{
6576
		dol_syslog("Try to add a message in stack with empty message", LOG_WARNING);
6577
	} else {
6578
		if ($messagekey)
6579
		{
6580
			// Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
6581
			// TODO
6582
			$mesg .= '';
6583
		}
6584
		if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey]))
6585
		{
6586
			if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) dol_print_error('', 'Bad parameter style='.$style.' for setEventMessages');
6587
			if (empty($mesgs)) setEventMessage($mesg, $style);
6588
			else {
6589
				if (!empty($mesg) && !in_array($mesg, $mesgs)) setEventMessage($mesg, $style); // Add message string if not already into array
6590
				setEventMessage($mesgs, $style);
6591
			}
6592
		}
6593
	}
6594
}
6595
6596
/**
6597
 *	Print formated messages to output (Used to show messages on html output).
6598
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function, so there is
6599
 *  no need to call it explicitely.
6600
 *
6601
 *  @param	int		$disabledoutputofmessages	Clear all messages stored into session without diplaying them
6602
 *  @return	void
6603
 *  @see    									dol_htmloutput_mesg()
6604
 */
6605
function dol_htmloutput_events($disabledoutputofmessages = 0)
6606
{
6607
	// Show mesgs
6608
	if (isset($_SESSION['dol_events']['mesgs'])) {
6609
		if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
6610
		unset($_SESSION['dol_events']['mesgs']);
6611
	}
6612
6613
	// Show errors
6614
	if (isset($_SESSION['dol_events']['errors'])) {
6615
		if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
6616
		unset($_SESSION['dol_events']['errors']);
6617
	}
6618
6619
	// Show warnings
6620
	if (isset($_SESSION['dol_events']['warnings'])) {
6621
		if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
6622
		unset($_SESSION['dol_events']['warnings']);
6623
	}
6624
}
6625
6626
/**
6627
 *	Get formated messages to output (Used to show messages on html output).
6628
 *  This include also the translation of the message key.
6629
 *
6630
 *	@param	string		$mesgstring		Message string or message key
6631
 *	@param	string[]	$mesgarray      Array of message strings or message keys
6632
 *  @param  string		$style          Style of message output ('ok' or 'error')
6633
 *  @param  int			$keepembedded   Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
6634
 *	@return	string						Return html output
6635
 *
6636
 *  @see    dol_print_error()
6637
 *  @see    dol_htmloutput_errors()
6638
 *  @see    setEventMessages()
6639
 */
6640
function get_htmloutput_mesg($mesgstring = '', $mesgarray = '', $style = 'ok', $keepembedded = 0)
6641
{
6642
	global $conf, $langs;
6643
6644
	$ret = 0; $return = '';
6645
	$out = '';
6646
	$divstart = $divend = '';
6647
6648
	// If inline message with no format, we add it.
6649
	if ((empty($conf->use_javascript_ajax) || !empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && !preg_match('/<div class=".*">/i', $out))
6650
	{
6651
		$divstart = '<div class="'.$style.' clearboth">';
6652
		$divend = '</div>';
6653
	}
6654
6655
	if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring)
6656
	{
6657
		$langs->load("errors");
6658
		$out .= $divstart;
6659
		if (is_array($mesgarray) && count($mesgarray))
6660
		{
6661
			foreach ($mesgarray as $message)
6662
			{
6663
				$ret++;
6664
				$out .= $langs->trans($message);
6665
				if ($ret < count($mesgarray)) $out .= "<br>\n";
6666
			}
6667
		}
6668
		if ($mesgstring)
6669
		{
6670
			$langs->load("errors");
6671
			$ret++;
6672
			$out .= $langs->trans($mesgstring);
6673
		}
6674
		$out .= $divend;
6675
	}
6676
6677
	if ($out)
6678
	{
6679
		if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded))
6680
		{
6681
			$return = '<script>
6682
					$(document).ready(function() {
6683
						var block = '.(!empty($conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false").'
6684
						if (block) {
6685
							$.dolEventValid("","'.dol_escape_js($out).'");
6686
						} else {
6687
							/* jnotify(message, preset of message type, keepmessage) */
6688
							$.jnotify("'.dol_escape_js($out).'",
6689
							"'.($style == "ok" ? 3000 : $style).'",
6690
							'.($style == "ok" ? "false" : "true").',
6691
							{ remove: function (){} } );
6692
						}
6693
					});
6694
				</script>';
6695
		} else {
6696
			$return = $out;
6697
		}
6698
	}
6699
6700
	return $return;
6701
}
6702
6703
/**
6704
 *  Get formated error messages to output (Used to show messages on html output).
6705
 *
6706
 *  @param  string	$mesgstring         Error message
6707
 *  @param  array	$mesgarray          Error messages array
6708
 *  @param  int		$keepembedded       Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
6709
 *  @return string                		Return html output
6710
 *
6711
 *  @see    dol_print_error()
6712
 *  @see    dol_htmloutput_mesg()
6713
 */
6714
function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
6715
{
6716
	return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
6717
}
6718
6719
/**
6720
 *	Print formated messages to output (Used to show messages on html output).
6721
 *
6722
 *	@param	string		$mesgstring		Message string or message key
6723
 *	@param	string[]	$mesgarray      Array of message strings or message keys
6724
 *	@param  string      $style          Which style to use ('ok', 'warning', 'error')
6725
 *	@param  int         $keepembedded   Set to 1 if message must be kept embedded into its html place (this disable jnotify)
6726
 *	@return	void
6727
 *
6728
 *	@see    dol_print_error()
6729
 *	@see    dol_htmloutput_errors()
6730
 *	@see    setEventMessages()
6731
 */
6732
function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
6733
{
6734
	if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) return;
6735
6736
	$iserror = 0;
6737
	$iswarning = 0;
6738
	if (is_array($mesgarray))
6739
	{
6740
		foreach ($mesgarray as $val)
6741
		{
6742
			if ($val && preg_match('/class="error"/i', $val)) { $iserror++; break; }
6743
			if ($val && preg_match('/class="warning"/i', $val)) { $iswarning++; break; }
6744
		}
6745
	} elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) $iserror++;
6746
	elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) $iswarning++;
6747
	if ($style == 'error') $iserror++;
6748
	if ($style == 'warning') $iswarning++;
6749
6750
	if ($iserror || $iswarning)
6751
	{
6752
		// Remove div from texts
6753
		$mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
6754
		$mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
6755
		$mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
6756
		// Remove div from texts array
6757
		if (is_array($mesgarray))
6758
		{
6759
			$newmesgarray = array();
6760
			foreach ($mesgarray as $val)
6761
			{
6762
				if (is_string($val))
6763
				{
6764
					$tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
6765
					$tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
6766
					$tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
6767
					$newmesgarray[] = $tmpmesgstring;
6768
				} else {
6769
					dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
6770
				}
6771
			}
6772
			$mesgarray = $newmesgarray;
6773
		}
6774
		print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
6775
	} else print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
6776
}
6777
6778
/**
6779
 *  Print formated error messages to output (Used to show messages on html output).
6780
 *
6781
 *  @param	string	$mesgstring          Error message
6782
 *  @param  array	$mesgarray           Error messages array
6783
 *  @param  int		$keepembedded        Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
6784
 *  @return	void
6785
 *
6786
 *  @see    dol_print_error()
6787
 *  @see    dol_htmloutput_mesg()
6788
 */
6789
function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
6790
{
6791
	dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
6792
}
6793
6794
/**
6795
 * 	Advanced sort array by second index function, which produces ascending (default)
6796
 *  or descending output and uses optionally natural case insensitive sorting (which
6797
 *  can be optionally case sensitive as well).
6798
 *
6799
 *  @param      array		$array      		Array to sort (array of array('key1'=>val1,'key2'=>val2,'key3'...) or array of objects)
6800
 *  @param      string		$index				Key in array to use for sorting criteria
6801
 *  @param      int			$order				Sort order ('asc' or 'desc')
6802
 *  @param      int			$natsort			1=use "natural" sort (natsort), 0=use "standard" sort (asort)
6803
 *  @param      int			$case_sensitive		1=sort is case sensitive, 0=not case sensitive
6804
 *  @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.
6805
 *  @return     array							Sorted array
6806
 */
6807
function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
6808
{
6809
	// Clean parameters
6810
	$order = strtolower($order);
6811
6812
	if (is_array($array))
6813
	{
6814
		$sizearray = count($array);
6815
		if ($sizearray > 0)
6816
		{
6817
			$temp = array();
6818
			foreach (array_keys($array) as $key)
6819
			{
6820
				if (is_object($array[$key]))
6821
				{
6822
					$temp[$key] = $array[$key]->$index;
6823
				} else {
6824
					$temp[$key] = $array[$key][$index];
6825
				}
6826
			}
6827
6828
			if (!$natsort) {
6829
				($order == 'asc') ? asort($temp) : arsort($temp);
6830
			} else {
6831
				($case_sensitive) ? natsort($temp) : natcasesort($temp);
6832
				if ($order != 'asc') $temp = array_reverse($temp, true);
6833
			}
6834
6835
			$sorted = array();
6836
6837
			foreach (array_keys($temp) as $key)
6838
			{
6839
				(is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
6840
			}
6841
6842
			return $sorted;
6843
		}
6844
	}
6845
	return $array;
6846
}
6847
6848
6849
/**
6850
 *      Check if a string is in UTF8
6851
 *
6852
 *      @param	string	$str        String to check
6853
 * 		@return	boolean				True if string is UTF8 or ISO compatible with UTF8, False if not (ISO with special char or Binary)
6854
 */
6855
function utf8_check($str)
6856
{
6857
	// We must use here a binary strlen function (so not dol_strlen)
6858
	$strLength = dol_strlen($str);
6859
	for ($i = 0; $i < $strLength; $i++)
6860
	{
6861
		if (ord($str[$i]) < 0x80) continue; // 0bbbbbbb
6862
		elseif ((ord($str[$i]) & 0xE0) == 0xC0) $n = 1; // 110bbbbb
6863
		elseif ((ord($str[$i]) & 0xF0) == 0xE0) $n = 2; // 1110bbbb
6864
		elseif ((ord($str[$i]) & 0xF8) == 0xF0) $n = 3; // 11110bbb
6865
		elseif ((ord($str[$i]) & 0xFC) == 0xF8) $n = 4; // 111110bb
6866
		elseif ((ord($str[$i]) & 0xFE) == 0xFC) $n = 5; // 1111110b
6867
		else return false; // Does not match any model
6868
		for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
6869
			if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
6870
			return false;
6871
		}
6872
	}
6873
	return true;
6874
}
6875
6876
/**
6877
 *      Check if a string is in ASCII
6878
 *
6879
 *      @param	string	$str        String to check
6880
 * 		@return	boolean				True if string is ASCII, False if not (byte value > 0x7F)
6881
 */
6882
function ascii_check($str)
6883
{
6884
	if (function_exists('mb_check_encoding')) {
6885
		//if (mb_detect_encoding($str, 'ASCII', true) return false;
6886
		if (!mb_check_encoding($str, 'ASCII')) return false;
6887
	} else {
6888
		if (preg_match('/[^\x00-\x7f]/', $str)) return false; // Contains a byte > 7f
6889
	}
6890
6891
	return true;
6892
}
6893
6894
6895
/**
6896
 *      Return a string encoded into OS filesystem encoding. This function is used to define
6897
 * 	    value to pass to filesystem PHP functions.
6898
 *
6899
 *      @param	string	$str        String to encode (UTF-8)
6900
 * 		@return	string				Encoded string (UTF-8, ISO-8859-1)
6901
 */
6902
function dol_osencode($str)
6903
{
6904
	global $conf;
6905
6906
	$tmp = ini_get("unicode.filesystem_encoding"); // Disponible avec PHP 6.0
6907
	if (empty($tmp) && !empty($_SERVER["WINDIR"])) $tmp = 'iso-8859-1'; // By default for windows
6908
	if (empty($tmp)) $tmp = 'utf-8'; // By default for other
6909
	if (!empty($conf->global->MAIN_FILESYSTEM_ENCODING)) $tmp = $conf->global->MAIN_FILESYSTEM_ENCODING;
6910
6911
	if ($tmp == 'iso-8859-1') return utf8_decode($str);
6912
	return $str;
6913
}
6914
6915
6916
/**
6917
 *      Return an id or code from a code or id.
6918
 *      Store also Code-Id into a cache to speed up next request on same key.
6919
 *
6920
 * 		@param	DoliDB	$db				Database handler
6921
 * 		@param	string	$key			Code or Id to get Id or Code
6922
 * 		@param	string	$tablename		Table name without prefix
6923
 * 		@param	string	$fieldkey		Field to search the key into
6924
 * 		@param	string	$fieldid		Field to get
6925
 *      @param  int		$entityfilter	Filter by entity
6926
 *      @return int						<0 if KO, Id of code if OK
6927
 *      @see $langs->getLabelFromKey
6928
 */
6929
function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0)
6930
{
6931
	global $cache_codes;
6932
6933
	// If key empty
6934
	if ($key == '') return '';
6935
6936
	// Check in cache
6937
	if (isset($cache_codes[$tablename][$key][$fieldid]))	// Can be defined to 0 or ''
6938
	{
6939
		return $cache_codes[$tablename][$key][$fieldid]; // Found in cache
6940
	}
6941
6942
	dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
6943
6944
	$sql = "SELECT ".$fieldid." as valuetoget";
6945
	$sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
6946
	$sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
6947
	if (!empty($entityfilter))
6948
		$sql .= " AND entity IN (".getEntity($tablename).")";
6949
6950
	$resql = $db->query($sql);
6951
	if ($resql)
6952
	{
6953
		$obj = $db->fetch_object($resql);
6954
		if ($obj) $cache_codes[$tablename][$key][$fieldid] = $obj->valuetoget;
6955
		else $cache_codes[$tablename][$key][$fieldid] = '';
6956
		$db->free($resql);
6957
		return $cache_codes[$tablename][$key][$fieldid];
6958
	} else {
6959
		return -1;
6960
	}
6961
}
6962
6963
/**
6964
 * Verify if condition in string is ok or not
6965
 *
6966
 * @param 	string		$strRights		String with condition to check
6967
 * @return 	boolean						True or False. Return True if strRights is ''
6968
 */
6969
function verifCond($strRights)
6970
{
6971
	global $user, $conf, $langs;
6972
	global $leftmenu;
6973
	global $rights; // To export to dol_eval function
6974
6975
	//print $strRights."<br>\n";
6976
	$rights = true;
6977
	if ($strRights != '')
6978
	{
6979
		$str = 'if(!('.$strRights.')) { $rights = false; }';
6980
		dol_eval($str); // The dol_eval must contains all the global $xxx used into a condition
6981
	}
6982
	return $rights;
6983
}
6984
6985
/**
6986
 * Replace eval function to add more security.
6987
 * This function is called by verifCond() or trans() and transnoentitiesnoconv().
6988
 *
6989
 * @param 	string	$s				String to evaluate
6990
 * @param	int		$returnvalue	0=No return (used to execute eval($a=something)). 1=Value of eval is returned (used to eval($something)).
6991
 * @param   int     $hideerrors     1=Hide errors
6992
 * @return	mixed					Nothing or return result of eval
6993
 */
6994
function dol_eval($s, $returnvalue = 0, $hideerrors = 1)
6995
{
6996
	// Only global variables can be changed by eval function and returned to caller
6997
	global $db, $langs, $user, $conf, $website, $websitepage;
6998
	global $action, $mainmenu, $leftmenu;
6999
	global $rights;
7000
	global $object;
7001
	global $mysoc;
7002
7003
	global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
7004
	global $soc; // For backward compatibility
7005
7006
	//print $s."<br>\n";
7007
	if ($returnvalue)
7008
	{
7009
		if ($hideerrors) return @eval('return '.$s.';');
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
7010
		else return eval('return '.$s.';');
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
7011
	} else {
7012
		if ($hideerrors) @eval($s);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
7013
		else eval($s);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
7014
	}
7015
}
7016
7017
/**
7018
 * Return if var element is ok
7019
 *
7020
 * @param   string      $element    Variable to check
7021
 * @return  boolean                 Return true of variable is not empty
7022
 */
7023
function dol_validElement($element)
7024
{
7025
	return (trim($element) != '');
7026
}
7027
7028
/**
7029
 * 	Return img flag of country for a language code or country code
7030
 *
7031
 * 	@param	string	$codelang	Language code (en_IN, fr_CA...) or Country code (IN, FR)
7032
 *  @param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
7033
 * 	@return	string				HTML img string with flag.
7034
 */
7035
function picto_from_langcode($codelang, $moreatt = '')
7036
{
7037
	global $langs;
7038
7039
	if (empty($codelang)) return '';
7040
7041
	if ($codelang == 'auto')
7042
	{
7043
		return '<span class="fa fa-globe"></span>';
7044
	}
7045
7046
	$langtocountryflag = array(
7047
		'ar_AR' => '',
7048
		'ca_ES' => 'catalonia',
7049
		'da_DA' => 'dk',
7050
		'fr_CA' => 'mq',
7051
		'sv_SV' => 'se'
7052
	);
7053
7054
	if (isset($langtocountryflag[$codelang])) $flagImage = $langtocountryflag[$codelang];
7055
	else {
7056
		$tmparray = explode('_', $codelang);
7057
		$flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
7058
	}
7059
7060
	return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt);
7061
}
7062
7063
/**
7064
 * Return default language from country code.
7065
 * Return null if not found.
7066
 *
7067
 * @param 	string 	$countrycode	Country code like 'US', 'FR', 'CA', ...
7068
 * @return	string					Value of locale like 'en_US', 'fr_FR', ...
7069
 */
7070
function getLanguageCodeFromCountryCode($countrycode)
7071
{
7072
	global $mysoc;
7073
7074
	if (empty($countrycode)) return null;
7075
7076
	if (strtoupper($countrycode) == 'MQ') return 'fr_CA';
7077
	if (strtoupper($countrycode) == 'SE') return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
7078
	if (strtoupper($countrycode) == 'CH')
7079
	{
7080
		if ($mysoc->country_code == 'FR') return 'fr_CH';
7081
		if ($mysoc->country_code == 'DE') return 'de_CH';
7082
	}
7083
7084
	// Locale list taken from:
7085
	// http://stackoverflow.com/questions/3191664/
7086
	// list-of-all-locales-and-their-short-codes
7087
	$locales = array(
7088
		'af-ZA',
7089
		'am-ET',
7090
		'ar-AE',
7091
		'ar-BH',
7092
		'ar-DZ',
7093
		'ar-EG',
7094
		'ar-IQ',
7095
		'ar-JO',
7096
		'ar-KW',
7097
		'ar-LB',
7098
		'ar-LY',
7099
		'ar-MA',
7100
		'ar-OM',
7101
		'ar-QA',
7102
		'ar-SA',
7103
		'ar-SY',
7104
		'ar-TN',
7105
		'ar-YE',
7106
		'as-IN',
7107
		'ba-RU',
7108
		'be-BY',
7109
		'bg-BG',
7110
		'bn-BD',
7111
		'bn-IN',
7112
		'bo-CN',
7113
		'br-FR',
7114
		'ca-ES',
7115
		'co-FR',
7116
		'cs-CZ',
7117
		'cy-GB',
7118
		'da-DK',
7119
		'de-AT',
7120
		'de-CH',
7121
		'de-DE',
7122
		'de-LI',
7123
		'de-LU',
7124
		'dv-MV',
7125
		'el-GR',
7126
		'en-AU',
7127
		'en-BZ',
7128
		'en-CA',
7129
		'en-GB',
7130
		'en-IE',
7131
		'en-IN',
7132
		'en-JM',
7133
		'en-MY',
7134
		'en-NZ',
7135
		'en-PH',
7136
		'en-SG',
7137
		'en-TT',
7138
		'en-US',
7139
		'en-ZA',
7140
		'en-ZW',
7141
		'es-AR',
7142
		'es-BO',
7143
		'es-CL',
7144
		'es-CO',
7145
		'es-CR',
7146
		'es-DO',
7147
		'es-EC',
7148
		'es-ES',
7149
		'es-GT',
7150
		'es-HN',
7151
		'es-MX',
7152
		'es-NI',
7153
		'es-PA',
7154
		'es-PE',
7155
		'es-PR',
7156
		'es-PY',
7157
		'es-SV',
7158
		'es-US',
7159
		'es-UY',
7160
		'es-VE',
7161
		'et-EE',
7162
		'eu-ES',
7163
		'fa-IR',
7164
		'fi-FI',
7165
		'fo-FO',
7166
		'fr-BE',
7167
		'fr-CA',
7168
		'fr-CH',
7169
		'fr-FR',
7170
		'fr-LU',
7171
		'fr-MC',
7172
		'fy-NL',
7173
		'ga-IE',
7174
		'gd-GB',
7175
		'gl-ES',
7176
		'gu-IN',
7177
		'he-IL',
7178
		'hi-IN',
7179
		'hr-BA',
7180
		'hr-HR',
7181
		'hu-HU',
7182
		'hy-AM',
7183
		'id-ID',
7184
		'ig-NG',
7185
		'ii-CN',
7186
		'is-IS',
7187
		'it-CH',
7188
		'it-IT',
7189
		'ja-JP',
7190
		'ka-GE',
7191
		'kk-KZ',
7192
		'kl-GL',
7193
		'km-KH',
7194
		'kn-IN',
7195
		'ko-KR',
7196
		'ky-KG',
7197
		'lb-LU',
7198
		'lo-LA',
7199
		'lt-LT',
7200
		'lv-LV',
7201
		'mi-NZ',
7202
		'mk-MK',
7203
		'ml-IN',
7204
		'mn-MN',
7205
		'mr-IN',
7206
		'ms-BN',
7207
		'ms-MY',
7208
		'mt-MT',
7209
		'nb-NO',
7210
		'ne-NP',
7211
		'nl-BE',
7212
		'nl-NL',
7213
		'nn-NO',
7214
		'oc-FR',
7215
		'or-IN',
7216
		'pa-IN',
7217
		'pl-PL',
7218
		'ps-AF',
7219
		'pt-BR',
7220
		'pt-PT',
7221
		'rm-CH',
7222
		'ro-RO',
7223
		'ru-RU',
7224
		'rw-RW',
7225
		'sa-IN',
7226
		'se-FI',
7227
		'se-NO',
7228
		'se-SE',
7229
		'si-LK',
7230
		'sk-SK',
7231
		'sl-SI',
7232
		'sq-AL',
7233
		'sv-FI',
7234
		'sv-SE',
7235
		'sw-KE',
7236
		'ta-IN',
7237
		'te-IN',
7238
		'th-TH',
7239
		'tk-TM',
7240
		'tn-ZA',
7241
		'tr-TR',
7242
		'tt-RU',
7243
		'ug-CN',
7244
		'uk-UA',
7245
		'ur-PK',
7246
		'vi-VN',
7247
		'wo-SN',
7248
		'xh-ZA',
7249
		'yo-NG',
7250
		'zh-CN',
7251
		'zh-HK',
7252
		'zh-MO',
7253
		'zh-SG',
7254
		'zh-TW',
7255
		'zu-ZA',
7256
	);
7257
7258
	$buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
7259
	if (in_array($buildprimarykeytotest, $locales)) return strtolower($countrycode).'_'.strtoupper($countrycode);
7260
7261
	if (function_exists('locale_get_primary_language') && function_exists('locale_get_region'))    // Need extension php-intl
7262
	{
7263
		foreach ($locales as $locale)
7264
		{
7265
			$locale_language = locale_get_primary_language($locale);
7266
			$locale_region = locale_get_region($locale);
7267
			if (strtoupper($countrycode) == $locale_region)
7268
			{
7269
				//var_dump($locale.'-'.$locale_language.'-'.$locale_region);
7270
				return strtolower($locale_language).'_'.strtoupper($locale_region);
7271
			}
7272
		}
7273
	} else {
7274
		dol_syslog("Warning Exention php-intl is not available", LOG_WARNING);
7275
	}
7276
7277
	return null;
7278
}
7279
7280
/**
7281
 *  Complete or removed entries into a head array (used to build tabs).
7282
 *  For example, with value added by external modules. Such values are declared into $conf->modules_parts['tab'].
7283
 *  Or by change using hook completeTabsHead
7284
 *
7285
 *  @param	Conf			$conf           Object conf
7286
 *  @param  Translate		$langs          Object langs
7287
 *  @param  object|null		$object         Object object
7288
 *  @param  array			$head          	Object head
7289
 *  @param  int				$h				New position to fill
7290
 *  @param  string			$type           Value for object where objectvalue can be
7291
 *                              			'thirdparty'       to add a tab in third party view
7292
 *		                        	      	'intervention'     to add a tab in intervention view
7293
 *     		                    	     	'supplier_order'   to add a tab in supplier order view
7294
 *          		            	        'supplier_invoice' to add a tab in supplier invoice view
7295
 *                  		    	        'invoice'          to add a tab in customer invoice view
7296
 *                          			    'order'            to add a tab in customer order view
7297
 *                          				'contract'		   to add a tabl in contract view
7298
 *                      			        'product'          to add a tab in product view
7299
 *                              			'propal'           to add a tab in propal view
7300
 *                              			'user'             to add a tab in user view
7301
 *                              			'group'            to add a tab in group view
7302
 * 		        	               	     	'member'           to add a tab in fundation member view
7303
 *      		                        	'categories_x'	   to add a tab in category view ('x': type of category (0=product, 1=supplier, 2=customer, 3=member)
7304
 *      									'ecm'			   to add a tab for another ecm view
7305
 *                                          'stock'            to add a tab for warehouse view
7306
 *  @param  string		$mode  	        	'add' to complete head, 'remove' to remove entries
7307
 *	@return	void
7308
 */
7309
function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add')
7310
{
7311
	global $hookmanager;
7312
7313
	if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type]))
7314
	{
7315
		foreach ($conf->modules_parts['tabs'][$type] as $value)
7316
		{
7317
			$values = explode(':', $value);
7318
7319
			if ($mode == 'add' && !preg_match('/^\-/', $values[1]))
7320
			{
7321
				if (count($values) == 6)       // new declaration with permissions:  $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
7322
				{
7323
					if ($values[0] != $type) continue;
7324
7325
					if (verifCond($values[4]))
7326
					{
7327
						if ($values[3]) $langs->load($values[3]);
7328
						if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg))
7329
						{
7330
							$substitutionarray = array();
7331
							complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
7332
							$label = make_substitutions($reg[1], $substitutionarray);
7333
						} else $label = $langs->trans($values[2]);
7334
7335
						$head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
7336
						$head[$h][1] = $label;
7337
						$head[$h][2] = str_replace('+', '', $values[1]);
7338
						$h++;
7339
					}
7340
				} elseif (count($values) == 5)       // deprecated
7341
				{
7342
					dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
7343
7344
					if ($values[0] != $type) continue;
7345
					if ($values[3]) $langs->load($values[3]);
7346
					if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg))
7347
					{
7348
						$substitutionarray = array();
7349
						complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
7350
						$label = make_substitutions($reg[1], $substitutionarray);
7351
					} else $label = $langs->trans($values[2]);
7352
7353
					$head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
7354
					$head[$h][1] = $label;
7355
					$head[$h][2] = str_replace('+', '', $values[1]);
7356
					$h++;
7357
				}
7358
			} elseif ($mode == 'remove' && preg_match('/^\-/', $values[1]))
7359
			{
7360
				if ($values[0] != $type) continue;
7361
				$tabname = str_replace('-', '', $values[1]);
7362
				foreach ($head as $key => $val)
7363
				{
7364
					$condition = (!empty($values[3]) ? verifCond($values[3]) : 1);
7365
					//var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
7366
					if ($head[$key][2] == $tabname && $condition)
7367
					{
7368
						unset($head[$key]);
7369
						break;
7370
					}
7371
				}
7372
			}
7373
		}
7374
	}
7375
7376
	// No need to make a return $head. Var is modified as a reference
7377
	if (!empty($hookmanager))
7378
	{
7379
		$parameters = array('object' => $object, 'mode' => $mode, 'head' => $head);
7380
		$reshook = $hookmanager->executeHooks('completeTabsHead', $parameters);
7381
		if ($reshook > 0)
7382
		{
7383
			$head = $hookmanager->resArray;
7384
			$h = count($head);
7385
		}
7386
	}
7387
}
7388
7389
/**
7390
 * Print common footer :
7391
 * 		conf->global->MAIN_HTML_FOOTER
7392
 *      js for switch of menu hider
7393
 * 		js for conf->global->MAIN_GOOGLE_AN_ID
7394
 * 		js for conf->global->MAIN_SHOW_TUNING_INFO or $_SERVER["MAIN_SHOW_TUNING_INFO"]
7395
 * 		js for conf->logbuffer
7396
 *
7397
 * @param	string	$zone	'private' (for private pages) or 'public' (for public pages)
7398
 * @return	void
7399
 */
7400
function printCommonFooter($zone = 'private')
7401
{
7402
	global $conf, $hookmanager, $user, $debugbar;
7403
	global $action;
7404
	global $micro_start_time;
7405
7406
	if ($zone == 'private') print "\n".'<!-- Common footer for private page -->'."\n";
7407
	else print "\n".'<!-- Common footer for public page -->'."\n";
7408
7409
	// A div to store page_y POST parameter so we can read it using javascript
7410
	print "\n<!-- A div to store page_y POST parameter -->\n";
7411
	print '<div id="page_y" style="display: none;">'.$_POST['page_y'].'</div>'."\n";
7412
7413
	$parameters = array();
7414
	$reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
7415
	if (empty($reshook))
7416
	{
7417
		if (!empty($conf->global->MAIN_HTML_FOOTER)) print $conf->global->MAIN_HTML_FOOTER."\n";
7418
7419
		print "\n";
7420
		if (!empty($conf->use_javascript_ajax))
7421
		{
7422
			print '<script>'."\n";
7423
			print 'jQuery(document).ready(function() {'."\n";
7424
7425
			if ($zone == 'private' && empty($conf->dol_use_jmobile))
7426
			{
7427
				print "\n";
7428
				print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
7429
				print 'jQuery("li.menuhider").click(function(event) {';
7430
				print '  if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
7431
				print '  console.log("We click on .menuhider");'."\n";
7432
				print '  $("body").toggleClass("sidebar-collapse")'."\n";
7433
				print '});'."\n";
7434
			}
7435
7436
			// Management of focus and mandatory for fields
7437
			if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"]))))
7438
			{
7439
				print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
7440
				$relativepathstring = $_SERVER["PHP_SELF"];
7441
				// Clean $relativepathstring
7442
				if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
7443
				$relativepathstring = preg_replace('/^\//', '', $relativepathstring);
7444
				$relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
7445
				//$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
7446
				if (!empty($user->default_values[$relativepathstring]['focus']))
7447
				{
7448
					foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval)
7449
					{
7450
						$qualified = 0;
7451
						if ($defkey != '_noquery_')
7452
						{
7453
							$tmpqueryarraytohave = explode('&', $defkey);
7454
							$foundintru = 0;
7455
							foreach ($tmpqueryarraytohave as $tmpquerytohave)
7456
							{
7457
								$tmpquerytohaveparam = explode('=', $tmpquerytohave);
7458
								//print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
7459
								if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) $foundintru = 1;
7460
							}
7461
							if (!$foundintru) $qualified = 1;
7462
							//var_dump($defkey.'-'.$qualified);
7463
						} else $qualified = 1;
7464
7465
						if ($qualified)
7466
						{
7467
							foreach ($defval as $paramkey => $paramval)
7468
							{
7469
								// Set focus on field
7470
								print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
7471
								print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n";
7472
								print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really usefull, but we keep it in case of.
7473
							}
7474
						}
7475
					}
7476
				}
7477
				if (!empty($user->default_values[$relativepathstring]['mandatory']))
7478
				{
7479
					foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval)
7480
					{
7481
						$qualified = 0;
7482
						if ($defkey != '_noquery_')
7483
						{
7484
							$tmpqueryarraytohave = explode('&', $defkey);
7485
							$foundintru = 0;
7486
							foreach ($tmpqueryarraytohave as $tmpquerytohave)
7487
							{
7488
								$tmpquerytohaveparam = explode('=', $tmpquerytohave);
7489
								//print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
7490
								if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) $foundintru = 1;
7491
							}
7492
							if (!$foundintru) $qualified = 1;
7493
							//var_dump($defkey.'-'.$qualified);
7494
						} else $qualified = 1;
7495
7496
						if ($qualified)
7497
						{
7498
							foreach ($defval as $paramkey => $paramval)
7499
							{
7500
								// Add property 'required' on input
7501
								print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
7502
								print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
7503
								print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; // required on a select works only if key is "", this does not happen in Dolibarr
7504
							}
7505
						}
7506
					}
7507
				}
7508
			}
7509
7510
			print '});'."\n";
7511
7512
			// End of tuning
7513
			if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || !empty($conf->global->MAIN_SHOW_TUNING_INFO))
7514
			{
7515
				print "\n";
7516
				print "/* JS CODE TO ENABLE to add memory info */\n";
7517
				print 'window.console && console.log("';
7518
				if (!empty($conf->global->MEMCACHED_SERVER)) print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
7519
				print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED) ? $conf->global->MAIN_OPTIMIZE_SPEED : 'off');
7520
				if (!empty($micro_start_time))   // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
7521
				{
7522
					$micro_end_time = microtime(true);
7523
					print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
7524
				}
7525
7526
				if (function_exists("memory_get_usage")) {
7527
					print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
7528
				}
7529
				if (function_exists("memory_get_peak_usage")) {
7530
					print ' - Real mem peak: '.memory_get_peak_usage(true);
7531
				}
7532
				if (function_exists("zend_loader_file_encoded"))
7533
				{
7534
					print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
7535
				}
7536
				print '");'."\n";
7537
			}
7538
7539
			print "\n".'</script>'."\n";
7540
7541
			// Google Analytics
7542
			// TODO Add a hook here
7543
			if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AN_ID))
7544
			{
7545
				$tmptagarray = explode(',', $conf->global->MAIN_GOOGLE_AN_ID);
7546
				foreach ($tmptagarray as $tmptag) {
7547
					print "\n";
7548
					print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
7549
					print "
7550
					<!-- Global site tag (gtag.js) - Google Analytics -->
7551
					<script async src=\"https://www.googletagmanager.com/gtag/js?id=".trim($tmptag)."\"></script>
7552
					<script>
7553
					window.dataLayer = window.dataLayer || [];
7554
					function gtag(){dataLayer.push(arguments);}
7555
					gtag('js', new Date());
7556
7557
					gtag('config', '".trim($tmptag)."');
7558
					</script>";
7559
					print "\n";
7560
				}
7561
			}
7562
		}
7563
7564
		// Add Xdebug coverage of code
7565
		if (defined('XDEBUGCOVERAGE'))
7566
		{
7567
			print_r(xdebug_get_code_coverage());
7568
		}
7569
7570
		// Add DebugBar data
7571
		if (!empty($user->rights->debugbar->read) && is_object($debugbar))
7572
		{
7573
			$debugbar['time']->stopMeasure('pageaftermaster');
7574
			print '<!-- Output debugbar data -->'."\n";
7575
			$renderer = $debugbar->getRenderer();
7576
			print $debugbar->getRenderer()->render();
7577
		} elseif (count($conf->logbuffer))    // If there is some logs in buffer to show
7578
		{
7579
			print "\n";
7580
			print "<!-- Start of log output\n";
7581
			//print '<div class="hidden">'."\n";
7582
			foreach ($conf->logbuffer as $logline)
7583
			{
7584
				print $logline."<br>\n";
7585
			}
7586
			//print '</div>'."\n";
7587
			print "End of log output -->\n";
7588
		}
7589
	}
7590
}
7591
7592
/**
7593
 * Split a string with 2 keys into key array.
7594
 * For example: "A=1;B=2;C=2" is exploded into array('A'=>1,'B'=>2,'C'=>3)
7595
 *
7596
 * @param 	string	$string		String to explode
7597
 * @param 	string	$delimiter	Delimiter between each couple of data
7598
 * @param 	string	$kv			Delimiter between key and value
7599
 * @return	array				Array of data exploded
7600
 */
7601
function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
7602
{
7603
	if ($a = explode($delimiter, $string))
7604
	{
7605
		$ka = array();
7606
		foreach ($a as $s) { // each part
7607
			if ($s) {
7608
				if ($pos = strpos($s, $kv)) { // key/value delimiter
7609
					$ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
7610
				} else { // key delimiter not found
7611
					$ka[] = trim($s);
7612
				}
7613
			}
7614
		}
7615
		return $ka;
7616
	}
7617
	return array();
7618
}
7619
7620
7621
/**
7622
 * Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag)
7623
 *
7624
 * @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.
7625
 * @return	string				HTML code to set focus
7626
 */
7627
function dol_set_focus($selector)
7628
{
7629
	print "\n".'<!-- Set focus onto a specific field -->'."\n";
7630
	print '<script>jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
7631
}
7632
7633
7634
/**
7635
 * Return getmypid() or random PID when function is disabled
7636
 * Some web hosts disable this php function for security reasons
7637
 * and sometimes we can't redeclare function
7638
 *
7639
 * @return	int
7640
 */
7641
function dol_getmypid()
7642
{
7643
	if (!function_exists('getmypid')) {
7644
		return mt_rand(1, 32768);
7645
	} else {
7646
		return getmypid();
7647
	}
7648
}
7649
7650
7651
/**
7652
 * Generate natural SQL search string for a criteria (this criteria can be tested on one or several fields)
7653
 *
7654
 * @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")
7655
 * @param   string 			$value 		The value to look for.
7656
 *                          		    If param $mode is 0, can contains several keywords separated with a space or |
7657
 *                                      like "keyword1 keyword2" = We want record field like keyword1 AND field like keyword2
7658
 *                                      or like "keyword1|keyword2" = We want record field like keyword1 OR field like keyword2
7659
 *                             			If param $mode is 1, can contains an operator <, > or = like "<10" or ">=100.5 < 1000"
7660
 *                             			If param $mode is 2, can contains a list of int id separated by comma like "1,3,4"
7661
 *                             			If param $mode is 3, can contains a list of string separated by comma like "a,b,c"
7662
 * @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')
7663
 * 										3=value is list of string separated with comma (Example 'text 1,text 2'), 4=value is a list of ID separated with comma (Example '1,3,4') for search into a multiselect string ('1,2')
7664
 * @param	integer			$nofirstand	1=Do not output the first 'AND'
7665
 * @return 	string 			$res 		The statement to append to the SQL query
7666
 */
7667
function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
7668
{
7669
	global $db, $langs;
7670
7671
	$value = trim($value);
7672
7673
	if ($mode == 0)
7674
	{
7675
		$value = preg_replace('/\*/', '%', $value); // Replace * with %
7676
	}
7677
	if ($mode == 1)
7678
	{
7679
		$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
7680
	}
7681
7682
	$value = preg_replace('/\s*\|\s*/', '|', $value);
7683
7684
	$crits = explode(' ', $value);
7685
	$res = '';
7686
	if (!is_array($fields)) $fields = array($fields);
7687
7688
	$nboffields = count($fields);
7689
	$end2 = count($crits);
7690
	$j = 0;
7691
	foreach ($crits as $crit)
7692
	{
7693
		$i = 0; $i2 = 0;
7694
		$newres = '';
7695
		foreach ($fields as $field)
7696
		{
7697
			if ($mode == 1)
7698
			{
7699
				$operator = '=';
7700
				$newcrit = preg_replace('/([<>=]+)/', '', trim($crit));
7701
7702
				$reg = array();
7703
				preg_match('/([<>=]+)/', trim($crit), $reg);
7704
				if ($reg[1])
7705
				{
7706
					$operator = $reg[1];
7707
				}
7708
				if ($newcrit != '')
7709
				{
7710
					$numnewcrit = price2num($newcrit);
7711
					if (is_numeric($numnewcrit))
7712
					{
7713
						$newres .= ($i2 > 0 ? ' OR ' : '').$field.' '.$operator.' '.$numnewcrit;
7714
					} else {
7715
						$newres .= ($i2 > 0 ? ' OR ' : '').'1 = 2'; // force false
7716
					}
7717
					$i2++; // a criteria was added to string
7718
				}
7719
			} elseif ($mode == 2 || $mode == -2)
7720
			{
7721
				$newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '')."IN (".$db->escape(trim($crit)).")";
7722
				if ($mode == -2) $newres .= ' OR '.$field.' IS NULL';
7723
				$i2++; // a criteria was added to string
7724
			} elseif ($mode == 3 || $mode == -3)
7725
			{
7726
				$tmparray = explode(',', trim($crit));
7727
				if (count($tmparray))
7728
				{
7729
					$listofcodes = '';
7730
					foreach ($tmparray as $val)
7731
					{
7732
						if ($val)
7733
						{
7734
							$listofcodes .= ($listofcodes ? ',' : '');
7735
							$listofcodes .= "'".$db->escape(trim($val))."'";
7736
						}
7737
					}
7738
					$newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$listofcodes.")";
7739
					$i2++; // a criteria was added to string
7740
				}
7741
				if ($mode == -3) $newres .= ' OR '.$field.' IS NULL';
7742
			} elseif ($mode == 4)
7743
			{
7744
				$tmparray = explode(',', trim($crit));
7745
				if (count($tmparray))
7746
				{
7747
					$listofcodes = '';
7748
					foreach ($tmparray as $val)
7749
					{
7750
						if ($val)
7751
						{
7752
							$newres .= ($i2 > 0 ? ' OR (' : '(').$field.' LIKE \''.$db->escape(trim($val)).',%\'';
7753
							$newres .= ' OR '.$field.' = \''.$db->escape(trim($val)).'\'';
7754
							$newres .= ' OR '.$field.' LIKE \'%,'.$db->escape(trim($val)).'\'';
7755
							$newres .= ' OR '.$field.' LIKE \'%,'.$db->escape(trim($val)).',%\'';
7756
							$newres .= ')';
7757
							$i2++;
7758
						}
7759
					}
7760
				}
7761
			} else // $mode=0
7762
			{
7763
				$tmpcrits = explode('|', $crit);
7764
				$i3 = 0;
7765
				foreach ($tmpcrits as $tmpcrit)
7766
				{
7767
					if ($tmpcrit !== '0' && empty($tmpcrit)) continue;
7768
7769
					$newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
7770
7771
					if (preg_match('/\.(id|rowid)$/', $field))	// Special case for rowid that is sometimes a ref so used as a search field
7772
					{
7773
						$newres .= $field." = ".(is_numeric(trim($tmpcrit)) ?trim($tmpcrit) : '0');
7774
					} else {
7775
						$newres .= $field." LIKE '";
7776
7777
						$tmpcrit = trim($tmpcrit);
7778
						$tmpcrit2 = $tmpcrit;
7779
						$tmpbefore = '%'; $tmpafter = '%';
7780
						if (preg_match('/^[\^\$]/', $tmpcrit))
7781
						{
7782
							$tmpbefore = '';
7783
							$tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
7784
						}
7785
						if (preg_match('/[\^\$]$/', $tmpcrit))
7786
						{
7787
							$tmpafter = '';
7788
							$tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
7789
						}
7790
						$newres .= $tmpbefore;
7791
						$newres .= $db->escape($tmpcrit2);
7792
						$newres .= $tmpafter;
7793
						$newres .= "'";
7794
						if ($tmpcrit2 == '')
7795
						{
7796
							$newres .= ' OR '.$field." IS NULL";
7797
						}
7798
					}
7799
7800
					$i3++;
7801
				}
7802
				$i2++; // a criteria was added to string
7803
			}
7804
			$i++;
7805
		}
7806
		if ($newres) $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
7807
		$j++;
7808
	}
7809
	$res = ($nofirstand ? "" : " AND ")."(".$res.")";
7810
	//print 'xx'.$res.'yy';
7811
	return $res;
7812
}
7813
7814
/**
7815
 * Return string with full Url. The file qualified is the one defined by relative path in $object->last_main_doc
7816
 *
7817
 * @param   Object	$object				Object
7818
 * @return	string						Url string
7819
 */
7820
function showDirectDownloadLink($object)
7821
{
7822
	global $conf, $langs;
7823
7824
	$out = '';
7825
	$url = $object->getLastMainDocLink($object->element);
7826
7827
	if ($url)
7828
	{
7829
		$out .= img_picto('', 'globe').' '.$langs->trans("DirectDownloadLink").'<br>';
7830
		$out .= '<input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'">';
7831
		$out .= ajax_autoselect("directdownloadlink", 0);
7832
	}
7833
	return $out;
7834
}
7835
7836
/**
7837
 * Return the filename of file to get the thumbs
7838
 *
7839
 * @param   string  $file           Original filename (full or relative path)
7840
 * @param   string  $extName        Extension to differenciate thumb file name ('', '_small', '_mini')
7841
 * @param   string  $extImgTarget   Force image extension for thumbs. Use '' to keep same extension than original image (default).
7842
 * @return  string                  New file name (full or relative path, including the thumbs/)
7843
 */
7844
function getImageFileNameForSize($file, $extName, $extImgTarget = '')
7845
{
7846
	$dirName = dirname($file);
7847
	if ($dirName == '.') $dirName = '';
7848
7849
	$fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
7850
	$fileName = basename($fileName);
7851
7852
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
7853
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
7854
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
7855
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
7856
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
7857
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
7858
7859
	if (!$extImgTarget) return $file;
7860
7861
	$subdir = '';
7862
	if ($extName) $subdir = 'thumbs/';
7863
7864
	return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
7865
}
7866
7867
7868
/**
7869
 * Return URL we can use for advanced preview links
7870
 *
7871
 * @param   string    $modulepart     propal, facture, facture_fourn, ...
7872
 * @param   string    $relativepath   Relative path of docs.
7873
 * @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)
7874
 * @param	string	  $param		  More param on http links
7875
 * @return  string|array              Output string with href link or array with all components of link
7876
 */
7877
function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
7878
{
7879
	global $conf, $langs;
7880
7881
	if (empty($conf->use_javascript_ajax)) return '';
7882
7883
	$isAllowedForPreview = dolIsAllowedForPreview($relativepath);
7884
7885
	if ($alldata == 1)
7886
	{
7887
		if ($isAllowedForPreview) 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),);
7888
		else return array();
7889
	}
7890
7891
	// old behavior, return a string
7892
	if ($isAllowedForPreview) 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')).'\')';
7893
	else return '';
7894
}
7895
7896
7897
/**
7898
 * Make content of an input box selected when we click into input field.
7899
 *
7900
 * @param string	$htmlname	Id of html object ('#idvalue' or '.classvalue')
7901
 * @param string	$addlink	Add a 'link to' after
7902
 * @return string
7903
 */
7904
function ajax_autoselect($htmlname, $addlink = '')
7905
{
7906
	global $langs;
7907
	$out = '<script>
7908
               jQuery(document).ready(function () {
7909
				    jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
7910
				});
7911
		    </script>';
7912
	if ($addlink) $out .= ' <a href="'.$addlink.'" target="_blank">'.$langs->trans("Link").'</a>';
7913
	return $out;
7914
}
7915
7916
7917
/**
7918
 *	Return if a file is qualified for preview
7919
 *
7920
 *	@param	string	$file		Filename we looking for information
7921
 *	@return int					1 If allowed, 0 otherwise
7922
 *  @see    dol_mimetype(), image_format_supported() from images.lib.php
7923
 */
7924
function dolIsAllowedForPreview($file) {
7925
	global $conf;
7926
7927
	// Check .noexe extension in filename
7928
	if (preg_match('/\.noexe$/i', $file)) return 0;
7929
7930
	// Check mime types
7931
	$mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
7932
	if (!empty($conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES)) $mime_preview[] = 'svg+xml';
7933
	//$mime_preview[]='vnd.oasis.opendocument.presentation';
7934
	//$mime_preview[]='archive';
7935
	$num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
7936
	if ($num_mime !== false) return 1;
7937
7938
	// By default, not allowed for preview
7939
	return 0;
7940
}
7941
7942
7943
/**
7944
 *	Return mime type of a file
7945
 *
7946
 *	@param	string	$file		Filename we looking for MIME type
7947
 *  @param  string	$default    Default mime type if extension not found in known list
7948
 * 	@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
7949
 *	@return string 		    	Return a mime type family (text/xxx, application/xxx, image/xxx, audio, video, archive)
7950
 *  @see    dolIsAllowedForPreview(), image_format_supported() from images.lib.php
7951
 */
7952
function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
7953
{
7954
	$mime = $default;
7955
	$imgmime = 'other.png';
7956
	$famime = 'file-o';
7957
	$srclang = '';
7958
7959
	$tmpfile = preg_replace('/\.noexe$/', '', $file);
7960
7961
	// Plain text files
7962
	if (preg_match('/\.txt$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $famime = 'file-text-o'; }
7963
	if (preg_match('/\.rtx$/i', $tmpfile)) { $mime = 'text/richtext'; $imgmime = 'text.png'; $famime = 'file-text-o'; }
7964
	if (preg_match('/\.csv$/i', $tmpfile)) { $mime = 'text/csv'; $imgmime = 'text.png'; $famime = 'file-text-o'; }
7965
	if (preg_match('/\.tsv$/i', $tmpfile)) { $mime = 'text/tab-separated-values'; $imgmime = 'text.png'; $famime = 'file-text-o'; }
7966
	if (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $famime = 'file-text-o'; }
7967
	if (preg_match('/\.ini$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'ini'; $famime = 'file-text-o'; }
7968
	if (preg_match('/\.md$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'md'; $famime = 'file-text-o'; }
7969
	if (preg_match('/\.css$/i', $tmpfile)) { $mime = 'text/css'; $imgmime = 'css.png'; $srclang = 'css'; $famime = 'file-text-o'; }
7970
	// Certificate files
7971
	if (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $famime = 'file-text-o'; }
7972
	// XML based (HTML/XML/XAML)
7973
	if (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { $mime = 'text/html'; $imgmime = 'html.png'; $srclang = 'html'; $famime = 'file-text-o'; }
7974
	if (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) { $mime = 'text/xml'; $imgmime = 'other.png'; $srclang = 'xml'; $famime = 'file-text-o'; }
7975
	if (preg_match('/\.xaml$/i', $tmpfile)) { $mime = 'text/xml'; $imgmime = 'other.png'; $srclang = 'xaml'; $famime = 'file-text-o'; }
7976
	// Languages
7977
	if (preg_match('/\.bas$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'bas'; $famime = 'file-code-o'; }
7978
	if (preg_match('/\.(c)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'c'; $famime = 'file-code-o'; }
7979
	if (preg_match('/\.(cpp)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'cpp'; $famime = 'file-code-o'; }
7980
	if (preg_match('/\.cs$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'cs'; $famime = 'file-code-o'; }
7981
	if (preg_match('/\.(h)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'h'; $famime = 'file-code-o'; }
7982
	if (preg_match('/\.(java|jsp)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'java'; $famime = 'file-code-o'; }
7983
	if (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'php.png'; $srclang = 'php'; $famime = 'file-code-o'; }
7984
	if (preg_match('/\.phtml$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'php.png'; $srclang = 'php'; $famime = 'file-code-o'; }
7985
	if (preg_match('/\.(pl|pm)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'pl.png'; $srclang = 'perl'; $famime = 'file-code-o'; }
7986
	if (preg_match('/\.sql$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'sql'; $famime = 'file-code-o'; }
7987
	if (preg_match('/\.js$/i', $tmpfile)) { $mime = 'text/x-javascript'; $imgmime = 'jscript.png'; $srclang = 'js'; $famime = 'file-code-o'; }
7988
	// Open office
7989
	if (preg_match('/\.odp$/i', $tmpfile)) { $mime = 'application/vnd.oasis.opendocument.presentation'; $imgmime = 'ooffice.png'; $famime = 'file-powerpoint-o'; }
7990
	if (preg_match('/\.ods$/i', $tmpfile)) { $mime = 'application/vnd.oasis.opendocument.spreadsheet'; $imgmime = 'ooffice.png'; $famime = 'file-excel-o'; }
7991
	if (preg_match('/\.odt$/i', $tmpfile)) { $mime = 'application/vnd.oasis.opendocument.text'; $imgmime = 'ooffice.png'; $famime = 'file-word-o'; }
7992
	// MS Office
7993
	if (preg_match('/\.mdb$/i', $tmpfile)) { $mime = 'application/msaccess'; $imgmime = 'mdb.png'; $famime = 'file-o'; }
7994
	if (preg_match('/\.doc(x|m)?$/i', $tmpfile)) { $mime = 'application/msword'; $imgmime = 'doc.png'; $famime = 'file-word-o'; }
7995
	if (preg_match('/\.dot(x|m)?$/i', $tmpfile)) { $mime = 'application/msword'; $imgmime = 'doc.png'; $famime = 'file-word-o'; }
7996
	if (preg_match('/\.xlt(x)?$/i', $tmpfile)) { $mime = 'application/vnd.ms-excel'; $imgmime = 'xls.png'; $famime = 'file-excel-o'; }
7997
	if (preg_match('/\.xla(m)?$/i', $tmpfile)) { $mime = 'application/vnd.ms-excel'; $imgmime = 'xls.png'; $famime = 'file-excel-o'; }
7998
	if (preg_match('/\.xls$/i', $tmpfile)) { $mime = 'application/vnd.ms-excel'; $imgmime = 'xls.png'; $famime = 'file-excel-o'; }
7999
	if (preg_match('/\.xls(b|m|x)$/i', $tmpfile)) { $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; $imgmime = 'xls.png'; $famime = 'file-excel-o'; }
8000
	if (preg_match('/\.pps(m|x)?$/i', $tmpfile)) { $mime = 'application/vnd.ms-powerpoint'; $imgmime = 'ppt.png'; $famime = 'file-powerpoint-o'; }
8001
	if (preg_match('/\.ppt(m|x)?$/i', $tmpfile)) { $mime = 'application/x-mspowerpoint'; $imgmime = 'ppt.png'; $famime = 'file-powerpoint-o'; }
8002
	// Other
8003
	if (preg_match('/\.pdf$/i', $tmpfile)) { $mime = 'application/pdf'; $imgmime = 'pdf.png'; $famime = 'file-pdf-o'; }
8004
	// Scripts
8005
	if (preg_match('/\.bat$/i', $tmpfile)) { $mime = 'text/x-bat'; $imgmime = 'script.png'; $srclang = 'dos'; $famime = 'file-code-o'; }
8006
	if (preg_match('/\.sh$/i', $tmpfile)) { $mime = 'text/x-sh'; $imgmime = 'script.png'; $srclang = 'bash'; $famime = 'file-code-o'; }
8007
	if (preg_match('/\.ksh$/i', $tmpfile)) { $mime = 'text/x-ksh'; $imgmime = 'script.png'; $srclang = 'bash'; $famime = 'file-code-o'; }
8008
	if (preg_match('/\.bash$/i', $tmpfile)) { $mime = 'text/x-bash'; $imgmime = 'script.png'; $srclang = 'bash'; $famime = 'file-code-o'; }
8009
	// Images
8010
	if (preg_match('/\.ico$/i', $tmpfile)) { $mime = 'image/x-icon'; $imgmime = 'image.png'; $famime = 'file-image-o'; }
8011
	if (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) { $mime = 'image/jpeg'; $imgmime = 'image.png'; $famime = 'file-image-o'; }
8012
	if (preg_match('/\.png$/i', $tmpfile)) { $mime = 'image/png'; $imgmime = 'image.png'; $famime = 'file-image-o'; }
8013
	if (preg_match('/\.gif$/i', $tmpfile)) { $mime = 'image/gif'; $imgmime = 'image.png'; $famime = 'file-image-o'; }
8014
	if (preg_match('/\.bmp$/i', $tmpfile)) { $mime = 'image/bmp'; $imgmime = 'image.png'; $famime = 'file-image-o'; }
8015
	if (preg_match('/\.(tif|tiff)$/i', $tmpfile)) { $mime = 'image/tiff'; $imgmime = 'image.png'; $famime = 'file-image-o'; }
8016
	if (preg_match('/\.svg$/i', $tmpfile)) { $mime = 'image/svg+xml'; $imgmime = 'image.png'; $famime = 'file-image-o'; }
8017
	if (preg_match('/\.webp$/i', $tmpfile)) { $mime = 'image/webp'; $imgmime = 'image.png'; $famime = 'file-image-o'; }
8018
	// Calendar
8019
	if (preg_match('/\.vcs$/i', $tmpfile)) { $mime = 'text/calendar'; $imgmime = 'other.png'; $famime = 'file-text-o'; }
8020
	if (preg_match('/\.ics$/i', $tmpfile)) { $mime = 'text/calendar'; $imgmime = 'other.png'; $famime = 'file-text-o'; }
8021
	// Other
8022
	if (preg_match('/\.torrent$/i', $tmpfile)) { $mime = 'application/x-bittorrent'; $imgmime = 'other.png'; $famime = 'file-o'; }
8023
	// Audio
8024
	if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { $mime = 'audio'; $imgmime = 'audio.png'; $famime = 'file-audio-o'; }
8025
	// Video
8026
	if (preg_match('/\.ogv$/i', $tmpfile)) { $mime = 'video/ogg'; $imgmime = 'video.png'; $famime = 'file-video-o'; }
8027
	if (preg_match('/\.webm$/i', $tmpfile)) { $mime = 'video/webm'; $imgmime = 'video.png'; $famime = 'file-video-o'; }
8028
	if (preg_match('/\.avi$/i', $tmpfile)) { $mime = 'video/x-msvideo'; $imgmime = 'video.png'; $famime = 'file-video-o'; }
8029
	if (preg_match('/\.divx$/i', $tmpfile)) { $mime = 'video/divx'; $imgmime = 'video.png'; $famime = 'file-video-o'; }
8030
	if (preg_match('/\.xvid$/i', $tmpfile)) { $mime = 'video/xvid'; $imgmime = 'video.png'; $famime = 'file-video-o'; }
8031
	if (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) { $mime = 'video'; $imgmime = 'video.png'; $famime = 'file-video-o'; }
8032
	// Archive
8033
	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, ...
8034
	// Exe
8035
	if (preg_match('/\.(exe|com)$/i', $tmpfile)) { $mime = 'application/octet-stream'; $imgmime = 'other.png'; $famime = 'file-o'; }
8036
	// Lib
8037
	if (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { $mime = 'library'; $imgmime = 'library.png'; $famime = 'file-o'; }
8038
	// Err
8039
	if (preg_match('/\.err$/i', $tmpfile)) { $mime = 'error'; $imgmime = 'error.png'; $famime = 'file-text-o'; }
8040
8041
	// Return string
8042
	if ($mode == 1)
8043
	{
8044
		$tmp = explode('/', $mime);
8045
		return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
8046
	}
8047
	if ($mode == 2)
8048
	{
8049
		return $imgmime;
8050
	}
8051
	if ($mode == 3)
8052
	{
8053
		return $srclang;
8054
	}
8055
	if ($mode == 4)
8056
	{
8057
		return $famime;
8058
	}
8059
	return $mime;
8060
}
8061
8062
/**
8063
 * Return value from dictionary
8064
 *
8065
 * @param string	$tablename		name of dictionary
8066
 * @param string	$field			the value to return
8067
 * @param int		$id				id of line
8068
 * @param bool		$checkentity	add filter on entity
8069
 * @param string	$rowidfield		name of the column rowid
8070
 * @return string
8071
 */
8072
function getDictvalue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
8073
{
8074
	global $dictvalues, $db, $langs;
8075
8076
	if (!isset($dictvalues[$tablename]))
8077
	{
8078
		$dictvalues[$tablename] = array();
8079
		$sql = 'SELECT * FROM '.$tablename.' WHERE 1'; // Here select * is allowed as it is generic code and we don't have list of fields
8080
		if ($checkentity) $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
8081
8082
		$resql = $db->query($sql);
8083
		if ($resql)
8084
		{
8085
			while ($obj = $db->fetch_object($resql))
8086
			{
8087
				$dictvalues[$tablename][$obj->{$rowidfield}] = $obj;
8088
			}
8089
		} else {
8090
			dol_print_error($db);
8091
		}
8092
	}
8093
8094
	if (!empty($dictvalues[$tablename][$id])) return $dictvalues[$tablename][$id]->{$field}; // Found
8095
	else // Not found
8096
	{
8097
		if ($id > 0) return $id;
8098
		return '';
8099
	}
8100
}
8101
8102
/**
8103
 *	Return true if the color is light
8104
 *
8105
 *  @param	string	$stringcolor		String with hex (FFFFFF) or comma RGB ('255,255,255')
8106
 *  @return	int							-1 : Error with argument passed |0 : color is dark | 1 : color is light
8107
 */
8108
function colorIsLight($stringcolor)
8109
{
8110
	$stringcolor = str_replace('#', '', $stringcolor);
8111
	$res = -1;
8112
	if (!empty($stringcolor))
8113
	{
8114
		$res = 0;
8115
		$tmp = explode(',', $stringcolor);
8116
		if (count($tmp) > 1)   // This is a comma RGB ('255','255','255')
8117
		{
8118
			$r = $tmp[0];
8119
			$g = $tmp[1];
8120
			$b = $tmp[2];
8121
		} else {
8122
			$hexr = $stringcolor[0].$stringcolor[1];
8123
			$hexg = $stringcolor[2].$stringcolor[3];
8124
			$hexb = $stringcolor[4].$stringcolor[5];
8125
			$r = hexdec($hexr);
8126
			$g = hexdec($hexg);
8127
			$b = hexdec($hexb);
8128
		}
8129
		$bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
8130
		if ($bright > 0.6) $res = 1;
8131
	}
8132
	return $res;
8133
}
8134
8135
/**
8136
 * Function to test if an entry is enabled or not
8137
 *
8138
 * @param	string		$type_user					0=We test for internal user, 1=We test for external user
8139
 * @param	array		$menuentry					Array for feature entry to test
8140
 * @param	array		$listofmodulesforexternal	Array with list of modules allowed to external users
8141
 * @return	int										0=Hide, 1=Show, 2=Show gray
8142
 */
8143
function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
8144
{
8145
	global $conf;
8146
8147
	//print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
8148
	//print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
8149
	if (empty($menuentry['enabled'])) return 0; // Entry disabled by condition
8150
	if ($type_user && $menuentry['module'])
8151
	{
8152
		$tmploops = explode('|', $menuentry['module']);
8153
		$found = 0;
8154
		foreach ($tmploops as $tmploop)
8155
		{
8156
			if (in_array($tmploop, $listofmodulesforexternal)) {
8157
				$found++; break;
8158
			}
8159
		}
8160
		if (!$found) return 0; // Entry is for menus all excluded to external users
8161
	}
8162
	if (!$menuentry['perms'] && $type_user) return 0; // No permissions and user is external
8163
	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
8164
	if (!$menuentry['perms']) return 2; // No permissions and user is external
8165
	return 1;
8166
}
8167
8168
/**
8169
 * Round to next multiple.
8170
 *
8171
 * @param 	double		$n		Number to round up
8172
 * @param 	integer		$x		Multiple. For example 60 to round up to nearest exact minute for a date with seconds.
8173
 * @return 	integer				Value rounded.
8174
 */
8175
function roundUpToNextMultiple($n, $x = 5)
8176
{
8177
	return (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x;
8178
}
8179
8180
/**
8181
 * Function dolGetBadge
8182
 *
8183
 * @param   string  $label      label of badge no html : use in alt attribute for accessibility
8184
 * @param   string  $html       optional : label of badge with html
8185
 * @param   string  $type       type of badge : Primary Secondary Success Danger Warning Info Light Dark status0 status1 status2 status3 status4 status5 status6 status7 status8 status9
8186
 * @param   string  $mode       default '' , 'pill', 'dot'
8187
 * @param   string  $url        the url for link
8188
 * @param   array   $params     various params for future : recommended rather than adding more fuction arguments. array('attr'=>array('title'=>'abc'))
8189
 * @return  string              Html badge
8190
 */
8191
function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
8192
{
8193
	$attr = array(
8194
		'class'=>'badge '.(!empty($mode) ? ' badge-'.$mode : '').(!empty($type) ? ' badge-'.$type : '').(empty($params['css']) ? '' : ' '.$params['css'])
8195
	);
8196
8197
	if (empty($html)) {
8198
		$html = $label;
8199
	}
8200
8201
	if (!empty($url)) {
8202
		$attr['href'] = $url;
8203
	}
8204
8205
	if ($mode === 'dot') {
8206
		$attr['class'] .= ' classfortooltip';
8207
		$attr['title'] = $html;
8208
		$attr['aria-label'] = $label;
8209
		$html = '';
8210
	}
8211
8212
	// Override attr
8213
	if (!empty($params['attr']) && is_array($params['attr'])) {
8214
		foreach ($params['attr']as $key => $value) {
8215
			if ($key == 'class') {
8216
				$attr['class'] .= ' '.$value;
8217
			} elseif ($key == 'classOverride') {
8218
				$attr['class'] = $value;
8219
			} else {
8220
				$attr[$key] = $value;
8221
			}
8222
		}
8223
	}
8224
8225
	// TODO: add hook
8226
8227
	// escape all attribute
8228
	$attr = array_map('dol_escape_htmltag', $attr);
8229
8230
	$TCompiledAttr = array();
8231
	foreach ($attr as $key => $value) {
8232
		$TCompiledAttr[] = $key.'="'.$value.'"';
8233
	}
8234
8235
	$compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : '';
8236
8237
	$tag = !empty($url) ? 'a' : 'span';
8238
8239
	return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
8240
}
8241
8242
8243
/**
8244
 * Function dolGetStatus
8245
 *
8246
 * @param   string  $statusLabel       Label of badge no html : use in alt attribute for accessibility
8247
 * @param   string  $statusLabelShort  Short label of badge no html
8248
 * @param   string  $html              Optional : label of badge with html
8249
 * @param   string  $statusType        status0 status1 status2 status3 status4 status5 status6 status7 status8 status9 : image name or badge name
8250
 * @param   int	    $displayMode       0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto, 6=Long label + Picto
8251
 * @param   string  $url               The url for link
8252
 * @param   array   $params            Various params for future : recommended rather than adding more function arguments
8253
 * @return  string                     Html status string
8254
 */
8255
function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
8256
{
8257
	global $conf;
8258
8259
	$return = '';
8260
	$dolGetBadgeParams = array();
8261
8262
	if (!empty($params['badgeParams'])) {
8263
		$dolGetBadgeParams = $params['badgeParams'];
8264
	}
8265
8266
	// TODO : add a hook
8267
	if ($displayMode == 0) {
8268
		$return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
8269
	} elseif ($displayMode == 1) {
8270
		$return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
8271
	} // Use status with images (for backward compatibility)
8272
	elseif (!empty($conf->global->MAIN_STATUS_USES_IMAGES)) {
8273
		$return = '';
8274
		$htmlLabel      = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
8275
		$htmlLabelShort = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : (!empty($statusLabelShort) ? $statusLabelShort : $statusLabel)).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
8276
8277
		// For small screen, we always use the short label instead of long label.
8278
		if (!empty($conf->dol_optimize_smallscreen))
8279
		{
8280
			if ($displayMode == 0) $displayMode = 1;
8281
			elseif ($displayMode == 4) $displayMode = 2;
8282
			elseif ($displayMode == 6) $displayMode = 5;
8283
		}
8284
8285
		// For backward compatibility. Image's filename are still in French, so we use this array to convert
8286
		$statusImg = array(
8287
			'status0' => 'statut0',
8288
			'status1' => 'statut1',
8289
			'status2' => 'statut2',
8290
			'status3' => 'statut3',
8291
			'status4' => 'statut4',
8292
			'status5' => 'statut5',
8293
			'status6' => 'statut6',
8294
			'status7' => 'statut7',
8295
			'status8' => 'statut8',
8296
			'status9' => 'statut9'
8297
		);
8298
8299
		if (!empty($statusImg[$statusType])) {
8300
			$htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
8301
		} else {
8302
			$htmlImg = img_picto($statusLabel, $statusType);
8303
		}
8304
8305
		if ($displayMode === 2) {
8306
			$return = $htmlImg.' '.$htmlLabelShort;
8307
		} elseif ($displayMode === 3) {
8308
			$return = $htmlImg;
8309
		} elseif ($displayMode === 4) {
8310
			$return = $htmlImg.' '.$htmlLabel;
8311
		} elseif ($displayMode === 5) {
8312
			$return = $htmlLabelShort.' '.$htmlImg;
8313
		} else { // $displayMode >= 6
8314
			$return = $htmlLabel.' '.$htmlImg;
8315
		}
8316
	} // Use new badge
8317
	elseif (empty($conf->global->MAIN_STATUS_USES_IMAGES) && !empty($displayMode)) {
8318
		$statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
8319
8320
		$dolGetBadgeParams['attr']['class'] = 'badge-status';
8321
		$dolGetBadgeParams['attr']['title'] = $statusLabel;
8322
8323
		if ($displayMode == 3) {
8324
			$return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
8325
		} elseif ($displayMode === 5) {
8326
			$return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
8327
		} else {
8328
			$return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
8329
		}
8330
	}
8331
8332
	return $return;
8333
}
8334
8335
8336
/**
8337
 * Function dolGetButtonAction
8338
 *
8339
 * @param string    $label      label of button no html : use in alt attribute for accessibility $html is not empty
8340
 * @param string    $html       optional : content with html
8341
 * @param string    $actionType default, delete, danger
8342
 * @param string    $url        the url for link
8343
 * @param string    $id         attribute id of button
8344
 * @param int       $userRight  user action right
8345
 * @param array     $params     various params for future : recommended rather than adding more function arguments
8346
 * @return string               html button
8347
 */
8348
function dolGetButtonAction($label, $html = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
8349
{
8350
	$class = 'butAction';
8351
	if ($actionType == 'danger' || $actionType == 'delete') {
8352
		$class = 'butActionDelete';
8353
	}
8354
8355
	$attr = array(
8356
		'class' => $class
8357
		,'href' => empty($url) ? '' : $url
8358
	);
8359
8360
	if (empty($html)) {
8361
		$html = $label;
8362
	} else {
8363
		$attr['aria-label'] = $label;
8364
	}
8365
8366
	if (empty($userRight)) {
8367
		$attr['class'] = 'butActionRefused';
8368
		$attr['href'] = '';
8369
	}
8370
8371
	if (!empty($id)) {
8372
		$attr['id'] = $id;
8373
	}
8374
8375
	// Override attr
8376
	if (!empty($params['attr']) && is_array($params['attr'])) {
8377
		foreach ($params['attr'] as $key => $value) {
8378
			if ($key == 'class') {
8379
				$attr['class'] .= ' '.$value;
8380
			} elseif ($key == 'classOverride') {
8381
				$attr['class'] = $value;
8382
			} else {
8383
				$attr[$key] = $value;
8384
			}
8385
		}
8386
	}
8387
8388
	if (isset($attr['href']) && empty($attr['href'])) {
8389
		unset($attr['href']);
8390
	}
8391
8392
	// TODO : add a hook
8393
8394
	// escape all attribute
8395
	$attr = array_map('dol_escape_htmltag', $attr);
8396
8397
	$TCompiledAttr = array();
8398
	foreach ($attr as $key => $value) {
8399
		$TCompiledAttr[] = $key.'="'.$value.'"';
8400
	}
8401
8402
	$compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : '';
8403
8404
	$tag = !empty($attr['href']) ? 'a' : 'span';
8405
8406
	return '<div class="inline-block divButAction"><'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'></div>';
8407
}
8408
8409
/**
8410
 * Function dolGetButtonTitle : this kind of buttons are used in title in list
8411
 *
8412
 * @param string    $label      label of button
8413
 * @param string    $helpText   optional : content for help tooltip
8414
 * @param string    $iconClass  class for icon element (Example: 'fa fa-file')
8415
 * @param string    $url        the url for link
8416
 * @param string    $id         attribute id of button
8417
 * @param int       $status     0 no user rights, 1 active, -1 Feature Disabled, -2 disable Other reason use helpText as tooltip
8418
 * @param array     $params     various params for future : recommended rather than adding more function arguments
8419
 * @return string               html button
8420
 */
8421
function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
8422
{
8423
	global $langs, $conf, $user;
8424
8425
	// Actually this conf is used in css too for external module compatibility and smooth transition to this function
8426
	if (!empty($conf->global->MAIN_BUTTON_HIDE_UNAUTHORIZED) && (!$user->admin) && $status <= 0) {
8427
		return '';
8428
	}
8429
8430
	$class = 'btnTitle';
8431
	if ($iconClass == 'fa fa-plus-circle') $class .= ' btnTitlePlus';
8432
	$useclassfortooltip = 1;
8433
8434
	if (!empty($params['morecss'])) $class .= ' '.$params['morecss'];
8435
8436
	$attr = array(
8437
		'class' => $class,
8438
		'href' => empty($url) ? '' : $url
8439
	);
8440
8441
	if (!empty($helpText)) {
8442
		$attr['title'] = dol_escape_htmltag($helpText);
8443
	} elseif (empty($attr['title']) && $label) {
8444
		$attr['title'] = $label;
8445
		$useclassfortooltip = 0;
8446
	}
8447
8448
	if ($status <= 0) {
8449
		$attr['class'] .= ' refused';
8450
8451
		$attr['href'] = '';
8452
8453
		if ($status == -1) { // disable
8454
			$attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
8455
		} elseif ($status == 0) { // Not enough permissions
8456
			$attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
8457
		}
8458
	}
8459
8460
	if (!empty($attr['title']) && $useclassfortooltip) {
8461
		$attr['class'] .= ' classfortooltip';
8462
	}
8463
8464
	if (!empty($id)) {
8465
		$attr['id'] = $id;
8466
	}
8467
8468
	// Override attr
8469
	if (!empty($params['attr']) && is_array($params['attr'])) {
8470
		foreach ($params['attr'] as $key => $value) {
8471
			if ($key == 'class') {
8472
				$attr['class'] .= ' '.$value;
8473
			} elseif ($key == 'classOverride') {
8474
				$attr['class'] = $value;
8475
			} else {
8476
				$attr[$key] = $value;
8477
			}
8478
		}
8479
	}
8480
8481
	if (isset($attr['href']) && empty($attr['href'])) {
8482
		unset($attr['href']);
8483
	}
8484
8485
	// TODO : add a hook
8486
8487
	// escape all attribute
8488
	$attr = array_map('dol_escape_htmltag', $attr);
8489
8490
	$TCompiledAttr = array();
8491
	foreach ($attr as $key => $value) {
8492
		$TCompiledAttr[] = $key.'="'.$value.'"';
8493
	}
8494
8495
	$compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
8496
8497
	$tag = (empty($attr['href']) ? 'span' : 'a');
8498
8499
	$button = '<'.$tag.' '.$compiledAttributes.'>';
8500
	$button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
8501
	if (!empty($params['forcenohideoftext'])) {
8502
		$button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
8503
	}
8504
	$button .= '</'.$tag.'>';
8505
8506
	return $button;
8507
}
8508
8509
/**
8510
 * Get an array with properties of an element.
8511
 * Called by fetchObjectByElement.
8512
 *
8513
 * @param   string 	$element_type 	Element type (Value of $object->element). Example: 'action', 'facture', 'project_task' or 'object@mymodule'...
8514
 * @return  array					(module, classpath, element, subelement, classfile, classname)
8515
 */
8516
function getElementProperties($element_type)
8517
{
8518
	$regs = array();
8519
8520
	$classfile = $classname = $classpath = '';
8521
8522
	// Parse element/subelement (ex: project_task)
8523
	$module = $element_type;
8524
	$element = $element_type;
8525
	$subelement = $element_type;
8526
8527
	// If we ask an resource form external module (instead of default path)
8528
	if (preg_match('/^([^@]+)@([^@]+)$/i', $element_type, $regs)) {
8529
		$element = $subelement = $regs[1];
8530
		$module = $regs[2];
8531
	}
8532
8533
	//print '<br>1. element : '.$element.' - module : '.$module .'<br>';
8534
	if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) {
8535
		$module = $element = $regs[1];
8536
		$subelement = $regs[2];
8537
	}
8538
8539
	// For compat
8540
	if ($element_type == "action") {
8541
		$classpath = 'comm/action/class';
8542
		$subelement = 'Actioncomm';
8543
		$module = 'agenda';
8544
	}
8545
8546
	// To work with non standard path
8547
	if ($element_type == 'facture' || $element_type == 'invoice') {
8548
		$classpath = 'compta/facture/class';
8549
		$module = 'facture';
8550
		$subelement = 'facture';
8551
	}
8552
	if ($element_type == 'commande' || $element_type == 'order') {
8553
		$classpath = 'commande/class';
8554
		$module = 'commande';
8555
		$subelement = 'commande';
8556
	}
8557
	if ($element_type == 'propal') {
8558
		$classpath = 'comm/propal/class';
8559
	}
8560
	if ($element_type == 'supplier_proposal') {
8561
		$classpath = 'supplier_proposal/class';
8562
	}
8563
	if ($element_type == 'shipping') {
8564
		$classpath = 'expedition/class';
8565
		$subelement = 'expedition';
8566
		$module = 'expedition_bon';
8567
	}
8568
	if ($element_type == 'delivery') {
8569
		$classpath = 'livraison/class';
8570
		$subelement = 'livraison';
8571
		$module = 'livraison_bon';
8572
	}
8573
	if ($element_type == 'contract') {
8574
		$classpath = 'contrat/class';
8575
		$module = 'contrat';
8576
		$subelement = 'contrat';
8577
	}
8578
	if ($element_type == 'member') {
8579
		$classpath = 'adherents/class';
8580
		$module = 'adherent';
8581
		$subelement = 'adherent';
8582
	}
8583
	if ($element_type == 'cabinetmed_cons') {
8584
		$classpath = 'cabinetmed/class';
8585
		$module = 'cabinetmed';
8586
		$subelement = 'cabinetmedcons';
8587
	}
8588
	if ($element_type == 'fichinter') {
8589
		$classpath = 'fichinter/class';
8590
		$module = 'ficheinter';
8591
		$subelement = 'fichinter';
8592
	}
8593
	if ($element_type == 'dolresource' || $element_type == 'resource') {
8594
		$classpath = 'resource/class';
8595
		$module = 'resource';
8596
		$subelement = 'dolresource';
8597
	}
8598
	if ($element_type == 'propaldet') {
8599
		$classpath = 'comm/propal/class';
8600
		$module = 'propal';
8601
		$subelement = 'propaleligne';
8602
	}
8603
	if ($element_type == 'order_supplier') {
8604
		$classpath = 'fourn/class';
8605
		$module = 'fournisseur';
8606
		$subelement = 'commandefournisseur';
8607
		$classfile = 'fournisseur.commande';
8608
	}
8609
	if ($element_type == 'invoice_supplier') {
8610
		$classpath = 'fourn/class';
8611
		$module = 'fournisseur';
8612
		$subelement = 'facturefournisseur';
8613
		$classfile = 'fournisseur.facture';
8614
	}
8615
	if ($element_type == "service") {
8616
		$classpath = 'product/class';
8617
		$subelement = 'product';
8618
	}
8619
8620
	if (empty($classfile)) $classfile = strtolower($subelement);
8621
	if (empty($classname)) $classname = ucfirst($subelement);
8622
	if (empty($classpath)) $classpath = $module.'/class';
8623
8624
	$element_properties = array(
8625
		'module' => $module,
8626
		'classpath' => $classpath,
8627
		'element' => $element,
8628
		'subelement' => $subelement,
8629
		'classfile' => $classfile,
8630
		'classname' => $classname
8631
	);
8632
	return $element_properties;
8633
}
8634
8635
/**
8636
 * Fetch an object from its id and element_type
8637
 * Inclusion of classes is automatic
8638
 *
8639
 * @param	int     	$element_id 	Element id
8640
 * @param	string  	$element_type 	Element type
8641
 * @param	string     	$element_ref 	Element ref (Use this or element_id but not both)
8642
 * @return 	int|object 					object || 0 || -1 if error
8643
 */
8644
function fetchObjectByElement($element_id, $element_type, $element_ref = '')
8645
{
8646
	global $conf, $db;
8647
8648
	$element_prop = getElementProperties($element_type);
8649
	if (is_array($element_prop) && $conf->{$element_prop['module']}->enabled)
8650
	{
8651
		dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
8652
8653
		$objecttmp = new $element_prop['classname']($db);
8654
		$ret = $objecttmp->fetch($element_id, $element_ref);
8655
		if ($ret >= 0)
8656
		{
8657
			return $objecttmp;
8658
		}
8659
	}
8660
	return 0;
8661
}
8662
8663
/**
8664
 * Return if a file can contains executable content
8665
 *
8666
 * @param   string  $filename       File name to test
8667
 * @return  boolean                 True if yes, False if no
8668
 */
8669
function isAFileWithExecutableContent($filename)
8670
{
8671
	if (preg_match('/\.(htm|html|js|phar|php|php\d+|phtml|pht|pl|py|cgi|ksh|sh|shtml|bash|bat|cmd|wpk|exe|dmg)$/i', $filename))
8672
	{
8673
		return true;
8674
	}
8675
8676
	return false;
8677
}
8678
8679
/**
8680
 * Return the value of token currently saved into session with name 'newtoken'.
8681
 * This token must be send by any POST as it will be used by next page for comparison with value in session.
8682
 *
8683
 * @return  string
8684
 */
8685
function newToken()
8686
{
8687
	return $_SESSION['newtoken'];
8688
}
8689
8690
/**
8691
 * Return the value of token currently saved into session with name 'token'.
8692
 *
8693
 * @return  string
8694
 */
8695
function currentToken()
8696
{
8697
	return $_SESSION['token'];
8698
}
8699
8700
/**
8701
 * Start a table with headers and a optinal clickable number
8702
 * (don't forget to use "finishSimpleTable()" after the last table row)
8703
 *
8704
 * @see finishSimpleTable
8705
 *
8706
 * @param string	$header		The first left header of the table (automatic translated)
8707
 * @param string	$link		(optional) The link to a internal dolibarr page, when click on the number (without the first "/")
8708
 * @param string	$arguments	(optional) Additional arguments for the link (e.g. "search_status=0")
8709
 * @param integer	$emptyRows	(optional) The count of empty rows after the first header
8710
 * @param integer	$number		(optional) The number that is shown right after the first header, when not set the link is shown on the right side of the header as "FullList"
8711
 * @return void
8712
 */
8713
function startSimpleTable($header, $link = "", $arguments = "", $emptyRows = 0, $number = -1)
8714
{
8715
	global $langs;
8716
8717
	print '<div class="div-table-responsive-no-min">';
8718
	print '<table class="noborder centpercent">';
8719
	print '<tr class="liste_titre">';
8720
8721
	print $emptyRows < 1 ? '<th>' : '<th colspan="'.($emptyRows + 1).'">';
8722
8723
	print $langs->trans($header);
8724
8725
	// extra space between the first header and the number
8726
	if ($number > -1) {
8727
		print ' ';
8728
	}
8729
8730
	if (!empty($link)) {
8731
		if (!empty($arguments)) {
8732
			print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
8733
		} else {
8734
			print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
8735
		}
8736
	}
8737
8738
	if ($number > -1) {
8739
		print '<span class="badge">'.$number.'</span>';
8740
	}
8741
8742
	if (!empty($link)) {
8743
		print '</a>';
8744
	}
8745
8746
	print '</th>';
8747
8748
	if ($number < 0 && !empty($link)) {
8749
		print '<th class="right">';
8750
8751
		if (!empty($arguments)) {
8752
			print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
8753
		} else {
8754
			print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'">';
8755
		}
8756
8757
		print $langs->trans("FullList");
8758
		print '</a>';
8759
		print '</th>';
8760
	}
8761
8762
	print '</tr>';
8763
}
8764
8765
/**
8766
 * Add the correct HTML close tags for "startSimpleTable(...)"
8767
 * (use after the last table line)
8768
 *
8769
 * @see startSimpleTable
8770
 *
8771
 * @param bool $addLineBreak	(optional) Add a extra line break after the complete table (\<br\>)
8772
 *
8773
 * @return void
8774
 */
8775
function finishSimpleTable($addLineBreak = false)
8776
{
8777
	print '</table>';
8778
	print '</div>';
8779
8780
	if ($addLineBreak) {
8781
		print '<br>';
8782
	}
8783
}
8784
8785
/**
8786
 * Add a summary line to the current open table ("None", "XMoreLines" or "Total xxx")
8787
 *
8788
 * @param integer	$tableColumnCount		The complete count columns of the table
8789
 * @param integer	$num					The count of the rows of the table, when it is zero (0) the "$noneWord" is shown instead
8790
 * @param integer	$nbofloop				(optional)	The maximum count of rows thaht the table show (when it is zero (0) no summary line will show, expect "$noneWord" when $num === 0)
8791
 * @param integer	$total					(optional)	The total value thaht is shown after when the table has minimum of one entire
8792
 * @param string	$noneWord				(optional)	The word that is shown when the table has no entires ($num === 0)
8793
 * @param boolean	$extraRightColumn		(optional)	Add a addtional column after the summary word and total number
8794
 * @return void
8795
 */
8796
function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
8797
{
8798
	global $langs;
8799
8800
	if ($num === 0) {
8801
		print '<tr class="oddeven">';
8802
		print '<td colspan="'.$tableColumnCount.'" class="opacitymedium">'.$langs->trans($noneWord).'</td>';
8803
		print '</tr>';
8804
		return;
8805
	}
8806
8807
	if ($nbofloop === 0)
8808
	{
8809
		// don't show a summary line
8810
		return;
8811
	}
8812
8813
	if ($num === 0) {
8814
		$colspan = $tableColumnCount;
8815
	}
8816
	elseif ($num > $nbofloop) {
8817
		$colspan = $tableColumnCount;
8818
	} else {
8819
		$colspan = $tableColumnCount - 1;
8820
	}
8821
8822
	if ($extraRightColumn) {
8823
		$colspan--;
8824
	}
8825
8826
	print '<tr class="liste_total">';
8827
8828
	if ($nbofloop > 0 && $num > $nbofloop) {
8829
		print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
8830
	} else {
8831
		print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
8832
		print '<td class="right" width="100">'.price($total).'</td>';
8833
	}
8834
8835
	if ($extraRightColumn) {
8836
		print '<td></td>';
8837
	}
8838
8839
	print '</tr>';
8840
}
8841