Completed
Branch develop (30eefb)
by
unknown
26:15
created

functions.lib.php ➔ vatrate()   C

Complexity

Conditions 8
Paths 64

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 17
nc 64
nop 4
dl 0
loc 31
rs 5.3846
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2000-2007	Rodolphe Quiedeville			<[email protected]>
3
 * Copyright (C) 2003		Jean-Louis Bergamo			<[email protected]>
4
 * Copyright (C) 2004-2013	Laurent Destailleur			<[email protected]>
5
 * Copyright (C) 2004		Sebastien Di Cintio			<[email protected]>
6
 * Copyright (C) 2004		Benoit Mortier				<[email protected]>
7
 * Copyright (C) 2004		Christophe Combelles			<[email protected]>
8
 * Copyright (C) 2005-2017	Regis Houssin				<[email protected]>
9
 * Copyright (C) 2008		Raphael Bertrand (Resultic)	<[email protected]>
10
 * Copyright (C) 2010-2016	Juanjo Menent				<[email protected]>
11
 * Copyright (C) 2013		Cédric Salvador				<[email protected]>
12
 * Copyright (C) 2013-2017	Alexandre Spangaro			<[email protected]>
13
 * Copyright (C) 2014		Cédric GROSS					<[email protected]>
14
 * Copyright (C) 2014-2015	Marcos García				<[email protected]>
15
 * Copyright (C) 2015		Jean-François Ferry			<[email protected]>
16
 *
17
 * This program is free software; you can redistribute it and/or modify
18
 * it under the terms of the GNU General Public License as published by
19
 * the Free Software Foundation; either version 3 of the License, or
20
 * (at your option) any later version.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
 * GNU General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU General Public License
28
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29
 * or see http://www.gnu.org/
30
 */
31
32
/**
33
 *	\file			htdocs/core/lib/functions.lib.php
34
 *	\brief			A set of functions for Dolibarr
35
 *					This file contains all frequently used functions.
36
 */
37
38
include_once DOL_DOCUMENT_ROOT .'/core/lib/json.lib.php';
39
40
/**
41
 * Function to return value of a static property when class
42
 * name is dynamically defined (not hard coded).
43
 * This is because $myclass::$myvar works from PHP 5.3.0+ only
44
 *
45
 * @param	string 	$class		Class name
46
 * @param 	string 	$member		Name of property
47
 * @return 	mixed				Return value of static property
48
 * @deprecated Dolibarr now requires 5.3.0+, use $class::$property syntax
49
 * @see https://php.net/manual/language.oop5.static.php
50
 */
51
function getStaticMember($class, $member)
52
{
53
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
54
55
	// This part is deprecated. Uncomment if for php 5.2.*, and comment next isset class::member
56
	/*if (version_compare(phpversion(), '5.3.0', '<'))
57
	{
58
		if (is_object($class)) $class = get_class($class);
59
		$classObj = new ReflectionClass($class);
60
		$result = null;
61
62
		$found=0;
63
		foreach($classObj->getStaticProperties() as $prop => $value)
64
		{
65
			if ($prop == $member)
66
			{
67
				$result = $value;
68
				$found++;
69
				break;
70
			}
71
		}
72
73
		if ($found) return $result;
74
	}*/
75
76
	if (isset($class::$member)) return $class::$member;
77
	dol_print_error('','Try to get a static member "'.$member.'" in class "'.$class.'" that does not exists or is not static.');
78
	return null;
79
}
80
81
82
/**
83
 * Return a DoliDB instance (database handler).
84
 *
85
 * @param   string	$type		Type of database (mysql, pgsql...)
86
 * @param	string	$host		Address of database server
87
 * @param	string	$user		Nom de l'utilisateur autorise
88
 * @param	string	$pass		Mot de passe
89
 * @param	string	$name		Nom de la database
90
 * @param	int		$port		Port of database server
91
 * @return	DoliDB				A DoliDB instance
92
 */
93
function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
94
{
95
	require_once DOL_DOCUMENT_ROOT ."/core/db/".$type.'.class.php';
96
97
	$class='DoliDB'.ucfirst($type);
98
	$dolidb=new $class($type, $host, $user, $pass, $name, $port);
99
	return $dolidb;
100
}
101
102
/**
103
 * 	Get list of entity id to use.
104
 *
105
 * 	@param	string	$element	Current element
106
 *                              'societe', 'socpeople', 'actioncomm', 'agenda', 'resource',
107
 *                              'product', 'productprice', 'stock',
108
 *                              'propal', 'supplier_proposal', 'facture', 'facture_fourn',
109
 *                              'categorie', 'bank_account', 'bank_account', 'adherent', 'user',
110
 *                              'commande', 'commande_fournisseur', 'expedition', 'intervention', 'survey',
111
 *                              'contract', 'tax', 'expensereport', 'holiday', 'multicurrency', 'project',
112
 *                              'email_template', 'event',
113
 * 	@param	int	     $shared	1=Return id of current entity + shared entities (default), 0=Return id of current entity only
114
 * 	@return	mixed				Entity id(s) to use
115
 */
116
function getEntity($element, $shared=1)
117
{
118
	global $conf, $mc;
119
120
	// For backward compatibilty
121
	if ($element == 'actioncomm') $element='agenda';
122
	if ($element == 'fichinter')  $element='intervention';
123
	if ($element == 'categorie')  $element='category';
124
125
	if (is_object($mc))
126
	{
127
		return $mc->getEntity($element, $shared);
128
	}
129
	else
130
	{
131
		$out='';
132
		$addzero = array('user', 'usergroup', 'email_template', 'default_values');
133
		if (in_array($element, $addzero)) $out.= '0,';
134
		$out.= $conf->entity;
135
		return $out;
136
	}
137
}
138
139
/**
140
 * Return information about user browser
141
 *
142
 * Returns array with the following format:
143
 * array(
144
 *  'browsername' => Browser name (firefox|chrome|iceweasel|epiphany|safari|opera|ie|unknown)
145
 *  'browserversion' => Browser version. Empty if unknown
146
 *  'browseros' => Set with mobile OS (android|blackberry|ios|palm|symbian|webos|maemo|windows|unknown)
147
 *  'layout' => (tablet|phone|classic)
148
 *  'phone' => empty if not mobile, (android|blackberry|ios|palm|unknown) if mobile
149
 *  'tablet' => true/false
150
 * )
151
 *
152
 * @param string $user_agent Content of $_SERVER["HTTP_USER_AGENT"] variable
153
 * @return	array Check function documentation
154
 */
155
function getBrowserInfo($user_agent)
156
{
157
	include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
158
159
	$name='unknown';
160
	$version='';
161
	$os='unknown';
162
	$phone = '';
163
164
	$detectmobile = new Mobile_Detect(null, $user_agent);
165
	$tablet = $detectmobile->isTablet();
166
167
	if ($detectmobile->isMobile()) {
168
169
		$phone = 'unknown';
170
171
		// If phone/smartphone, we set phone os name.
172
		if ($detectmobile->is('AndroidOS')) {
173
			$os = $phone = 'android';
174
		} elseif ($detectmobile->is('BlackBerryOS')) {
175
			$os = $phone = 'blackberry';
176
		} elseif ($detectmobile->is('iOS')) {
177
			$os = 'ios';
178
			$phone = 'iphone';
179
		} elseif ($detectmobile->is('PalmOS')) {
180
			$os = $phone = 'palm';
181
		} elseif ($detectmobile->is('SymbianOS')) {
182
			$os = 'symbian';
183
		} elseif ($detectmobile->is('webOS')) {
184
			$os = 'webos';
185
		} elseif ($detectmobile->is('MaemoOS')) {
186
			$os = 'maemo';
187
		} elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
188
			$os = 'windows';
189
		}
190
	}
191
192
	// OS
193
	if (preg_match('/linux/i', $user_agent))			{ $os='linux'; }
194
	elseif (preg_match('/macintosh/i', $user_agent))	{ $os='macintosh'; }
195
	elseif (preg_match('/windows/i', $user_agent))		{ $os='windows'; }
196
197
	// Name
198
	if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg))      { $name='firefox';   $version=$reg[2]; }
199
	elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg))   { $name='chrome';    $version=$reg[2]; }    // we can have 'chrome (Mozilla...) chrome x.y' in one string
200
	elseif (preg_match('/chrome/i', $user_agent, $reg))                   { $name='chrome'; }
201
	elseif (preg_match('/iceweasel/i', $user_agent))                      { $name='iceweasel'; }
202
	elseif (preg_match('/epiphany/i', $user_agent))                       { $name='epiphany';  }
203
	elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg))   { $name='safari';    $version=$reg[2]; }	// Safari is often present in string for mobile but its not.
204
	elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg))    { $name='opera';     $version=$reg[2]; }
205
	elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg))  { $name='ie'; $version=end($reg); }    // MS products at end
206
	elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg))  { $name='ie'; $version=end($reg); }    // MS products at end
207
	elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) { $name='lynxlinks'; $version=$reg[4]; }
208
209
	if ($tablet) {
210
		$layout = 'tablet';
211
	} elseif ($phone) {
212
		$layout = 'phone';
213
	} else {
214
		$layout = 'classic';
215
	}
216
217
	return array(
218
		'browsername' => $name,
219
		'browserversion' => $version,
220
		'browseros' => $os,
221
		'layout' => $layout,
222
		'phone' => $phone,
223
		'tablet' => $tablet
224
	);
225
}
226
227
/**
228
 *  Function called at end of web php process
229
 *
230
 *  @return	void
231
 */
232
function dol_shutdown()
233
{
234
	global $conf,$user,$langs,$db;
235
	$disconnectdone=false; $depth=0;
236
	if (is_object($db) && ! empty($db->connected)) { $depth=$db->transaction_opened; $disconnectdone=$db->close(); }
237
	dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth)?' (Warn: db disconnection forced, transaction depth was '.$depth.')':''), (($disconnectdone && $depth)?LOG_WARNING:LOG_INFO));
238
}
239
240
241
/**
242
 *  Return value of a param into GET or POST supervariable.
243
 *  Use the property $user->default_values[path]['creatform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder']
244
 *  Note: The property $user->default_values is loaded by main.php when loading the user.
245
 *
246
 *  @param	string	$paramname   Name of parameter to found
247
 *  @param	string	$check	     Type of check
248
 *                                  ''=no check (deprecated)
249
 *                                  'none'=no check (only for param that should have very rich content)
250
 *                                  'int'=check it's numeric (integer or float)
251
 *                                  'alpha'=check it's text and sign
252
 *                                  'aZ'=check it's a-z only
253
 *                                  'aZ09'=check it's simple alpha string (recommended for keys)
254
 *                                  'array'=check it's array
255
 *                                  'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string)
256
 *                                  'nohtml', 'alphanohtml'=check there is no html content
257
 *                                  'custom'= custom filter specify $filter and $options)
258
 *  @param	int		$method	     Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get, 4 = post then get then cookie)
259
 *  @param  int     $filter      Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails)
260
 *  @param  mixed   $options     Options to pass to filter_var when $check is set to 'custom'.
261
 *  @return string|string[]      Value found (string or array), or '' if check fails
262
 *
263
 *  @TODO Set default value for check to alpha. Check all WYSIWYG edition (email and description...) is still ok with rich text.
264
 */
265
function GETPOST($paramname, $check='', $method=0, $filter=NULL, $options=NULL)
3 ignored issues
show
Coding Style introduced by
GETPOST uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
GETPOST uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
GETPOST uses the super-global variable $_COOKIE which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
266
{
267
    global $mysoc,$user,$conf;
268
269
    if (empty($paramname)) return 'BadFirstParameterForGETPOST';
270
271
    if (empty($method)) $out = isset($_GET[$paramname])?$_GET[$paramname]:(isset($_POST[$paramname])?$_POST[$paramname]:'');
272
	elseif ($method==1) $out = isset($_GET[$paramname])?$_GET[$paramname]:'';
273
	elseif ($method==2) $out = isset($_POST[$paramname])?$_POST[$paramname]:'';
274
	elseif ($method==3) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:'');
275
	elseif ($method==4) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:(isset($_COOKIE[$paramname])?$_COOKIE[$paramname]:''));
276
	else return 'BadThirdParameterForGETPOST';
277
278
	if (empty($method) || $method == 3 || $method == 4)
279
	{
280
    	$relativepathstring = $_SERVER["PHP_SELF"];
281
    	// Clean $relativepathstring
282
    	if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'),'/').'/', '', $relativepathstring);
283
    	$relativepathstring = preg_replace('/^\//', '', $relativepathstring);
284
    	$relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
285
    	//var_dump($relativepathstring);
286
287
        // Code for search criteria persistence.
288
    	// Retrieve values if restore_lastsearch_values is set and there is saved values
289
    	if (! empty($_GET['restore_lastsearch_values']) && ! empty($_SESSION['lastsearch_values_'.$relativepathstring]))        // Keep $_GET here
290
    	{
291
	        $tmp=json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
292
	        if (is_array($tmp))
293
	        {
294
	            foreach($tmp as $key => $val)
295
	            {
296
	                if ($key == $paramname)
297
	                {
298
	                    $out=$val;
299
	                    break;
300
	                }
301
	            }
302
	        }
303
    	}
304
	    // Else, retreive default values if we are not doing a sort
305
	    elseif (! isset($_GET['sortfield']) && ! empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES))	// 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
306
	    {
307
	        if (! empty($_GET['action']) && $_GET['action'] == 'create' && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
308
	        {
309
	            if (! empty($user->default_values))		// $user->default_values defined from menu default values
310
	            {
311
	                //var_dump($user->default_values[$relativepathstring]['createform']);
312
	                if (isset($user->default_values[$relativepathstring]['createform'][$paramname])) $out = $user->default_values[$relativepathstring]['createform'][$paramname];
313
	            }
314
	        }
315
	        // Management of default search_filters and sort order
316
	        //elseif (preg_match('/list.php$/', $_SERVER["PHP_SELF"]) && ! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
317
	        elseif (! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
318
	        {
319
	            if (! empty($user->default_values))		// $user->default_values defined from menu default values
320
	            {
321
	                //var_dump($user->default_values[$relativepathstring]);
322
	                if ($paramname == 'sortfield')
323
	                {
324
	                    if (isset($user->default_values[$relativepathstring]['sortorder']))    // We will use the key of $user->default_values[path][sortorder]
325
	                    {
326
	                        $forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
327
	                        foreach($user->default_values[$relativepathstring]['sortorder'] as $key => $val)
328
	                        {
329
	                            if ($out) $out.=', ';
330
	                            $out.=dol_string_nospecial($key, '', $forbidden_chars_to_replace);
331
	                        }
332
	                    }
333
	                }
334
	                elseif ($paramname == 'sortorder')
335
	                {
336
	                    if (isset($user->default_values[$relativepathstring]['sortorder']))    // We will use the val of $user->default_values[path][sortorder]
337
	                    {
338
	                        $forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
339
	                        foreach($user->default_values[$relativepathstring]['sortorder'] as $key => $val)
340
	                        {
341
	                            if ($out) $out.=', ';
342
	                            $out.=dol_string_nospecial($val, '', $forbidden_chars_to_replace);
343
	                        }
344
	                    }
345
	                }
346
	                elseif (isset($user->default_values[$relativepathstring]['filters'][$paramname]))
347
	                {
348
	                    $forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
349
	                    $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$paramname], '', $forbidden_chars_to_replace);
350
	                }
351
	            }
352
	        }
353
	    }
354
355
	}
356
357
	if (empty($check) && ! empty($conf->global->MAIN_FEATURES_LEVEL) && $conf->global->MAIN_FEATURES_LEVEL >= 2)
