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