358
	{
359
	   dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param not defined, when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
360
	   // Enable this line to know who call the GETPOST with empty $check parameter.
361
	   //var_dump(debug_backtrace()[0]);
362
	}
363
364
	if (! empty($check))
365
	{
366
	    // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable paramaters)
367
	    // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOUNTRYID__, __USERID__, __ENTITYID__, ...
368
	    // 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.
369
	    if (! is_array($out) && empty($_POST[$paramname]))
370
	    {
371
	        $maxloop=20; $loopnb=0;    // Protection against infinite loop
372
	        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.
373
	        {
374
	            $loopnb++; $newout = '';
375
376
    	        if ($reg[1] == 'DAY')                { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['mday']; }
377
    	        elseif ($reg[1] == 'MONTH')          { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['mon'];  }
378
    	        elseif ($reg[1] == 'YEAR')           { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['year']; }
379
    	    	elseif ($reg[1] == 'PREVIOUS_DAY')   { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; }
380
    	        elseif ($reg[1] == 'PREVIOUS_MONTH') { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_prev_month($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['month']; }
381
    	        elseif ($reg[1] == 'PREVIOUS_YEAR')  { $tmp=dol_getdate(dol_now(), true); $newout = ($tmp['year'] - 1); }
382
    	    	elseif ($reg[1] == 'NEXT_DAY')       { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; }
383
    	        elseif ($reg[1] == 'NEXT_MONTH')     { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_next_month($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['month']; }
384
    	        elseif ($reg[1] == 'NEXT_YEAR')      { $tmp=dol_getdate(dol_now(), true); $newout = ($tmp['year'] + 1); }
385
    	        elseif ($reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID')
386
    	        {
387
    	            $newout = $mysoc->country_id;
388
    	        }
389
    	        elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID')
390
    	        {
391
    	            $newout = $user->id;
392
    	        }
393
    	    	elseif ($reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID')
394
    	        {
395
    	            $newout = $user->fk_user;
396
    	        }
397
    	        elseif ($reg[1] == 'ENTITYID')
398
    	        {
399
    	            $newout = $conf->entity;
400
    	        }
401
    	        else $newout = '';     // Key not found, we replace with empty string
402
    	        //var_dump('__'.$reg[1].'__ -> '.$newout);
403
    	        $out = preg_replace('/__'.preg_quote($reg[1],'/').'__/', $newout, $out);
404
	        }
405
	    }
406
407
	    // Check is done after replacement
408
	    switch ($check)
409
	    {
410
	        case 'none':
411
	            break;
412
	        case 'int':    // Check param is a numeric value (integer but also float or hexadecimal)
413
	            if (! is_numeric($out)) { $out=''; }
414
	            break;
415
	        case 'intcomma':
416
	            if (preg_match('/[^0-9,]+/i',$out)) $out='';
417
	            break;
418
	        case 'alpha':
419
	            $out=trim($out);
420
	            // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
421
	            // '../' is dangerous because it allows dir transversals
422
	            if (preg_match('/"/',$out)) $out='';
423
	            else if (preg_match('/\.\.\//',$out)) $out='';
424
	            break;
425
	        case 'san_alpha':
426
	            $out=filter_var($out,FILTER_SANITIZE_STRING);
427
	            break;
428
	        case 'aZ':
429
	            $out=trim($out);
430
	            if (preg_match('/[^a-z]+/i',$out)) $out='';
431
	            break;
432
	        case 'aZ09':
433
	            $out=trim($out);
434
	            if (preg_match('/[^a-z0-9_\-\.]+/i',$out)) $out='';
435
	            break;
436
	        case 'array':
437
	            if (! is_array($out) || empty($out)) $out=array();
438
	            break;
439
			case 'nohtml':
440
			    $out=dol_string_nohtmltag($out);
441
				break;
442
			case 'alphanohtml':	// Recommended for search params
443
	            $out=trim($out);
444
	            // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
445
	            // '../' is dangerous because it allows dir transversals
446
	            if (preg_match('/"/',$out)) $out='';
447
	            else if (preg_match('/\.\.\//',$out)) $out='';
448
			    $out=dol_string_nohtmltag($out);
449
				break;
450
			case 'custom':
451
	            if (empty($filter)) return 'BadFourthParameterForGETPOST';
452
	            $out=filter_var($out, $filter, $options);
453
	            break;
454
	    }
455
	}
456
457
    // Code for search criteria persistence.
458
	// Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
459
	if (empty($method) || $method == 3 || $method == 4)
460
	{
461
	    //if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield", 'smonth', 'syear', 'month', 'year')))
462
	    if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder','sortfield')))
463
	    {
464
	        //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
465
466
	        // We save search key only if:
467
	        // - not empty, or
468
	        // - if value is empty and a default value exists that is not empty (it means we did a filter to an empty value when default was not).
469
470
	        //if (! empty($out) || ! empty($user->default_values[$relativepathstring]['filters'][$paramname]))
471
	        if (! empty($out))
472
	        {
473
                $user->lastsearch_values_tmp[$relativepathstring][$paramname]=$out;
0 ignored issues
show
Bug introduced by
The variable $relativepathstring does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
4059
   	else $sql.= " AND t.recuperableonly ='".$vatnpr."'";
4060
   	dol_syslog("get_localtax", LOG_DEBUG);
4061
   	$resql=$db->query($sql);
4062
4063
   	if ($resql)
4064
   	{
4065
   		$obj = $db->fetch_object($resql);
4066
   		if ($local==1) return $obj->localtax1;
4067
   		elseif ($local==2) return $obj->localtax2;
4068
	}
4069
4070
	return 0;
4071
}
4072
4073
4074
/**
4075
 * Return true if LocalTax (1 or 2) is unique.
4076
 * Example: If localtax1 is 5 on line with highest common vat rate, return true
4077
 * Example: If localtax1 is 5:8:15 on line with highest common vat rate, return false
4078
 *
4079
 * @param   int 	$local	Local tax to test (1 or 2)
4080
 * @return  boolean 		True if LocalTax have multiple values, False if not
4081
 */
4082
function isOnlyOneLocalTax($local)
4083
{
4084
	$tax=get_localtax_by_third($local);
4085
4086
	$valors=explode(":", $tax);
4087
4088
	if (count($valors)>1)
4089
	{
4090
		return false;
4091
	}
4092
	else
4093
	{
4094
		return true;
4095
	}
4096
}
4097
4098
/**
4099
 * Get values of localtaxes (1 or 2) for company country for the common vat with the highest value
4100
 *
4101
 * @param	int		$local 	LocalTax to get
4102
 * @return	number			Values of localtax
4103
 */
4104
function get_localtax_by_third($local)
4105
{
4106
	global $db, $mysoc;
4107
	$sql ="SELECT t.localtax1, t.localtax2 ";
4108
	$sql.=" FROM ".MAIN_DB_PREFIX."c_tva as t inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=t.fk_pays";
4109
	$sql.=" WHERE c.code = '".$mysoc->country_code."' AND t.active = 1 AND t.taux=(";
4110
	$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";
4111
	$sql.="  WHERE c.code = '".$mysoc->country_code."' AND tt.active = 1";
4112
	$sql.="  )";
4113
4114
	$resql=$db->query($sql);
4115
	if ($resql)
4116
	{
4117
		$obj = $db->fetch_object($resql);
4118
		if ($local==1) return $obj->localtax1;
4119
		elseif ($local==2) return $obj->localtax2;
4120
	}
4121
4122
	return 0;
4123
4124
}
4125
4126
4127
/**
4128
 *  Get vat main information from Id.
4129
 *  You can call getLocalTaxesFromRate after to get other fields.
4130
 *
4131
 *  @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.
4132
 *  @param	Societe	    $buyer         		Company object
4133
 *  @param	Societe	    $seller        		Company object
4134
 *  @param  int         $firstparamisid     1 if first param is id into table (use this if you can)
4135
 *  @return	array       	  				array('rowid'=> , 'code'=> ...)
4136
 *  @see getLocalTaxesFromRate
4137
 */
4138
function getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
4139
{
4140
    global $db, $mysoc;
4141
4142
    dol_syslog("getTaxesFromId vatrowid=".$vatrate);
4143
4144
    // Search local taxes
4145
    $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy";
4146
    $sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t";
4147
    if ($firstparamisid) $sql.= " WHERE t.rowid = ".(int) $vatrate;
4148
    else
4149
    {
4150
        $vatratecleaned = $vatrate;
4151
        $vatratecode = '';
4152
        if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
4153
        {
4154
            $vatratecleaned = $reg[1];
4155
            $vatratecode = $reg[2];
4156
        }
4157
4158
        $sql.=", ".MAIN_DB_PREFIX."c_country as c";
4159
        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 ??
4160
        else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
4161
        $sql.= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4162
        if ($vatratecode) $sql.= " AND t.code = '".$vatratecode."'";
4163
    }
4164
4165
    $resql=$db->query($sql);
4166
    if ($resql)
4167
    {
4168
        $obj = $db->fetch_object($resql);
4169
        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);
4170
        else return array();
4171
    }
4172
    else dol_print_error($db);
4173
4174
    return array();
4175
}
4176
4177
/**
4178
 *  Get type and rate of localtaxes for a particular vat rate/country fo thirdparty
4179
 *  TODO
4180
 *  This function is ALSO called to retrieve type for building PDF. Such call of function must be removed.
4181
 *  Instead this function must be called when adding a line to get the array of localtax and type, and then
4182
 *  provide it to the function calcul_price_total.
4183
 *
4184
 *  @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.
4185
 *  @param	int		    $local              Number of localtax (1 or 2, or 0 to return 1 & 2)
4186
 *  @param	Societe	    $buyer         		Company object
4187
 *  @param	Societe	    $seller        		Company object
4188
 *  @param  int         $firstparamisid     1 if first param is id into table (use this if you can)
4189
 *  @return	array    	    				array(localtax_type1(1-6/0 if not found), rate localtax1, localtax_type2, rate localtax2, accountancycodecust, accountancycodesupp)
4190
 *  @see getTaxesFromId
4191
 */
4192
function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
4193
{
4194
	global $db, $mysoc;
4195
4196
	dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
4197
4198
	// Search local taxes
4199
	$sql  = "SELECT t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy";
4200
	$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
4201
	if ($firstparamisid) $sql.= " WHERE t.rowid = ".(int) $vatrate;
4202
	else
4203
	{
4204
	    $vatratecleaned = $vatrate;
4205
	    $vatratecode = '';
4206
	    if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
4207
	    {
4208
	        $vatratecleaned = $reg[1];
4209
	        $vatratecode = $reg[2];
4210
	    }
4211
4212
	    $sql.=", ".MAIN_DB_PREFIX."c_country as c";
4213
    	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 ??
4214
    	else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
4215
    	$sql.= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4216
    	if ($vatratecode) $sql.= " AND t.code = '".$vatratecode."'";
4217
	}
4218
4219
	$resql=$db->query($sql);
4220
	if ($resql)
4221
	{
4222
		$obj = $db->fetch_object($resql);
4223
		if ($local == 1)
4224
		{
4225
			if (! isOnlyOneLocalTax(1))
4226
			{
4227
				return array($obj->localtax1_type, get_localtax($vatrate, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4228
			}
4229
			else
4230
			{
4231
				return array($obj->localtax1_type, $obj->localtax1,$obj->accountancy_code_sell, $obj->accountancy_code_buy);
4232
			}
4233
		}
4234
		elseif ($local == 2)
4235
		{
4236
			if (! isOnlyOneLocalTax(2))
4237
			{
4238
				return array($obj->localtax2_type, get_localtax($vatrate, $local, $buyer, $seller),$obj->accountancy_code_sell, $obj->accountancy_code_buy);
4239
			}
4240
			else
4241
			{
4242
				return array($obj->localtax2_type, $obj->localtax2,$obj->accountancy_code_sell, $obj->accountancy_code_buy);
4243
			}
4244
		}
4245
		else
4246
		{
4247
			if(! isOnlyOneLocalTax(1))
4248
			{
4249
				if(! isOnlyOneLocalTax(2))
4250
				{
4251
					return array($obj->localtax1_type, get_localtax($vatrate, 1, $buyer, $seller), $obj->localtax2_type, get_localtax($vatrate, 2, $buyer, $seller), $obj->accountancy_code_sell,$obj->accountancy_code_buy);
4252
				}
4253
				else
4254
				{
4255
					return array($obj->localtax1_type, get_localtax($vatrate, 1, $buyer, $seller), $obj->localtax2_type, $obj->localtax2, $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4256
				}
4257
			}
4258
			else
4259
			{
4260
				if(! isOnlyOneLocalTax(2))
4261
				{
4262
					return array($obj->localtax1_type, $obj->localtax1, $obj->localtax2_type, get_localtax($vatrate, 2, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4263
				}
4264
				else
4265
				{
4266
					return array($obj->localtax1_type, $obj->localtax1, $obj->localtax2_type, $obj->localtax2, $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4267
				}
4268
			}
4269
		}
4270
	}
4271
4272
	return 0;
4273
}
4274
4275
/**
4276
 *	Return vat rate of a product in a particular selling country or default country vat if product is unknown
4277
 *  Function called by get_default_tva
4278
 *
4279
 *  @param	int			$idprod          	Id of product or 0 if not a predefined product
4280
 *  @param  Societe		$thirdparty_seller  Thirdparty with a ->country_code defined (FR, US, IT, ...)
4281
 *	@param	int			$idprodfournprice	Id product_fournisseur_price (for "supplier" order/invoice)
4282
 *  @return float|string   				    Vat rate to use with format 5.0 or '5.0 (XXX)'
4283
 *  @see get_product_localtax_for_country
4284
 */
4285
function get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice=0)
4286
{
4287
	global $db,$conf,$mysoc;
4288
4289
	require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4290
4291
	$ret=0;
4292
	$found=0;
4293
4294
	if ($idprod > 0)
4295
	{
4296
		// Load product
4297
		$product=new Product($db);
4298
		$result=$product->fetch($idprod);
4299
4300
		if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
4301
		{
4302
			if ($idprodfournprice > 0)     // We want vat for product for a "supplier" order or invoice
4303
			{
4304
				$product->get_buyprice($idprodfournprice,0,0,0);
4305
				$ret=$product->vatrate_supplier;
4306
			}
4307
			else
4308
			{
4309
				$ret=$product->tva_tx;    // Default vat of product we defined
4310
				if ($product->default_vat_code) $ret.=' ('.$product->default_vat_code.')';
4311
			}
4312
			$found=1;
4313
		}
4314
		else
4315
		{
4316
			// TODO Read default product vat according to countrycode and product. Vat for couple countrycode/product is a feature not implemeted yet.
4317
			// May be usefull/required if hidden option SERVICE_ARE_ECOMMERCE_200238EC is on
4318
		}
4319
	}
4320
4321
	if (! $found)
4322
	{
4323
		if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS))
4324
		{
4325
			// If vat of product for the country not found or not defined, we return the first higher vat of country.
4326
			$sql = "SELECT taux as vat_rate";
4327
			$sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4328
			$sql.= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
4329
			$sql.= " ORDER BY t.taux DESC, t.code ASC, t.recuperableonly ASC";
4330
			$sql.= $db->plimit(1);
4331
4332
			$resql=$db->query($sql);
4333
			if ($resql)
4334
			{
4335
				$obj=$db->fetch_object($resql);
4336
				if ($obj)
4337
				{
4338
					$ret=$obj->vat_rate;
4339
				}
4340
				$db->free($sql);
4341
			}
4342
			else dol_print_error($db);
4343
		}
4344
		else $ret=$conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;    // Forced value if autodetect fails
4345
	}
4346
4347
	dol_syslog("get_product_vat_for_country: ret=".$ret);
4348
	return $ret;
4349
}
4350
4351
/**
4352
 *	Return localtax vat rate of a product in a particular selling country or default country vat if product is unknown
4353
 *
4354
 *  @param	int		$idprod         		Id of product
4355
 *  @param  int		$local          		1 for localtax1, 2 for localtax 2
4356
 *  @param  Societe	$thirdparty_seller    	Thirdparty with a ->country_code defined (FR, US, IT, ...)
4357
 *  @return int             				<0 if KO, Vat rate if OK
4358
 *  @see get_product_vat_for_country
4359
 */
4360
function get_product_localtax_for_country($idprod, $local, $thirdparty_seller)
4361
{
4362
	global $db,$mysoc;
4363
4364
	if (! class_exists('Product')) {
4365
		require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4366
	}
4367
4368
	$ret=0;
4369
	$found=0;
4370
4371
	if ($idprod > 0)
4372
	{
4373
		// Load product
4374
		$product=new Product($db);
4375
		$result=$product->fetch($idprod);
4376
4377
		if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
4378
		{
4379
			/* Not defined yet, so we don't use this
4380
			if ($local==1) $ret=$product->localtax1_tx;
4381
			elseif ($local==2) $ret=$product->localtax2_tx;
4382
			$found=1;
4383
			*/
4384
		}
4385
		else
4386
		{
4387
			// TODO Read default product vat according to countrycode and product
4388
4389
4390
		}
4391
	}
4392
4393
	if (! $found)
4394
	{
4395
		// If vat of product for the country not found or not defined, we return higher vat of country.
4396
		$sql = "SELECT taux as vat_rate, localtax1, localtax2";
4397
		$sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4398
		$sql.= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
4399
		$sql.= " ORDER BY t.taux DESC, t.recuperableonly ASC";
4400
		$sql.= $db->plimit(1);
4401
4402
		$resql=$db->query($sql);
4403
		if ($resql)
4404
		{
4405
			$obj=$db->fetch_object($resql);
4406
			if ($obj)
4407
			{
4408
				if ($local==1) $ret=$obj->localtax1;
4409
				elseif ($local==2) $ret=$obj->localtax2;
4410
			}
4411
		}
4412
		else dol_print_error($db);
4413
	}
4414
4415
	dol_syslog("get_product_localtax_for_country: ret=".$ret);
4416
	return $ret;
4417
}
4418
4419
/**
4420
 *	Function that return vat rate of a product line (according to seller, buyer and product vat rate)
4421
 *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
4422
 *	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
4423
 *	 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.
4424
 *	 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
4425
 *	 Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise avec num TVA) intra alors TVA par defaut=0. Fin de regle
4426
 *	 Sinon TVA proposee par defaut=0. Fin de regle.
4427
 *
4428
 *	@param	Societe		$thirdparty_seller    	Objet societe vendeuse
4429
 *	@param  Societe		$thirdparty_buyer   	Objet societe acheteuse
4430
 *	@param  int			$idprod					Id product
4431
 *	@param	int			$idprodfournprice		Id product_fournisseur_price (for supplier order/invoice)
4432
 *	@return float|string   				      	Vat rate to use with format 5.0 or '5.0 (XXX)', -1 if we can't guess it
4433
 *  @see get_default_npr, get_default_localtax
4434
 */
4435
function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
4436
{
4437
	global $conf;
4438
4439
	// Note: possible values for tva_assuj are 0/1 or franchise/reel
4440
	$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;
4441
4442
	$seller_country_code=$thirdparty_seller->country_code;
4443
	$seller_in_cee=$thirdparty_seller->isInEEC();
4444
4445
	$buyer_country_code=$thirdparty_buyer->country_code;
4446
	$buyer_in_cee=$thirdparty_buyer->isInEEC();
4447
4448
	dol_syslog("get_default_tva: seller use vat=".$seller_use_vat.", seller country=".$seller_country_code.", seller in cee=".$seller_in_cee.", buyer country=".$buyer_country_code.", buyer in cee=".$buyer_in_cee.", idprod=".$idprod.", idprodfournprice=".$idprodfournprice.", SERVICE_ARE_ECOMMERCE_200238EC=".(! empty($conf->global->SERVICES_ARE_ECOMMERCE_200238EC)?$conf->global->SERVICES_ARE_ECOMMERCE_200238EC:''));
4449
4450
	// 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)
4451
	// we use the buyer VAT.
4452
	if (! empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC))
4453
	{
4454
		if ($seller_in_cee && $buyer_in_cee && ! $thirdparty_buyer->isACompany())
4455
		{
4456
			//print 'VATRULE 0';
4457
			return get_product_vat_for_country($idprod,$thirdparty_buyer,$idprodfournprice);
4458
		}
4459
	}
4460
4461
	// If seller does not use VAT
4462
	if (! $seller_use_vat)
4463
	{
4464
		//print 'VATRULE 1';
4465
		return 0;
4466
	}
4467
4468
	// 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.
4469
4470
	// Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
4471
	if (($seller_country_code == $buyer_country_code)
4472
	|| (in_array($seller_country_code,array('FR,MC')) && in_array($buyer_country_code,array('FR','MC')))) // Warning ->country_code not always defined
4473
	{
4474
		//print 'VATRULE 2';
4475
		return get_product_vat_for_country($idprod,$thirdparty_seller,$idprodfournprice);
4476
	}
4477
4478
	// 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.
4479
	// Not supported
4480
4481
	// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
4482
	// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
4483
	if (($seller_in_cee && $buyer_in_cee))
4484
	{
4485
		$isacompany=$thirdparty_buyer->isACompany();
4486
		if ($isacompany)
4487
		{
4488
			//print 'VATRULE 3';
4489
			return 0;
4490
		}
4491
		else
4492
		{
4493
			//print 'VATRULE 4';
4494
			return get_product_vat_for_country($idprod,$thirdparty_seller,$idprodfournprice);
4495
		}
4496
	}
4497
4498
	// Sinon la TVA proposee par defaut=0. Fin de regle.
4499
	// Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
4500
	//print 'VATRULE 5';
4501
	return 0;
4502
}
4503
4504
4505
/**
4506
 *	Fonction qui renvoie si tva doit etre tva percue recuperable
4507
 *
4508
 *	@param	Societe		$thirdparty_seller    	Thirdparty seller
4509
 *	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
4510
 *  @param  int			$idprod                 Id product
4511
 *  @param	int			$idprodfournprice		Id supplier price for product
4512
 *	@return float       			        	0 or 1
4513
 *  @see get_default_tva, get_default_localtax
4514
 */
4515
function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
4516
{
4517
	global $db;
4518
4519
	if ($idprodfournprice > 0)
4520
	{
4521
		if (! class_exists('ProductFournisseur'))
4522
			require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
4523
		$prodprice = new ProductFournisseur($db);
4524
		$prodprice->fetch_product_fournisseur_price($idprodfournprice);
4525
		return $prodprice->fourn_tva_npr;
4526
	}
4527
	elseif ($idprod > 0)
4528
	{
4529
		if (! class_exists('Product'))
4530
			require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4531
		$prod = new Product($db);
4532
		$prod->fetch($idprod);
4533
		return $prod->tva_npr;
4534
	}
4535
4536
	return 0;
4537
}
4538
4539
/**
4540
 *	Function that return localtax of a product line (according to seller, buyer and product vat rate)
4541
 *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
4542
 *	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
4543
 *	 Sinon TVA proposee par defaut=0. Fin de regle.
4544
 *
4545
 *	@param	Societe		$thirdparty_seller    	Thirdparty seller
4546
 *	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
4547
 *  @param	int			$local					Localtax to process (1 or 2)
4548
 *	@param  int			$idprod					Id product
4549
 *	@return integer        				       	localtax, -1 si ne peut etre determine
4550
 *  @see get_default_tva, get_default_npr
4551
 */
4552
function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
4553
{
4554
	global $mysoc;
4555
4556
	if (!is_object($thirdparty_seller)) return -1;
4557
	if (!is_object($thirdparty_buyer)) return -1;
4558
4559
	if ($local==1) // Localtax 1
4560
	{
4561
		if ($mysoc->country_code == 'ES')
4562
		{
4563
			if (is_numeric($thirdparty_buyer->localtax1_assuj) && ! $thirdparty_buyer->localtax1_assuj) return 0;
4564
		}
4565
		else
4566
		{
4567
			// Si vendeur non assujeti a Localtax1, localtax1 par default=0
4568
			if (is_numeric($thirdparty_seller->localtax1_assuj) && ! $thirdparty_seller->localtax1_assuj) return 0;
4569
			if (! is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj=='localtax1off') return 0;
4570
		}
4571
	}
4572
	elseif ($local==2) //I Localtax 2
4573
	{
4574
		// Si vendeur non assujeti a Localtax2, localtax2 par default=0
4575
		if (is_numeric($thirdparty_seller->localtax2_assuj) && ! $thirdparty_seller->localtax2_assuj) return 0;
4576
		if (! is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj=='localtax2off') return 0;
4577
	}
4578
4579
	if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code)
4580
	{
4581
		return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
4582
	}
4583
4584
	return 0;
4585
}
4586
4587
/**
4588
 *	Return yes or no in current language
4589
 *
4590
 *	@param	string	$yesno			Value to test (1, 'yes', 'true' or 0, 'no', 'false')
4591
 *	@param	integer	$case			1=Yes/No, 0=yes/no, 2=Disabled checkbox, 3=Disabled checkbox + Yes/No
4592
 *	@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.
4593
 *	@return	string					HTML string
4594
 */
4595
function yn($yesno, $case=1, $color=0)
4596
{
4597
	global $langs;
4598
	$result='unknown'; $classname='';
4599
	if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') 	// A mettre avant test sur no a cause du == 0
4600
	{
4601
		$result=$langs->trans('yes');
4602
		if ($case == 1 || $case == 3) $result=$langs->trans("Yes");
4603
		if ($case == 2) $result='<input type="checkbox" value="1" checked disabled>';
4604
		if ($case == 3) $result='<input type="checkbox" value="1" checked disabled> '.$result;
4605
4606
		$classname='ok';
4607
	}
4608
	elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false')
4609
	{
4610
		$result=$langs->trans("no");
4611
		if ($case == 1 || $case == 3) $result=$langs->trans("No");
4612
		if ($case == 2) $result='<input type="checkbox" value="0" disabled>';
4613
		if ($case == 3) $result='<input type="checkbox" value="0" disabled> '.$result;
4614
4615
		if ($color == 2) $classname='ok';
4616
		else $classname='error';
4617
	}
4618
	if ($color) return '<font class="'.$classname.'">'.$result.'</font>';
4619
	return $result;
4620
}
4621
4622
4623
/**
4624
 *	Return a path to have a directory according to object.
4625
 *  New usage:       $conf->module->multidir_output[$object->entity].'/'.get_exdir(0, 0, 0, 1, $object, $modulepart)
4626
 *         or:       $conf->module->dir_output.'/'.get_exdir(0, 0, 0, 1, $object, $modulepart)     if multidir_output not defined.
4627
 *  Old usage:       '015' with level 3->"0/1/5/", '015' with level 1->"5/", 'ABC-1' with level 3 ->"0/0/1/"
4628
 *
4629
 *	@param	string	$num            Id of object (deprecated, $object will be used in future)
4630
 *	@param  int		$level		    Level of subdirs to return (1, 2 or 3 levels). (deprecated, global option will be used in future)
4631
 * 	@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)
4632
 *  @param  int		$withoutslash   0=With slash at end (except if '/', we return ''), 1=without slash at end
4633
 *  @param	Object	$object			Object
4634
 *  @param	string	$modulepart		Type of object ('invoice_supplier, 'donation', 'invoice', ...')
4635
 *  @return	string					Dir to use ending. Example '' or '1/' or '1/2/'
4636
 */
4637
function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart)
4638
{
4639
	global $conf;
4640
4641
	$path = '';
4642
4643
	$arrayforoldpath=array('cheque','user','category','holiday','shipment','supplier_invoice','invoice_supplier','mailing','supplier_payment');
4644
	if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) $arrayforoldpath[]='product';
4645
	if (! empty($level) && in_array($modulepart, $arrayforoldpath))
4646
	{
4647
		// This part should be removed once all code is using "get_exdir" to forge path, with all parameters provided.
4648
		if (empty($alpha)) $num = preg_replace('/([^0-9])/i','',$num);
4649
		else $num = preg_replace('/^.*\-/i','',$num);
4650
		$num = substr("000".$num, -$level);
4651
		if ($level == 1) $path = substr($num,0,1);
4652
		if ($level == 2) $path = substr($num,1,1).'/'.substr($num,0,1);
4653
		if ($level == 3) $path = substr($num,2,1).'/'.substr($num,1,1).'/'.substr($num,0,1);
4654
	}
4655
	else
4656
	{
4657
		// TODO
4658
		// We will enhance here a common way of forging path for document storage
4659
		// Here, object->id, object->ref and object->modulepart are required.
4660
        if (in_array($modulepart, array('thirdparty','contact','member')))
4661
        {
4662
            $path=$object->ref?$object->ref:$object->id;
4663
        }
4664
	}
4665
4666
	if (empty($withoutslash) && ! empty($path)) $path.='/';
4667
	return $path;
4668
}
4669
4670
/**
4671
 *	Creation of a directory (this can create recursive subdir)
4672
 *
4673
 *	@param	string	$dir		Directory to create (Separator must be '/'. Example: '/mydir/mysubdir')
4674
 *	@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)
4675
 *  @param	int		$newmask	Mask for new file (Defaults to $conf->global->MAIN_UMASK or 0755 if unavailable). Example: '0444'
4676
 *	@return int         		< 0 if KO, 0 = already exists, > 0 if OK
4677
 */
4678
function dol_mkdir($dir, $dataroot='', $newmask=null)
4679
{
4680
	global $conf;
4681
4682
	dol_syslog("functions.lib::dol_mkdir: dir=".$dir,LOG_INFO);
4683
4684
	$dir_osencoded=dol_osencode($dir);
4685
	if (@is_dir($dir_osencoded)) return 0;
4686
4687
	$nberr=0;
4688
	$nbcreated=0;
4689
4690
	$ccdir='';
4691
	if (! empty($dataroot)) {
4692
		// Remove data root from loop
4693
		$dir = str_replace($dataroot.'/', '', $dir);
4694
		$ccdir = $dataroot.'/';
4695
	}
4696
4697
	$cdir = explode("/", $dir);
4698
	$num=count($cdir);
4699
	for ($i = 0; $i < $num; $i++)
4700
	{
4701
		if ($i > 0) $ccdir .= '/'.$cdir[$i];
4702
		else $ccdir .= $cdir[$i];
4703
		if (preg_match("/^.:$/",$ccdir,$regs)) continue;	// Si chemin Windows incomplet, on poursuit par rep suivant
4704
4705
		// Attention, le is_dir() peut echouer bien que le rep existe.
4706
		// (ex selon config de open_basedir)
4707
		if ($ccdir)
4708
		{
4709
			$ccdir_osencoded=dol_osencode($ccdir);
4710
			if (! @is_dir($ccdir_osencoded))
4711
			{
4712
				dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.",LOG_DEBUG);
4713
4714
				umask(0);
4715
				$dirmaskdec=octdec($newmask);
4716
				if (empty($newmask)) {
4717
					$dirmaskdec = empty( $conf->global->MAIN_UMASK ) ? octdec( '0755' ) : octdec( $conf->global->MAIN_UMASK );
4718
				}
4719
				$dirmaskdec |= octdec('0111');  // Set x bit required for directories
4720
				if (! @mkdir($ccdir_osencoded, $dirmaskdec))
4721
				{
4722
					// Si le is_dir a renvoye une fausse info, alors on passe ici.
4723
					dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.",LOG_WARNING);
4724
					$nberr++;
4725
				}
4726
				else
4727
				{
4728
					dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created",LOG_DEBUG);
4729
					$nberr=0;	// On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
4730
					$nbcreated++;
4731
				}
4732
			}
4733
			else
4734
			{
4735
				$nberr=0;	// On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
4736
			}
4737
		}
4738
	}
4739
	return ($nberr ? -$nberr : $nbcreated);
4740
}
4741
4742
4743
/**
4744
 *	Return picto saying a field is required
4745
 *
4746
 *	@return  string		Chaine avec picto obligatoire
4747
 */
4748
function picto_required()
4749
{
4750
	return '<span class="fieldrequired">*</span>';
4751
}
4752
4753
4754
/**
4755
 *	Clean a string from all HTML tags and entities
4756
 *
4757
 *	@param	string	$StringHtml			String to clean
4758
 *	@param	integer	$removelinefeed		1=Replace also new lines by a space, 0=Only last one are removed
4759
 *  @param  string	$pagecodeto      	Encoding of input/output string
4760
 *	@return string	    				String cleaned
4761
 *
4762
 * 	@see		dol_escape_htmltag
4763
 */
4764
function dol_string_nohtmltag($StringHtml,$removelinefeed=1,$pagecodeto='UTF-8')
4765
{
4766
	$pattern = "/<[^<>]+>/";
4767
	$StringHtml = preg_replace('/<br[^>]*>/', "\n", $StringHtml);
4768
	$temp = dol_html_entity_decode($StringHtml,ENT_COMPAT,$pagecodeto);
4769
4770
    // Exemple of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
4771
    $temp = preg_replace($pattern,"",$temp);    // pass 1
4772
    // $temp after pass 1: <a href="/myurl" title="A title">0000-021
4773
    $temp = preg_replace($pattern,"",$temp);    // pass 2
4774
    // $temp after pass 2: 0000-021
4775
4776
	// Supprime aussi les retours
4777
	if ($removelinefeed) $temp=str_replace(array("\r\n","\r","\n")," ",$temp);
4778
4779
	// et les espaces doubles
4780
	while(strpos($temp,"  "))
4781
	{
4782
		$temp = str_replace("  "," ",$temp);
4783
	}
4784
	$CleanString = trim($temp);
4785
	return $CleanString;
4786
}
4787
4788
4789
/**
4790
 * Return first line of text. Cut will depends if content is HTML or not.
4791
 *
4792
 * @param 	string	$text		Input text
4793
 * @param	int		$nboflines  Nb of lines to get (default is 1 = first line only)
4794
 * @return	string				Output text
4795
 * @see dol_nboflines_bis, dol_string_nohtmltag, dol_escape_htmltag
4796
 */
4797
function dolGetFirstLineOfText($text, $nboflines=1)
4798
{
4799
	if ($nboflines == 1)
4800
	{
4801
		if (dol_textishtml($text))
4802
		{
4803
			$firstline=preg_replace('/<br[^>]*>.*$/s','',$text);		// The s pattern modifier means the . can match newline characters
4804
			$firstline=preg_replace('/<div[^>]*>.*$/s','',$firstline);	// The s pattern modifier means the . can match newline characters
4805
4806
		}
4807
		else
4808
		{
4809
	    	$firstline=preg_replace('/[\n\r].*/','',$text);
4810
		}
4811
    	return $firstline.((strlen($firstline) != strlen($text))?'...':'');
4812
	}
4813
	else
4814
	{
4815
		$ishtml=0;
4816
		if (dol_textishtml($text))
4817
		{
4818
			$text=preg_replace('/\n/','',$text);
4819
			$ishtml=1;
4820
			$repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
4821
		}
4822
		else
4823
		{
4824
			$repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
4825
		}
4826
4827
		$text = strtr($text, $repTable);
4828
		if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; }	// /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
0 ignored issues
show
Bug introduced by
The variable $charset does not exist. Did you forget to declare it?

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

Loading history...
4829
		else $pattern = '/(<br[^>]*>)/U';							// /U is to have UNGREEDY regex to limit to one html tag.
4830
		$a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
4831
4832
		$firstline='';
4833
		$i=0;
4834
		$nba = count($a);	// 2x nb of lines in $a because $a contains also a line for each new line separator
4835
		while (($i < $nba) && ($i < ($nboflines * 2)))
4836
		{
4837
			if ($i % 2 == 0) $firstline .= $a[$i];
4838
			elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) $firstline .= ($ishtml?"<br>\n":"\n");
4839
			$i++;
4840
		}
4841
		unset($a);
4842
    	return $firstline.(($i < $nba)?'...':'');
4843
	}
4844
}
4845
4846
4847
/**
4848
 * Replace CRLF in string with a HTML BR tag
4849
 *
4850
 * @param	string	$stringtoencode		String to encode
4851
 * @param	int     $nl2brmode			0=Adding br before \n, 1=Replacing \n by br
4852
 * @param   bool	$forxml             false=Use <br>, true=Use <br />
4853
 * @return	string						String encoded
4854
 * @see dol_nboflines, dolGetFirstLineOfText
4855
 */
4856
function dol_nl2br($stringtoencode,$nl2brmode=0,$forxml=false)
4857
{
4858
	if (!$nl2brmode) {
4859
		return nl2br($stringtoencode, $forxml);
4860
	} else {
4861
		$ret=preg_replace('/(\r\n|\r|\n)/i', ($forxml?'<br />':'<br>'), $stringtoencode);
4862
		return $ret;
4863
	}
4864
}
4865
4866
4867
/**
4868
 *	This function is called to encode a string into a HTML string but differs from htmlentities because
4869
 * 	a detection is done before to see if text is already HTML or not. Also, all entities but &,<,> are converted.
4870
 *  This permits to encode special chars to entities with no double encoding for already encoded HTML strings.
4871
 * 	This function also remove last EOL or BR if $removelasteolbr=1 (default).
4872
 *  For PDF usage, you can show text by 2 ways:
4873
 *              - writeHTMLCell -> param must be encoded into HTML.
4874
 *              - MultiCell -> param must not be encoded into HTML.
4875
 *              Because writeHTMLCell convert also \n into <br>, if function
4876
 *              is used to build PDF, nl2brmode must be 1.
4877
 *
4878
 *	@param	string	$stringtoencode		String to encode
4879
 *	@param	int		$nl2brmode			0=Adding br before \n, 1=Replacing \n by br (for use with FPDF writeHTMLCell function for example)
4880
 *  @param  string	$pagecodefrom       Pagecode stringtoencode is encoded
4881
 *  @param	int		$removelasteolbr	1=Remove last br or lasts \n (default), 0=Do nothing
4882
 *  @return	string						String encoded
4883
 */
4884
function dol_htmlentitiesbr($stringtoencode,$nl2brmode=0,$pagecodefrom='UTF-8',$removelasteolbr=1)
4885
{
4886
	$newstring=$stringtoencode;
4887
	if (dol_textishtml($stringtoencode))	// Check if text is already HTML or not
4888
	{
4889
		$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.
4890
		if ($removelasteolbr) $newstring=preg_replace('/<br>$/i','',$newstring);	// Remove last <br> (remove only last one)
4891
		$newstring=strtr($newstring,array('&'=>'__and__','<'=>'__lt__','>'=>'__gt__','"'=>'__dquot__'));
4892
		$newstring=dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom);	// Make entity encoding
4893
		$newstring=strtr($newstring,array('__and__'=>'&','__lt__'=>'<','__gt__'=>'>','__dquot__'=>'"'));
4894
	}
4895
	else
4896
	{
4897
		if ($removelasteolbr) $newstring=preg_replace('/(\r\n|\r|\n)$/i','',$newstring);	// Remove last \n (may remove several)
4898
		$newstring=dol_nl2br(dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom),$nl2brmode);
4899
	}
4900
	// Other substitutions that htmlentities does not do
4901
	//$newstring=str_replace(chr(128),'&euro;',$newstring);	// 128 = 0x80. Not in html entity table.     // Seems useles with TCPDF. Make bug with UTF8 languages
4902
	return $newstring;
4903
}
4904
4905
/**
4906
 *	This function is called to decode a HTML string (it decodes entities and br tags)
4907
 *
4908
 *	@param	string	$stringtodecode		String to decode
4909
 *	@param	string	$pagecodeto			Page code for result
4910
 *	@return	string						String decoded
4911
 */
4912
function dol_htmlentitiesbr_decode($stringtodecode,$pagecodeto='UTF-8')
4913
{
4914
	$ret=dol_html_entity_decode($stringtodecode,ENT_COMPAT,$pagecodeto);
4915
	$ret=preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i',"<br>",$ret);
4916
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i',"\r\n",$ret);
4917
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i',"\n",$ret);
4918
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i',"\n",$ret);
4919
	return $ret;
4920
}
4921
4922
/**
4923
 *	This function remove all ending \n and br at end
4924
 *
4925
 *	@param	string	$stringtodecode		String to decode
4926
 *	@return	string						String decoded
4927
 */
4928
function dol_htmlcleanlastbr($stringtodecode)
4929
{
4930
	$ret=preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i',"",$stringtodecode);
4931
	return $ret;
4932
}
4933
4934
/**
4935
 * Replace html_entity_decode functions to manage errors
4936
 *
4937
 * @param   string	$a		Operand a
4938
 * @param   string	$b		Operand b (ENT_QUOTES=convert simple and double quotes)
4939
 * @param   string	$c		Operand c
4940
 * @return  string			String decoded
4941
 */
4942
function dol_html_entity_decode($a,$b,$c='UTF-8')
4943
{
4944
	return html_entity_decode($a,$b,$c);
4945
}
4946
4947
/**
4948
 * Replace htmlentities functions to manage errors http://php.net/manual/en/function.htmlentities.php
4949
 * Goal of this function is to be sure to have default values of htmlentities that match what we need.
4950
 *
4951
 * @param   string  $string         The input string.
4952
 * @param   int     $flags          Flags(see PHP doc above)
4953
 * @param   string  $encoding       Encoding
4954
 * @param   bool    $double_encode  When double_encode is turned off PHP will not encode existing html entities
4955
 * @return  string  $ret            Encoded string
4956
 */
4957
function dol_htmlentities($string, $flags=null, $encoding='UTF-8', $double_encode=false)
4958
{
4959
	return htmlentities($string, $flags, $encoding, $double_encode);
4960
}
4961
4962
4963
/**
4964
 *	Check if a string is a correct iso string
4965
 *	If not, it will we considered not HTML encoded even if it is by FPDF.
4966
 *	Example, if string contains euro symbol that has ascii code 128
4967
 *
4968
 *	@param	string	$s      String to check
4969
 *	@return	int     		0 if bad iso, 1 if good iso
4970
 */
4971
function dol_string_is_good_iso($s)
4972
{
4973
	$len=dol_strlen($s);
4974
	$ok=1;
4975
	for($scursor=0;$scursor<$len;$scursor++)
4976
	{
4977
		$ordchar=ord($s{$scursor});
4978
		//print $scursor.'-'.$ordchar.'<br>';
4979
		if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) { $ok=0; break; }
4980
		if ($ordchar > 126 && $ordchar < 160) { $ok=0; break; }
4981
	}
4982
	return $ok;
4983
}
4984
4985
4986
/**
4987
 *	Return nb of lines of a clear text
4988
 *
4989
 *	@param	string	$s			String to check
4990
 * 	@param	int     $maxchar	Not yet used
4991
 *	@return	int					Number of lines
4992
 *  @see	dol_nboflines_bis, dolGetFirstLineOfText
4993
 */
4994
function dol_nboflines($s,$maxchar=0)
4995
{
4996
	if ($s == '') return 0;
4997
	$arraystring=explode("\n",$s);
4998
	$nb=count($arraystring);
4999
5000
	return $nb;
5001
}
5002
5003
5004
/**
5005
 *	Return nb of lines of a formated text with \n and <br> (WARNING: string must not have mixed \n and br separators)
5006
 *
5007
 *	@param	string	$text      		Text
5008
 *	@param	int		$maxlinesize  	Largeur de ligne en caracteres (ou 0 si pas de limite - defaut)
5009
 * 	@param	string	$charset		Give the charset used to encode the $text variable in memory.
5010
 *	@return int						Number of lines
5011
 *	@see	dol_nboflines, dolGetFirstLineOfText
5012
 */
5013
function dol_nboflines_bis($text,$maxlinesize=0,$charset='UTF-8')
5014
{
5015
	$repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5016
	if (dol_textishtml($text)) $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5017
5018
	$text = strtr($text, $repTable);
5019
	if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; }	// /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
5020
	else $pattern = '/(<br[^>]*>)/U';							// /U is to have UNGREEDY regex to limit to one html tag.
5021
	$a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5022
5023
	$nblines = (int) floor((count($a)+1)/2);
5024
	// count possible auto line breaks
5025
	if($maxlinesize)
5026
	{
5027
		foreach ($a as $line)
5028
		{
5029
			if (dol_strlen($line)>$maxlinesize)
5030
			{
5031
				//$line_dec = html_entity_decode(strip_tags($line));
5032
				$line_dec = html_entity_decode($line);
5033
				if(dol_strlen($line_dec)>$maxlinesize)
5034
				{
5035
					$line_dec=wordwrap($line_dec,$maxlinesize,'\n',true);
5036
					$nblines+=substr_count($line_dec,'\n');
5037
				}
5038
			}
5039
		}
5040
	}
5041
5042
	unset($a);
5043
	return $nblines;
5044
}
5045
5046
/**
5047
 *	 Same function than microtime in PHP 5 but compatible with PHP4
5048
 *
5049
 * @return		float		Time (millisecondes) with microsecondes in decimal part
5050
 * @deprecated Dolibarr does not support PHP4, you should use native function
5051
 * @see microtime()
5052
 */
5053
function dol_microtime_float()
5054
{
5055
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
5056
5057
	return microtime(true);
5058
}
5059
5060
/**
5061
 *	Return if a text is a html content
5062
 *
5063
 *	@param	string	$msg		Content to check
5064
 *	@param	int		$option		0=Full detection, 1=Fast check
5065
 *	@return	boolean				true/false
5066
 *	@see	dol_concatdesc
5067
 */
5068
function dol_textishtml($msg,$option=0)
5069
{
5070
	if ($option == 1)
5071
	{
5072
		if (preg_match('/<html/i',$msg))				return true;
5073
		elseif (preg_match('/<body/i',$msg))			return true;
5074
		elseif (preg_match('/<br/i',$msg))				return true;
5075
		return false;
5076
	}
5077
	else
5078
	{
5079
		if (preg_match('/<html/i',$msg))				return true;
5080
		elseif (preg_match('/<body/i',$msg))			return true;
5081
		elseif (preg_match('/<(b|em|i|u)>/i',$msg))		return true;
5082
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i',$msg)) 	  return true;
5083
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*>/i',$msg)) return true;
5084
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/>/i',$msg)) return true;
5085
		elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i',$msg)) return true;	// must accept <img src="http://example.com/aaa.png" />
5086
		elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i',$msg)) return true;	// must accept <a href="http://example.com/aaa.png" />
5087
		elseif (preg_match('/<h[0-9]>/i',$msg))			return true;
5088
		elseif (preg_match('/&[A-Z0-9]{1,6};/i',$msg))	return true;    // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
5089
		elseif (preg_match('/&#[0-9]{2,3};/i',$msg))	return true;    // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
5090
		return false;
5091
	}
5092
}
5093
5094
/**
5095
 *  Concat 2 descriptions with a new line between them (second operand after first one with appropriate new line separator)
5096
 *  text1 html + text2 html => text1 + '<br>' + text2
5097
 *  text1 html + text2 txt  => text1 + '<br>' + dol_nl2br(text2)
5098
 *  text1 txt  + text2 html => dol_nl2br(text1) + '<br>' + text2
5099
 *  text1 txt  + text2 txt  => text1 + '\n' + text2
5100
 *
5101
 *  @param	string	$text1		Text 1
5102
 *  @param	string	$text2		Text 2
5103
 *  @param  bool	$forxml     false=Use <br>, true=Use <br />
5104
 *  @return	string				Text 1 + new line + Text2
5105
 *  @see    dol_textishtml
5106
 */
5107
function dol_concatdesc($text1,$text2,$forxml=false)
5108
{
5109
	$ret='';
5110
	$ret.= (! dol_textishtml($text1) && dol_textishtml($text2))?dol_nl2br($text1, 0, $forxml):$text1;
5111
	$ret.= (! empty($text1) && ! empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2))?($forxml?"<br \>\n":"<br>\n") : "\n") : "";
5112
	$ret.= (dol_textishtml($text1) && ! dol_textishtml($text2))?dol_nl2br($text2, 0, $forxml):$text2;
5113
	return $ret;
5114
}
5115
5116
5117
/**
5118
 * Return array of possible common substitutions.
5119
 *
5120
 * @param	Translate	$outputlangs	Output language
5121
 * @param   int         $onlykey        Do not calculate heavy values of keys (performance enhancement when we need only the keys)
5122
 * @param   array       $exclude        Array of family keys we want to exclude. For example array('mycompany', 'objectamount', 'date', 'user', ...)
5123
 * @param   Object      $object         Object for keys on object
5124
 * @return	array						Array of substitutions
5125
 */
5126
function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
5127
{
5128
    global $conf, $mysoc, $user;
5129
5130
    $substitutionarray=array();
5131
5132
    if (empty($exclude) || ! in_array('mycompany', $exclude))
5133
    {
5134
        $substitutionarray=array_merge($substitutionarray, array(
5135
            '__MYCOMPANY_NAME__' => $mysoc->name,
5136
            '__MYCOMPANY_EMAIL__' => $mysoc->email,
5137
            '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
5138
            '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
5139
            '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
5140
            '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
5141
            '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
5142
            '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
5143
            '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
5144
            '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id
5145
        ));
5146
    }
5147
    if (empty($exclude) || ! in_array('objectamount', $exclude))
5148
    {
5149
        if (is_object($object))       // For backward compatibility
5150
        {
5151
            $substitutionarray['__TOTAL_TTC__']    =is_object($object)?$object->total_ttc:'';
5152
            $substitutionarray['__TOTAL_HT__']     =is_object($object)?$object->total_ht:'';
5153
            $substitutionarray['__TOTAL_VAT__']    =is_object($object)?($object->total_vat?$object->total_vat:$object->total_tva):'';
5154
        }
5155
        $substitutionarray['__AMOUNT__']       =is_object($object)?$object->total_ttc:'';
5156
        $substitutionarray['__AMOUNT_WO_TAX__']=is_object($object)?$object->total_ht:'';
5157
        $substitutionarray['__AMOUNT_VAT__']   =is_object($object)?($object->total_vat?$object->total_vat:$object->total_tva):'';
5158
    }
5159
5160
    if (empty($exclude) || ! in_array('date', $exclude))
5161
    {
5162
        include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
5163
5164
        if (! empty($onlykey))
5165
        {
5166
            $tmp=$tmp2=$tmp3=$tmp4=$tmp5=array();
5167
        }
5168
        else
5169
        {
5170
            $tmp=dol_getdate(dol_now(), true);
5171
            $tmp2=dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
5172
            $tmp3=dol_get_prev_month($tmp['mday'], $tmp['mon'], $tmp['year']);
5173
            $tmp4=dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
5174
            $tmp5=dol_get_next_month($tmp['mday'], $tmp['mon'], $tmp['year']);
5175
        }
5176
        $substitutionarray=array_merge($substitutionarray, array(
5177
            '__DAY__' => $tmp['mday'],
5178
            '__MONTH__' => $tmp['mon'],
5179
            '__YEAR__' => $tmp['year'],
5180
            '__PREVIOUS_DAY__' => $tmp2['day'],
5181
            '__PREVIOUS_MONTH__' => $tmp3['month'],
5182
            '__PREVIOUS_YEAR__' => ($tmp['year'] - 1),
5183
            '__NEXT_DAY__' => $tmp4['day'],
5184
            '__NEXT_MONTH__' => $tmp5['month'],
5185
            '__NEXT_YEAR__' => ($tmp['year'] + 1),
5186
        ));
5187
    }
5188
5189
    if (empty($exclude) || ! in_array('user', $exclude))
5190
    {
5191
        $substitutionarray=array_merge($substitutionarray, array(
5192
            '__USER_ID__' => $user->id,
5193
            '__USER_LOGIN__' => $user->login,
5194
            '__USER_LASTNAME__' => $user->lastname,
5195
            '__USER_FIRSTNAME__' => $user->firstname,
5196
            '__USER_FULLNAME__' => $user->getFullName($outputlangs),
5197
            '__USER_SUPERVISOR_ID__' => $user->fk_user
5198
        ));
5199
    }
5200
    if (! empty($conf->multicompany->enabled))
5201
    {
5202
        $substitutionarray=array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
5203
    }
5204
5205
    return $substitutionarray;
5206
}
5207
5208
/**
5209
 *  Make substition into a text string, replacing keys with vals from $substitutionarray (oldval=>newval).
5210
 *
5211
 *  @param	string		$text	      			Source string in which we must do substitution
5212
 *  @param  array		$substitutionarray		Array with key->val to substitute
5213
 *  @param	Translate	$outputlangs			Output language
5214
 * 	@return string  		    				Output string after substitutions
5215
 *  @see	complete_substitutions_array
5216
 */
5217
function make_substitutions($text, $substitutionarray, $outputlangs=null)
5218
{
5219
	global $conf, $langs;
5220
5221
	if (! is_array($substitutionarray)) return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
5222
5223
	if (empty($outputlangs)) $outputlangs=$langs;
5224
5225
	// Make substitution for language keys
5226
	if (is_object($outputlangs))
5227
	{
5228
		while (preg_match('/__\(([^\)]*)\)__/', $text, $reg))
5229
		{
5230
		    // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
5231
			$tmp=explode('|',$reg[1]);
5232
			if (! empty($tmp[1])) $outputlangs->load($tmp[1]);
5233
5234
			$msgishtml = 0;
5235
			if (dol_textishtml($text,1)) $msgishtml = 1;
5236
5237
			$text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $msgishtml?dol_htmlentitiesbr($outputlangs->transnoentitiesnoconv($reg[1])):$outputlangs->transnoentitiesnoconv($reg[1]), $text);
5238
		}
5239
	}
5240
5241
	// Make substitition for array $substitutionarray
5242
	foreach ($substitutionarray as $key => $value)
5243
	{
5244
		if ($key == '__SIGNATURE__' && (! empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) $value='';
5245
		$text=str_replace("$key","$value",$text);	// We must keep the " to work when value is 123.5 for example
5246
	}
5247
5248
	return $text;
5249
}
5250
5251
/**
5252
 *  Complete the $substitutionarray with more entries.
5253
 *  Can also add substitution keys coming from external module that had set the "substitutions=1" into module_part array. In this case, method completesubstitutionarray provided by module is called.
5254
 *
5255
 *  @param  array		$substitutionarray		Array substitution old value => new value value
5256
 *  @param  Translate	$outputlangs            Output language
5257
 *  @param  Object		$object                 Source object
5258
 *  @param  mixed		$parameters       		Add more parameters (useful to pass product lines)
5259
 *  @param  string      $callfunc               What is the name of the custom function that will be called? (default: completesubstitutionarray)
5260
 *  @return	void
5261
 *  @see 	make_substitutions
5262
 */
5263
function complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
5264
{
5265
	global $conf,$user;
5266
5267
	require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5268
5269
	// Add a substitution key for each object property
5270
	if (is_object($object))
5271
	{
5272
		// TODO
5273
	}
5274
5275
	// Add a substitution key for each extrafields, using key __EXTRA_XXX__
5276
	if (is_object($object) && is_array($object->array_options))
5277
	{
5278
		foreach($object->array_options as $key => $val)
5279
		{
5280
			$keyshort=preg_replace('/^(options|extra)_/','',$key);
5281
			$substitutionarray['__EXTRA_'.$keyshort.'__']=$val;
5282
			// For backward compatibiliy
5283
			$substitutionarray['%EXTRA_'.$keyshort.'%']=$val;
5284
		}
5285
	}
5286
5287
	// Check if there is external substitution to do, requested by plugins
5288
	$dirsubstitutions=array_merge(array(),(array) $conf->modules_parts['substitutions']);
5289
5290
	foreach($dirsubstitutions as $reldir)
5291
	{
5292
		$dir=dol_buildpath($reldir,0);
5293
5294
		// Check if directory exists
5295
		if (! dol_is_dir($dir)) continue;
5296
5297
		$substitfiles=dol_dir_list($dir,'files',0,'functions_');
5298
		foreach($substitfiles as $substitfile)
5299
		{
5300
			if (preg_match('/functions_(.*)\.lib\.php/i',$substitfile['name'],$reg))
5301
			{
5302
				$module=$reg[1];
5303
5304
				dol_syslog("Library functions_".$substitfile['name']." found into ".$dir);
5305
				// Include the user's functions file
5306
				require_once $dir.$substitfile['name'];
5307
				// Call the user's function, and only if it is defined
5308
				$function_name=$module."_".$callfunc;
5309
				if (function_exists($function_name)) $function_name($substitutionarray,$outputlangs,$object,$parameters);
5310
			}
5311
		}
5312
	}
5313
}
5314
5315
/**
5316
 *    Format output for start and end date
5317
 *
5318
 *    @param	int	$date_start    Start date
5319
 *    @param    int	$date_end      End date
5320
 *    @param    string		$format        Output format
5321
 *    @param	Translate	$outputlangs   Output language
5322
 *    @return	void
5323
 */
5324
function print_date_range($date_start,$date_end,$format = '',$outputlangs='')
5325
{
5326
	print get_date_range($date_start,$date_end,$format,$outputlangs);
5327
}
5328
5329
/**
5330
 *    Format output for start and end date
5331
 *
5332
 *    @param	int			$date_start    		Start date
5333
 *    @param    int			$date_end      		End date
5334
 *    @param    string		$format        		Output format
5335
 *    @param	Translate	$outputlangs   		Output language
5336
 *    @param	integer		$withparenthesis	1=Add parenthesis, 0=non parenthesis
5337
 *    @return	string							String
5338
 */
5339
function get_date_range($date_start,$date_end,$format = '',$outputlangs='', $withparenthesis=1)
5340
{
5341
	global $langs;
5342
5343
	$out='';
5344
5345
	if (! is_object($outputlangs)) $outputlangs=$langs;
5346
5347
	if ($date_start && $date_end)
5348
	{
5349
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFromTo',dol_print_date($date_start, $format, false, $outputlangs),dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
5350
	}
5351
	if ($date_start && ! $date_end)
5352
	{
5353
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFrom',dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis?')':'');
5354
	}
5355
	if (! $date_start && $date_end)
5356
	{
5357
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateUntil',dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
5358
	}
5359
5360
	return $out;
5361
}
5362
5363
/**
5364
 * Return firstname and lastname in correct order
5365
 *
5366
 * @param	string	$firstname		Firstname
5367
 * @param	string	$lastname		Lastname
5368
 * @param	int		$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname
5369
 * @return	string					Firstname + lastname or Lastname + firstname
5370
 */
5371
function dolGetFirstLastname($firstname,$lastname,$nameorder=-1)
5372
{
5373
	global $conf;
5374
5375
	$ret='';
5376
	// If order not defined, we use the setup
5377
	if ($nameorder < 0) $nameorder=(empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION));
5378
	if ($nameorder && ((string) $nameorder != '2'))
5379
	{
5380
        $ret.=$firstname;
5381
		if ($firstname && $lastname) $ret.=' ';
5382
		$ret.=$lastname;
5383
	}
5384
	else if ($nameorder == 2)
5385
	{
5386
	   $ret.=$firstname;
5387
	}
5388
	else
5389
	{
5390
		$ret.=$lastname;
5391
		if ($firstname && $lastname) $ret.=' ';
5392
		$ret.=$firstname;
5393
	}
5394
	return $ret;
5395
}
5396
5397
5398
/**
5399
 *	Set event message in dol_events session object. Will be output by calling dol_htmloutput_events.
5400
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
5401
 *  Note: Prefer to use setEventMessages instead.
5402
 *
5403
 *	@param	mixed	$mesgs			Message string or array
5404
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
5405
 *  @return	void
5406
 *  @see	dol_htmloutput_events
5407
 */
5408
function setEventMessage($mesgs, $style='mesgs')
5409
{
5410
	//dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);		This is not deprecated, it is used by setEventMessages function
5411
	if (! is_array($mesgs))		// If mesgs is a string
5412
	{
5413
		if ($mesgs) $_SESSION['dol_events'][$style][] = $mesgs;
5414
	}
5415
	else						// If mesgs is an array
5416
	{
5417
		foreach($mesgs as $mesg)
5418
		{
5419
			if ($mesg) $_SESSION['dol_events'][$style][] = $mesg;
5420
		}
5421
	}
5422
}
5423
5424
/**
5425
 *	Set event messages in dol_events session object. Will be output by calling dol_htmloutput_events.
5426
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
5427
 *
5428
 *	@param	string	$mesg			Message string
5429
 *	@param	array	$mesgs			Message array
5430
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
5431
 *  @return	void
5432
 *  @see	dol_htmloutput_events
5433
 */
5434
function setEventMessages($mesg, $mesgs, $style='mesgs')
5435
{
5436
	if (! in_array((string) $style, array('mesgs','warnings','errors'))) dol_print_error('','Bad parameter style='.$style.' for setEventMessages');
5437
	if (empty($mesgs)) setEventMessage($mesg, $style);
5438
	else
5439
	{
5440
		if (! empty($mesg) && ! in_array($mesg, $mesgs)) setEventMessage($mesg, $style);	// Add message string if not already into array
5441
		setEventMessage($mesgs, $style);
5442
	}
5443
}
5444
5445
/**
5446
 *	Print formated messages to output (Used to show messages on html output).
5447
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function, so there is
5448
 *  no need to call it explicitely.
5449
 *
5450
 *  @return	void
5451
 *  @see    dol_htmloutput_mesg
5452
 */
5453
function dol_htmloutput_events()
5454
{
5455
	// Show mesgs
5456
	if (isset($_SESSION['dol_events']['mesgs'])) {
5457
		dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
5458
		unset($_SESSION['dol_events']['mesgs']);
5459
	}
5460
5461
	// Show errors
5462
	if (isset($_SESSION['dol_events']['errors'])) {
5463
		dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
5464
		unset($_SESSION['dol_events']['errors']);
5465
	}
5466
5467
	// Show warnings
5468
	if (isset($_SESSION['dol_events']['warnings'])) {
5469
		dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
5470
		unset($_SESSION['dol_events']['warnings']);
5471
	}
5472
}
5473
5474
/**
5475
 *	Get formated messages to output (Used to show messages on html output).
5476
 *  This include also the translation of the message key.
5477
 *
5478
 *	@param	string		$mesgstring		Message string or message key
5479
 *	@param	string[]	$mesgarray      Array of message strings or message keys
5480
 *  @param  string		$style          Style of message output ('ok' or 'error')
5481
 *  @param  int			$keepembedded   Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
5482
 *	@return	string						Return html output
5483
 *
5484
 *  @see    dol_print_error
5485
 *  @see    dol_htmloutput_errors
5486
 *  @see    setEventMessages
5487
 */
5488
function get_htmloutput_mesg($mesgstring='',$mesgarray='', $style='ok', $keepembedded=0)
5489
{
5490
	global $conf, $langs;
5491
5492
	$ret=0; $return='';
5493
	$out='';
5494
	$divstart=$divend='';
5495
5496
	// If inline message with no format, we add it.
5497
	if ((empty($conf->use_javascript_ajax) || ! empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && ! preg_match('/<div class=".*">/i',$out))
5498
	{
5499
		$divstart='<div class="'.$style.'">';
5500
		$divend='</div>';
5501
	}
5502
5503
	if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring)
5504
	{
5505
		$langs->load("errors");
5506
		$out.=$divstart;
5507
		if (is_array($mesgarray) && count($mesgarray))
5508
		{
5509
			foreach($mesgarray as $message)
5510
			{
5511
				$ret++;
5512
				$out.= $langs->trans($message);
5513
				if ($ret < count($mesgarray)) $out.= "<br>\n";
5514
			}
5515
		}
5516
		if ($mesgstring)
5517
		{
5518
			$langs->load("errors");
5519
			$ret++;
5520
			$out.= $langs->trans($mesgstring);
5521
		}
5522
		$out.=$divend;
5523
	}
5524
5525
	if ($out)
5526
	{
5527
		if (! empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded))
5528
		{
5529
			$return = '<script type="text/javascript">
5530
					$(document).ready(function() {
5531
						var block = '.(! empty($conf->global->MAIN_USE_JQUERY_BLOCKUI)?"true":"false").'
5532
						if (block) {
5533
							$.dolEventValid("","'.dol_escape_js($out).'");
5534
						} else {
5535
							/* jnotify(message, preset of message type, keepmessage) */
5536
							$.jnotify("'.dol_escape_js($out).'",
5537
							"'.($style=="ok" ? 3000 : $style).'",
5538
							'.($style=="ok" ? "false" : "true").',
5539
							{ remove: function (){} } );
5540
						}
5541
					});
5542
				</script>';
5543
		}
5544
		else
5545
		{
5546
			$return = $out;
5547
		}
5548
	}
5549
5550
	return $return;
5551
}
5552
5553
/**
5554
 *  Get formated error messages to output (Used to show messages on html output).
5555
 *
5556
 *  @param	string	$mesgstring         Error message
5557
 *  @param  array	$mesgarray          Error messages array
5558
 *  @param  int		$keepembedded       Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
5559
 *  @return string                		Return html output
5560
 *
5561
 *  @see    dol_print_error
5562
 *  @see    dol_htmloutput_mesg
5563
 */
5564
function get_htmloutput_errors($mesgstring='', $mesgarray='', $keepembedded=0)
5565
{
5566
	return get_htmloutput_mesg($mesgstring, $mesgarray,'error',$keepembedded);
1 ignored issue
show
Bug introduced by
It seems like $mesgarray defined by parameter $mesgarray on line 5564 can also be of type array; however, get_htmloutput_mesg() does only seem to accept string|array<integer,string>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
5567
}
5568
5569
/**
5570
 *	Print formated messages to output (Used to show messages on html output).
5571
 *
5572
 *	@param	string		$mesgstring		Message string or message key
5573
 *	@param	string[]	$mesgarray      Array of message strings or message keys
5574
 *  @param  string      $style          Which style to use ('ok', 'warning', 'error')
5575
 *  @param  int         $keepembedded   Set to 1 if message must be kept embedded into its html place (this disable jnotify)
5576
 *  @return	void
5577
 *
5578
 *  @see    dol_print_error
5579
 *  @see    dol_htmloutput_errors
5580
 *  @see    setEventMessages
5581
 */
5582
function dol_htmloutput_mesg($mesgstring='',$mesgarray='', $style='ok', $keepembedded=0)
5583
{
5584
	if (empty($mesgstring) && (! is_array($mesgarray) || count($mesgarray) == 0)) return;
5585
5586
	$iserror=0;
5587
	$iswarning=0;
5588
	if (is_array($mesgarray))
5589
	{
5590
		foreach($mesgarray as $val)
5591
		{
5592
			if ($val && preg_match('/class="error"/i',$val)) { $iserror++; break; }
5593
			if ($val && preg_match('/class="warning"/i',$val)) { $iswarning++; break; }
5594
		}
5595
	}
5596
	else if ($mesgstring && preg_match('/class="error"/i',$mesgstring)) $iserror++;
5597
	else if ($mesgstring && preg_match('/class="warning"/i',$mesgstring)) $iswarning++;
5598
	if ($style=='error') $iserror++;
5599
	if ($style=='warning') $iswarning++;
5600
5601
	if ($iserror || $iswarning)
5602
	{
5603
		// Remove div from texts
5604
		$mesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$mesgstring);
5605
		$mesgstring=preg_replace('/<div class="(error|warning)">/','',$mesgstring);
5606
		$mesgstring=preg_replace('/<\/div>/','',$mesgstring);
5607
		// Remove div from texts array
5608
		if (is_array($mesgarray))
5609
		{
5610
			$newmesgarray=array();
5611
			foreach($mesgarray as $val)
5612
			{
5613
				$tmpmesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$val);
5614
				$tmpmesgstring=preg_replace('/<div class="(error|warning)">/','',$tmpmesgstring);
5615
				$tmpmesgstring=preg_replace('/<\/div>/','',$tmpmesgstring);
5616
				$newmesgarray[]=$tmpmesgstring;
5617
			}
5618
			$mesgarray=$newmesgarray;
5619
		}
5620
		print get_htmloutput_mesg($mesgstring,$mesgarray,($iserror?'error':'warning'),$keepembedded);
5621
	}
5622
	else print get_htmloutput_mesg($mesgstring,$mesgarray,'ok',$keepembedded);
5623
}
5624
5625
/**
5626
 *  Print formated error messages to output (Used to show messages on html output).
5627
 *
5628
 *  @param	string	$mesgstring          Error message
5629
 *  @param  array	$mesgarray           Error messages array
5630
 *  @param  int		$keepembedded        Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
5631
 *  @return	void
5632
 *
5633
 *  @see    dol_print_error
5634
 *  @see    dol_htmloutput_mesg
5635
 */
5636
function dol_htmloutput_errors($mesgstring='', $mesgarray='', $keepembedded=0)
5637
{
5638
	dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
1 ignored issue
show
Bug introduced by
It seems like $mesgarray defined by parameter $mesgarray on line 5636 can also be of type array; however, dol_htmloutput_mesg() does only seem to accept string|array<integer,string>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
5639
}
5640
5641
/**
5642
 * 	Advanced sort array by second index function, which produces ascending (default)
5643
 *  or descending output and uses optionally natural case insensitive sorting (which
5644
 *  can be optionally case sensitive as well).
5645
 *
5646
 *  @param      array		$array      		Array to sort (array of array('key','otherkey1','otherkey2'...))
5647
 *  @param      string		$index				Key in array to use for sorting criteria
5648
 *  @param      int			$order				Sort order ('asc' or 'desc')
5649
 *  @param      int			$natsort			1=use "natural" sort (natsort), 0=use "standard" sort (asort)
5650
 *  @param      int			$case_sensitive		1=sort is case sensitive, 0=not case sensitive
5651
 *  @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.
5652
 *  @return     array							Sorted array
5653
 */
5654
function dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
5655
{
5656
	// Clean parameters
5657
	$order=strtolower($order);
5658
5659
	$sizearray=count($array);
5660
	if (is_array($array) && $sizearray>0)
5661
	{
5662
        $temp = array();
5663
        foreach(array_keys($array) as $key) $temp[$key]=$array[$key][$index];
5664
5665
		if (!$natsort) ($order=='asc') ? asort($temp) : arsort($temp);
5666
		else
5667
		{
5668
			($case_sensitive) ? natsort($temp) : natcasesort($temp);
5669
			if($order!='asc') $temp=array_reverse($temp,TRUE);
5670
		}
5671
5672
		$sorted = array();
5673
5674
		foreach(array_keys($temp) as $key)
5675
		{
5676
			(is_numeric($key) && empty($keepindex)) ? $sorted[]=$array[$key] : $sorted[$key]=$array[$key];
5677
		}
5678
5679
		return $sorted;
5680
	}
5681
	return $array;
5682
}
5683
5684
5685
/**
5686
 *      Check if a string is in UTF8
5687
 *
5688
 *      @param	string	$str        String to check
5689
 * 		@return	boolean				True if string is UTF8 or ISO compatible with UTF8, False if not (ISO with special char or Binary)
5690
 */
5691
function utf8_check($str)
5692
{
5693
	// We must use here a binary strlen function (so not dol_strlen)
5694
	$strLength = dol_strlen($str);
5695
	for ($i=0; $i<$strLength; $i++)
5696
	{
5697
		if (ord($str[$i]) < 0x80) continue; // 0bbbbbbb
5698
		elseif ((ord($str[$i]) & 0xE0) == 0xC0) $n=1; // 110bbbbb
5699
		elseif ((ord($str[$i]) & 0xF0) == 0xE0) $n=2; // 1110bbbb
5700
		elseif ((ord($str[$i]) & 0xF8) == 0xF0) $n=3; // 11110bbb
5701
		elseif ((ord($str[$i]) & 0xFC) == 0xF8) $n=4; // 111110bb
5702
		elseif ((ord($str[$i]) & 0xFE) == 0xFC) $n=5; // 1111110b
5703
		else return false; // Does not match any model
5704
		for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ?
5705
			if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
5706
			return false;
5707
		}
5708
	}
5709
	return true;
5710
}
5711
5712
5713
/**
5714
 *      Return a string encoded into OS filesystem encoding. This function is used to define
5715
 * 	    value to pass to filesystem PHP functions.
5716
 *
5717
 *      @param	string	$str        String to encode (UTF-8)
5718
 * 		@return	string				Encoded string (UTF-8, ISO-8859-1)
5719
 */
5720
function dol_osencode($str)
5721
{
5722
	global $conf;
5723
5724
	$tmp=ini_get("unicode.filesystem_encoding");						// Disponible avec PHP 6.0
5725
	if (empty($tmp) && ! empty($_SERVER["WINDIR"])) $tmp='iso-8859-1';	// By default for windows
5726
	if (empty($tmp)) $tmp='utf-8';										// By default for other
5727
	if (! empty($conf->global->MAIN_FILESYSTEM_ENCODING)) $tmp=$conf->global->MAIN_FILESYSTEM_ENCODING;
5728
5729
	if ($tmp == 'iso-8859-1') return utf8_decode($str);
5730
	return $str;
5731
}
5732
5733
5734
/**
5735
 *      Return an id or code from a code or id.
5736
 *      Store also Code-Id into a cache to speed up next request on same key.
5737
 *
5738
 * 		@param	DoliDB	$db			Database handler
5739
 * 		@param	string	$key		Code or Id to get Id or Code
5740
 * 		@param	string	$tablename	Table name without prefix
5741
 * 		@param	string	$fieldkey	Field for code
5742
 * 		@param	string	$fieldid	Field for id
5743
 *      @return int					<0 if KO, Id of code if OK
5744
 *      @see $langs->getLabelFromKey
5745
 */
5746
function dol_getIdFromCode($db,$key,$tablename,$fieldkey='code',$fieldid='id')
5747
{
5748
	global $cache_codes;
5749
5750
	// If key empty
5751
	if ($key == '') return '';
5752
5753
	// Check in cache
5754
	if (isset($cache_codes[$tablename][$key]))	// Can be defined to 0 or ''
5755
	{
5756
		return $cache_codes[$tablename][$key];   // Found in cache
5757
	}
5758
5759
	$sql = "SELECT ".$fieldid." as valuetoget";
5760
	$sql.= " FROM ".MAIN_DB_PREFIX.$tablename;
5761
	$sql.= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
5762
	dol_syslog('dol_getIdFromCode', LOG_DEBUG);
5763
	$resql = $db->query($sql);
5764
	if ($resql)
5765
	{
5766
		$obj = $db->fetch_object($resql);
5767
		if ($obj) $cache_codes[$tablename][$key]=$obj->valuetoget;
5768
		else $cache_codes[$tablename][$key]='';
5769
		$db->free($resql);
5770
		return $cache_codes[$tablename][$key];
5771
	}
5772
	else
5773
	{
5774
		return -1;
5775
	}
5776
}
5777
5778
/**
5779
 * Verify if condition in string is ok or not
5780
 *
5781
 * @param 	string		$strRights		String with condition to check
5782
 * @return 	boolean						True or False. Return true if strRights is ''
5783
 */
5784
function verifCond($strRights)
5785
{
5786
	global $user,$conf,$langs;
5787
	global $leftmenu;
5788
	global $rights;    // To export to dol_eval function
5789
5790
	//print $strRights."<br>\n";
5791
	$rights = true;
5792
	if ($strRights != '')
5793
	{
5794
		//$tab_rights = explode('&&', $strRights);
5795
		//$i = 0;
5796
		//while (($i < count($tab_rights)) && ($rights == true)) {
5797
		$str = 'if(!(' . $strRights . ')) { $rights = false; }';
5798
		dol_eval($str);
5799
		//	$i++;
5800
		//}
5801
	}
5802
	return $rights;
5803
}
5804
5805
/**
5806
 * Replace eval function to add more security.
5807
 * This function is called by verifCond() or trans() and transnoentitiesnoconv().
5808
 *
5809
 * @param 	string	$s				String to evaluate
5810
 * @param	int		$returnvalue	0=No return (used to execute eval($a=something)). 1=Value of eval is returned (used to eval($something)).
5811
 * @param   int     $hideerrors     1=Hide errors
5812
 * @return	mixed					Nothing or return of eval
5813
 */
5814
function dol_eval($s, $returnvalue=0, $hideerrors=1)
5815
{
5816
	// Only global variables can be changed by eval function and returned to caller
5817
	global $db, $langs, $user, $conf;
5818
	global $mainmenu, $leftmenu;
5819
	global $rights;
5820
	global $object;
5821
	global $mysoc;
5822
5823
	global $obj;       // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
5824
	global $soc;       // For backward compatibility
5825
5826
	//print $s."<br>\n";
5827
	if ($returnvalue)
5828
	{
5829
	    if ($hideerrors) return @eval('return '.$s.';');
0 ignored issues
show
Coding Style introduced by
The function dol_eval() contains an eval expression.

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

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

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

Loading history...
5831
	}
5832
	else
5833
	{
5834
	    if ($hideerrors) @eval($s);
0 ignored issues
show
Coding Style introduced by
The function dol_eval() contains an eval expression.

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

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

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

Loading history...
5836
	}
5837
}
5838
5839
/**
5840
 * Return if var element is ok
5841
 *
5842
 * @param   string      $element    Variable to check
5843
 * @return  boolean                 Return true of variable is not empty
5844
 */
5845
function dol_validElement($element)
5846
{
5847
	return (trim($element) != '');
5848
}
5849
5850
/**
5851
 * 	Return img flag of country for a language code or country code
5852
 *
5853
 * 	@param	string	$codelang	Language code (en_IN, fr_CA...) or Country code (IN, FR)
5854
 * 	@return	string				HTML img string with flag.
5855
 */
5856
function picto_from_langcode($codelang)
5857
{
5858
	global $langs;
5859
5860
	if (empty($codelang)) return '';
5861
5862
	if (empty($codelang)) return '';
5863
5864
	if ($codelang == 'auto')
5865
	{
5866
		return img_picto_common($langs->trans('AutoDetectLang'), 'flags/int.png');
5867
	}
5868
5869
	$langtocountryflag = array(
5870
		'ar_AR' => '',
5871
		'ca_ES' => 'catalonia',
5872
		'da_DA' => 'dk',
5873
		'fr_CA' => 'mq',
5874
		'sv_SV' => 'se'
5875
	);
5876
5877
	if (isset($langtocountryflag[$codelang])) $flagImage = $langtocountryflag[$codelang];
5878
	else
5879
	{
5880
		$tmparray = explode('_', $codelang);
5881
		$flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
5882
	}
5883
5884
	return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png');
5885
}
5886
5887
/**
5888
 *  Complete or removed entries into a head array (used to build tabs).
5889
 *  For example, with value added by external modules. Such values are declared into $conf->modules_parts['tab'].
5890
 *  Or by change using hook completeTabsHead
5891
 *
5892
 *  @param	Conf			$conf           Object conf
5893
 *  @param  Translate		$langs          Object langs
5894
 *  @param  object|null		$object         Object object
5895
 *  @param  array			$head          	Object head
5896
 *  @param  int				$h				New position to fill
5897
 *  @param  string			$type           Value for object where objectvalue can be
5898
 *                              			'thirdparty'       to add a tab in third party view
5899
 *		                        	      	'intervention'     to add a tab in intervention view
5900
 *     		                    	     	'supplier_order'   to add a tab in supplier order view
5901
 *          		            	        'supplier_invoice' to add a tab in supplier invoice view
5902
 *                  		    	        'invoice'          to add a tab in customer invoice view
5903
 *                          			    'order'            to add a tab in customer order view
5904
 *                          				'contract'		   to add a tabl in contract view
5905
 *                      			        'product'          to add a tab in product view
5906
 *                              			'propal'           to add a tab in propal view
5907
 *                              			'user'             to add a tab in user view
5908
 *                              			'group'            to add a tab in group view
5909
 * 		        	               	     	'member'           to add a tab in fundation member view
5910
 *      		                        	'categories_x'	   to add a tab in category view ('x': type of category (0=product, 1=supplier, 2=customer, 3=member)
5911
 *      									'ecm'			   to add a tab for another ecm view
5912
 *                                          'stock'            to add a tab for warehouse view
5913
 *  @param  string		$mode  	        	'add' to complete head, 'remove' to remove entries
5914
 *	@return	void
5915
 */
5916
function complete_head_from_modules($conf,$langs,$object,&$head,&$h,$type,$mode='add')
5917
{
5918
	global $hookmanager;
5919
5920
	if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type]))
5921
	{
5922
		foreach ($conf->modules_parts['tabs'][$type] as $value)
5923
		{
5924
			$values=explode(':',$value);
5925
5926
			if ($mode == 'add' && ! preg_match('/^\-/',$values[1]))
5927
			{
5928
				if (count($values) == 6)       // new declaration with permissions:  $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
5929
				{
5930
					if ($values[0] != $type) continue;
5931
5932
					if (verifCond($values[4]))
5933
					{
5934
						if ($values[3]) $langs->load($values[3]);
5935
						if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
5936
						{
5937
							$substitutionarray=array();
5938
							complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
5939
							$label=make_substitutions($reg[1], $substitutionarray);
5940
						}
5941
						else $label=$langs->trans($values[2]);
5942
5943
						$head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && ! empty($object->id))?$object->id:''), $values[5]), 1);
5944
						$head[$h][1] = $label;
5945
						$head[$h][2] = str_replace('+','',$values[1]);
5946
						$h++;
5947
					}
5948
				}
5949
				else if (count($values) == 5)       // deprecated
5950
				{
5951
					dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
5952
5953
					if ($values[0] != $type) continue;
5954
					if ($values[3]) $langs->load($values[3]);
5955
					if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
5956
					{
5957
						$substitutionarray=array();
5958
						complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
5959
						$label=make_substitutions($reg[1], $substitutionarray);
5960
					}
5961
					else $label=$langs->trans($values[2]);
5962
5963
					$head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && ! empty($object->id))?$object->id:''), $values[4]), 1);
5964
					$head[$h][1] = $label;
5965
					$head[$h][2] = str_replace('+','',$values[1]);
5966
					$h++;
5967
				}
5968
			}
5969
			else if ($mode == 'remove' && preg_match('/^\-/',$values[1]))
5970
			{
5971
				if ($values[0] != $type) continue;
5972
				$tabname=str_replace('-','',$values[1]);
5973
				foreach($head as $key => $val)
5974
				{
5975
					$condition = (! empty($values[3]) ? verifCond($values[3]) : 1);
5976
					if ($head[$key][2]==$tabname && $condition)
5977
					{
5978
						unset($head[$key]);
5979
						break;
5980
					}
5981
				}
5982
			}
5983
		}
5984
	}
5985
5986
	// No need to make a return $head. Var is modified as a reference
5987
	if (! empty($hookmanager))
5988
	{
5989
		$parameters=array('object' => $object, 'mode' => $mode, 'head'=>$head);
5990
		$reshook=$hookmanager->executeHooks('completeTabsHead',$parameters);
5991
		if ($reshook > 0)
5992
		{
5993
			$head = $hookmanager->resArray;
5994
		}
5995
	}
5996
}
5997
5998
/**
5999
 * Print common footer :
6000
 * 		conf->global->MAIN_HTML_FOOTER
6001
 * 		conf->global->MAIN_GOOGLE_AN_ID
6002
 * 		conf->global->MAIN_SHOW_TUNING_INFO or $_SERVER["MAIN_SHOW_TUNING_INFO"]
6003
 * 		conf->logbuffer
6004
 *
6005
 * @param	string	$zone	'private' (for private pages) or 'public' (for public pages)
6006
 * @return	void
6007
 */
6008
function printCommonFooter($zone='private')
6009
{
6010
	global $conf, $hookmanager;
6011
	global $micro_start_time;
6012
6013
	if ($zone == 'private') print "\n".'<!-- Common footer for private page -->'."\n";
6014
	else print "\n".'<!-- Common footer for public page -->'."\n";
6015
6016
	if (! empty($conf->global->MAIN_HTML_FOOTER)) print $conf->global->MAIN_HTML_FOOTER."\n";
6017
6018
	print "\n";
6019
	if (! empty($conf->use_javascript_ajax))
6020
	{
6021
		print '<!-- Reposition management (does not work if a redirect is done after action of submission) -->'."\n";
6022
    	print '<script type="text/javascript" language="javascript">jQuery(document).ready(function() {'."\n";
6023
6024
    	print '<!-- If page_y set, we set scollbar with it -->'."\n";
6025
    	print "page_y=getParameterByName('page_y', 0);";
6026
    	print "if (page_y > 0) $('html, body').scrollTop(page_y);\n";
6027
6028
    	print '<!-- Set handler to add page_y param on some a href links -->'."\n";
6029
    	print 'jQuery(".reposition").click(function() {
6030
    	           var page_y = $(document).scrollTop();
6031
    	           /*alert(page_y);*/
6032
    	           this.href=this.href+\'&page_y=\'+page_y;
6033
    	           });'."\n";
6034
    	print '});'."\n";
6035
6036
    	if (empty($conf->dol_use_jmobile))
6037
    	{
6038
        	print '<!-- Set handler to switch left menu page (menuhider) -->'."\n";
6039
        	print 'jQuery(".menuhider").click(function() {';
6040
        	print '  console.log("We click on .menuhider");'."\n";
6041
        	//print "  $('.side-nav').animate({width:'toggle'},200);\n";     // OK with eldy theme but not with md
6042
        	print "  $('.side-nav').toggle()\n";
6043
        	print "  $('.login_block').toggle()\n";
6044
        	print '});'."\n";
6045
    	}
6046
6047
    	print '</script>'."\n";
6048
	}
6049
6050
	// Google Analytics (need Google module)
6051
	if (! empty($conf->google->enabled) && ! empty($conf->global->MAIN_GOOGLE_AN_ID))
6052
	{
6053
		if (($conf->dol_use_jmobile != 4))
6054
		{
6055
			print "\n";
6056
			print '<script type="text/javascript">'."\n";
6057
			print '  var _gaq = _gaq || [];'."\n";
6058
			print '  _gaq.push([\'_setAccount\', \''.$conf->global->MAIN_GOOGLE_AN_ID.'\']);'."\n";
6059
			print '  _gaq.push([\'_trackPageview\']);'."\n";
6060
			print ''."\n";
6061
			print '  (function() {'."\n";
6062
			print '    var ga = document.createElement(\'script\'); ga.type = \'text/javascript\'; ga.async = true;'."\n";
6063
			print '    ga.src = (\'https:\' == document.location.protocol ? \'https://ssl\' : \'http://www\') + \'.google-analytics.com/ga.js\';'."\n";
6064
			print '    var s = document.getElementsByTagName(\'script\')[0]; s.parentNode.insertBefore(ga, s);'."\n";
6065
			print '  })();'."\n";
6066
			print '</script>'."\n";
6067
		}
6068
	}
6069
6070
	// End of tuning
6071
	if (! empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || ! empty($conf->global->MAIN_SHOW_TUNING_INFO))
6072
	{
6073
		print "\n".'<script type="text/javascript">'."\n";
6074
		print 'window.console && console.log("';
6075
		if (! empty($conf->global->MEMCACHED_SERVER)) print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
6076
		print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED)?$conf->global->MAIN_OPTIMIZE_SPEED:'off');
6077
		if (! empty($micro_start_time))   // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
6078
		{
6079
			$micro_end_time = microtime(true);
6080
			print ' - Build time: '.ceil(1000*($micro_end_time-$micro_start_time)).' ms';
6081
		}
6082
		if (function_exists("memory_get_usage"))
6083
		{
6084
			print ' - Mem: '.memory_get_usage();
6085
		}
6086
		if (function_exists("xdebug_memory_usage"))
6087
		{
6088
			print ' - XDebug time: '.ceil(1000*xdebug_time_index()).' ms';
6089
			print ' - XDebug mem: '.xdebug_memory_usage();
6090
			print ' - XDebug mem peak: '.xdebug_peak_memory_usage();
6091
		}
6092
		if (function_exists("zend_loader_file_encoded"))
6093
		{
6094
			print ' - Zend encoded file: '.(zend_loader_file_encoded()?'yes':'no');
6095
		}
6096
		print '");'."\n";
6097
		print '</script>'."\n";
6098
6099
		// Add Xdebug coverage of code
6100
		if (defined('XDEBUGCOVERAGE'))
6101
		{
6102
			print_r(xdebug_get_code_coverage());
6103
		}
6104
	}
6105
6106
	// If there is some logs in buffer to show
6107
	if (count($conf->logbuffer))
6108
	{
6109
		print "\n";
6110
		print "<!-- Start of log output\n";
6111
		//print '<div class="hidden">'."\n";
6112
		foreach($conf->logbuffer as $logline)
6113
		{
6114
			print $logline."<br>\n";
6115
		}
6116
		//print '</div>'."\n";
6117
		print "End of log output -->\n";
6118
	}
6119
6120
	$parameters=array();
6121
	$reshook=$hookmanager->executeHooks('printCommonFooter',$parameters);    // Note that $action and $object may have been modified by some hooks
6122
}
6123
6124
/**
6125
 * Split a string with 2 keys into key array.
6126
 * For example: "A=1;B=2;C=2" is exploded into array('A'=>1,'B'=>2,'C'=>3)
6127
 *
6128
 * @param 	string	$string		String to explode
6129
 * @param 	string	$delimiter	Delimiter between each couple of data
6130
 * @param 	string	$kv			Delimiter between key and value
6131
 * @return	array				Array of data exploded
6132
 */
6133
function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
6134
{
6135
	if ($a = explode($delimiter, $string))
6136
	{
6137
	    $ka = array();
6138
		foreach ($a as $s) { // each part
6139
			if ($s) {
6140
				if ($pos = strpos($s, $kv)) { // key/value delimiter
6141
					$ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
6142
				} else { // key delimiter not found
6143
					$ka[] = trim($s);
6144
				}
6145
			}
6146
		}
6147
		return $ka;
6148
	}
6149
	return array();
6150
}
6151
6152
6153
/**
6154
 * Set focus onto field with selector
6155
 *
6156
 * @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.
6157
 * @return	string				HTML code to set focus
6158
 */
6159
function dol_set_focus($selector)
6160
{
6161
	print "\n".'<!-- Set focus onto a specific field -->'."\n";
6162
	print '<script type="text/javascript" language="javascript">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
6163
}
6164
6165
6166
/**
6167
 * Return getmypid() or random PID when function is disabled
6168
 * Some web hosts disable this php function for security reasons
6169
 * and sometimes we can't redeclare function
6170
 *
6171
 * @return	int
6172
 */
6173
function dol_getmypid()
6174
{
6175
    if (! function_exists('getmypid')) {
6176
        return mt_rand(1,32768);
6177
    } else {
6178
        return getmypid();
6179
    }
6180
}
6181
6182
6183
/**
6184
 * Generate natural SQL search string for a criteria (this criteria can be tested on one or several fields)
6185
 *
6186
 * @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)
6187
 * @param 	string 			$value 		The value to look for.
6188
 *                          		    If param $mode is 0, can contains several keywords separated with a space or |
6189
 *                                         like "keyword1 keyword2" = We want record field like keyword1 AND field like keyword2
6190
 *                                         or like "keyword1|keyword2" = We want record field like keyword1 OR field like keyword2
6191
 *                             			If param $mode is 1, can contains an operator <, > or = like "<10" or ">=100.5 < 1000"
6192
 *                             			If param $mode is 2, can contains a list of id separated by comma like "1,3,4"
6193
 * @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')
6194
 * @param	integer			$nofirstand	1=Do not output the first 'AND'
6195
 * @return 	string 			$res 		The statement to append to the SQL query
6196
 */
6197
function natural_search($fields, $value, $mode=0, $nofirstand=0)
6198
{
6199
    global $db,$langs;
6200
6201
    if ($mode == 0)
6202
    {
6203
    	$value=preg_replace('/\*/','%',$value);	// Replace * with %
6204
    }
6205
    if ($mode == 1)
6206
    {
6207
    	$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
6208
    }
6209
6210
    $value = preg_replace('/\s*\|\s*/','|', $value);
6211
6212
    $crits = explode(' ', $value);
6213
    $res = '';
6214
    if (! is_array($fields)) $fields = array($fields);
6215
6216
    $nboffields = count($fields);
6217
    $end2 = count($crits);
6218
    $j = 0;
6219
    foreach ($crits as $crit)
6220
    {
6221
        $i = 0; $i2 = 0;
6222
        $newres = '';
6223
        foreach ($fields as $field)
6224
        {
6225
            if ($mode == 1)
6226
            {
6227
            	$operator='=';
6228
            	$newcrit = preg_replace('/([<>=]+)/','',trim($crit));
6229
6230
            	preg_match('/([<>=]+)/',trim($crit), $reg);
6231
            	if ($reg[1])
6232
            	{
6233
            		$operator = $reg[1];
6234
            	}
6235
            	if ($newcrit != '')
6236
            	{
6237
            		$numnewcrit = price2num($newcrit);
6238
            		if (is_numeric($numnewcrit))
6239
            		{
6240
            			$newres .= ($i2 > 0 ? ' OR ' : '') . $field . ' '.$operator.' '.$numnewcrit;
6241
            		}
6242
            		else
6243
            		{
6244
            			$newres .= ($i2 > 0 ? ' OR ' : '') . '1 = 2';	// force false
6245
            		}
6246
            		$i2++;	// a criteria was added to string
6247
            	}
6248
            }
6249
            else if ($mode == 2)
6250
            {
6251
				$newres .= ($i2 > 0 ? ' OR ' : '') . $field . " IN (" . $db->escape(trim($crit)) . ")";
6252
            	$i2++;	// a criteria was added to string
6253
            }
6254
            else    // $mode=0
6255
			{
6256
				$textcrit = '';
6257
				$tmpcrits = explode('|',$crit);
6258
				$i3 = 0;
6259
				foreach($tmpcrits as $tmpcrit)
6260
				{
6261
	            	$newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '') . $field . " LIKE '";
6262
6263
	            	$tmpcrit=trim($tmpcrit);
6264
	            	$tmpcrit2=$tmpcrit;
6265
	            	$tmpbefore='%'; $tmpafter='%';
6266
	            	if (preg_match('/^[\^\$]/', $tmpcrit))
6267
	            	{
6268
	            	    $tmpbefore='';
6269
	            	    $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
6270
	            	}
6271
					if (preg_match('/[\^\$]$/', $tmpcrit))
6272
	            	{
6273
	            	    $tmpafter='';
6274
	            	    $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
6275
	            	}
6276
	            	$newres .= $tmpbefore;
6277
	            	$newres .= $db->escape($tmpcrit2);
6278
	            	$newres .= $tmpafter;
6279
	            	$newres .= "'";
6280
	            	if ($tmpcrit2 == '')
6281
	            	{
6282
	            	    $newres .= ' OR ' . $field . " IS NULL";
6283
	            	}
6284
	            	$i3++;
6285
				}
6286
				$i2++;	// a criteria was added to string
6287
            }
6288
            $i++;
6289
        }
6290
        if ($newres) $res = $res . ($res ? ' AND ' : '') . ($i2 > 1 ? '(' : '') .$newres . ($i2 > 1 ? ')' : '');
6291
        $j++;
6292
    }
6293
    $res = ($nofirstand?"":" AND ")."(" . $res . ")";
6294
    //print 'xx'.$res.'yy';
6295
    return $res;
6296
}
6297
6298
/**
6299
 * Return the filename of file to get the thumbs
6300
 *
6301
 * @param   string  $file           Original filename (full or relative path)
6302
 * @param   string  $extName        Extension to differenciate thumb file name ('', '_small', '_mini')
6303
 * @param   string  $extImgTarget   Force image extension for thumbs. Use '' to keep same extension than original image (default).
6304
 * @return  string                  New file name (full or relative path, including the thumbs/)
6305
 */
6306
function getImageFileNameForSize($file, $extName, $extImgTarget='')
6307
{
6308
	$dirName = dirname($file);
6309
	if ($dirName == '.') $dirName='';
6310
6311
    $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp)$/i','',$file);	// We remove extension, whatever is its case
6312
	$fileName = basename($fileName);
6313
6314
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpg$/i',$file)?'.jpg':'');
6315
    if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpeg$/i',$file)?'.jpeg':'');
6316
    if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.gif$/i',$file)?'.gif':'');
6317
    if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.png$/i',$file)?'.png':'');
6318
    if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.bmp$/i',$file)?'.bmp':'');
6319
6320
    if (! $extImgTarget) return $file;
6321
6322
    $subdir='';
6323
    if ($extName) $subdir = 'thumbs/';
6324
6325
    return ($dirName?$dirName.'/':'').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
6326
}
6327
6328
6329
/**
6330
 * Return URL we can use for advanced preview links
6331
 *
6332
 * @param   string    $modulepart     propal, facture, facture_fourn, ...
6333
 * @param   string    $relativepath   Relative path of docs.
6334
 * @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)
6335
 * @param	string	  $param		  More param on http links
6336
 * @return  string|array              Output string with href link or array with all components of link
6337
 */
6338
function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
6339
{
6340
    global $conf, $langs;
6341
6342
    if (empty($conf->use_javascript_ajax)) return '';
6343
6344
    $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css');
6345
    //$mime_preview[]='vnd.oasis.opendocument.presentation';
6346
    //$mime_preview[]='archive';
6347
    $num_mime = array_search(dol_mimetype($relativepath, '', 1), $mime_preview);
6348
6349
    if ($alldata == 1)
6350
    {
6351
    	if ($num_mime !== false) return array('target'=>'_blank', 'css'=>'documentpreview', 'url'=>DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&attachment=0&file='.urlencode($relativepath), 'mime'=>dol_mimetype($relativepath), );
6352
    	else return array();
6353
    }
6354
6355
    // old behavior
6356
    if ($num_mime !== false) return 'javascript:document_preview(\''.dol_escape_js(DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&attachment=0&file='.urlencode($relativepath).($param?'&'.$param:'')).'\', \''.dol_mimetype($relativepath).'\', \''.dol_escape_js($langs->trans('Preview')).'\')';
6357
    else return '';
6358
}
6359
6360
6361
/**
6362
 *	Return mime type of a file
6363
 *
6364
 *	@param	string	$file		Filename we looking for MIME type
6365
 *  @param  string	$default    Default mime type if extension not found in known list
6366
 * 	@param	int		$mode    	0=Return full mime, 1=otherwise short mime string, 2=image for mime type, 3=source language
6367
 *	@return string 		    	Return a mime type family (text/xxx, application/xxx, image/xxx, audio, video, archive)
6368
 *  @see    image_format_supported (images.lib.php)
6369
 */
6370
function dol_mimetype($file,$default='application/octet-stream',$mode=0)
6371
{
6372
    $mime=$default;
6373
    $imgmime='other.png';
6374
    $srclang='';
6375
6376
    $tmpfile=preg_replace('/\.noexe$/','',$file);
6377
6378
    // Text files
6379
    if (preg_match('/\.txt$/i',$tmpfile))         			   { $mime='text/plain'; $imgmime='text.png'; }
6380
    if (preg_match('/\.rtx$/i',$tmpfile))                      { $mime='text/richtext'; $imgmime='text.png'; }
6381
    if (preg_match('/\.csv$/i',$tmpfile))					   { $mime='text/csv'; $imgmime='text.png'; }
6382
    if (preg_match('/\.tsv$/i',$tmpfile))					   { $mime='text/tab-separated-values'; $imgmime='text.png'; }
6383
    if (preg_match('/\.(cf|conf|log)$/i',$tmpfile))            { $mime='text/plain'; $imgmime='text.png'; }
6384
    if (preg_match('/\.ini$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='ini'; }
6385
    if (preg_match('/\.css$/i',$tmpfile))                      { $mime='text/css'; $imgmime='css.png'; $srclang='css'; }
6386
    // Certificate files
6387
    if (preg_match('/\.(crt|cer|key|pub)$/i',$tmpfile))        { $mime='text/plain'; $imgmime='text.png'; }
6388
    // HTML/XML
6389
    if (preg_match('/\.(html|htm|shtml)$/i',$tmpfile))         { $mime='text/html'; $imgmime='html.png'; $srclang='html'; }
6390
    if (preg_match('/\.(xml|xhtml)$/i',$tmpfile))              { $mime='text/xml'; $imgmime='other.png'; $srclang='xml'; }
6391
    // Languages
6392
    if (preg_match('/\.bas$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='bas'; }
6393
    if (preg_match('/\.(c)$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='c'; }
6394
    if (preg_match('/\.(cpp)$/i',$tmpfile))                    { $mime='text/plain'; $imgmime='text.png'; $srclang='cpp'; }
6395
    if (preg_match('/\.(h)$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='h'; }
6396
    if (preg_match('/\.(java|jsp)$/i',$tmpfile))               { $mime='text/plain'; $imgmime='text.png'; $srclang='java'; }
6397
    if (preg_match('/\.php([0-9]{1})?$/i',$tmpfile))           { $mime='text/plain'; $imgmime='php.png'; $srclang='php'; }
6398
    if (preg_match('/\.phtml$/i',$tmpfile))                    { $mime='text/plain'; $imgmime='php.png'; $srclang='php'; }
6399
    if (preg_match('/\.(pl|pm)$/i',$tmpfile))                  { $mime='text/plain'; $imgmime='pl.png'; $srclang='perl'; }
6400
    if (preg_match('/\.sql$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='sql'; }
6401
    if (preg_match('/\.js$/i',$tmpfile))                       { $mime='text/x-javascript'; $imgmime='jscript.png'; $srclang='js'; }
6402
    // Open office
6403
    if (preg_match('/\.odp$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.presentation'; $imgmime='ooffice.png'; }
6404
    if (preg_match('/\.ods$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.spreadsheet'; $imgmime='ooffice.png'; }
6405
    if (preg_match('/\.odt$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.text'; $imgmime='ooffice.png'; }
6406
    // MS Office
6407
    if (preg_match('/\.mdb$/i',$tmpfile))					   { $mime='application/msaccess'; $imgmime='mdb.png'; }
6408
    if (preg_match('/\.doc(x|m)?$/i',$tmpfile))				   { $mime='application/msword'; $imgmime='doc.png'; }
6409
    if (preg_match('/\.dot(x|m)?$/i',$tmpfile))				   { $mime='application/msword'; $imgmime='doc.png'; }
6410
    if (preg_match('/\.xlt(x)?$/i',$tmpfile))				   { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; }
6411
    if (preg_match('/\.xla(m)?$/i',$tmpfile))				   { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; }
6412
    if (preg_match('/\.xls$/i',$tmpfile))			           { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; }
6413
    if (preg_match('/\.xls(b|m|x)$/i',$tmpfile))			   { $mime='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; $imgmime='xls.png'; }
6414
    if (preg_match('/\.pps(m|x)?$/i',$tmpfile))				   { $mime='application/vnd.ms-powerpoint'; $imgmime='ppt.png'; }
6415
    if (preg_match('/\.ppt(m|x)?$/i',$tmpfile))				   { $mime='application/x-mspowerpoint'; $imgmime='ppt.png'; }
6416
    // Other
6417
    if (preg_match('/\.pdf$/i',$tmpfile))                      { $mime='application/pdf'; $imgmime='pdf.png'; }
6418
    // Scripts
6419
    if (preg_match('/\.bat$/i',$tmpfile))                      { $mime='text/x-bat'; $imgmime='script.png'; $srclang='dos'; }
6420
    if (preg_match('/\.sh$/i',$tmpfile))                       { $mime='text/x-sh'; $imgmime='script.png'; $srclang='bash'; }
6421
    if (preg_match('/\.ksh$/i',$tmpfile))                      { $mime='text/x-ksh'; $imgmime='script.png'; $srclang='bash'; }
6422
    if (preg_match('/\.bash$/i',$tmpfile))                     { $mime='text/x-bash'; $imgmime='script.png'; $srclang='bash'; }
6423
    // Images
6424
    if (preg_match('/\.ico$/i',$tmpfile))                      { $mime='image/x-icon'; $imgmime='image.png'; }
6425
    if (preg_match('/\.(jpg|jpeg)$/i',$tmpfile))			   { $mime='image/jpeg'; $imgmime='image.png'; }
6426
    if (preg_match('/\.png$/i',$tmpfile))					   { $mime='image/png'; $imgmime='image.png'; }
6427
    if (preg_match('/\.gif$/i',$tmpfile))					   { $mime='image/gif'; $imgmime='image.png'; }
6428
    if (preg_match('/\.bmp$/i',$tmpfile))					   { $mime='image/bmp'; $imgmime='image.png'; }
6429
    if (preg_match('/\.(tif|tiff)$/i',$tmpfile))			   { $mime='image/tiff'; $imgmime='image.png'; }
6430
    // Calendar
6431
    if (preg_match('/\.vcs$/i',$tmpfile))					   { $mime='text/calendar'; $imgmime='other.png'; }
6432
    if (preg_match('/\.ics$/i',$tmpfile))					   { $mime='text/calendar'; $imgmime='other.png'; }
6433
    // Other
6434
    if (preg_match('/\.torrent$/i',$tmpfile))				   { $mime='application/x-bittorrent'; $imgmime='other.png'; }
6435
    // Audio
6436
    if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i',$tmpfile)) { $mime='audio'; $imgmime='audio.png'; }
6437
    // Video
6438
    if (preg_match('/\.ogv$/i',$tmpfile))                      { $mime='video/ogg'; $imgmime='video.png'; }
6439
    if (preg_match('/\.webm$/i',$tmpfile))                     { $mime='video/webm'; $imgmime='video.png'; }
6440
    if (preg_match('/\.avi$/i',$tmpfile))                      { $mime='video/x-msvideo'; $imgmime='video.png'; }
6441
    if (preg_match('/\.divx$/i',$tmpfile))                     { $mime='video/divx'; $imgmime='video.png'; }
6442
    if (preg_match('/\.xvid$/i',$tmpfile))                     { $mime='video/xvid'; $imgmime='video.png'; }
6443
    if (preg_match('/\.(wmv|mpg|mpeg)$/i',$tmpfile))           { $mime='video'; $imgmime='video.png'; }
6444
    // Archive
6445
    if (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh)$/i',$tmpfile))   { $mime='archive'; $imgmime='archive.png'; }    // application/xxx where zzz is zip, ...
6446
    // Exe
6447
    if (preg_match('/\.(exe|com)$/i',$tmpfile))                { $mime='application/octet-stream'; $imgmime='other.png'; }
6448
    // Lib
6449
    if (preg_match('/\.(dll|lib|o|so|a)$/i',$tmpfile))         { $mime='library'; $imgmime='library.png'; }
6450
    // Err
6451
    if (preg_match('/\.err$/i',$tmpfile))                      { $mime='error'; $imgmime='error.png'; }
6452
6453
    // Return string
6454
    if ($mode == 1)
6455
    {
6456
        $tmp=explode('/',$mime);
6457
        return (! empty($tmp[1])?$tmp[1]:$tmp[0]);
6458
    }
6459
    if ($mode == 2)
6460
    {
6461
        return $imgmime;
6462
    }
6463
    if ($mode == 3)
6464
    {
6465
        return $srclang;
6466
    }
6467
6468
    return $mime;
6469
}
6470
6471
/**
6472
 * Return value from dictionary
6473
 *
6474
 * @param string	$tablename		name of dictionary
6475
 * @param string	$field			the value to return
6476
 * @param int		$id				id of line
6477
 * @param bool		$checkentity	add filter on entity
6478
 * @param string	$rowidfield		name of the column rowid
6479
 */
6480
function getDictvalue($tablename, $field, $id, $checkentity=false, $rowidfield='rowid')
6481
{
6482
	global $dictvalues,$db,$langs;
6483
6484
	if (!isset($dictvalues[$tablename]))
6485
	{
6486
		$dictvalues[$tablename] = array();
6487
		$sql = 'SELECT * FROM '.$tablename.' WHERE 1';
6488
		if ($checkentity) $sql.= ' entity IN (0,'.getEntity('').')';
6489
6490
		$resql = $db->query($sql);
6491
		if ($resql)
6492
		{
6493
			while ($obj = $db->fetch_object($resql))
6494
			{
6495
				$dictvalues[$tablename][$obj->{$rowidfield}] = $obj;
6496
			}
6497
		}
6498
		else
6499
		{
6500
			dol_print_error($db);
6501
		}
6502
	}
6503
6504
	if (!empty($dictvalues[$tablename][$id])) return $dictvalues[$tablename][$id]->{$field}; // Found
6505
	else // Not found
6506
	{
6507
		if ($id > 0) return $id;
6508
		return '';
6509
	}
6510
}