Completed
Branch develop (348e00)
by
unknown
24:02
created

functions.lib.php ➔ complete_head_from_modules()   D

Complexity

Conditions 28
Paths 6

Size

Total Lines 81
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 28
eloc 45
nc 6
nop 7
dl 0
loc 81
rs 4.9163
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/* Copyright (C) 2000-2007 Rodolphe Quiedeville <[email protected]>
3
 * Copyright (C) 2003      Jean-Louis Bergamo   <[email protected]>
4
 * Copyright (C) 2004-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-2012 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      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 ('actioncomm', ...)
106
 * 	@param	int		$shared		0=Return id of entity, 1=Return id entity + shared entities
107
 * 	@return	mixed				Entity id(s) to use
108
 */
109
function getEntity($element=false, $shared=0)
110
{
111
	global $conf, $mc;
112
113
	if (is_object($mc))
114
	{
115
		return $mc->getEntity($element, $shared);
116
	}
117
	else
118
	{
119
		$out='';
120
		$addzero = array('user', 'usergroup');
121
		if (in_array($element, $addzero)) $out.= '0,';
122
		$out.= $conf->entity;
123
		return $out;
124
	}
125
}
126
127
/**
128
 * Return information about user browser
129
 *
130
 * Returns array with the following format:
131
 * array(
132
 *  'browsername' => Browser name (firefox|chrome|iceweasel|epiphany|safari|opera|ie|unknown)
133
 *  'browserversion' => Browser version. Empty if unknown
134
 *  'browseros' => Set with mobile OS (android|blackberry|ios|palm|symbian|webos|maemo|windows|unknown)
135
 *  'layout' => (tablet|phone|classic)
136
 *  'phone' => empty if not mobile, (android|blackberry|ios|palm|unknown) if mobile
137
 *  'tablet' => true/false
138
 * )
139
 *
140
 * @param string $user_agent Content of $_SERVER["HTTP_USER_AGENT"] variable
141
 * @return	array Check function documentation
142
 */
143
function getBrowserInfo($user_agent)
144
{
145
	include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
146
147
	$name='unknown';
148
	$version='';
149
	$os='unknown';
150
	$phone = '';
151
152
	$detectmobile = new Mobile_Detect(null, $user_agent);
153
	$tablet = $detectmobile->isTablet();
154
155
	if ($detectmobile->isMobile()) {
156
157
		$phone = 'unknown';
158
159
		// If phone/smartphone, we set phone os name.
160
		if ($detectmobile->is('AndroidOS')) {
161
			$os = $phone = 'android';
162
		} elseif ($detectmobile->is('BlackBerryOS')) {
163
			$os = $phone = 'blackberry';
164
		} elseif ($detectmobile->is('iOS')) {
165
			$os = 'ios';
166
			$phone = 'iphone';
167
		} elseif ($detectmobile->is('PalmOS')) {
168
			$os = $phone = 'palm';
169
		} elseif ($detectmobile->is('SymbianOS')) {
170
			$os = 'symbian';
171
		} elseif ($detectmobile->is('webOS')) {
172
			$os = 'webos';
173
		} elseif ($detectmobile->is('MaemoOS')) {
174
			$os = 'maemo';
175
		} elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
176
			$os = 'windows';
177
		}
178
	}
179
180
	// OS
181
	if (preg_match('/linux/i', $user_agent))	{ $os='linux'; }
182
	elseif (preg_match('/macintosh/i', $user_agent))	{ $os='macintosh'; }
183
184
	// Name
185
	if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg))      { $name='firefox';   $version=$reg[2]; }
186
	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
187
	elseif (preg_match('/chrome/i', $user_agent, $reg))                   { $name='chrome'; }
188
	elseif (preg_match('/iceweasel/i', $user_agent))                      { $name='iceweasel'; }
189
	elseif (preg_match('/epiphany/i', $user_agent))                       { $name='epiphany';  }
190
	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.
191
	elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg))    { $name='opera';     $version=$reg[2]; }
192
	elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];\srv:([0-9]+\.[0-9]+))/i', $user_agent, $reg))  { $name='ie'; $version=end($reg); }    // MS products at end
193
	elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) { $name='lynxlinks'; $version=$reg[4]; }
194
	
195
	if ($tablet) {
196
		$layout = 'tablet';
197
	} elseif ($phone) {
198
		$layout = 'phone';
199
	} else {
200
		$layout = 'classic';
201
	}
202
203
	return array(
204
		'browsername' => $name,
205
		'browserversion' => $version,
206
		'browseros' => $os,
207
		'layout' => $layout,
208
		'phone' => $phone,
209
		'tablet' => $tablet
210
	);
211
}
212
213
/**
214
 *  Function called at end of web php process
215
 *
216
 *  @return	void
217
 */
218
function dol_shutdown()
219
{
220
	global $conf,$user,$langs,$db;
221
	$disconnectdone=false; $depth=0;
222
	if (is_object($db) && ! empty($db->connected)) { $depth=$db->transaction_opened; $disconnectdone=$db->close(); }
223
	dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth)?' (Warn: db disconnection forced, transaction depth was '.$depth.')':''), (($disconnectdone && $depth)?LOG_WARNING:LOG_INFO));
224
}
225
226
227
/**
228
 *  Return value of a param into GET or POST supervariable
229
 *
230
 *  @param	string	$paramname   Name of parameter to found
231
 *  @param	string	$check	     Type of check (''=no check,  'int'=check it's numeric, 'alpha'=check it's text and sign, 'aZ'=check it's a-z only, 'array'=check it's array, 'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string), 'day', 'month', 'year', 'custom'= custom filter specify $filter and $options)
232
 *  @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)
233
 *  @param  int     $filter      Filter to apply when $check is set to custom. (See http://php.net/manual/en/filter.filters.php for détails)
234
 *  @param  mixed   $options     Options to pass to filter_var when $check is set to custom
235
 *  @return string|string[]      Value found (string or array), or '' if check fails
236
 */
237
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...
238
{
239
	if (empty($method)) $out = isset($_GET[$paramname])?$_GET[$paramname]:(isset($_POST[$paramname])?$_POST[$paramname]:'');
240
	elseif ($method==1) $out = isset($_GET[$paramname])?$_GET[$paramname]:'';
241
	elseif ($method==2) $out = isset($_POST[$paramname])?$_POST[$paramname]:'';
242
	elseif ($method==3) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:'');
243
	elseif ($method==4) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:(isset($_COOKIE[$paramname])?$_COOKIE[$paramname]:''));
244
	else return 'BadThirdParameterForGETPOST';
245
246
	if (! empty($check))
247
	{
248
	    if (! is_array($out) && preg_match('/^__([a-z0-9]+)__$/i', $out, $reg))
249
	    {
250
	        if ($reg[1] == 'DAY')
251
	        {
252
    	        $tmp=dol_getdate(dol_now(), true);
253
    	        $out = $tmp['mday'];
254
	        }
255
	        elseif ($reg[1] == 'MONTH')
256
	        {
257
    	        $tmp=dol_getdate(dol_now(), true);
258
    	        $out = $tmp['mon'];
259
	        }	         
260
	        elseif ($reg[1] == 'YEAR')
261
	        {
262
	           $tmp=dol_getdate(dol_now(), true);
263
	           $out = $tmp['year'];
264
	        }
265
	    }
266
	     
267
	    switch ($check)
268
	    {
269
	        case 'int':
270
	            if (! is_numeric($out)) { $out=''; }
271
	            break;
272
	        case 'alpha':
273
	            $out=trim($out);
274
	            // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
275
	            // '../' is dangerous because it allows dir transversals
276
	            if (preg_match('/"/',$out)) $out='';
277
	            else if (preg_match('/\.\.\//',$out)) $out='';
278
	            break;
279
	        case 'san_alpha':
280
	            $out=filter_var($out,FILTER_SANITIZE_STRING);
281
	            break;
282
	        case 'aZ':
283
	            $out=trim($out);
284
	            if (preg_match('/[^a-z]+/i',$out)) $out='';
285
	            break;
286
	        case 'aZ09':
287
	            $out=trim($out);
288
	            if (preg_match('/[^a-z0-9]+/i',$out)) $out='';
289
	            break;
290
	        case 'array':
291
	            if (! is_array($out) || empty($out)) $out=array();
292
	            break;
293
			case 'nohtml':
294
				$out=dol_string_nohtmltag($out);
295
				break;
296
	        case 'custom':
297
	            if (empty($filter)) return 'BadFourthParameterForGETPOST';
298
	            $out=filter_var($out, $filter, $options);
299
	            break;
300
	    }
301
	}
302
303
	return $out;
304
}
305
306
307
/**
308
 *  Return a prefix to use for this Dolibarr instance for session or cookie names.
309
 *  This prefix is unique for instance and avoid conflict between multi-instances,
310
 *  even when having two instances with one root dir or two instances in virtual servers
311
 *
312
 *  @return	string      		A calculated prefix
313
 */
314
function dol_getprefix()
315
{
316
	if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"]))
317
	{
318
		return dol_hash($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
319
		// Use this for a "clear" cookie name
320
		//return dol_sanitizeFileName($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
321
	}
322
	else return dol_hash(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
323
}
324
325
/**
326
 *	Make an include_once using default root and alternate root if it fails.
327
 *  To link to a core file, use include(DOL_DOCUMENT_ROOT.'/pathtofile')
328
 *  To link to a module file from a module file, use include './mymodulefile';
329
 *  To link to a module file from a core file, then this function can be used (call by hook / trigger / speciales pages)
330
 *
331
 * 	@param	string	$relpath	Relative path to file (Ie: mydir/myfile, ../myfile, ...)
332
 * 	@param	string	$classname	Class name (deprecated)
333
 *  @return bool                True if load is a success, False if it fails
334
 */
335
function dol_include_once($relpath, $classname='')
336
{
337
	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']
338
339
	$fullpath = dol_buildpath($relpath);
340
341
	if (!file_exists($fullpath)) {
342
		dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_ERR);
343
		return false;
344
	}
345
346
	if (! empty($classname) && ! class_exists($classname)) {
347
		return include $fullpath;
348
	} else {
349
		return include_once $fullpath;
350
	}
351
}
352
353
354
/**
355
 *	Return path of url or filesystem. Return alternate root if exists
356
 *
357
 * 	@param	string	$path		Relative path to file (if mode=0) or relative url (if mode=1). Ie: mydir/myfile, ../myfile
358
 *  @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)
359
 *  @return string				Full filesystem path (if mode=0), Full url path (if mode=1)
360
 */
361
function dol_buildpath($path, $type=0)
362
{
363
	global $conf;
364
365
	$path=preg_replace('/^\//','',$path);
366
367
	if (empty($type))	// For a filesystem path
368
	{
369
		$res = DOL_DOCUMENT_ROOT.'/'.$path;	// Standard value
370
		foreach ($conf->file->dol_document_root as $key => $dirroot)	// ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
371
		{
372
			if ($key == 'main') continue;
373
			if (file_exists($dirroot.'/'.$path))
374
			{
375
				$res=$dirroot.'/'.$path;
376
				break;
377
			}
378
		}
379
	}
380
	else				// For an url path
381
	{
382
		// We try to get local path of file on filesystem from url
383
		// Note that trying to know if a file on disk exist by forging path on disk from url
384
		// works only for some web server and some setup. This is bugged when
385
		// using proxy, rewriting, virtual path, etc...
386
		$res='';
387
		if ($type == 1) $res = DOL_URL_ROOT.'/'.$path;			// Standard value
388
		if ($type == 2) $res = DOL_MAIN_URL_ROOT.'/'.$path;		// Standard value
389
		if ($type == 3) $res = DOL_URL_ROOT.'/'.$path;
390
		
391
		foreach ($conf->file->dol_document_root as $key => $dirroot)	// ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
392
		{
393
			if ($key == 'main') 
394
			{
395
			    if ($type == 3)
396
			    {
397
			        global $dolibarr_main_url_root;
398
			        	
399
			        // Define $urlwithroot
400
			        $urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
401
			        $urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
402
			        //$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
403
404
			        $res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':$urlwithroot).'/'.$path;     // Test on start with http is for old conf syntax
405
			    }
406
			    continue;
407
			}
408
			preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i',$path,$regs);    // Take part before '?'
409
			if (! empty($regs[1]))
410
			{
411
				//print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
412
				if (file_exists($dirroot.'/'.$regs[1]))
413
				{
414
					if ($type == 1)
415
					{
416
						$res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
417
					}
418
					if ($type == 2)
419
					{
420
					    $res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
421
					}
422
					if ($type == 3)
423
					{
424
					    global $dolibarr_main_url_root;
425
					    
426
					    // Define $urlwithroot
427
					    $urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
428
					    $urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
429
					    //$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
430
					    					
431
					    $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
432
					}
433
					break;
434
				}
435
			}
436
		}
437
	}
438
439
	return $res;
440
}
441
442
/**
443
 *	Create a clone of instance of object (new instance with same properties)
444
 * 	This function works for both PHP4 and PHP5
445
 *
446
 * 	@param	object	$object		Object to clone
447
 *	@return object				Object clone
448
 *  @deprecated Dolibarr no longer supports PHP4, use PHP5 native clone construct
449
 *  @see https://php.net/manual/language.oop5.cloning.php
450
 */
451
function dol_clone($object)
452
{
453
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
454
455
	$myclone = clone $object;
456
	return $myclone;
457
}
458
459
/**
460
 *	Optimize a size for some browsers (phone, smarphone, ...)
461
 *
462
 * 	@param	int		$size		Size we want
463
 * 	@param	string	$type		Type of optimizing:
464
 * 								'' = function used to define a size for truncation
465
 * 								'width' = function is used to define a width
466
 *	@return int					New size after optimizing
467
 */
468
function dol_size($size,$type='')
469
{
470
	global $conf;
471
	if (empty($conf->dol_optimize_smallscreen)) return $size;
472
	if ($type == 'width' && $size > 250) return 250;
473
	else return 10;
474
}
475
476
477
/**
478
 *	Clean a string to use it as a file name
479
 *
480
 *	@param	string	$str            String to clean
481
 * 	@param	string	$newstr			String to replace bad chars with
482
 *  @param	int	    $unaccent		1=Remove also accent (default), 0 do not remove them
483
 *	@return string          		String cleaned (a-zA-Z_)
484
 *
485
 * 	@see        	dol_string_nospecial, dol_string_unaccent, dol_sanitizePathName
486
 */
487
function dol_sanitizeFileName($str,$newstr='_',$unaccent=1)
488
{
489
	$filesystem_forbidden_chars = array('<','>',':','/','\\','?','*','|','"','°');
490
	return dol_string_nospecial($unaccent?dol_string_unaccent($str):$str, $newstr, $filesystem_forbidden_chars);
491
}
492
493
/**
494
 *	Clean a string to use it as a path name
495
 *
496
 *	@param	string	$str            String to clean
497
 * 	@param	string	$newstr			String to replace bad chars with
498
 *  @param	int	    $unaccent		1=Remove also accent (default), 0 do not remove them
499
 *	@return string          		String cleaned (a-zA-Z_)
500
 *
501
 * 	@see        	dol_string_nospecial, dol_string_unaccent, dol_sanitizeFileName
502
 */
503
function dol_sanitizePathName($str,$newstr='_',$unaccent=1)
504
{
505
    $filesystem_forbidden_chars = array('<','>','?','*','|','"','°');
506
    return dol_string_nospecial($unaccent?dol_string_unaccent($str):$str, $newstr, $filesystem_forbidden_chars);
507
}
508
509
/**
510
 *	Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName
511
 *
512
 *	@param	string	$str			String to clean
513
 *	@return string   	       		Cleaned string
514
 *
515
 * 	@see    		dol_sanitizeFilename, dol_string_nospecial
516
 */
517
function dol_string_unaccent($str)
518
{
519
	if (utf8_check($str))
520
	{
521
		// See http://www.utf8-chartable.de/
522
		$string = rawurlencode($str);
523
		$replacements = array(
524
		'%C3%80' => 'A','%C3%81' => 'A','%C3%82' => 'A','%C3%83' => 'A','%C3%84' => 'A','%C3%85' => 'A',
525
		'%C3%88' => 'E','%C3%89' => 'E','%C3%8A' => 'E','%C3%8B' => 'E',
526
		'%C3%8C' => 'I','%C3%8D' => 'I','%C3%8E' => 'I','%C3%8F' => 'I',
527
		'%C3%92' => 'O','%C3%93' => 'O','%C3%94' => 'O','%C3%95' => 'O','%C3%96' => 'O',
528
		'%C3%99' => 'U','%C3%9A' => 'U','%C3%9B' => 'U','%C3%9C' => 'U',
529
		'%C3%A0' => 'a','%C3%A1' => 'a','%C3%A2' => 'a','%C3%A3' => 'a','%C3%A4' => 'a','%C3%A5' => 'a',
530
		'%C3%A7' => 'c',
531
		'%C3%A8' => 'e','%C3%A9' => 'e','%C3%AA' => 'e','%C3%AB' => 'e',
532
		'%C3%AC' => 'i','%C3%AD' => 'i','%C3%AE' => 'i','%C3%AF' => 'i',
533
		'%C3%B1' => 'n',
534
		'%C3%B2' => 'o','%C3%B3' => 'o','%C3%B4' => 'o','%C3%B5' => 'o','%C3%B6' => 'o',
535
		'%C3%B9' => 'u','%C3%BA' => 'u','%C3%BB' => 'u','%C3%BC' => 'u',
536
		'%C3%BF' => 'y'
537
		);
538
		$string=strtr($string, $replacements);
539
		return rawurldecode($string);
540
	}
541
	else
542
	{
543
		// See http://www.ascii-code.com/
544
		$string = strtr(
545
			$str,
546
			"\xC0\xC1\xC2\xC3\xC4\xC5\xC7
547
			\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
548
			\xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
549
			\xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
550
			\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
551
			\xF9\xFA\xFB\xFC\xFD\xFF",
552
			"AAAAAAC
553
			EEEEIIIIDN
554
			OOOOOUUUY
555
			aaaaaaceeee
556
			iiiidnooooo
557
			uuuuyy"
558
		);
559
		$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"));
560
		return $string;
561
	}
562
}
563
564
/**
565
 *	Clean a string from all punctuation characters to use it as a ref or login
566
 *
567
 *	@param	string	$str            	String to clean
568
 * 	@param	string	$newstr				String to replace forbidden chars with
569
 *  @param  array	$badcharstoreplace  List of forbidden characters
570
 * 	@return string          			Cleaned string
571
 *
572
 * 	@see    		dol_sanitizeFilename, dol_string_unaccent
573
 */
574
function dol_string_nospecial($str,$newstr='_',$badcharstoreplace='')
575
{
576
	$forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",",",";","=");
577
	$forbidden_chars_to_remove=array();
578
	if (is_array($badcharstoreplace)) $forbidden_chars_to_replace=$badcharstoreplace;
579
	//$forbidden_chars_to_remove=array("(",")");
580
581
	return str_replace($forbidden_chars_to_replace,$newstr,str_replace($forbidden_chars_to_remove,"",$str));
582
}
583
584
585
/**
586
 * Encode string for xml usage
587
 *
588
 * @param 	string	$string		String to encode
589
 * @return	string				String encoded
590
 */
591
function dolEscapeXML($string)
592
{
593
	return strtr($string, array('\''=>'&apos;','"'=>'&quot;','&'=>'&amp;','<'=>'&lt;','>'=>'&gt;'));
594
}
595
596
/**
597
 *  Returns text escaped for inclusion into javascript code
598
 *
599
 *  @param      string		$stringtoescape		String to escape
600
 *  @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 \
601
 *  @param		int		$noescapebackslashn	0=Escape also \n. 1=Do not escape \n.
602
 *  @return     string     		 				Escaped string. Both ' and " are escaped into ' if they are escaped.
603
 */
604
function dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
605
{
606
	// escape quotes and backslashes, newlines, etc.
607
	$substitjs=array("&#039;"=>"\\'","\r"=>'\\r');
608
	//$substitjs['</']='<\/';	// We removed this. Should be useless.
609
	if (empty($noescapebackslashn)) { $substitjs["\n"]='\\n'; $substitjs['\\']='\\\\'; }
610
	if (empty($mode)) { $substitjs["'"]="\\'"; $substitjs['"']="\\'"; }
611
	else if ($mode == 1) $substitjs["'"]="\\'";
612
	else if ($mode == 2) { $substitjs['"']='\\"'; }
613
	else if ($mode == 3) { $substitjs["'"]="\\'"; $substitjs['"']="\\\""; }
614
	return strtr($stringtoescape, $substitjs);
615
}
616
617
618
/**
619
 *  Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
620
 *
621
 *  @param      string		$stringtoescape		String to escape
622
 *  @param		int			$keepb				Do not clean b tags
623
 *  @return     string     				 		Escaped string
624
 *
625
 *  @see		dol_string_nohtmltag
626
 */
627
function dol_escape_htmltag($stringtoescape,$keepb=0)
628
{
629
	// escape quotes and backslashes, newlines, etc.
630
	$tmp=dol_html_entity_decode($stringtoescape,ENT_COMPAT,'UTF-8');
631
	if ($keepb) $tmp=strtr($tmp, array("\r"=>'\\r',"\n"=>'\\n'));
632
	else $tmp=strtr($tmp, array("\r"=>'\\r',"\n"=>'\\n',"<b>"=>'','</b>'=>''));
633
	return dol_htmlentities($tmp,ENT_COMPAT,'UTF-8');
634
}
635
636
637
/**
638
 * Convert a string to lower. Never use strtolower because it does not works with UTF8 strings.
639
 *
640
 * @param 	string		$utf8_string		String to encode
641
 * @return 	string							String converted
642
 */
643
function dol_strtolower($utf8_string)
644
{
645
	return mb_strtolower($utf8_string, "UTF-8");
646
}
647
648
/**
649
 * Convert a string to upper. Never use strtolower because it does not works with UTF8 strings.
650
 *
651
 * @param 	string		$utf8_string		String to encode
652
 * @return 	string							String converted
653
 */
654
function dol_strtoupper($utf8_string)
655
{
656
	return mb_strtoupper($utf8_string, "UTF-8");
657
}
658
659
660
/**
661
 *	Write log message into outputs. Possible outputs can be:
662
 *	SYSLOG_HANDLERS = ["mod_syslog_file"]  		file name is then defined by SYSLOG_FILE
663
 *	SYSLOG_HANDLERS = ["mod_syslog_syslog"]  	facility is then defined by SYSLOG_FACILITY
664
 *  Warning, syslog functions are bugged on Windows, generating memory protection faults. To solve
665
 *  this, use logging to files instead of syslog (see setup of module).
666
 *  Note: If SYSLOG_FILE_NO_ERROR defined, we never output any error message when writing to log fails.
667
 *  Note: You can get log message into html sources by adding parameter &logtohtml=1 (constant MAIN_LOGTOHTML must be set)
668
 *  This function works only if syslog module is enabled.
669
 * 	This must not use any call to other function calling dol_syslog (avoid infinite loop).
670
 *
671
 * 	@param  string		$message				Line to log. ''=Show nothing
672
 *  @param  int			$level					Log level
673
 *												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
674
 *												On Linux   LOG_ERR=3, LOG_WARNING=4, LOG_INFO=6, LOG_DEBUG=7
675
 *  @param	int			$ident					1=Increase ident of 1, -1=Decrease ident of 1
676
 *  @param	string		$suffixinfilename		When output is a file, append this suffix into default log filename.
677
 *  @param	string		$restricttologhandler	Output log only for this log handler
678
 *  @return	void
679
 */
680
function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename='', $restricttologhandler='')
2 ignored issues
show
Coding Style introduced by
dol_syslog uses the super-global variable $_REQUEST 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
dol_syslog 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...
681
{
682
	global $conf, $user;
683
684
	// If syslog module enabled
685
	if (empty($conf->syslog->enabled)) return;
686
687
	if (! empty($message))
688
	{
689
    	// Test log level
690
    	$logLevels = array(LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG);
691
    	if (!in_array($level, $logLevels, true))
692
    	{
693
    		throw new Exception('Incorrect log level');
694
    	}
695
    	if ($level > $conf->global->SYSLOG_LEVEL) return;
696
    
697
    	// If adding log inside HTML page is required
698
    	if (! empty($_REQUEST['logtohtml']) && (! empty($conf->global->MAIN_ENABLE_LOG_TO_HTML) || ! empty($conf->global->MAIN_LOGTOHTML)))   // MAIN_LOGTOHTML kept for backward compatibility
699
    	{
700
    		$conf->logbuffer[] = dol_print_date(time(),"%Y-%m-%d %H:%M:%S")." ".$message;
701
    	}
702
    
703
    	//TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
704
    	// If enable html log tag enabled and url parameter log defined, we show output log on HTML comments
705
    	if (! empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && ! empty($_GET["log"]))
706
    	{
707
    		print "\n\n<!-- Log start\n";
708
    		print $message."\n";
709
    		print "Log end -->\n";
710
    	}
711
    
712
    	$data = array(
713
    		'message' => $message,
714
    		'script' => (isset($_SERVER['PHP_SELF'])? basename($_SERVER['PHP_SELF'],'.php') : false),
715
    		'level' => $level,
716
    		'user' => ((is_object($user) && $user->id) ? $user->login : false),
717
    		'ip' => false
718
    	);
719
    
720
    	if (! empty($_SERVER["REMOTE_ADDR"])) $data['ip'] = $_SERVER['REMOTE_ADDR'];
721
    	// This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
722
    	else if (! empty($_SERVER['SERVER_ADDR'])) $data['ip'] = $_SERVER['SERVER_ADDR'];
723
    	// 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).
724
    	else if (! empty($_SERVER['COMPUTERNAME'])) $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME'])?'':'@'.$_SERVER['USERNAME']);
725
    	// 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).
726
    	else if (! empty($_SERVER['LOGNAME'])) $data['ip'] = '???@'.$_SERVER['LOGNAME'];
727
    	// Loop on each log handler and send output
728
    	foreach ($conf->loghandlers as $loghandlerinstance)
729
    	{
730
    		if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) continue;
731
    		$loghandlerinstance->export($data,$suffixinfilename);
732
    	}
733
    	unset($data);
734
	}
735
736
	if (! empty($ident))
737
	{
738
		foreach ($conf->loghandlers as $loghandlerinstance)
739
		{
740
			$loghandlerinstance->setIdent($ident);
741
		}
742
	}
743
}
744
745
746
/**
747
 *	Show tab header of a card
748
 *
749
 *	@param	array	$links				Array of tabs. Currently initialized by calling a function xxx_admin_prepare_head
750
 *	@param	string	$active     		Active tab name (document', 'info', 'ldap', ....)
751
 *	@param  string	$title      		Title
752
 *	@param  int		$notab				0=Add tab header, 1=no tab header. If you set this to 1, using dol_fiche_end() to close tab is not required.
753
 * 	@param	string	$picto				Add a picto on tab title
754
 *	@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.
755
 * 	@return	void
756
 */
757
function dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0)
758
{
759
	print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath);
760
}
761
762
/**
763
 *  Show tab header of a card
764
 *
765
 *	@param	array	$links				Array of tabs
766
 *	@param	string	$active     		Active tab name
767
 *	@param  string	$title      		Title
768
 *	@param  int		$notab				0=Add tab header, 1=no tab header. If you set this to 1, using dol_fiche_end() to close tab is not required.
769
 * 	@param	string	$picto				Add a picto on tab title
770
 *	@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.
771
 * 	@return	string
772
 */
773
function dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0)
774
{
775
	global $conf, $langs, $hookmanager;
776
777
	$out="\n".'<div class="tabs" data-role="controlgroup" data-type="horizontal">'."\n";
778
779
	// Show title
780
	$showtitle=1;
781
	if (! empty($conf->dol_optimize_smallscreen)) $showtitle=0;
782
	if (! empty($title) && $showtitle)
783
	{
784
		$limittitle=30;
785
		$out.='<a class="tabTitle">';
786
		if ($picto) $out.=img_picto($title,($pictoisfullpath?'':'object_').$picto,'',$pictoisfullpath).' ';
787
		$out.='<span class="tabTitleText">'.dol_trunc($title,$limittitle).'</span>';
788
		$out.='</a>';
789
	}
790
791
	// Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
792
	$maxkey=-1;
793
	if (is_array($links) && ! empty($links))
794
	{
795
		$keys=array_keys($links);
796
		if (count($keys)) $maxkey=max($keys);
797
	}
798
799
	// Show tabs
800
	$bactive=false;
801
	// if =0 we don't use the feature
802
	$limittoshow=(empty($conf->global->MAIN_MAXTABS_IN_CARD)?99:$conf->global->MAIN_MAXTABS_IN_CARD);
803
	$displaytab=0;
804
	$nbintab=0;
805
    $popuptab=0;
806
	for ($i = 0 ; $i <= $maxkey ; $i++)
807
	{
808
		if ((is_numeric($active) && $i == $active) || (! empty($links[$i][2]) && ! is_numeric($active) && $active == $links[$i][2]))
809
		{
810
			// si l'active est présent dans la box
811
			if ($i >= $limittoshow)
812
				$limittoshow--;
813
		}
814
	}
815
816
	for ($i = 0 ; $i <= $maxkey ; $i++)
817
	{
818
		if ((is_numeric($active) && $i == $active) || (! empty($links[$i][2]) && ! is_numeric($active) && $active == $links[$i][2]))
819
		{
820
			$isactive=true;
821
			$bactive=true;
822
		}
823
		else
824
			$isactive=false;
825
826
		if ($i < $limittoshow || $isactive)
827
		{
828
			$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]).' -->';
829
			if (isset($links[$i][2]) && $links[$i][2] == 'image')
830
			{
831
				if (!empty($links[$i][0]))
832
				{
833
					$out.='<a data-role="button" class="tabimage" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
834
				}
835
				else
836
				{
837
					$out.='<span data-role="button" class="tabspan">'.$links[$i][1].'</span>'."\n";
838
				}
839
			}
840
			else if (! empty($links[$i][1]))
841
			{
842
				//print "x $i $active ".$links[$i][2]." z";
843
				if ($isactive)
844
				{
845
					$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";
846
				}
847
				else
848
				{
849
					$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";
850
				}
851
			}
852
			$out.='</div>';
853
		}
854
		else
855
		{
856
		    // The popup with the other tabs
857
			if (! $popuptab)
858
			{
859
			    $popuptab=1;
860
			    $outmore.='<div class="popuptabset">';
0 ignored issues
show
Bug introduced by
The variable $outmore 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...
861
			}
862
		    $outmore.='<div class="popuptab" style="display:inherit;">';
863
			if (isset($links[$i][2]) && $links[$i][2] == 'image')
864
			{
865
				if (!empty($links[$i][0]))
866
					$outmore.='<a class="tabimage" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
867
				else
868
					$outmore.='<span class="tabspan">'.$links[$i][1].'</span>'."\n";
869
870
			}
871
			else if (! empty($links[$i][1]))
872
				$outmore.='<a'.(! empty($links[$i][2])?' id="'.$links[$i][2].'"':'').' class="inline-block" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
873
874
			$outmore.='</div>';
875
876
			$nbintab++;
877
		}
878
		$displaytab=$i;
879
	}
880
	if ($popuptab) $outmore.='</div>';
881
882
	if ($displaytab > $limittoshow)
883
	{
884
		$tabsname=str_replace("@", "", $picto);
885
		$out.='<div id="moretabs'.$tabsname.'" class="inline-block tabsElem">';
886
		$out.='<a href="#" data-role="button" class="tab moretab inline-block">'.$langs->trans("More").'... ('.$nbintab.')</a>';
887
		$out.='<div id="moretabsList'.$tabsname.'" style="position: absolute; left: -999em;text-align: left;margin:0px;padding:2px">'.$outmore.'</div>';
888
		$out.="</div>\n";
889
890
		$out.="<script>";
891
		$out.="$('#moretabs".$tabsname."').mouseenter( function() { $('#moretabsList".$tabsname."').css('left','auto');});";
892
		$out.="$('#moretabs".$tabsname."').mouseleave( function() { $('#moretabsList".$tabsname."').css('left','-999em');});";
893
		$out.="</script>";
894
	}
895
896
	$out.="</div>\n";
897
898
	if (! $notab) $out.="\n".'<div class="tabBar">'."\n";
899
900
	$parameters=array('tabname' => $active, 'out' => $out);
901
	$reshook=$hookmanager->executeHooks('printTabsHead',$parameters);	// This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
902
	if ($reshook > 0)
903
	{
904
		$out = $hookmanager->resPrint;
905
	}
906
	
907
	return $out;
908
}
909
910
/**
911
 *  Show tab footer of a card
912
 *
913
 *  @param	int		$notab       0=Add tab footer, 1=no tab footer
914
 *  @return	void
915
 */
916
function dol_fiche_end($notab=0)
917
{
918
	print dol_get_fiche_end($notab);
919
}
920
921
/**
922
 *	Return tab footer of a card
923
 *
924
 *	@param  int		$notab		0=Add tab footer, 1=no tab footer
925
 *  @return	string
926
 */
927
function dol_get_fiche_end($notab=0)
928
{
929
	if (! $notab) return "\n</div>\n";
930
	else return '';
931
}
932
933
/**
934
 *  Show tab footer of a card
935
 *
936
 *  @param	object	$object			Object to show
937
 *  @param	string	$paramid   		Name of parameter to use to name the id into the URL next/previous link
938
 *  @param	string	$morehtml  		More html content to output just before the nav bar
939
 *  @param	int		$shownav	  	Show Condition (navigation is shown if value is 1)
940
 *  @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)
941
 *  @param	string	$fieldref   	Nom du champ objet ref (object->ref) a utiliser pour select next et previous
942
 *  @param	string	$morehtmlref  	More html to show after ref
943
 *  @param	string	$moreparam  	More param to add in nav link url.
944
 *	@param	int		$nodbprefix		Do not include DB prefix to forge table name
945
 *	@param	string	$morehtmlleft	More html code to show before ref
946
 *	@param	string	$morehtmlstatus	More html code to show under navigation arrows
947
 *	@param	string	$morehtmlright	More html code to show before navigation arrows
948
 *  @param  int     $onlybanner     Put this to 1, if the card will contains only a banner
949
 *  @return	void
950
 */
951
function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $onlybanner=0, $morehtmlright='')
952
{
953
	global $conf, $form, $user, $langs;
954
955
	$maxvisiblephotos=1;
956
	$showimage=1;
957
	$showbarcode=empty($conf->barcode->enabled)?0:($object->barcode?1:0);
958
	if (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) $showbarcode=0;
959
	$modulepart='unknown';
960
	if ($object->element == 'societe') $modulepart='societe';
961
	if ($object->element == 'contact') $modulepart='contact';
962
	if ($object->element == 'member')  $modulepart='memberphoto';
963
	if ($object->element == 'user')    $modulepart='userphoto';
964
	if ($object->element == 'product') $modulepart='product';
965
	
966
	if ($object->element == 'product')
967
	{
968
	    $width=80; $cssclass='photoref';
969
        $showimage=$object->is_photo_available($conf->product->multidir_output[$object->entity]);
970
	    $maxvisiblephotos=(isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO)?$conf->global->PRODUCT_MAX_VISIBLE_PHOTO:5);
971
		if ($conf->browser->phone) $maxvisiblephotos=1;
972
		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>';
973
        else 
974
        {
975
			if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
976
				$nophoto='';
977
				$morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"></div>';
978
			}
979
			elseif ($conf->browser->layout != 'phone') {    // Show no photo link
980
				$nophoto='/public/theme/common/nophoto.png';
981
				$morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass?' '.$cssclass:'').'" alt="No photo" border="0"'.($width?' width="'.$width.'"':'').($height?' height="'.$height.'"':'').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
0 ignored issues
show
Bug introduced by
The variable $height 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...
982
			}
983
            
984
        }
985
	}
986
	else 
987
	{
988
        if ($showimage) 
989
        {
990
            if ($modulepart != 'unknown') 
991
            {
992
                $phototoshow = $form->showphoto($modulepart,$object,0,0,0,'photoref','small',1,0,$maxvisiblephotos);
993
                if ($phototoshow)
994
                {
995
                    $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">';
996
                    $morehtmlleft.=$phototoshow;
997
                    $morehtmlleft.='</div>';
998
                }
999
            }
1000
            elseif ($conf->browser->layout != 'phone')      // Show no photo link
1001
            {
1002
                $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">';
1003
                if ($object->element == 'action') 
1004
                {
1005
                    $cssclass='photorefcenter';
1006
                    $nophoto=img_picto('', 'title_agenda', '', false, 1);
1007
                    $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.'"':'').($height?' height="'.$height.'"':'').' src="'.$nophoto.'"></div></div>';
0 ignored issues
show
Bug introduced by
The variable $width seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1008
                }
1009
                else
1010
                {
1011
                    $width=14; $cssclass='photorefcenter';
1012
    				$nophoto=img_picto('', 'object_'.$object->picto, '', false, 1);
1013
    				$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.'"':'').($height?' height="'.$height.'"':'').' src="'.$nophoto.'"></div></div>';
1014
                }
1015
                $morehtmlleft.='</div>';
1016
            }
1017
        }
1018
	}
1019
	if ($showbarcode) $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object).'</div>';
1020
	if ($object->element == 'societe' && ! empty($conf->use_javascript_ajax) && $user->rights->societe->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1021
		$morehtmlstatus.=ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
1022
	} 
1023
	elseif ($object->element == 'product')
1024
	{
1025
	    //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
1026
        if (! empty($conf->use_javascript_ajax) && $user->rights->produit->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1027
            $morehtmlstatus.=ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
1028
        } else {
1029
            $morehtmlstatus.=$object->getLibStatut(5,0);
1030
        }
1031
        $morehtmlstatus.=' &nbsp; ';
1032
        //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
1033
	    if (! empty($conf->use_javascript_ajax) && $user->rights->produit->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1034
            $morehtmlstatus.=ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
1035
        } else {
1036
            $morehtmlstatus.=$object->getLibStatut(5,1);
1037
        }
1038
	}
1039
	elseif ($object->element == 'facture' || $object->element == 'invoice' || $object->element == 'invoice_supplier')
1040
	{
1041
	    $tmptxt=$object->getLibStatut(6, $object->totalpaye);
1042
	    if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3) || $conf->browser->layout=='phone') $tmptxt=$object->getLibStatut(5, $object->totalpaye); 
1043
		$morehtmlstatus.=$tmptxt;
1044
	}
1045
	elseif ($object->element == 'facturerec') 
1046
	{
1047
	    $morehtmlstatus.='<!-- No status for recurring invoice -->';
1048
	}
1049
	else { // Generic case
1050
	    $tmptxt=$object->getLibStatut(6);
1051
	    if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3) || $conf->browser->layout=='phone') $tmptxt=$object->getLibStatut(5); 
1052
		$morehtmlstatus.=$tmptxt;
1053
	}
1054
	if (! empty($object->name_alias)) $morehtmlref.='<div class="refidno">'.$object->name_alias.'</div>';      // For thirdparty
1055
	if (! empty($object->label))      $morehtmlref.='<div class="refidno">'.$object->label.'</div>';           // For product
1056
	if ($object->element != 'product') 
1057
	{
1058
    	$morehtmlref.='<div class="refidno">';
1059
    	$morehtmlref.=$object->getBannerAddress('refaddress',$object);
1060
    	$morehtmlref.='</div>';
1061
	}
1062
	if (! empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && in_array($object->element, array('societe', 'contact', 'member', 'product')))
1063
	{
1064
		$morehtmlref.='<div style="clear: both;"></div><div class="refidno">';
1065
		$morehtmlref.=$langs->trans("TechnicalID").': '.$object->id;
1066
		$morehtmlref.='</div>';
1067
	}
1068
	
1069
	print '<div class="'.($onlybanner?'':'arearef ').'heightref valignmiddle" width="100%">';
1070
	print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
1071
	print '</div>';
1072
	print '<div class="underrefbanner clearboth"></div>';
1073
}
1074
1075
/**
1076
 * Show a string with the label tag dedicated to the HTML edit field.
1077
 *
1078
 * @param	string	$langkey		Translation key
1079
 * @param 	string	$fieldkey		Key of the html select field the text refers to
1080
 * @param	int		$fieldrequired	1=Field is mandatory
1081
 * @deprecated Form::editfieldkey
1082
 */
1083
function fieldLabel($langkey, $fieldkey, $fieldrequired=0)
1084
{
1085
	global $conf, $langs;
1086
	$ret='';
1087
	if ($fieldrequired) $ret.='<span class="fieldrequired">';
1088
	if (($conf->dol_use_jmobile != 4)) $ret.='<label for="'.$fieldkey.'">';
1089
	$ret.=$langs->trans($langkey);
1090
	if (($conf->dol_use_jmobile != 4)) $ret.='</label>';
1091
	if ($fieldrequired) $ret.='</span>';
1092
	return $ret;
1093
}
1094
1095
/**
1096
 * Return string to add class property on html element with pair/impair.
1097
 *
1098
 * @param	string	$var			0 or 1
1099
 * @param	string	$moreclass		More class to add
1100
 * @return	string					String to add class onto HTML element
1101
 */
1102
function dol_bc($var,$moreclass='')
1103
{
1104
	global $bc;
1105
	$ret=' '.$bc[$var];
1106
	if ($moreclass) $ret=preg_replace('/class=\"/','class="'.$moreclass.' ',$ret);
1107
	return $ret;
1108
}
1109
1110
/**
1111
 *      Return a formated address (part address/zip/town/state) according to country rules
1112
 *
1113
 *      @param  Object		$object         A company or contact object
1114
 * 	    @param	int			$withcountry	1=Add country into address string
1115
 *      @param	string		$sep			Separator to use to build string
1116
 *      @param	Translate	$outputlangs	Object lang that contains language for text translation.
1117
 *      @return string          			Formated string
1118
 *      @see dol_print_address
1119
 */
1120
function dol_format_address($object,$withcountry=0,$sep="\n",$outputlangs='')
1121
{
1122
	global $conf,$langs;
1123
1124
	$ret='';
1125
	$countriesusingstate=array('AU','CA','US','IN','GB','ES','UK','TR');    // See also MAIN_FORCE_STATE_INTO_ADDRESS
1126
1127
	// Address
1128
	$ret .= $object->address;
1129
	// Zip/Town/State
1130
	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
1131
	{
1132
		$ret .= ($ret ? $sep : '' ).$object->town;
1133
		if ($object->state)
1134
		{
1135
			$ret.=($ret?", ":'').$object->state;
1136
		}
1137
		if ($object->zip) $ret .= ($ret?", ":'').$object->zip;
1138
	}
1139
	else if (in_array($object->country_code,array('GB','UK'))) // UK: title firstname name \n address lines \n town state \n zip \n country
1140
	{
1141
		$ret .= ($ret ? $sep : '' ).$object->town;
1142
		if ($object->state)
1143
		{
1144
			$ret.=($ret?", ":'').$object->state;
1145
		}
1146
		if ($object->zip) $ret .= ($ret ? $sep : '' ).$object->zip;
1147
	}
1148
	else if (in_array($object->country_code,array('ES','TR'))) // ES: title firstname name \n address lines \n zip town \n state \n country
1149
	{
1150
		$ret .= ($ret ? $sep : '' ).$object->zip;
1151
		$ret .= ($object->town?(($object->zip?' ':'').$object->town):'');
1152
		if ($object->state)
1153
		{
1154
			$ret.="\n".$object->state;
1155
		}
1156
	}
1157
	else                                        		// Other: title firstname name \n address lines \n zip town \n country
1158
	{
1159
		$ret .= $object->zip ? (($ret ? $sep : '' ).$object->zip) : '';
1160
		$ret .= ($object->town?(($object->zip?' ':($ret ? $sep : '' )).$object->town):'');
1161
		if ($object->state && in_array($object->country_code,$countriesusingstate))
1162
		{
1163
			$ret.=($ret?", ":'').$object->state;
1164
		}
1165
	}
1166
	if (! is_object($outputlangs)) $outputlangs=$langs;
1167
	if ($withcountry) $ret.=($object->country_code?($ret?$sep:'').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)):'');
1168
1169
	return $ret;
1170
}
1171
1172
1173
1174
/**
1175
 *	Format a string.
1176
 *
1177
 *	@param	string	$fmt		Format of strftime function (http://php.net/manual/fr/function.strftime.php)
1178
 *  @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)
1179
 *  @param	int		$is_gmt		See comment of timestamp parameter
1180
 *	@return	string				A formatted string
1181
 */
1182
function dol_strftime($fmt, $ts=false, $is_gmt=false)
1183
{
1184
	if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
1185
		return ($is_gmt)? @gmstrftime($fmt,$ts): @strftime($fmt,$ts);
1186
	}
1187
	else return 'Error date into a not supported range';
1188
}
1189
1190
/**
1191
 *	Output date in a string format according to outputlangs (or langs if not defined).
1192
 * 	Return charset is always UTF-8, except if encodetoouput is defined. In this case charset is output charset
1193
 *
1194
 *	@param	int			$time			GM Timestamps date
1195
 *	@param	string		$format      	Output date format (tag of strftime function)
1196
 *										"%d %b %Y",
1197
 *										"%d/%m/%Y %H:%M",
1198
 *										"%d/%m/%Y %H:%M:%S",
1199
 *										"day", "daytext", "dayhour", "dayhourldap", "dayhourtext", "dayrfc", "dayhourrfc", "...reduceformat"
1200
 * 	@param	string		$tzoutput		true or 'gmt' => string is for Greenwich location
1201
 * 										false or 'tzserver' => output string is for local PHP server TZ usage
1202
 * 										'tzuser' => output string is for user TZ (current browser TZ with current dst)
1203
 *                                      'tzuserrel' => output string is for user TZ (current browser TZ with dst or not, depending on date position)
1204
 *	@param	Translate	$outputlangs	Object lang that contains language for text translation.
1205
 *  @param  boolean		$encodetooutput false=no convert into output pagecode
1206
 * 	@return string      				Formated date or '' if time is null
1207
 *
1208
 *  @see        dol_mktime, dol_stringtotime, dol_getdate
1209
 */
1210
function dol_print_date($time,$format='',$tzoutput='tzserver',$outputlangs='',$encodetooutput=false)
1211
{
1212
	global $conf,$langs;
1213
1214
	// Clean parameters
1215
	$to_gmt=false;
1216
	$offsettz=$offsetdst=0;
1217
	if ($tzoutput)
1218
	{
1219
		$to_gmt=true;	// For backward compatibility
1220
		if (is_string($tzoutput))
1221
		{
1222
			if ($tzoutput == 'tzserver')
1223
			{
1224
				$to_gmt=false;
1225
				$offsettzstring=@date_default_timezone_get();		// Example 'Europe/Berlin' or 'Indian/Reunion'
1226
				$offsettz=0;
1227
				$offsetdst=0;
1228
			}
1229
			elseif ($tzoutput == 'tzuser')
1230
			{
1231
				$to_gmt=true;
1232
				$offsettzstring=(empty($_SESSION['dol_tz_string'])?'UTC':$_SESSION['dol_tz_string']);	// Example 'Europe/Berlin' or 'Indian/Reunion'
1233
				$offsettz=(empty($_SESSION['dol_tz'])?0:$_SESSION['dol_tz'])*60*60;
1234
				$offsetdst=(empty($_SESSION['dol_dst'])?0:$_SESSION['dol_dst'])*60*60;
1235
			}
1236
		}
1237
	}
1238
	if (! is_object($outputlangs)) $outputlangs=$langs;
1239
	if (! $format) $format='daytextshort';
1240
	$reduceformat=(! empty($conf->dol_optimize_smallscreen) && in_array($format,array('day','dayhour')))?1:0;
1241
	$formatwithoutreduce = preg_replace('/reduceformat/','',$format);
1242
	if ($formatwithoutreduce != $format) { $format = $formatwithoutreduce; $reduceformat=1; }  // so format 'dayreduceformat' is processed like day
1243
    
1244
	// Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
1245
	// TODO Add format daysmallyear and dayhoursmallyear 
1246
	if ($format == 'day')				$format=($outputlangs->trans("FormatDateShort")!="FormatDateShort"?$outputlangs->trans("FormatDateShort"):$conf->format_date_short);
1247
	else if ($format == 'hour')			$format=($outputlangs->trans("FormatHourShort")!="FormatHourShort"?$outputlangs->trans("FormatHourShort"):$conf->format_hour_short);
1248
	else if ($format == 'hourduration')	$format=($outputlangs->trans("FormatHourShortDuration")!="FormatHourShortDuration"?$outputlangs->trans("FormatHourShortDuration"):$conf->format_hour_short_duration);
1249
	else if ($format == 'daytext')			 $format=($outputlangs->trans("FormatDateText")!="FormatDateText"?$outputlangs->trans("FormatDateText"):$conf->format_date_text);
1250
	else if ($format == 'daytextshort')	$format=($outputlangs->trans("FormatDateTextShort")!="FormatDateTextShort"?$outputlangs->trans("FormatDateTextShort"):$conf->format_date_text_short);
1251
	else if ($format == 'dayhour')			 $format=($outputlangs->trans("FormatDateHourShort")!="FormatDateHourShort"?$outputlangs->trans("FormatDateHourShort"):$conf->format_date_hour_short);
1252
	else if ($format == 'dayhoursec')		 $format=($outputlangs->trans("FormatDateHourSecShort")!="FormatDateHourSecShort"?$outputlangs->trans("FormatDateHourSecShort"):$conf->format_date_hour_sec_short);
1253
	else if ($format == 'dayhourtext')		 $format=($outputlangs->trans("FormatDateHourText")!="FormatDateHourText"?$outputlangs->trans("FormatDateHourText"):$conf->format_date_hour_text);
1254
	else if ($format == 'dayhourtextshort') $format=($outputlangs->trans("FormatDateHourTextShort")!="FormatDateHourTextShort"?$outputlangs->trans("FormatDateHourTextShort"):$conf->format_date_hour_text_short);
1255
	// Format not sensitive to language
1256
	else if ($format == 'dayhourlog')		 $format='%Y%m%d%H%M%S';
1257
	else if ($format == 'dayhourldap')		 $format='%Y%m%d%H%M%SZ';
1258
	else if ($format == 'dayhourxcard')	$format='%Y%m%dT%H%M%SZ';
1259
	else if ($format == 'dayxcard')	 	$format='%Y%m%d';
1260
	else if ($format == 'dayrfc')			 $format='%Y-%m-%d';             // DATE_RFC3339
1261
	else if ($format == 'dayhourrfc')		 $format='%Y-%m-%dT%H:%M:%SZ';   // DATETIME RFC3339
1262
	else if ($format == 'standard')		$format='%Y-%m-%d %H:%M:%S';
1263
1264
	if ($reduceformat)
1265
	{
1266
		$format=str_replace('%Y','%y',$format);
1267
		$format=str_replace('yyyy','yy',$format);
1268
	}
1269
1270
	// If date undefined or "", we return ""
1271
	if (dol_strlen($time) == 0) return '';		// $time=0 allowed (it means 01/01/1970 00:00:00)
1272
1273
	// Clean format
1274
	if (preg_match('/%b/i',$format))		// There is some text to translate
1275
	{
1276
		// We inhibate translation to text made by strftime functions. We will use trans instead later.
1277
		$format=str_replace('%b','__b__',$format);
1278
		$format=str_replace('%B','__B__',$format);
1279
	}
1280
	if (preg_match('/%a/i',$format))		// There is some text to translate
1281
	{
1282
		// We inhibate translation to text made by strftime functions. We will use trans instead later.
1283
		$format=str_replace('%a','__a__',$format);
1284
		$format=str_replace('%A','__A__',$format);
1285
	}
1286
1287
	// Analyze date
1288
	if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i',$time,$reg)
1289
	|| 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
1290
	{
1291
		// This part of code should not be used. TODO Remove this.
1292
		dol_syslog("Functions.lib::dol_print_date function call with deprecated value of time in page ".$_SERVER["PHP_SELF"], LOG_WARNING);
1293
		// Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' or 'YYYYMMDDHHMMSS'
1294
		$syear	= (! empty($reg[1]) ? $reg[1] : '');
1295
		$smonth	= (! empty($reg[2]) ? $reg[2] : '');
1296
		$sday	= (! empty($reg[3]) ? $reg[3] : '');
1297
		$shour	= (! empty($reg[4]) ? $reg[4] : '');
1298
		$smin	= (! empty($reg[5]) ? $reg[5] : '');
1299
		$ssec	= (! empty($reg[6]) ? $reg[6] : '');
1300
1301
		$time=dol_mktime($shour,$smin,$ssec,$smonth,$sday,$syear,true);
1302
		$ret=adodb_strftime($format,$time+$offsettz+$offsetdst,$to_gmt);
1303
	}
1304
	else
1305
	{
1306
		// Date is a timestamps
1307
		if ($time < 100000000000)	// Protection against bad date values
1308
		{
1309
			$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.
1310
		}
1311
		else $ret='Bad value '.$time.' for date';
1312
	}
1313
1314
	if (preg_match('/__b__/i',$format))
1315
	{
1316
		// Here ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs.
1317
		$month=adodb_strftime('%m',$time+$offsettz+$offsetdst);					// TODO Remove this
1318
		if ($encodetooutput)
1319
		{
1320
			$monthtext=$outputlangs->transnoentities('Month'.$month);
1321
			$monthtextshort=$outputlangs->transnoentities('MonthShort'.$month);
1322
		}
1323
		else
1324
		{
1325
			$monthtext=$outputlangs->transnoentitiesnoconv('Month'.$month);
1326
			$monthtextshort=$outputlangs->transnoentitiesnoconv('MonthShort'.$month);
1327
		}
1328
		//print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
1329
		$ret=str_replace('__b__',$monthtextshort,$ret);
1330
		$ret=str_replace('__B__',$monthtext,$ret);
1331
		//print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
1332
		//return $ret;
1333
	}
1334
	if (preg_match('/__a__/i',$format))
1335
	{
1336
		$w=adodb_strftime('%w',$time+$offsettz+$offsetdst);						// TODO Remove this
1337
		$dayweek=$outputlangs->transnoentitiesnoconv('Day'.$w);
1338
		$ret=str_replace('__A__',$dayweek,$ret);
1339
		$ret=str_replace('__a__',dol_substr($dayweek,0,3),$ret);
1340
	}
1341
1342
	return $ret;
1343
}
1344
1345
1346
/**
1347
 *	Return an array with locale date info.
1348
 *  PHP getdate is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
1349
 *  WARNING: This function always use PHP server timezone to return locale informations !!!
1350
 *  Usage must be avoid.
1351
 *  FIXME: Replace this with PHP date function and a parameter $gm
1352
 *
1353
 *	@param	int			$timestamp      Timestamp
1354
 *	@param	boolean		$fast           Fast mode
1355
 *	@return	array						Array of informations
1356
 *										If no fast mode:
1357
 *										'seconds' => $secs,
1358
 *										'minutes' => $min,
1359
 *										'hours' => $hour,
1360
 *										'mday' => $day,
1361
 *										'wday' => $dow,		0=sunday, 6=saturday
1362
 *										'mon' => $month,
1363
 *										'year' => $year,
1364
 *										'yday' => floor($secsInYear/$_day_power),
1365
 *										'weekday' => gmdate('l',$_day_power*(3+$dow)),
1366
 *										'month' => gmdate('F',mktime(0,0,0,$month,2,1971)),
1367
 *										If fast mode:
1368
 *										'seconds' => $secs,
1369
 *										'minutes' => $min,
1370
 *										'hours' => $hour,
1371
 *										'mday' => $day,
1372
 *										'mon' => $month,
1373
 *										'year' => $year,
1374
 *										'yday' => floor($secsInYear/$_day_power),
1375
 *										'leap' => $leaf,
1376
 *										'ndays' => $ndays
1377
 * 	@see 								dol_print_date, dol_stringtotime, dol_mktime
1378
 */
1379
function dol_getdate($timestamp,$fast=false)
1380
{
1381
	global $conf;
1382
1383
	$usealternatemethod=false;
1384
	if ($timestamp <= 0) $usealternatemethod=true;				// <= 1970
1385
	if ($timestamp >= 2145913200) $usealternatemethod=true;		// >= 2038
1386
1387
	if ($usealternatemethod)
1388
	{
1389
		$arrayinfo=adodb_getdate($timestamp,$fast);
1390
	}
1391
	else
1392
	{
1393
		$arrayinfo=getdate($timestamp);
1394
	}
1395
1396
	return $arrayinfo;
1397
}
1398
1399
/**
1400
 *	Return a timestamp date built from detailed informations (by default a local PHP server timestamp)
1401
 * 	Replace function mktime not available under Windows if year < 1970
1402
 *	PHP mktime is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
1403
 *
1404
 * 	@param	int			$hour			Hour	(can be -1 for undefined)
1405
 *	@param	int			$minute			Minute	(can be -1 for undefined)
1406
 *	@param	int			$second			Second	(can be -1 for undefined)
1407
 *	@param	int			$month			Month (1 to 12)
1408
 *	@param	int			$day			Day (1 to 31)
1409
 *	@param	int			$year			Year
1410
 *	@param	mixed		$gm				True or 1 or 'gmt'=Input informations are GMT values
1411
 *										False or 0 or 'server' = local to server TZ
1412
 *										'user' = local to user TZ
1413
 *										'tz,TimeZone' = use specified timezone
1414
 *	@param	int			$check			0=No check on parameters (Can use day 32, etc...)
1415
 *	@return	int|string					Date as a timestamp, '' or false if error
1416
 * 	@see 								dol_print_date, dol_stringtotime, dol_getdate
1417
 */
1418
function dol_mktime($hour,$minute,$second,$month,$day,$year,$gm=false,$check=1)
1419
{
1420
	global $conf;
1421
	//print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
1422
1423
	// Clean parameters
1424
	if ($hour   == -1 || empty($hour)) $hour=0;
1425
	if ($minute == -1 || empty($minute)) $minute=0;
1426
	if ($second == -1 || empty($second)) $second=0;
1427
1428
	// Check parameters
1429
	if ($check)
1430
	{
1431
		if (! $month || ! $day)  return '';
1432
		if ($day   > 31) return '';
1433
		if ($month > 12) return '';
1434
		if ($hour  < 0 || $hour   > 24) return '';
1435
		if ($minute< 0 || $minute > 60) return '';
1436
		if ($second< 0 || $second > 60) return '';
1437
	}
1438
1439
	if (method_exists('DateTime','getTimestamp'))
1440
	{
1441
		if (empty($gm) || $gm === 'server')
1442
		{
1443
			$default_timezone=@date_default_timezone_get();		// Example 'Europe/Berlin'
1444
			$localtz = new DateTimeZone($default_timezone);
1445
		}
1446
		else if ($gm === 'user')
1447
		{
1448
			// We use dol_tz_string first because it is more reliable.
1449
			$default_timezone=(empty($_SESSION["dol_tz_string"])?@date_default_timezone_get():$_SESSION["dol_tz_string"]);		// Example 'Europe/Berlin'
1450
			try {
1451
				$localtz = new DateTimeZone($default_timezone);
1452
			}
1453
			catch(Exception $e)
1454
			{
1455
				dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
1456
				$default_timezone=@date_default_timezone_get();
1457
			}
1458
		}
1459
		else if (strrpos($gm, "tz,") !== false)
1460
		{
1461
			$timezone=str_replace("tz,", "", $gm);  // Example 'tz,Europe/Berlin'
1462
			try
1463
			{
1464
				$localtz = new DateTimeZone($timezone);
1465
			}
1466
			catch(Exception $e)
1467
			{
1468
				dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
1469
			}
1470
		}
1471
1472
		if (empty($localtz)) {
1473
			$localtz = new DateTimeZone('UTC');
1474
		}
1475
1476
		$dt = new DateTime(null,$localtz);
1477
		$dt->setDate($year,$month,$day);
1478
		$dt->setTime((int) $hour, (int) $minute, (int) $second);
1479
		$date=$dt->getTimestamp();	// should include daylight saving time
1480
		return $date;
1481
	}
1482
	else
1483
	{
1484
		dol_print_error('','PHP version must be 5.3+');
1485
		return '';
1486
	}
1487
}
1488
1489
1490
/**
1491
 *	Return date for now. In mot cases, we use this function without parameters (that means GMT time).
1492
 *
1493
 * 	@param	string		$mode	'gmt' => we return GMT timestamp,
1494
 * 								'tzserver' => we add the PHP server timezone
1495
 *  							'tzref' => we add the company timezone
1496
 * 								'tzuser' => we add the user timezone
1497
 *	@return int   $date	Timestamp
1498
 */
1499
function dol_now($mode='gmt')
1500
{
1501
	// Note that gmmktime and mktime return same value (GMT) when used without parameters
1502
	//if ($mode == 'gmt') $ret=gmmktime(); // Strict Standards: gmmktime(): You should be using the time() function instead
1503
	if ($mode == 'gmt') $ret=time();	// Time for now at greenwich.
1504
	else if ($mode == 'tzserver')		// Time for now with PHP server timezone added
1505
	{
1506
		require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1507
		$tzsecond=getServerTimeZoneInt('now');    // Contains tz+dayling saving time
1508
		$ret=dol_now('gmt')+($tzsecond*3600);
1509
	}
1510
	/*else if ($mode == 'tzref')				// Time for now with parent company timezone is added
1511
	{
1512
		require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
1513
		$tzsecond=getParentCompanyTimeZoneInt();    // Contains tz+dayling saving time
1514
		$ret=dol_now('gmt')+($tzsecond*3600);
1515
	}*/
1516
	else if ($mode == 'tzuser')				// Time for now with user timezone added
1517
	{
1518
		//print 'eeee'.time().'-'.mktime().'-'.gmmktime();
1519
		$offsettz=(empty($_SESSION['dol_tz'])?0:$_SESSION['dol_tz'])*60*60;
1520
		$offsetdst=(empty($_SESSION['dol_dst'])?0:$_SESSION['dol_dst'])*60*60;
1521
		$ret=dol_now('gmt')+($offsettz+$offsetdst);
1522
	}
1523
	return $ret;
0 ignored issues
show
Bug introduced by
The variable $ret 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...
1524
}
1525
1526
1527
/**
1528
 * Return string with formated size
1529
 *
1530
 * @param	int		$size		Size to print
1531
 * @param	int		$shortvalue	Tell if we want long value to use another unit (Ex: 1.5Kb instead of 1500b)
1532
 * @param	int		$shortunit	Use short value of size unit
1533
 * @return	string				Link
1534
 */
1535
function dol_print_size($size,$shortvalue=0,$shortunit=0)
1536
{
1537
	global $conf,$langs;
1538
	$level=1024;
1539
1540
	if (! empty($conf->dol_optimize_smallscreen)) $shortunit=1;
1541
1542
	// Set value text
1543
	if (empty($shortvalue) || $size < ($level*10))
1544
	{
1545
		$ret=$size;
1546
		$textunitshort=$langs->trans("b");
1547
		$textunitlong=$langs->trans("Bytes");
1548
	}
1549
	else
1550
	{
1551
		$ret=round($size/$level,0);
1552
		$textunitshort=$langs->trans("Kb");
1553
		$textunitlong=$langs->trans("KiloBytes");
1554
	}
1555
	// Use long or short text unit
1556
	if (empty($shortunit)) { $ret.=' '.$textunitlong; }
1557
	else { $ret.=' '.$textunitshort; }
1558
1559
	return $ret;
1560
}
1561
1562
/**
1563
 * Show Url link
1564
 *
1565
 * @param	string		$url		Url to show
1566
 * @param	string		$target		Target for link
1567
 * @param	int			$max		Max number of characters to show
1568
 * @param	int			$withpicto	With picto
1569
 * @return	string					HTML Link
1570
 */
1571
function dol_print_url($url,$target='_blank',$max=32,$withpicto=0)
1572
{
1573
	global $langs;
1574
1575
	if (empty($url)) return '';
1576
1577
	$link='<a href="';
1578
	if (! preg_match('/^http/i',$url)) $link.='http://';
1579
	$link.=$url;
1580
	$link.='"';
1581
	if ($target) $link.=' target="'.$target.'"';
1582
	$link.='>';
1583
	if (! preg_match('/^http/i',$url)) $link.='http://';
1584
	$link.=dol_trunc($url,$max);
1585
	$link.='</a>';
1586
	return '<div class="nospan float" style="margin-right: 10px">'.($withpicto?img_picto($langs->trans("Url"), 'object_globe.png').' ':'').$link.'</div>';
1587
}
1588
1589
/**
1590
 * Show EMail link
1591
 *
1592
 * @param	string		$email			EMail to show (only email, without 'Name of recipient' before)
1593
 * @param 	int			$cid 			Id of contact if known
1594
 * @param 	int			$socid 			Id of third party if known
1595
 * @param 	int			$addlink		0=no link, 1=email has a html email link (+ link to create action if constant AGENDA_ADDACTIONFOREMAIL is on)
1596
 * @param	int			$max			Max number of characters to show
1597
 * @param	int			$showinvalid	Show warning if syntax email is wrong
1598
 * @param	int			$withpicto		Show picto
1599
 * @return	string						HTML Link
1600
 */
1601
function dol_print_email($email,$cid=0,$socid=0,$addlink=0,$max=64,$showinvalid=1,$withpicto=0)
1602
{
1603
	global $conf,$user,$langs;
1604
1605
	$newemail=$email;
1606
1607
	if (empty($email)) return '&nbsp;';
1608
1609
	if (! empty($addlink))
1610
	{
1611
		$newemail='<a style="text-overflow: ellipsis;" href="';
1612
		if (! preg_match('/^mailto:/i',$email)) $newemail.='mailto:';
1613
		$newemail.=$email;
1614
		$newemail.='">';
1615
		$newemail.=dol_trunc($email,$max);
1616
		$newemail.='</a>';
1617
		if ($showinvalid && ! isValidEmail($email))
1618
		{
1619
			$langs->load("errors");
1620
			$newemail.=img_warning($langs->trans("ErrorBadEMail",$email));
1621
		}
1622
1623
		if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
1624
		{
1625
			$type='AC_EMAIL'; $link='';
1626
			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>';
1627
			if ($link) $newemail='<div>'.$newemail.' '.$link.'</div>';
1628
		}
1629
	}
1630
	else
1631
	{
1632
		if ($showinvalid && ! isValidEmail($email))
1633
		{
1634
			$langs->load("errors");
1635
			$newemail.=img_warning($langs->trans("ErrorBadEMail",$email));
1636
		}
1637
	}
1638
	return '<div class="nospan float" style="margin-right: 10px">'.($withpicto?img_picto($langs->trans("EMail"), 'object_email.png').' ':'').$newemail.'</div>';
1639
}
1640
1641
/**
1642
 * Show Skype link
1643
 *
1644
 * @param	string		$skype			Skype to show (only skype, without 'Name of recipient' before)
1645
 * @param	int 		$cid 			Id of contact if known
1646
 * @param	int 		$socid 			Id of third party if known
1647
 * @param	int 		$addlink		0=no link to create action
1648
 * @param	int			$max			Max number of characters to show
1649
 * @return	string						HTML Link
1650
 */
1651
function dol_print_skype($skype,$cid=0,$socid=0,$addlink=0,$max=64)
1652
{
1653
	global $conf,$user,$langs;
1654
1655
	$newskype=$skype;
1656
1657
	if (empty($skype)) return '&nbsp;';
1658
1659
	if (! empty($addlink))
1660
	{
1661
		$newskype =img_picto($langs->trans("Skype"), 'object_skype.png');
1662
		$newskype.= '&nbsp;';
1663
		$newskype.=dol_trunc($skype,$max);
1664
		$newskype.= '&nbsp;';
1665
		$newskype.='<a href="skype:';
1666
		$newskype.=dol_trunc($skype,$max);
1667
		$newskype.='?call" alt="'.$langs->trans("Call").'&nbsp;'.$skype.'" title="'.$langs->trans("Call").'&nbsp;'.$skype.'">';
1668
		$newskype.='<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
1669
		$newskype.='</a>&nbsp;&nbsp;&nbsp;<a href="skype:';
1670
		$newskype.=dol_trunc($skype,$max);
1671
		$newskype.='?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$skype.'" title="'.$langs->trans("Chat").'&nbsp;'.$skype.'">';
1672
		$newskype.='<img src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
1673
		$newskype.='</a>';
1674
1675
		if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
1676
		{
1677
			$type='AC_SKYPE'; $link='';
1678
			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>';
1679
			$newskype='<div class="divskype nowrap">'.$newskype.($link?' '.$link:'').'</div>';
1680
		}
1681
	}
1682
	else
1683
	{
1684
		$langs->load("errors");
1685
		$newskype.=img_warning($langs->trans("ErrorBadSkype",$skype));
1686
	}
1687
	return $newskype;
1688
}
1689
1690
/**
1691
 * 	Format phone numbers according to country
1692
 *
1693
 * 	@param  string  $phone          Phone number to format
1694
 * 	@param  string  $countrycode    Country code to use for formatting
1695
 * 	@param 	int		$cid 		    Id of contact if known
1696
 * 	@param 	int		$socid          Id of third party if known
1697
 * 	@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)
1698
 * 	@param 	string	$separ 		    Separation between numbers for a better visibility example : xx.xx.xx.xx.xx
1699
 *  @param	string  $withpicto      Show picto
1700
 *  @param	string	$titlealt	    Text to show on alt
1701
 *  @param  int     $adddivfloat    Add div float around phone.
1702
 * 	@return string 				    Formated phone number
1703
 */
1704
function dol_print_phone($phone,$countrycode='',$cid=0,$socid=0,$addlink='',$separ="&nbsp;",$withpicto='',$titlealt='',$adddivfloat=0)
1705
{
1706
	global $conf,$user,$langs,$mysoc;
1707
1708
	// Clean phone parameter
1709
	$phone = preg_replace("/[\s.-]/","",trim($phone));
1710
	if (empty($phone)) { return ''; }
1711
	if (empty($countrycode)) $countrycode=$mysoc->country_code;
1712
1713
	// Short format for small screens
1714
	if ($conf->dol_optimize_smallscreen) $separ='';
1715
1716
	$newphone=$phone;
1717
	if (strtoupper($countrycode) == "FR")
1718
	{
1719
		// France
1720
		if (dol_strlen($phone) == 10) {
1721
			$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);
1722
		}
1723
		elseif (dol_strlen($newphone) == 7)
1724
		{
1725
			$newphone=substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,2);
1726
		}
1727
		elseif (dol_strlen($newphone) == 9)
1728
		{
1729
			$newphone=substr($newphone,0,2).$separ.substr($newphone,2,3).$separ.substr($newphone,5,2).$separ.substr($newphone,7,2);
1730
		}
1731
		elseif (dol_strlen($newphone) == 11)
1732
		{
1733
			$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);
1734
		}
1735
		elseif (dol_strlen($newphone) == 12)
1736
		{
1737
			$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);
1738
		}
1739
	}
1740
1741
	if (strtoupper($countrycode) == "CA")
1742
	{
1743
	    if (dol_strlen($phone) == 10) {
1744
	        $newphone=($separ!=''?'(':'').substr($newphone,0,3).($separ!=''?')':'').$separ.substr($newphone,3,3).($separ!=''?'-':'').substr($newphone,6,4);
1745
	    }
1746
	}
1747
	
1748
	if (! empty($addlink))	// Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
1749
	{
1750
		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
1751
		{
1752
			$newphone ='<a href="tel:'.$phone.'"';
1753
			$newphone.='>'.$phone.'</a>';
1754
		}
1755
		else if (! empty($conf->clicktodial->enabled) && $addlink == 'AC_TEL')		// If click to dial, we use click to dial url
1756
		{
1757
			if (empty($user->clicktodial_loaded)) $user->fetch_clicktodial();
1758
1759
			// Define urlmask
1760
			$urlmask='ErrorClickToDialModuleNotConfigured';
1761
			if (! empty($conf->global->CLICKTODIAL_URL)) $urlmask=$conf->global->CLICKTODIAL_URL;
1762
			if (! empty($user->clicktodial_url)) $urlmask=$user->clicktodial_url;
1763
1764
			$clicktodial_poste=(! empty($user->clicktodial_poste)?urlencode($user->clicktodial_poste):'');
1765
			$clicktodial_login=(! empty($user->clicktodial_login)?urlencode($user->clicktodial_login):'');
1766
			$clicktodial_password=(! empty($user->clicktodial_password)?urlencode($user->clicktodial_password):'');
1767
			// This line is for backward compatibility
1768
			$url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
1769
			// Thoose lines are for substitution
1770
			$substitarray=array('__PHONEFROM__'=>$clicktodial_poste,
1771
								'__PHONETO__'=>urlencode($phone),
1772
								'__LOGIN__'=>$clicktodial_login,
1773
								'__PASS__'=>$clicktodial_password);
1774
			$url = make_substitutions($url, $substitarray);
1775
			$newphonesav=$newphone;
1776
			$newphone ='<a href="'.$url.'"';
1777
			if (! empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) $newphone.=' target="_blank"';
1778
			$newphone.='>'.$newphonesav.'</a>';
1779
		}
1780
1781
		//if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
1782
		if (! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
1783
		{
1784
			$type='AC_TEL'; $link='';
1785
			if ($addlink == 'AC_FAX') $type='AC_FAX';
1786
			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>';
1787
			if ($link) $newphone='<div>'.$newphone.' '.$link.'</div>';
1788
		}
1789
	}
1790
1791
	if (empty($titlealt))
1792
	{
1793
		$titlealt=($withpicto=='fax'?$langs->trans("Fax"):$langs->trans("Phone"));
1794
	}
1795
	$rep='';
1796
	if ($adddivfloat) $rep.='<div class="nospan float" style="margin-right: 10px">';
1797
	else $rep.='<span style="margin-right: 10px;">';
1798
	$rep.=($withpicto?img_picto($titlealt, 'object_'.($withpicto=='fax'?'phoning_fax':'phoning').'.png').' ':'').$newphone;
1799
	if ($adddivfloat) $rep.='</div>';
1800
	else $rep.='</span>';
1801
	return $rep;
1802
}
1803
1804
/**
1805
 * 	Return an IP formated to be shown on screen
1806
 *
1807
 * 	@param	string	$ip			IP
1808
 * 	@param	int		$mode		0=return IP + country/flag, 1=return only country/flag, 2=return only IP
1809
 * 	@return string 				Formated IP, with country if GeoIP module is enabled
1810
 */
1811
function dol_print_ip($ip,$mode=0)
1812
{
1813
	global $conf,$langs;
1814
1815
	$ret='';
1816
1817
	if (empty($mode)) $ret.=$ip;
1818
1819
	if (! empty($conf->geoipmaxmind->enabled) && $mode != 2)
1820
	{
1821
		$datafile=$conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
1822
		//$ip='24.24.24.24';
1823
		//$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)
1824
1825
		include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
1826
		$geoip=new DolGeoIP('country',$datafile);
1827
		//print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
1828
		//print "geoip_country_id_by_addr=".geoip_country_id_by_addr($geoip->gi,$ip)."\n";
1829
		$countrycode=$geoip->getCountryCodeFromIP($ip);
1830
		if ($countrycode)	// If success, countrycode is us, fr, ...
1831
		{
1832
			if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png'))
1833
			{
1834
				$ret.=' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"),DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png','',1);
1835
			}
1836
			else $ret.=' ('.$countrycode.')';
1837
		}
1838
	}
1839
1840
	return $ret;
1841
}
1842
1843
/**
1844
 *  Return country code for current user.
1845
 *  If software is used inside a local network, detection may fails (we need a public ip)
1846
 *
1847
 *  @return     string      Country code (fr, es, it, us, ...)
1848
 */
1849
function dol_user_country()
1850
{
1851
	global $conf,$langs,$user;
1852
1853
	//$ret=$user->xxx;
1854
	$ret='';
1855
	if (! empty($conf->geoipmaxmind->enabled))
1856
	{
1857
		$ip=$_SERVER["REMOTE_ADDR"];
1858
		$datafile=$conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
1859
		//$ip='24.24.24.24';
1860
		//$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
1861
		include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
1862
		$geoip=new DolGeoIP('country',$datafile);
1863
		$countrycode=$geoip->getCountryCodeFromIP($ip);
1864
		$ret=$countrycode;
1865
	}
1866
	return $ret;
1867
}
1868
1869
/**
1870
 *  Format address string
1871
 *
1872
 *  @param	string	$address    Address
1873
 *  @param  int		$htmlid     Html ID (for example 'gmap')
1874
 *  @param  int		$mode       thirdparty|contact|member|other
1875
 *  @param  int		$id         Id of object
1876
 *  @param	int		$noprint	No output. Result is the function return
1877
 *  @param  string  $charfornl  Char to use instead of nl2br. '' means we use a standad nl2br.  
1878
 *  @return string|void			Nothing if noprint is 0, formatted address if noprint is 1
1879
 *  @see dol_format_address
1880
 */
1881
function dol_print_address($address, $htmlid, $mode, $id, $noprint=0, $charfornl='')
1882
{
1883
	global $conf, $user, $langs, $hookmanager;
1884
1885
	$out = '';
1886
1887
	if ($address)
1888
	{
1889
        if ($hookmanager) {
1890
            $parameters = array('element' => $mode, 'id' => $id);
1891
            $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
1892
            $out.=$hookmanager->resPrint;
1893
        }
1894
        if (empty($reshook))
1895
        {
1896
            if (empty($charfornl)) $out.=nl2br($address);
1897
            else $out.=preg_replace('/[\r\n]+/', $charfornl, $address);
1898
            
1899
            $showgmap=$showomap=0;
1900
1901
            // TODO Add a hook here
1902
            if (($mode=='thirdparty' || $mode =='societe') && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS)) $showgmap=1;
1903
            if ($mode=='contact' && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) $showgmap=1;
1904
            if ($mode=='member' && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) $showgmap=1;
1905
            if (($mode=='thirdparty' || $mode =='societe') && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) $showomap=1;
1906
            if ($mode=='contact' && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) $showomap=1;
1907
            if ($mode=='member' && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) $showomap=1;
1908
1909
            if ($showgmap)
1910
            {
1911
                $url=dol_buildpath('/google/gmaps.php?mode='.$mode.'&id='.$id,1);
1912
                $out.=' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" border="0" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
1913
            }
1914
            if ($showomap)
1915
            {
1916
                $url=dol_buildpath('/openstreetmap/maps.php?mode='.$mode.'&id='.$id,1);
1917
                $out.=' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" border="0" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
1918
            }
1919
        }
1920
	}
1921
	if ($noprint) return $out;
1922
	else print $out;
1923
}
1924
1925
1926
/**
1927
 *	Return true if email syntax is ok
1928
 *
1929
 *	@param	    string		$address    			email (Ex: "[email protected]", "John Do <[email protected]>")
1930
 *  @param		int			$acceptsupervisorkey	If 1, the special string '__SUPERVISOREMAIL__' is also accepted as valid
1931
 *	@return     boolean     						true if email syntax is OK, false if KO or empty string
1932
 */
1933
function isValidEmail($address, $acceptsupervisorkey=0)
1934
{
1935
	if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') return true;
1936
	if (filter_var($address, FILTER_VALIDATE_EMAIL)) return true;
1937
1938
	return false;
1939
}
1940
1941
/**
1942
 *  Return true if phone number syntax is ok
1943
 *  TODO Decide what to do with this
1944
 *
1945
 *  @param	string		$phone		phone (Ex: "0601010101")
1946
 *  @return boolean     			true if phone syntax is OK, false if KO or empty string
1947
 */
1948
function isValidPhone($phone)
1949
{
1950
	return true;
1951
}
1952
1953
1954
/**
1955
 * Make a strlen call. Works even if mbstring module not enabled
1956
 *
1957
 * @param   string		$string				String to calculate length
1958
 * @param   string		$stringencoding		Encoding of string
1959
 * @return  int								Length of string
1960
 */
1961
function dol_strlen($string,$stringencoding='UTF-8')
1962
{
1963
	if (function_exists('mb_strlen')) return mb_strlen($string,$stringencoding);
1964
	else return strlen($string);
1965
}
1966
1967
/**
1968
 * Make a substring. Works even in mbstring module is not enabled.
1969
 *
1970
 * @param	string	$string				String to scan
1971
 * @param	string	$start				Start position
1972
 * @param	int		$length				Length
1973
 * @param   string	$stringencoding		Page code used for input string encoding
1974
 * @return  string						substring
1975
 */
1976
function dol_substr($string,$start,$length,$stringencoding='')
1977
{
1978
	global $langs;
1979
1980
	if (empty($stringencoding)) $stringencoding=$langs->charset_output;
1981
1982
	$ret='';
1983
	if (function_exists('mb_substr'))
1984
	{
1985
		$ret=mb_substr($string,$start,$length,$stringencoding);
1986
	}
1987
	else
1988
	{
1989
		$ret=substr($string,$start,$length);
1990
	}
1991
	return $ret;
1992
}
1993
1994
1995
/**
1996
 *  Show a javascript graph.
1997
 *  Do not use this function anymore. Use DolGraph class instead.
1998
 *
1999
 *  @param		string	$htmlid			Html id name
2000
 *  @param		int		$width			Width in pixel
2001
 *  @param		int		$height			Height in pixel
2002
 *  @param		array	$data			Data array
2003
 *  @param		int		$showlegend		1 to show legend, 0 otherwise
2004
 *  @param		string	$type			Type of graph ('pie', 'barline')
2005
 *  @param		int		$showpercent	Show percent (with type='pie' only)
2006
 *  @param		string	$url			Param to add an url to click values
2007
 *  @param		int		$combineother	0=No combine, 0.05=Combine if lower than 5%
2008
 *  @param      int     $shownographyet Show graph to say there is not enough data
2009
 *  @return		void
2010
 *  @deprecated
2011
 *  @see DolGraph
2012
 */
2013
function dol_print_graph($htmlid,$width,$height,$data,$showlegend=0,$type='pie',$showpercent=0,$url='',$combineother=0.05,$shownographyet=0)
2014
{
2015
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
2016
2017
	global $conf,$langs;
2018
	global $theme_datacolor;    // To have var kept when function is called several times
2019
2020
	if ($shownographyet)
2021
	{
2022
	    print '<div class="nographyet" style="width:'.$width.'px;height:'.$height.'px;"></div>';
2023
	    print '<div class="nographyettext">'.$langs->trans("NotEnoughDataYet").'</div>';
2024
	    return;
2025
	}
2026
	
2027
	if (empty($conf->use_javascript_ajax)) return;
2028
	$jsgraphlib='flot';
2029
	$datacolor=array();
2030
2031
	// Load colors of theme into $datacolor array
2032
	$color_file = DOL_DOCUMENT_ROOT."/theme/".$conf->theme."/graph-color.php";
2033
	if (is_readable($color_file))
2034
	{
2035
		include_once $color_file;
2036
		if (isset($theme_datacolor))
2037
		{
2038
			$datacolor=array();
2039
			foreach($theme_datacolor as $val)
2040
			{
2041
				$datacolor[]="#".sprintf("%02x",$val[0]).sprintf("%02x",$val[1]).sprintf("%02x",$val[2]);
2042
			}
2043
		}
2044
	}
2045
	print '<div id="'.$htmlid.'" style="width:'.$width.'px;height:'.$height.'px;"></div>';
2046
2047
	// We use Flot js lib
2048
	if ($jsgraphlib == 'flot')
2049
	{
2050
		if ($type == 'pie')
2051
		{
2052
			// data is   array('series'=>array(serie1,serie2,...),
2053
			//                 'seriestype'=>array('bar','line',...),
2054
			//                 'seriescolor'=>array(0=>'#999999',1=>'#999999',...)
2055
			//                 'xlabel'=>array(0=>labelx1,1=>labelx2,...));
2056
			// serieX is array('label'=>'label', data=>val)
2057
			print '
2058
			<script type="text/javascript">
2059
			$(function () {
2060
				var data = '.json_encode($data['series']).';
2061
2062
				function plotWithOptions() {
2063
					$.plot($("#'.$htmlid.'"), data,
2064
					{
2065
						series: {
2066
							pie: {
2067
								show: true,
2068
								radius: 0.8,';
2069
			if ($combineother)
2070
			{
2071
				print '
2072
								combine: {
2073
								 	threshold: '.$combineother.'
2074
								},';
2075
			}
2076
			print '
2077
								label: {
2078
									show: true,
2079
									radius: 0.9,
2080
									formatter: function(label, series) {
2081
										var percent=Math.round(series.percent);
2082
										var number=series.data[0][1];
2083
										return \'';
2084
										print '<div style="font-size:8pt;text-align:center;padding:2px;color:black;">';
2085
										if ($url) print '<a style="color: #FFFFFF;" border="0" href="'.$url.'=">';
2086
										print '\'+'.($showlegend?'number':'label+\' \'+number');
2087
										if (! empty($showpercent)) print '+\'<br/>\'+percent+\'%\'';
2088
										print '+\'';
2089
										if ($url) print '</a>';
2090
										print '</div>\';
2091
									},
2092
									background: {
2093
										opacity: 0.0,
2094
										color: \'#000000\'
2095
									},
2096
								}
2097
							}
2098
						},
2099
						zoom: {
2100
							interactive: true
2101
						},
2102
						pan: {
2103
							interactive: true
2104
						},';
2105
						if (count($datacolor))
2106
						{
2107
							print 'colors: '.(! empty($data['seriescolor']) ? json_encode($data['seriescolor']) : json_encode($datacolor)).',';
2108
						}
2109
						print 'legend: {show: '.($showlegend?'true':'false').', position: \'ne\' }
2110
					});
2111
				}
2112
				plotWithOptions();
2113
			});
2114
			</script>';
2115
		}
2116
		else if ($type == 'barline')
2117
		{
2118
			// data is   array('series'=>array(serie1,serie2,...),
2119
			//                 'seriestype'=>array('bar','line',...),
2120
			//                 'seriescolor'=>array(0=>'#999999',1=>'#999999',...)
2121
			//                 'xlabel'=>array(0=>labelx1,1=>labelx2,...));
2122
			// serieX is array('label'=>'label', data=>array(0=>y1,1=>y2,...)) with same nb of value than into xlabel
2123
			print '
2124
			<script type="text/javascript">
2125
			$(function () {
2126
				var data = [';
2127
				$i=0; $outputserie=0;
2128
				foreach($data['series'] as $serie)
2129
				{
2130
					if ($data['seriestype'][$i]=='line') { $i++; continue; };
2131
					if ($outputserie > 0) print ',';
2132
					print '{ bars: { stack: 0, show: true, barWidth: 0.9, align: \'center\' }, label: \''.dol_escape_js($serie['label']).'\', data: '.json_encode($serie['data']).'}'."\n";
2133
					$outputserie++; $i++;
2134
				}
2135
				if ($outputserie) print ', ';
2136
				//print '];
2137
				//var datalines = [';
2138
				$i=0; $outputserie=0;
2139
				foreach($data['series'] as $serie)
2140
				{
2141
					if (empty($data['seriestype'][$i]) || $data['seriestype'][$i]=='bar') { $i++; continue; };
2142
					if ($outputserie > 0) print ',';
2143
					print '{ lines: { show: true }, label: \''.dol_escape_js($serie['label']).'\', data: '.json_encode($serie['data']).'}'."\n";
2144
					$outputserie++; $i++;
2145
				}
2146
				print '];
2147
				var dataticks = '.json_encode($data['xlabel']).'
2148
2149
				function plotWithOptions() {
2150
					$.plot(jQuery("#'.$htmlid.'"), data,
2151
					{
2152
						series: {
2153
							stack: 0
2154
						},
2155
						zoom: {
2156
							interactive: true
2157
						},
2158
						pan: {
2159
							interactive: true
2160
						},';
2161
						if (count($datacolor))
2162
						{
2163
							print 'colors: '.json_encode($datacolor).',';
2164
						}
2165
						print 'legend: {show: '.($showlegend?'true':'false').'},
2166
						xaxis: {ticks: dataticks}
2167
					});
2168
				}
2169
				plotWithOptions();
2170
			});
2171
			</script>';
2172
		}
2173
		else print 'BadValueForParameterType';
2174
	}
2175
}
2176
2177
/**
2178
 *	Truncate a string to a particular length adding '...' if string larger than length.
2179
 * 	If length = max length+1, we do no truncate to avoid having just 1 char replaced with '...'.
2180
 *  MAIN_DISABLE_TRUNC=1 can disable all truncings
2181
 *
2182
 *	@param	string	$string				String to truncate
2183
 *	@param  int		$size				Max string size visible. 0 for no limit. Final string size can be 1 more (if size was max+1) or 3 more (if we added ...)
2184
 *	@param	string	$trunc				Where to trunc: right, left, middle (size must be a 2 power), wrap
2185
 * 	@param	string	$stringencoding		Tell what is source string encoding
2186
 *  @param	int		$nodot				Truncation do not add ... after truncation. So it's an exact truncation.
2187
 *  @param  int     $display            Trunc is use to display and can be changed for small screen
2188
 *	@return string						Truncated string
2189
 */
2190
function dol_trunc($string,$size=40,$trunc='right',$stringencoding='UTF-8',$nodot=0, $display=0)
2191
{
2192
	global $conf;
2193
2194
	if ($size==0 || ! empty($conf->global->MAIN_DISABLE_TRUNC)) return $string;
2195
	
2196
	if (empty($stringencoding)) $stringencoding='UTF-8';
2197
	// reduce for small screen
2198
	if ($conf->dol_optimize_smallscreen==1 && $display==1) $size = round($size/3);
2199
2200
	// We go always here
2201
	if ($trunc == 'right')
2202
	{
2203
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2204
		if (dol_strlen($newstring,$stringencoding) > ($size+($nodot?0:1)))
2205
		return dol_substr($newstring,0,$size,$stringencoding).($nodot?'':'...');
2206
		else
2207
		return $string;
2208
	}
2209
	elseif ($trunc == 'middle')
2210
	{
2211
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2212
		if (dol_strlen($newstring,$stringencoding) > 2 && dol_strlen($newstring,$stringencoding) > ($size+1))
2213
		{
2214
			$size1=round($size/2);
2215
			$size2=round($size/2);
2216
			return dol_substr($newstring,0,$size1,$stringencoding).'...'.dol_substr($newstring,dol_strlen($newstring,$stringencoding) - $size2,$size2,$stringencoding);
2217
		}
2218
		else
2219
		return $string;
2220
	}
2221
	elseif ($trunc == 'left')
2222
	{
2223
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2224
		if (dol_strlen($newstring,$stringencoding) > ($size+1))
2225
		return '...'.dol_substr($newstring,dol_strlen($newstring,$stringencoding) - $size,$size,$stringencoding);
2226
		else
2227
		return $string;
2228
	}
2229
	elseif ($trunc == 'wrap')
2230
	{
2231
		$newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2232
		if (dol_strlen($newstring,$stringencoding) > ($size+1))
2233
		return dol_substr($newstring,0,$size,$stringencoding)."\n".dol_trunc(dol_substr($newstring,$size,dol_strlen($newstring,$stringencoding)-$size,$stringencoding),$size,$trunc);
2234
		else
2235
		return $string;
2236
	}
2237
	else return 'BadParam3CallingDolTrunc';
2238
}
2239
2240
/**
2241
 *	Show picto whatever it's its name (generic function)
2242
 *
2243
 *	@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.
2244
 *	@param      string		$picto       		Name of image file to show ('filenew', ...)
2245
 *												If no extension provided, we use '.png'. Image must be stored into theme/xxx/img directory.
2246
 *                                  			Example: picto.png                  if picto.png is stored into htdocs/theme/mytheme/img
2247
 *                                  			Example: picto.png@mymodule         if picto.png is stored into htdocs/mymodule/img
2248
 *                                  			Example: /mydir/mysubdir/picto.png  if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1)
2249
 *	@param		string		$morealt			Add more attribute on img tag (For example 'style="float: right"')
2250
 *	@param		int			$pictoisfullpath	If 1, image path is a full path
2251
 *	@param		int			$srconly			Return only content of the src attribute of img.
2252
 *  @param		int			$notitle			1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip.
2253
 *  @return     string       				    Return img tag
2254
 *  @see        #img_object, #img_picto_common
2255
 */
2256
function img_picto($titlealt, $picto, $morealt = '', $pictoisfullpath = false, $srconly=0, $notitle=0)
2257
{
2258
	global $conf;
2259
2260
	// Define fullpathpicto to use into src
2261
	if ($pictoisfullpath)
2262
	{
2263
		// Clean parameters
2264
		if (! preg_match('/(\.png|\.gif)$/i',$picto)) $picto .= '.png';
2265
		$fullpathpicto = $picto;
2266
	}
2267
	else
2268
	{
2269
		// We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
2270
		$url = DOL_URL_ROOT;
2271
		$theme = $conf->theme;
2272
2273
		$path = 'theme/'.$theme;
2274
		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
2275
		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
2276
		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
2277
2278
		// If we ask an image into $url/$mymodule/img (instead of default path)
2279
		if (preg_match('/^([^@]+)@([^@]+)$/i',$picto,$regs))
2280
		{
2281
			$picto = $regs[1];
2282
			$path = $regs[2];	// $path is $mymodule
2283
		}
2284
		// Clean parameters
2285
		if (! preg_match('/(\.png|\.gif)$/i',$picto)) $picto .= '.png';
2286
		// If alt path are defined, define url where img file is, according to physical path
2287
		foreach ($conf->file->dol_document_root as $type => $dirroot)	// ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
2288
		{
2289
			if ($type == 'main') continue;
2290
			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
2291
			{
2292
				$url=DOL_URL_ROOT.$conf->file->dol_url_root[$type];
2293
				break;
2294
			}
2295
		}
2296
2297
		// $url is '' or '/custom', $path is current theme or
2298
		$fullpathpicto = $url.'/'.$path.'/img/'.$picto;
2299
	}
2300
2301
	if ($srconly) return $fullpathpicto;
2302
	else
2303
	{
2304
		$tmparray=array(0=>$titlealt);
2305
		if (preg_match('/:[^\s0-9]/',$titlealt)) $tmparray=explode(':',$titlealt);		// We explode if we have TextA:TextB. Not if we have TextA: TextB
2306
		$title=$tmparray[0];
2307
		$alt=empty($tmparray[1])?'':$tmparray[1];
2308
		return '<img src="'.$fullpathpicto.'" border="0" alt="'.dol_escape_htmltag($alt).'"'.($notitle?'':' title="'.dol_escape_htmltag($title).'"').($morealt?' '.$morealt:'').'>';	// Alt is used for accessibility, title for popup
2309
	}
2310
}
2311
2312
/**
2313
 *	Show a picto called object_picto (generic function)
2314
 *
2315
 *	@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.
2316
 *	@param	string	$picto				Name of image to show object_picto (example: user, group, action, bill, contract, propal, product, ...)
2317
 *										For external modules use imagename@mymodule to search into directory "img" of module.
2318
 *	@param	string	$morealt			Add more attribute on img tag (ie: class="datecallink")
2319
 *	@param	int		$pictoisfullpath	If 1, image path is a full path
2320
 *	@param	int		$srconly			Return only content of the src attribute of img.
2321
 *  @param	int		$notitle			1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip.
2322
 *	@return	string						Return img tag
2323
 *	@see	#img_picto, #img_picto_common
2324
 */
2325
function img_object($titlealt, $picto, $morealt = '', $pictoisfullpath = false, $srconly=0, $notitle=0)
2326
{
2327
	return img_picto($titlealt, 'object_'.$picto, $morealt, $pictoisfullpath, $srconly, $notitle);
2328
}
2329
2330
/**
2331
 *	Show weather picto
2332
 *
2333
 *	@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.
2334
 *	@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.
2335
 *	@param		string		$morealt			Add more attribute on img tag
2336
 *	@param		int			$pictoisfullpath	If 1, image path is a full path
2337
 *	@return     string      					Return img tag
2338
 *  @see        #img_object, #img_picto
2339
 */
2340
function img_weather($titlealt, $picto, $morealt = '', $pictoisfullpath = 0)
2341
{
2342
	global $conf;
2343
2344
	if (! preg_match('/(\.png|\.gif)$/i', $picto)) $picto .= '.png';
2345
2346
	$path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
2347
2348
	return img_picto($titlealt, $path, $morealt, 1);
2349
}
2350
2351
/**
2352
 *	Show picto (generic function)
2353
 *
2354
 *	@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.
2355
 *	@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.
2356
 *	@param		string		$morealt			Add more attribute on img tag
2357
 *	@param		int			$pictoisfullpath	If 1, image path is a full path
2358
 *	@return     string      					Return img tag
2359
 *  @see        #img_object, #img_picto
2360
 */
2361
function img_picto_common($titlealt, $picto, $morealt = '', $pictoisfullpath = 0)
2362
{
2363
	global $conf;
2364
2365
	if (! preg_match('/(\.png|\.gif)$/i', $picto)) $picto .= '.png';
2366
2367
	if ($pictoisfullpath) $path = $picto;
2368
	else
2369
	{
2370
		$path = DOL_URL_ROOT.'/theme/common/'.$picto;
2371
2372
		if (! empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS))
2373
		{
2374
			$themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
2375
2376
			if (file_exists($themepath)) $path = $themepath;
2377
		}
2378
	}
2379
2380
	return img_picto($titlealt, $path, $morealt, 1);
2381
}
2382
2383
/**
2384
 *	Show logo action
2385
 *
2386
 *	@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.
2387
 *	@param  string		$numaction   	Action id or code to show
2388
 *	@return string      				Return an img tag
2389
 */
2390
function img_action($titlealt, $numaction)
2391
{
2392
	global $conf, $langs;
2393
2394
	if (empty($titlealt) || $titlealt == 'default')
2395
	{
2396
		if ($numaction == '-1' || $numaction == 'ST_NO')			{ $numaction = -1; $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact'); }
2397
		elseif ($numaction ==  '0' || $numaction == 'ST_NEVER') 	{ $numaction = 0; $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted'); }
2398
		elseif ($numaction ==  '1' || $numaction == 'ST_TODO')  	{ $numaction = 1; $titlealt = $langs->transnoentitiesnoconv('ChangeToContact'); }
2399
		elseif ($numaction ==  '2' || $numaction == 'ST_PEND')  	{ $numaction = 2; $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess'); }
2400
		elseif ($numaction ==  '3' || $numaction == 'ST_DONE')  	{ $numaction = 3; $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone'); }
2401
		else { $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction); $numaction = 0; }
2402
	}
2403
	if (! is_numeric($numaction)) $numaction=0;
2404
2405
	return img_picto($titlealt, 'stcomm'.$numaction.'.png');
2406
}
2407
2408
/**
2409
 *  Show pdf logo
2410
 *
2411
 *  @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.
2412
 *  @param  int		    $size       Taille de l'icone : 3 = 16x16px , 2 = 14x14px
2413
 *  @return string      			Retourne tag img
2414
 */
2415
function img_pdf($titlealt = 'default', $size = 3)
2416
{
2417
	global $conf, $langs;
2418
2419
	if ($titlealt == 'default') $titlealt = $langs->trans('Show');
2420
2421
	return img_picto($titlealt, 'pdf'.$size.'.png');
2422
}
2423
2424
/**
2425
 *	Show logo +
2426
 *
2427
 *	@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.
2428
 *	@param  string	$other      Add more attributes on img
2429
 *	@return string      		Return tag img
2430
 */
2431
function img_edit_add($titlealt = 'default', $other = '')
2432
{
2433
	global $conf, $langs;
2434
2435
	if ($titlealt == 'default') $titlealt = $langs->trans('Add');
2436
2437
	return img_picto($titlealt, 'edit_add.png', $other);
2438
}
2439
/**
2440
 *	Show logo -
2441
 *
2442
 *	@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.
2443
 *	@param  string	$other      Add more attributes on img
2444
 *	@return string      		Return tag img
2445
 */
2446
function img_edit_remove($titlealt = 'default', $other='')
2447
{
2448
	global $conf, $langs;
2449
2450
	if ($titlealt == 'default') $titlealt = $langs->trans('Remove');
2451
2452
	return img_picto($titlealt, 'edit_remove.png', $other);
2453
}
2454
2455
/**
2456
 *	Show logo editer/modifier fiche
2457
 *
2458
 *	@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.
2459
 *	@param  integer	$float      Si il faut y mettre le style "float: right"
2460
 *	@param  string	$other		Add more attributes on img
2461
 *	@return string      		Return tag img
2462
 */
2463
function img_edit($titlealt = 'default', $float = 0, $other = '')
2464
{
2465
	global $conf, $langs;
2466
2467
	if ($titlealt == 'default') $titlealt = $langs->trans('Modify');
2468
2469
	return img_picto($titlealt, 'edit.png', ($float ? 'style="float: right"' : $other));
2470
}
2471
2472
/**
2473
 *	Show logo view card
2474
 *
2475
 *	@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.
2476
 *	@param  integer	$float      Si il faut y mettre le style "float: right"
2477
 *	@param  string	$other		Add more attributes on img
2478
 *	@return string      		Return tag img
2479
 */
2480
function img_view($titlealt = 'default', $float = 0, $other = '')
2481
{
2482
	global $conf, $langs;
2483
2484
	if ($titlealt == 'default') $titlealt = $langs->trans('View');
2485
2486
	$morealt = ($float ? 'style="float: right" ' : '').$other;
2487
2488
	return img_picto($titlealt, 'view.png', $morealt);
2489
}
2490
2491
/**
2492
 *  Show delete logo
2493
 *
2494
 *  @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.
2495
 *	@param  string	$other      Add more attributes on img
2496
 *  @return string      		Retourne tag img
2497
 */
2498
function img_delete($titlealt = 'default', $other = '')
2499
{
2500
	global $conf, $langs;
2501
2502
	if ($titlealt == 'default') $titlealt = $langs->trans('Delete');
2503
2504
	return img_picto($titlealt, 'delete.png', $other);
2505
}
2506
2507
/**
2508
 *  Show printer logo
2509
 *
2510
 *  @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.
2511
 *  @param  string  $other      Add more attributes on img
2512
 *  @return string              Retourne tag img
2513
 */
2514
function img_printer($titlealt = "default", $other='')
2515
{
2516
    global $conf,$langs;
2517
    if ($titlealt=="default") $titlealt=$langs->trans("Print");
2518
    return img_picto($titlealt,'printer.png',$other);
2519
}
2520
2521
/**
2522
 *	Show help logo with cursor "?"
2523
 *
2524
 * 	@param	int              	$usehelpcursor		Use help cursor
2525
 * 	@param	int|string	        $usealttitle		Text to use as alt title
2526
 * 	@return string            	           			Return tag img
2527
 */
2528
function img_help($usehelpcursor = 1, $usealttitle = 1)
2529
{
2530
	global $conf, $langs;
2531
2532
	if ($usealttitle)
2533
	{
2534
		if (is_string($usealttitle)) $usealttitle = dol_escape_htmltag($usealttitle);
2535
		else $usealttitle = $langs->trans('Info');
2536
	}
2537
2538
	return img_picto($usealttitle, 'info.png', ($usehelpcursor ? 'style="vertical-align: middle; cursor: help"' : 'style="vertical-align: middle;"'));
2539
}
2540
2541
/**
2542
 *	Show info logo
2543
 *
2544
 *	@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.
2545
 *	@return string      		Return img tag
2546
 */
2547
function img_info($titlealt = 'default')
2548
{
2549
	global $conf, $langs;
2550
2551
	if ($titlealt == 'default') $titlealt = $langs->trans('Informations');
2552
2553
	return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
2554
}
2555
2556
/**
2557
 *	Show warning logo
2558
 *
2559
 *	@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.
2560
 *	@param	string	$morealt	Add more attribute on img tag (For example 'style="float: right"'). If 1
2561
 *	@return string      		Return img tag
2562
 */
2563
function img_warning($titlealt = 'default', $morealt = '')
2564
{
2565
	global $conf, $langs;
2566
2567
	if ($titlealt == 'default') $titlealt = $langs->trans('Warning');
2568
2569
	return img_picto($titlealt, 'warning.png', 'class="pictowarning"'.($morealt ? ($morealt == '1' ? ' style="float: right"' : ' '.$morealt): ''));
2570
}
2571
2572
/**
2573
 *  Show error logo
2574
 *
2575
 *	@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.
2576
 *	@return string      		Return img tag
2577
 */
2578
function img_error($titlealt = 'default')
2579
{
2580
	global $conf, $langs;
2581
2582
	if ($titlealt == 'default') $titlealt = $langs->trans('Error');
2583
2584
	return img_picto($titlealt, 'error.png');
2585
}
2586
2587
/**
2588
 *	Show next logo
2589
 *
2590
 *	@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.
2591
*	@param	string	$morealt	Add more attribute on img tag (For example 'style="float: right"')
2592
  *	@return string      		Return img tag
2593
 */
2594
function img_next($titlealt = 'default', $morealt='')
2595
{
2596
	global $conf, $langs;
2597
2598
	if ($titlealt == 'default') $titlealt = $langs->trans('Next');
2599
2600
	return img_picto($titlealt, 'next.png', $morealt);
2601
}
2602
2603
/**
2604
 *	Show previous logo
2605
 *
2606
 *	@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.
2607
 *	@param	string	$morealt	Add more attribute on img tag (For example 'style="float: right"')
2608
 *	@return string      		Return img tag
2609
 */
2610
function img_previous($titlealt = 'default', $morealt='')
2611
{
2612
	global $conf, $langs;
2613
2614
	if ($titlealt == 'default') $titlealt = $langs->trans('Previous');
2615
2616
	return img_picto($titlealt, 'previous.png', $morealt);
2617
}
2618
2619
/**
2620
 *	Show down arrow logo
2621
 *
2622
 *	@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.
2623
 *	@param  int		$selected   Selected
2624
 *  @param	string	$moreclass	Add more CSS classes
2625
 *	@return string      		Return img tag
2626
 */
2627
function img_down($titlealt = 'default', $selected = 0, $moreclass='')
2628
{
2629
	global $conf, $langs;
2630
2631
	if ($titlealt == 'default') $titlealt = $langs->trans('Down');
2632
2633
	return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass?" ".$moreclass:"").'"');
2634
}
2635
2636
/**
2637
 *	Show top arrow logo
2638
 *
2639
 *	@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.
2640
 *	@param  int		$selected	Selected
2641
 *  @param	string	$moreclass	Add more CSS classes
2642
 *	@return string      		Return img tag
2643
 */
2644
function img_up($titlealt = 'default', $selected = 0, $moreclass='')
2645
{
2646
	global $conf, $langs;
2647
2648
	if ($titlealt == 'default') $titlealt = $langs->trans('Up');
2649
2650
	return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass?" ".$moreclass:"").'"');
2651
}
2652
2653
/**
2654
 *	Show left arrow logo
2655
 *
2656
 *	@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.
2657
 *	@param  int		$selected	Selected
2658
 *	@param	string	$morealt	Add more attribute on img tag (For example 'style="float: right"')
2659
 *	@return string      		Return img tag
2660
 */
2661
function img_left($titlealt = 'default', $selected = 0, $morealt='')
2662
{
2663
	global $conf, $langs;
2664
2665
	if ($titlealt == 'default') $titlealt = $langs->trans('Left');
2666
2667
	return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $morealt);
2668
}
2669
2670
/**
2671
 *	Show right arrow logo
2672
 *
2673
 *	@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.
2674
 *	@param  int		$selected	Selected
2675
 *	@param	string	$morealt	Add more attribute on img tag (For example 'style="float: right"')
2676
 *	@return string      		Return img tag
2677
 */
2678
function img_right($titlealt = 'default', $selected = 0, $morealt='')
2679
{
2680
	global $conf, $langs;
2681
2682
	if ($titlealt == 'default') $titlealt = $langs->trans('Right');
2683
2684
	return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $morealt);
2685
}
2686
2687
/**
2688
 *	Show tick logo if allowed
2689
 *
2690
 *	@param	string	$allow		Allow
2691
 *	@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.
2692
 *	@return string      		Return img tag
2693
 */
2694
function img_allow($allow, $titlealt = 'default')
2695
{
2696
	global $conf, $langs;
2697
2698
	if ($titlealt == 'default') $titlealt = $langs->trans('Active');
2699
2700
	if ($allow == 1) return img_picto($titlealt, 'tick.png');
2701
2702
	return '-';
2703
}
2704
2705
2706
/**
2707
 *	Show MIME img of a file
2708
 *
2709
 *	@param	string	$file		Filename
2710
 * 	@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.
2711
 *	@return string     			Return img tag
2712
 */
2713
function img_mime($file, $titlealt = '')
2714
{
2715
	require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2716
2717
	$mimetype = dol_mimetype($file, '', 1);
2718
	$mimeimg = dol_mimetype($file, '', 2);
2719
2720
	if (empty($titlealt)) $titlealt = 'Mime type: '.$mimetype;
2721
2722
	return img_picto_common($titlealt, 'mime/'.$mimeimg);
2723
}
2724
2725
2726
/**
2727
 *	Show phone logo.
2728
 *  Use img_picto instead.
2729
 *
2730
 *	@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.
2731
 *	@param  int		$option		Option
2732
 *	@return string      		Return img tag
2733
 *  @deprecated
2734
 *  @see img_picto
2735
 */
2736
function img_phone($titlealt = 'default', $option = 0)
2737
{
2738
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
2739
2740
	global $conf,$langs;
2741
2742
	if ($titlealt == 'default') $titlealt = $langs->trans('Call');
2743
2744
	if ($option == 1) $img = 'call';
2745
	else $img = 'call_out';
2746
2747
	return img_picto($titlealt, $img);
2748
}
2749
2750
/**
2751
 *  Show search logo
2752
 *
2753
 *  @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.
2754
 *	@param  string	$other      Add more attributes on img
2755
 *  @return string      		Retourne tag img
2756
 */
2757
function img_search($titlealt = 'default', $other = '')
2758
{
2759
	global $conf, $langs;
2760
2761
	if ($titlealt == 'default') $titlealt = $langs->trans('Search');
2762
2763
	$img = img_picto($titlealt, 'search.png', $other, false, 1);
2764
2765
	$input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
2766
	$input.= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
2767
2768
	return $input;
2769
}
2770
2771
/**
2772
 *  Show search logo
2773
 *
2774
 *  @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.
2775
 *	@param  string	$other      Add more attributes on img
2776
 *  @return string      		Retourne tag img
2777
 */
2778
function img_searchclear($titlealt = 'default', $other = '')
2779
{
2780
	global $conf, $langs;
2781
2782
	if ($titlealt == 'default') $titlealt = $langs->trans('Search');
2783
2784
	$img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
2785
2786
	$input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
2787
	$input.= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
2788
2789
	return $input;
2790
}
2791
2792
/**
2793
 *	Show information for admin users or standard users
2794
 *
2795
 *	@param	string	$text			Text info
2796
 *	@param  integer	$infoonimgalt	Info is shown only on alt of star picto, otherwise it is show on output after the star picto
2797
 *	@param	int		$nodiv			No div
2798
 *  @param  string  $admin          '1'=Info for admin users. '0'=Info for standard users (change only the look), 'xxx'=Other
2799
 *	@return	string					String with info text
2800
 */
2801
function info_admin($text, $infoonimgalt = 0, $nodiv=0, $admin='1')
2802
{
2803
	global $conf, $langs;
2804
2805
	if ($infoonimgalt)
2806
	{
2807
		return img_picto($text, 'info', 'class="hideonsmartphone"');
2808
	}
2809
2810
	return ($nodiv?'':'<div class="'.(empty($admin)?'':($admin=='1'?'info':$admin)).' hideonsmartphone">').img_picto($admin?$langs->trans('InfoAdmin'):$langs->trans('Note'), ($nodiv?'info':'info_black'), 'class="hideonsmartphone"').' '.$text.($nodiv?'':'</div>');
2811
}
2812
2813
2814
/**
2815
 *	Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remontee des bugs.
2816
 *	On doit appeler cette fonction quand une erreur technique bloquante est rencontree.
2817
 *	Toutefois, il faut essayer de ne l'appeler qu'au sein de pages php, les classes devant
2818
 *	renvoyer leur erreur par l'intermediaire de leur propriete "error".
2819
 *
2820
 *	@param	 	DoliDB	$db      	Database handler
2821
 *	@param  	mixed	$error		String or array of errors strings to show
2822
 *  @param		array	$errors		Array of errors
2823
 *	@return 	void
2824
 *  @see    	dol_htmloutput_errors
2825
 */
2826
function dol_print_error($db='',$error='',$errors=null)
2827
{
2828
	global $conf,$langs,$argv;
2829
	global $dolibarr_main_prod;
2830
2831
	$out = '';
2832
	$syslog = '';
2833
2834
	// Si erreur intervenue avant chargement langue
2835
	if (! $langs)
2836
	{
2837
		require_once DOL_DOCUMENT_ROOT .'/core/class/translate.class.php';
2838
		$langs = new Translate('', $conf);
2839
		$langs->load("main");
2840
	}
2841
	$langs->load("main");
2842
	$langs->load("errors");
2843
2844
	if ($_SERVER['DOCUMENT_ROOT'])    // Mode web
2845
	{
2846
		$out.=$langs->trans("DolibarrHasDetectedError").".<br>\n";
2847
		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";
2848
		$out.=$langs->trans("InformationToHelpDiagnose").":<br>\n";
2849
2850
		$out.="<b>".$langs->trans("Date").":</b> ".dol_print_date(time(),'dayhourlog')."<br>\n";
2851
		$out.="<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION."<br>\n";
2852
		if (isset($conf->global->MAIN_FEATURES_LEVEL)) $out.="<b>".$langs->trans("LevelOfFeature").":</b> ".$conf->global->MAIN_FEATURES_LEVEL."<br>\n";
2853
		if (function_exists("phpversion"))
2854
		{
2855
			$out.="<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
2856
		}
2857
		$out.="<b>".$langs->trans("Server").":</b> ".$_SERVER["SERVER_SOFTWARE"]."<br>\n";
2858
		if (function_exists("php_uname"))
2859
		{
2860
			$out.="<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
2861
		}
2862
		$out.="<b>".$langs->trans("UserAgent").":</b> ".$_SERVER["HTTP_USER_AGENT"]."<br>\n";
2863
		$out.="<br>\n";
2864
		$out.="<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"],ENT_COMPAT,'UTF-8')."<br>\n";
2865
		$out.="<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"])?dol_htmlentities($_SERVER["HTTP_REFERER"],ENT_COMPAT,'UTF-8'):'')."<br>\n";
2866
		$out.="<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu)?$conf->standard_menu:'')."<br>\n";
2867
		$out.="<br>\n";
2868
		$syslog.="url=".$_SERVER["REQUEST_URI"];
2869
		$syslog.=", query_string=".$_SERVER["QUERY_STRING"];
2870
	}
2871
	else                              // Mode CLI
2872
	{
2873
		$out.='> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
2874
		$syslog.="pid=".dol_getmypid();
2875
	}
2876
2877
	if (is_object($db))
2878
	{
2879
		if ($_SERVER['DOCUMENT_ROOT'])  // Mode web
2880
		{
2881
			$out.="<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
2882
			$out.="<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror()?$db->lastqueryerror():$langs->trans("ErrorNoRequestInError"))."<br>\n";
2883
			$out.="<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno()?$db->lasterrno():$langs->trans("ErrorNoRequestInError"))."<br>\n";
2884
			$out.="<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror()?$db->lasterror():$langs->trans("ErrorNoRequestInError"))."<br>\n";
2885
			$out.="<br>\n";
2886
		}
2887
		else                            // Mode CLI
2888
		{
2889
			$out.='> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
2890
			$out.='> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror()?$db->lastqueryerror():$langs->trans("ErrorNoRequestInError"))."\n";
2891
			$out.='> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno()?$db->lasterrno():$langs->trans("ErrorNoRequestInError"))."\n";
2892
			$out.='> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror()?$db->lasterror():$langs->trans("ErrorNoRequestInError"))."\n";
2893
2894
		}
2895
		$syslog.=", sql=".$db->lastquery();
2896
		$syslog.=", db_error=".$db->lasterror();
2897
	}
2898
2899
	if ($error || $errors)
2900
	{
2901
		$langs->load("errors");
2902
2903
		// Merge all into $errors array
2904
		if (is_array($error) && is_array($errors)) $errors=array_merge($error,$errors);
2905
		elseif (is_array($error)) $errors=$error;
2906
		elseif (is_array($errors)) $errors=array_merge(array($error),$errors);
2907
		else $errors=array_merge(array($error));
2908
2909
		foreach($errors as $msg)
2910
		{
2911
			if (empty($msg)) continue;
2912
			if ($_SERVER['DOCUMENT_ROOT'])  // Mode web
2913
			{
2914
				$out.="<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n" ;
2915
			}
2916
			else                        // Mode CLI
2917
			{
2918
				$out.='> '.$langs->transnoentities("Message").":\n".$msg."\n" ;
2919
			}
2920
			$syslog.=", msg=".$msg;
2921
		}
2922
	}
2923
	if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file'))
2924
	{
2925
		xdebug_print_function_stack();
2926
		$out.='<b>XDebug informations:</b>'."<br>\n";
2927
		$out.='File: '.xdebug_call_file()."<br>\n";
2928
		$out.='Line: '.xdebug_call_line()."<br>\n";
2929
		$out.='Function: '.xdebug_call_function()."<br>\n";
2930
		$out.="<br>\n";
2931
	}
2932
2933
	if (empty($dolibarr_main_prod)) print $out;
2934
	else define("MAIN_CORE_ERROR", 1);
2935
	//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.';
2936
	dol_syslog("Error ".$syslog, LOG_ERR);
2937
}
2938
2939
/**
2940
 * Show a public email and error code to contact if technical error
2941
 *
2942
 * @param	string	$prefixcode		Prefix of public error code
2943
 * @return	void
2944
 */
2945
function dol_print_error_email($prefixcode)
2946
{
2947
	global $langs,$conf;
2948
2949
	$langs->load("errors");
2950
	$now=dol_now();
2951
	print '<br><div class="error">'.$langs->trans("ErrorContactEMail", $conf->global->MAIN_INFO_SOCIETE_MAIL, $prefixcode.dol_print_date($now,'%Y%m%d')).'</div>';
2952
}
2953
2954
/**
2955
 *	Show title line of an array
2956
 *
2957
 *	@param	string	$name        Label of field
2958
 *	@param	string	$file        Url used when we click on sort picto
2959
 *	@param	string	$field       Field to use for new sorting
2960
 *	@param	string	$begin       ("" by defaut)
2961
 *	@param	string	$moreparam   Add more parameters on sort url links ("" by default)
2962
 *	@param  string	$td          Options of attribute td ("" by defaut, example: 'align="center"')
2963
 *	@param  string	$sortfield   Current field used to sort
2964
 *	@param  string	$sortorder   Current sort order
2965
 *  @param	string	$prefix		 Prefix for css. Use space after prefix to add your own CSS tag.
2966
 *	@return	void
2967
 */
2968
function print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $td="", $sortfield="", $sortorder="", $prefix="")
2969
{
2970
	print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $td, $sortfield, $sortorder, $prefix);
2971
}
2972
2973
/**
2974
 *	Get title line of an array
2975
 *
2976
 *	@param	string	$name        Label of field
2977
 *	@param	int		$thead		 0=To use with standard table format, 1=To use inside <thead><tr>, 2=To use with <div>
2978
 *	@param	string	$file        Url used when we click on sort picto
2979
 *	@param	string	$field       Field to use for new sorting. Empty if this field is not sortable.
2980
 *	@param	string	$begin       ("" by defaut)
2981
 *	@param	string	$moreparam   Add more parameters on sort url links ("" by default)
2982
 *	@param  string	$moreattrib  Add more attributes on th ("" by defaut)
2983
 *	@param  string	$sortfield   Current field used to sort
2984
 *	@param  string	$sortorder   Current sort order
2985
 *  @param	string	$prefix		 Prefix for css. Use space after prefix to add your own CSS tag.
2986
 *	@return	string
2987
 */
2988
function getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="")
2989
{
2990
	global $conf;
2991
	//print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
2992
2993
	$sortorder=strtoupper($sortorder);
2994
	$out='';
2995
    $sortimg='';
2996
2997
	$tag='th';
2998
	if ($thead==2) $tag='div';
2999
3000
	// If field is used as sort criteria we use a specific class
3001
	// Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
3002
	if ($field && ($sortfield == $field || $sortfield == preg_replace("/^[^\.]+\./","",$field))) $out.= '<'.$tag.' class="'.$prefix.'liste_titre_sel" '. $moreattrib.'>';
3003
	else $out.= '<'.$tag.' class="'.$prefix.'liste_titre" '. $moreattrib.'>';
3004
3005
	if (empty($thead) && $field)    // If this is a sort field
3006
	{
3007
		$options=preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i','',$moreparam);
3008
		$options=preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i','',$options);
3009
		$options=preg_replace('/&+/i','&',$options);
3010
		if (! preg_match('/^&/',$options)) $options='&'.$options;
3011
3012
		if ($field != $sortfield)
3013
		{
3014
            if ($sortorder == 'DESC') $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">';
3015
            if ($sortorder == 'ASC' || ! $sortorder) $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">';
3016
		}
3017
		else
3018
		{
3019
            if ($sortorder == 'DESC' || ! $sortorder) $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">';
3020
            if ($sortorder == 'ASC') $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">';
3021
		}
3022
	}
3023
3024
	$out.=$name;
3025
3026
	if (empty($thead) && $field)    // If this is a sort field
3027
	{
3028
		$out.='</a>';
3029
	}
3030
3031
	if (empty($thead) && $field)    // If this is a sort field
3032
	{
3033
		$options=preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i','',$moreparam);
3034
		$options=preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i','',$options);
3035
		$options=preg_replace('/&+/i','&',$options);
3036
		if (! preg_match('/^&/',$options)) $options='&'.$options;
3037
3038
		//print "&nbsp;";
3039
		//$sortimg.= '<img width="2" src="'.DOL_URL_ROOT.'/theme/common/transparent.png" alt="">';
3040
		//$sortimg.= '<span class="nowrap">';
3041
3042
		if (! $sortorder || $field != $sortfield)
3043
		{
3044
			//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
3045
			//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
3046
		}
3047
		else
3048
		{
3049
			if ($sortorder == 'DESC' ) {
3050
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
3051
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
3052
				$sortimg.= '<span class="nowrap">'.img_up("Z-A",0).'</span>';
3053
			}
3054
			if ($sortorder == 'ASC' ) {
3055
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
3056
				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
3057
				$sortimg.= '<span class="nowrap">'.img_down("A-Z",0).'</span>';
3058
			}
3059
		}
3060
3061
		//$sortimg.= '</span>';
3062
	}
3063
3064
	$out.=$sortimg;
3065
3066
	$out.='</'.$tag.'>';
3067
3068
	return $out;
3069
}
3070
3071
/**
3072
 *	Show a title.
3073
 *
3074
 *	@param	string	$title			Title to show
3075
 *	@return	string					Title to show
3076
 *  @deprecated						Use load_fiche_titre instead
3077
 *  @see load_fiche_titre
3078
 */
3079
function print_titre($title)
3080
{
3081
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
3082
3083
	print '<div class="titre">'.$title.'</div>';
3084
}
3085
3086
/**
3087
 *	Show a title with picto
3088
 *
3089
 *	@param	string	$title				Title to show
3090
 *	@param	string	$mesg				Added message to show on right
3091
 *	@param	string	$picto				Icon to use before title (should be a 32x32 transparent png file)
3092
 *	@param	int		$pictoisfullpath	1=Icon name is a full absolute url of image
3093
 * 	@param	int		$id					To force an id on html objects
3094
 * 	@return	void
3095
 *  @deprecated Use print load_fiche_titre instead
3096
 */
3097
function print_fiche_titre($title, $mesg='', $picto='title_generic.png', $pictoisfullpath=0, $id='')
3098
{
3099
	print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
3100
}
3101
3102
/**
3103
 *	Load a title with picto
3104
 *
3105
 *	@param	string	$titre				Title to show
3106
 *	@param	string	$morehtmlright		Added message to show on right
3107
 *	@param	string	$picto				Icon to use before title (should be a 32x32 transparent png file)
3108
 *	@param	int		$pictoisfullpath	1=Icon name is a full absolute url of image
3109
 * 	@param	int		$id					To force an id on html objects
3110
 *  @param  string  $morecssontable     More css on table
3111
 * 	@return	string
3112
 *  @see print_barre_liste
3113
 */
3114
function load_fiche_titre($titre, $morehtmlright='', $picto='title_generic.png', $pictoisfullpath=0, $id=0, $morecssontable='')
3115
{
3116
	global $conf;
3117
3118
	$return='';
3119
3120
	if ($picto == 'setup') $picto='title.png';
3121
	if (($conf->browser->name == 'ie') && $picto=='title.png') $picto='title.gif';
3122
3123
	$return.= "\n";
3124
	$return.= '<table '.($id?'id="'.$id.'" ':'').'summary="" class="centpercent notopnoleftnoright'.($morecssontable?' '.$morecssontable:'').'" style="margin-bottom: 2px;"><tr>';
3125
	if ($picto) $return.= '<td class="nobordernopadding widthpictotitle" valign="middle">'.img_picto('',$picto, 'id="pictotitle"', $pictoisfullpath).'</td>';
3126
	$return.= '<td class="nobordernopadding" valign="middle">';
3127
	$return.= '<div class="titre">'.$titre.'</div>';
3128
	$return.= '</td>';
3129
	if (dol_strlen($morehtmlright))
3130
	{
3131
		$return.= '<td class="nobordernopadding titre_right" align="right" valign="middle">'.$morehtmlright.'</td>';
3132
	}
3133
	$return.= '</tr></table>'."\n";
3134
3135
	return $return;
3136
}
3137
3138
/**
3139
 *	Print a title with navigation controls for pagination
3140
 *
3141
 *	@param	string	    $titre				Title to show (required)
3142
 *	@param	int   	    $page				Numero of page to show in navigation links (required)
3143
 *	@param	string	    $file				Url of page (required)
3144
 *	@param	string	    $options         	More parameters for links ('' by default, does not include sortfield neither sortorder)
3145
 *	@param	string    	$sortfield       	Field to sort on ('' by default)
3146
 *	@param	string	    $sortorder       	Order to sort ('' by default)
3147
 *	@param	string	    $center          	Strin gin the middle ('' by default). We often find here string $massaction comming from $form->selectMassAction() 
3148
 *	@param	int		    $num				Number of records found by select with limit+1
3149
 *	@param	int		    $totalnboflines		Total number of records/lines for all pages (if known). Use a negative value to not show number.
3150
 *	@param	string	    $picto				Icon to use before title (should be a 32x32 transparent png file)
3151
 *	@param	int		    $pictoisfullpath	1=Icon name is a full absolute url of image
3152
 *  @param	string	    $morehtml			More html to show
3153
 *  @param  string      $morecss            More css to the table
3154
 *  @param  int         $limit              Max number of lines (-1 = use default, 0 = no limit, > 0 = limit).
3155
 *  @param  int         $hideselectlimit    Force to hide select limit
3156
 *	@return	void
3157
 */
3158
function print_barre_liste($titre, $page, $file, $options='', $sortfield='', $sortorder='', $center='', $num=-1, $totalnboflines=0, $picto='title_generic.png', $pictoisfullpath=0, $morehtml='', $morecss='', $limit=-1, $hideselectlimit=0)
3159
{
3160
	global $conf,$langs;
3161
	
3162
	$savlimit = $limit;
3163
    $savtotalnboflines = $totalnboflines;
3164
    $totalnboflines=abs($totalnboflines);
3165
    
3166
	if ($picto == 'setup') $picto='title_setup.png';
3167
	if (($conf->browser->name == 'ie') && $picto=='title_generic.png') $picto='title.gif';
3168
	if ($limit < 0) $limit = $conf->liste_limit;
3169
	if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0)))
3170
	{
3171
		$nextpage = 1;
3172
	}
3173
	else
3174
	{
3175
		$nextpage = 0;
3176
	}
3177
	//print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage;
3178
	
3179
	print "\n";
3180
	print "<!-- Begin title '".$titre."' -->\n";
3181
	print '<table width="100%" border="0" class="notopnoleftnoright'.($morecss?' '.$morecss:'').'" style="margin-bottom: 6px;"><tr>';
3182
3183
	// Left
3184
	//if ($picto && $titre) print '<td class="nobordernopadding hideonsmartphone" width="40" align="left" valign="middle">'.img_picto('', $picto, 'id="pictotitle"', $pictoisfullpath).'</td>';
3185
	print '<td class="nobordernopadding valignmiddle">';
3186
	if ($picto && $titre) print img_picto('', $picto, 'class="hideonsmartphone valignmiddle" id="pictotitle"', $pictoisfullpath);
3187
	print '<div class="titre inline-block">'.$titre;
3188
	if (!empty($titre) && $savtotalnboflines > 0) print ' ('.$totalnboflines.')';
3189
	print '</div></td>';
3190
3191
	// Center
3192
	if ($center)
3193
	{
3194
		print '<td class="nobordernopadding center valignmiddle">'.$center.'</td>';
3195
	}
3196
3197
	// Right
3198
	print '<td class="nobordernopadding valignmiddle" align="right">';
3199
	if ($sortfield) $options .= "&amp;sortfield=".$sortfield;
3200
	if ($sortorder) $options .= "&amp;sortorder=".$sortorder;
3201
	// Show navigation bar
3202
	$pagelist = '';
3203
	if ($savlimit != 0 && ($page > 0 || $num > $limit))
3204
	{
3205
		if ($totalnboflines)	// If we know total nb of lines
3206
		{
3207
			$maxnbofpage=(empty($conf->dol_optimize_smallscreen) ? 4 : 1);		// page nb before and after selected page + ... + first or last
3208
3209
			if ($limit > 0) $nbpages=ceil($totalnboflines/$limit);
3210
			else $nbpages=1;
3211
			$cpt=($page-$maxnbofpage);
3212
			if ($cpt < 0) { $cpt=0; }
3213
3214
			if ($cpt>=1)
3215
			{
3216
				$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a '.(($conf->dol_use_jmobile != 4)?'':'data-role="button" ').'href="'.$file.'?page=0'.$options.'">1</a></li>';
3217
				if ($cpt > 2) $pagelist.='<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="inactive"':'data-role="button"').'>...</span></li>';
3218
				else if ($cpt == 2) $pagelist.='<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a '.(($conf->dol_use_jmobile != 4)?'':'data-role="button" ').'href="'.$file.'?page=1'.$options.'">2</a></li>';
3219
			}
3220
3221
			do
3222
			{
3223
				if ($cpt==$page)
3224
				{
3225
					$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="active"':'data-role="button"').'>'.($page+1).'</span></li>';
3226
				}
3227
				else
3228
				{
3229
					$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a '.(($conf->dol_use_jmobile != 4)?'':'data-role="button" ').'href="'.$file.'?page='.$cpt.$options.'">'.($cpt+1).'</a></li>';
3230
				}
3231
				$cpt++;
3232
			}
3233
			while ($cpt < $nbpages && $cpt<=$page+$maxnbofpage);
3234
3235
			if ($cpt<$nbpages)
3236
			{
3237
				if ($cpt<$nbpages-2) $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="inactive"':'data-role="button"').'>...</span></li>';
3238
				else if ($cpt == $nbpages-2) $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a '.(($conf->dol_use_jmobile != 4)?'':'data-role="button" ').'href="'.$file.'?page='.($nbpages-2).$options.'">'.($nbpages - 1).'</a></li>';
3239
				$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a '.(($conf->dol_use_jmobile != 4)?'':'data-role="button" ').'href="'.$file.'?page='.($nbpages-1).$options.'">'.$nbpages.'</a></li>';
3240
			}
3241
		}
3242
		else
3243
		{
3244
			$pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="active"':'data-role="button"').'>'.($page+1)."</li>";
3245
		}
3246
	}
3247
	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
3248
	print '</td>';
3249
3250
	print '</tr></table>'."\n";
3251
	print "<!-- End title -->\n\n";
3252
}
3253
3254
/**
3255
 *	Function to show navigation arrows into lists
3256
 *
3257
 *	@param	int				$page				Number of page
3258
 *	@param	string			$file				Page URL (in most cases provided with $_SERVER["PHP_SELF"])
3259
 *	@param	string			$options         	Other url paramaters to propagate ("" by default, may include sortfield and sortorder)
3260
 *	@param	integer			$nextpage	    	Do we show a next page button
3261
 *	@param	string			$betweenarrows		HTML content to show between arrows. MUST contains '<li> </li>' tags or '<li><span> </span></li>'.
3262
 *  @param	string			$afterarrows		HTML content to show after arrows. Must NOT contains '<li> </li>' tags.
3263
 *  @param  int             $limit              Max nb of record to show  (-1 = no combo with limit, 0 = no limit, > 0 = limit)
3264
 *	@param	int		        $totalnboflines		Total number of records/lines for all pages (if known)
3265
 *  @param  int             $hideselectlimit    Force to hide select limit
3266
 *	@return	void
3267
 */
3268
function print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $hideselectlimit=0)
3269
{
3270
	global $conf, $langs;
3271
3272
	print '<div class="pagination"><ul>';
3273
	if ((int) $limit >= 0 && empty($hideselectlimit))
3274
	{
3275
	    $pagesizechoices='10:10,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000,5000:5000';
3276
	    //$pagesizechoices.=',0:'.$langs->trans("All");     // Not yet supported
3277
	    //$pagesizechoices.=',2:2';
3278
	    if (! empty($conf->global->MAIN_PAGESIZE_CHOICES)) $pagesizechoices=$conf->global->MAIN_PAGESIZE_CHOICES;
3279
	     
3280
        print '<li class="pagination">';
3281
        print '<select class="flat selectlimit" name="limit">';
3282
        $tmpchoice=explode(',',$pagesizechoices);
3283
        $tmpkey=$limit.':'.$limit;
3284
        if (! in_array($tmpkey, $tmpchoice)) $tmpchoice[]=$tmpkey;
3285
        $tmpkey=$conf->liste_limit.':'.$conf->liste_limit;
3286
        if (! in_array($tmpkey, $tmpchoice)) $tmpchoice[]=$tmpkey;
3287
        asort($tmpchoice, SORT_NUMERIC);
3288
        $found=false;
3289
        foreach($tmpchoice as $val)
3290
        {
3291
            $selected='';
3292
            $tmp=explode(':',$val);
3293
            $key=$tmp[0];
3294
            $val=$tmp[1];
3295
            if ($key != '' && $val != '')
3296
            {
3297
                if ((int) $key == (int) $limit)
3298
                {
3299
                    $selected = ' selected="selected"';
3300
                    $found = true;
3301
                }
3302
                print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
3303
            }
3304
        }
3305
        print '</select>';
3306
        if ($conf->use_javascript_ajax)
3307
        {
3308
            print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
3309
            		<script type="text/javascript">
3310
                	jQuery(document).ready(function () {
3311
            	  		jQuery(".selectlimit").change(function() {
3312
                            console.log("Change limit. Send submit");
3313
                            $(this).parents(\'form:first\').submit();
3314
            	  		});
3315
                	});
3316
            		</script>
3317
                ';
3318
        }
3319
        print '</li>';	    
3320
	}
3321
	if ($page > 0)
3322
	{
3323
		if (($conf->dol_use_jmobile != 4)) print '<li class="pagination"><a class="paginationprevious" href="'.$file.'?page='.($page-1).$options.'"><</a></li>';
3324
		else print '<li><a data-role="button" data-icon="arrow-l" data-iconpos="left" href="'.$file.'?page='.($page-1).$options.'">'.$langs->trans("Previous").'</a></li>';
3325
	}
3326
	if ($betweenarrows)
3327
	{
3328
		print $betweenarrows;
3329
	}
3330
	if ($nextpage > 0)
3331
	{
3332
		if (($conf->dol_use_jmobile != 4)) print '<li class="pagination"><a class="paginationnext" href="'.$file.'?page='.($page+1).$options.'">></a></li>';
3333
		else print '<li><a data-role="button" data-icon="arrow-r" data-iconpos="right" href="'.$file.'?page='.($page+1).$options.'">'.$langs->trans("Next").'</a></li>';
3334
	}
3335
	if ($afterarrows)
3336
	{
3337
		print '<li class="paginationafterarrows">';
3338
		print $afterarrows;
3339
		print '</li>';
3340
	}
3341
	print '</ul></div>'."\n";
3342
}
3343
3344
3345
/**
3346
 *	Return a string with VAT rate label formated for view output
3347
 *	Used into pdf and HTML pages
3348
 *
3349
 *	@param	string	$rate			Rate value to format ('19.6', '19,6', '19.6%', '19,6%', '19.6 (CODEX)', ...)
3350
 *  @param	boolean	$addpercent		Add a percent % sign in output
3351
 *	@param	int		$info_bits		Miscellaneous information on vat (0=Default, 1=French NPR vat)
3352
 *	@param	int		$usestarfornpr	1=Use '*' for NPR vat rate intead of MAIN_LABEL_MENTION_NPR
3353
 *  @return	string					String with formated amounts ('19,6' or '19,6%' or '8.5% (NPR)' or '8.5% *' or '19,6 (CODEX)')
3354
 */
3355
function vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0)
3356
{
3357
    $morelabel='';
3358
    
3359
    if (preg_match('/%/',$rate))
3360
	{
3361
		$rate=str_replace('%','',$rate);
3362
		$addpercent=true;
3363
	}
3364
	if (preg_match('/\((.*)\)/',$rate,$reg))
3365
	{
3366
	    $morelabel=' ('.$reg[1].')';
3367
	    $rate=preg_replace('/\s*'.preg_quote($morelabel,'/').'/','',$rate);
3368
	}
3369
	if (preg_match('/\*/',$rate) || preg_match('/'.constant('MAIN_LABEL_MENTION_NPR').'/i',$rate))
3370
	{
3371
		$rate=str_replace('*','',$rate);
3372
		$info_bits |= 1;
3373
	}
3374
3375
	$ret=price($rate,0,'',0,0).($addpercent?'%':'');
3376
	if ($info_bits & 1) $ret.=' '.($usestarfornpr?'*':constant('MAIN_LABEL_MENTION_NPR'));
3377
	$ret.=$morelabel;
3378
	return $ret;
3379
}
3380
3381
3382
/**
3383
 *		Function to format a value into an amount for visual output
3384
 *		Function used into PDF and HTML pages
3385
 *
3386
 *		@param	float		$amount			Amount to format
3387
 *		@param	integer		$form			Type of format, HTML or not (not by default)
3388
 *		@param	Translate	$outlangs		Object langs for output
3389
 *		@param	int			$trunc			1=Truncate if there is too much decimals (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.
3390
 *		@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)
3391
 *		@param	int			$forcerounding	Force the number of decimal to forcerounding decimal (-1=do not force)
3392
 *		@param	string		$currency_code	To add currency symbol (''=add nothing, 'auto'=Use default currency, 'XXX'=add currency symbols for XXX currency)
3393
 *		@return	string						Chaine avec montant formate
3394
 *
3395
 *		@see	price2num					Revert function of price
3396
 */
3397
function price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
3398
{
3399
	global $langs,$conf;
3400
3401
	// Clean parameters
3402
	if (empty($amount)) $amount=0;	// To have a numeric value if amount not defined or = ''
3403
	$amount = (is_numeric($amount)?$amount:0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
3404
	if ($rounding < 0) $rounding=min($conf->global->MAIN_MAX_DECIMALS_UNIT,$conf->global->MAIN_MAX_DECIMALS_TOT);
3405
	$nbdecimal=$rounding;
3406
3407
	// Output separators by default (french)
3408
	$dec=','; $thousand=' ';
3409
3410
	// If $outlangs not forced, we use use language
3411
	if (! is_object($outlangs)) $outlangs=$langs;
3412
3413
	if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal")  $dec=$outlangs->transnoentitiesnoconv("SeparatorDecimal");
3414
	if ($outlangs->transnoentitiesnoconv("SeparatorThousand")!= "SeparatorThousand") $thousand=$outlangs->transnoentitiesnoconv("SeparatorThousand");
3415
	if ($thousand == 'None') $thousand='';
3416
	else if ($thousand == 'Space') $thousand=' ';
3417
	//print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
3418
3419
	//print "amount=".$amount."-";
3420
	$amount = str_replace(',','.',$amount);	// should be useless
3421
	//print $amount."-";
3422
	$datas = explode('.',$amount);
3423
	$decpart = isset($datas[1])?$datas[1]:'';
3424
	$decpart = preg_replace('/0+$/i','',$decpart);	// Supprime les 0 de fin de partie decimale
3425
	//print "decpart=".$decpart."<br>";
3426
	$end='';
3427
3428
	// We increase nbdecimal if there is more decimal than asked (to not loose information)
3429
	if (dol_strlen($decpart) > $nbdecimal) $nbdecimal=dol_strlen($decpart);
3430
	// Si on depasse max
3431
	if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN)
3432
	{
3433
		$nbdecimal=$conf->global->MAIN_MAX_DECIMALS_SHOWN;
3434
		if (preg_match('/\.\.\./i',$conf->global->MAIN_MAX_DECIMALS_SHOWN))
3435
		{
3436
			// Si un affichage est tronque, on montre des ...
3437
			$end='...';
3438
		}
3439
	}
3440
3441
	// If force rounding
3442
	if ($forcerounding >= 0) $nbdecimal = $forcerounding;
3443
3444
	// Format number
3445
	$output=number_format($amount, $nbdecimal, $dec, $thousand);
3446
	if ($form)
3447
	{
3448
		$output=preg_replace('/\s/','&nbsp;',$output);
3449
		$output=preg_replace('/\'/','&#039;',$output);
3450
	}
3451
	// Add symbol of currency if requested
3452
	$cursymbolbefore=$cursymbolafter='';
3453
	if ($currency_code)
3454
	{
3455
		if ($currency_code == 'auto') $currency_code=$conf->currency;
3456
3457
		$listofcurrenciesbefore=array('USD','GBP','AUD','MXN');
3458
		if (in_array($currency_code,$listofcurrenciesbefore)) $cursymbolbefore.=$outlangs->getCurrencySymbol($currency_code);
3459
		else
3460
		{
3461
			$tmpcur=$outlangs->getCurrencySymbol($currency_code);
3462
			$cursymbolafter.=($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
3463
		}
3464
	}
3465
	$output=$cursymbolbefore.$output.$end.($cursymbolafter?' ':'').$cursymbolafter;
3466
3467
	return $output;
3468
}
3469
3470
/**
3471
 *	Function that return a number with universal decimal format (decimal separator is '.') from an amount typed by a user.
3472
 *	Function to use on each input amount before any numeric test or database insert
3473
 *
3474
 *	@param	float	$amount			Amount to convert/clean
3475
 *	@param	string	$rounding		''=No rounding
3476
 * 									'MU'=Round to Max unit price (MAIN_MAX_DECIMALS_UNIT)
3477
 *									'MT'=Round to Max for totals with Tax (MAIN_MAX_DECIMALS_TOT)
3478
 *									'MS'=Round to Max Shown (MAIN_MAX_DECIMALS_SHOWN)
3479
 * 	@param	int		$alreadysqlnb	Put 1 if you know that content is already universal format number
3480
 *	@return	string					Amount with universal numeric format (Example: '99.99999') or unchanged text if conversion fails.
3481
 *
3482
 *	@see    price					Opposite function of price2num
3483
 */
3484
function price2num($amount,$rounding='',$alreadysqlnb=0)
3485
{
3486
	global $langs,$conf;
3487
3488
	// Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
3489
	// Numbers must be '1234.56'
3490
	// Decimal delimiter for PHP and database SQL requests must be '.'
3491
	$dec=','; $thousand=' ';
3492
	if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal")  $dec=$langs->transnoentitiesnoconv("SeparatorDecimal");
3493
	if ($langs->transnoentitiesnoconv("SeparatorThousand")!= "SeparatorThousand") $thousand=$langs->transnoentitiesnoconv("SeparatorThousand");
3494
	if ($thousand == 'None') $thousand='';
3495
	elseif ($thousand == 'Space') $thousand=' ';
3496
	//print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
3497
3498
	// Convert value to universal number format (no thousand separator, '.' as decimal separator)
3499
	if ($alreadysqlnb != 1)	// If not a PHP number or unknown, we change format
3500
	{
3501
		//print 'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
3502
3503
		// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
3504
		// to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
3505
		if (is_numeric($amount))
3506
		{
3507
			// We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
3508
			$temps=sprintf("%0.10F",$amount-intval($amount));	// temps=0.0000000000 or 0.0000200000 or 9999.1000000000
3509
			$temps=preg_replace('/([\.1-9])0+$/','\\1',$temps); // temps=0. or 0.00002 or 9999.1
3510
			$nbofdec=max(0,dol_strlen($temps)-2);	// -2 to remove "0."
3511
			$amount=number_format($amount,$nbofdec,$dec,$thousand);
3512
		}
3513
		//print "QQ".$amount.'<br>';
3514
3515
		// Now make replace (the main goal of function)
3516
		if ($thousand != ',' && $thousand != '.') $amount=str_replace(',','.',$amount);	// To accept 2 notations for french users
3517
		$amount=str_replace(' ','',$amount);		// To avoid spaces
3518
		$amount=str_replace($thousand,'',$amount);	// Replace of thousand before replace of dec to avoid pb if thousand is .
3519
		$amount=str_replace($dec,'.',$amount);
3520
	}
3521
3522
	// Now, make a rounding if required
3523
	if ($rounding)
3524
	{
3525
		$nbofdectoround='';
3526
		if ($rounding == 'MU')     $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_UNIT;
3527
		elseif ($rounding == 'MT') $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_TOT;
3528
		elseif ($rounding == 'MS') $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_SHOWN;
3529
		elseif (is_numeric($rounding))  $nbofdectoround=$rounding; 	// For admin info page
3530
		//print "RR".$amount.' - '.$nbofdectoround.'<br>';
3531
		if (dol_strlen($nbofdectoround)) $amount = round($amount,$nbofdectoround);	// $nbofdectoround can be 0.
3532
		else return 'ErrorBadParameterProvidedToFunction';
3533
		//print 'SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
3534
3535
		// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
3536
		// to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
3537
		if (is_numeric($amount))
3538
		{
3539
			// We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
3540
			$temps=sprintf("%0.10F",$amount-intval($amount));	// temps=0.0000000000 or 0.0000200000 or 9999.1000000000
3541
			$temps=preg_replace('/([\.1-9])0+$/','\\1',$temps); // temps=0. or 0.00002 or 9999.1
3542
			$nbofdec=max(0,dol_strlen($temps)-2);	// -2 to remove "0."
3543
			$amount=number_format($amount,min($nbofdec,$nbofdectoround),$dec,$thousand);		// Convert amount to format with dolibarr dec and thousand
3544
		}
3545
		//print "TT".$amount.'<br>';
3546
3547
		// Always make replace because each math function (like round) replace
3548
		// with local values and we want a number that has a SQL string format x.y
3549
		if ($thousand != ',' && $thousand != '.') $amount=str_replace(',','.',$amount);	// To accept 2 notations for french users
3550
		$amount=str_replace(' ','',$amount);		// To avoid spaces
3551
		$amount=str_replace($thousand,'',$amount);	// Replace of thousand before replace of dec to avoid pb if thousand is .
3552
		$amount=str_replace($dec,'.',$amount);
3553
	}
3554
3555
	return $amount;
3556
}
3557
3558
3559
/**
3560
 * Output a dimension with best unit
3561
 *  
3562
 * @param   float       $dimension      Dimension
3563
 * @param   int         $unit           Unit of dimension (0, -3, ...)
3564
 * @param   string      $type           'weight', 'volume', ...
3565
 * @param   Translate   $outputlangs    Translate language object
3566
 * @param   int         $round          -1 = non rounding, x = number of decimal
3567
 * @param   string      $forceunitoutput    'no' or numeric (-3, -6, ...) compared to $unit
3568
 * @return  string                      String to show dimensions
3569
 */
3570
function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no')
3571
{
3572
    require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
3573
    
3574
    if (($forceunitoutput == 'no' && $dimension < 1/10000) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) 
3575
    {
3576
        $dimension = $dimension * 1000000;
3577
        $unit = $unit - 6; 
3578
    }
3579
    elseif (($forceunitoutput == 'no' && $dimension < 1/10) || (is_numeric($forceunitoutput) && $forceunitoutput == -3))
3580
    {
3581
        $dimension = $dimension * 1000;
3582
        $unit = $unit - 3; 
3583
    }
3584
    elseif (($forceunitoutput == 'no' && $dimension > 100000000) || (is_numeric($forceunitoutput) && $forceunitoutput == 6))
3585
    {
3586
        $dimension = $dimension / 1000000;
3587
        $unit = $unit + 6;
3588
    }
3589
    elseif (($forceunitoutput == 'no' && $dimension > 100000) || (is_numeric($forceunitoutput) && $forceunitoutput == 3))
3590
    {
3591
        $dimension = $dimension / 1000;
3592
        $unit = $unit + 3;
3593
    }
3594
    
3595
    $ret=price($dimension, 0, $outputlangs, 0, 0, $round).' '.measuring_units_string($unit, $type);
3596
    
3597
    return $ret;
3598
}
3599
3600
3601
/**
3602
 *	Return localtax rate for a particular vat, when selling a product with vat $vatrate, from a $thirdparty_buyer to a $thirdparty_seller
3603
 *  Note: This function applies same rules than get_default_tva
3604
 *
3605
 * 	@param	float		$vatrate		        Vat rate. Can be '8.5' or '8.5 (VATCODEX)' for example
3606
 * 	@param  int			$local		         	Local tax to search and return (1 or 2 return only tax rate 1 or tax rate 2)
3607
 *  @param  Societe		$thirdparty_buyer    	Object of buying third party
3608
 *  @param	Societe		$thirdparty_seller		Object of selling third party
3609
 *  @param	int			$vatnpr					If vat rate is NPR or not
3610
 * 	@return	mixed			   					0 if not found, localtax rate if found
3611
 *  @see get_default_tva
3612
 */
3613
function get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
3614
{
3615
	global $db, $conf, $mysoc;
3616
3617
	if (empty($thirdparty_seller) || ! is_object($thirdparty_seller)) $thirdparty_seller=$mysoc;
3618
3619
	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);
3620
3621
	$vatratecleaned = $vatrate;
3622
	if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
3623
	{
3624
        $vatratecleaned = trim($reg[1]);
3625
	    $vatratecode = $reg[2];
3626
	}
3627
	
3628
	/*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
3629
	{
3630
		return 0;
3631
	}*/
3632
	
3633
	// Some test to guess with no need to make database access
3634
	if ($mysoc->country_code == 'ES') // For spain localtaxes 1 and 2, tax is qualified if buyer use local taxe
3635
	{
3636
		if ($local == 1)
3637
		{
3638
			if (! $mysoc->localtax1_assuj || (string) $vatratecleaned == "0") return 0;
3639
			if ($thirdparty_seller->id == $mysoc->id)
3640
			{
3641
				if (! $thirdparty_buyer->localtax1_assuj) return 0;
3642
			}
3643
			else
3644
			{
3645
				if (! $thirdparty_seller->localtax1_assuj) return 0;
3646
			}
3647
		}
3648
3649
		if ($local == 2)
3650
		{
3651
			if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
3652
			if ($thirdparty_seller->id == $mysoc->id)
3653
			{
3654
				if (! $thirdparty_buyer->localtax2_assuj) return 0;
3655
			}
3656
			else
3657
			{
3658
				if (! $thirdparty_seller->localtax2_assuj) return 0;
3659
			}
3660
		}
3661
	}
3662
	else
3663
	{
3664
		if ($local == 1 && ! $thirdparty_seller->localtax1_assuj) return 0;
3665
		if ($local == 2 && ! $thirdparty_seller->localtax2_assuj) return 0;
3666
	}
3667
	//if ($local == 0 && ! $thirdparty_seller->localtax1_assuj && ! $thirdparty_seller->localtax2_assuj) return array('localtax1'=>0,'localtax2'=>0);
3668
3669
	// Do not enabled this. We want localtax that match the vat rate.
3670
	// If we forced a vat, we must also force local tax
3671
	/*
3672
	if (is_object($thirdparty_buyer))
3673
	{
3674
		if ($thirdparty_seller->country_code != $thirdparty_buyer->country_code) return 0;
3675
	}*/
3676
3677
	// Search local taxes
3678
	if ($mysoc->country_code == 'ES' || ! empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY))
3679
	{
3680
    	if ($local==1)
3681
    	{
3682
    		if ($thirdparty_seller != $mysoc)
3683
    		{
3684
    			if (!isOnlyOneLocalTax($local))  // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
3685
    			{
3686
    				return $thirdparty_seller->localtax1_value;
3687
    			}
3688
    		}
3689
    		else  // i am the seller
3690
    		{
3691
    			if (!isOnlyOneLocalTax($local))  // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
3692
    			{
3693
    				return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
3694
    			}
3695
    		}
3696
    	}
3697
    	if ($local==2)
3698
    	{
3699
    		if ($thirdparty_seller != $mysoc)
3700
    		{
3701
    			if (!isOnlyOneLocalTax($local))  // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
3702
    			// TODO We should also return value defined on thirdparty only if defined
3703
    			{
3704
    				return $thirdparty_seller->localtax2_value;
3705
    			}
3706
    		}
3707
    		else  // i am the seller
3708
    		{
3709
    			if (!isOnlyOneLocalTax($local))  // This is for spain only, we don't return value found into datbase even if there is only one locatax vat.
3710
    			{
3711
    				return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
3712
    			}
3713
    		}
3714
    	}
3715
	}
3716
3717
	// By default, search value of local tax on line of common tax
3718
	$sql  = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
3719
   	$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
3720
   	$sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$thirdparty_seller->country_code."'";
3721
   	$sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
3722
   	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...
3723
   	else $sql.= " AND t.recuperableonly ='".$npr."'";
0 ignored issues
show
Bug introduced by
The variable $npr 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...
3724
   	dol_syslog("get_localtax", LOG_DEBUG);
3725
   	$resql=$db->query($sql);
3726
3727
   	if ($resql)
3728
   	{
3729
   		$obj = $db->fetch_object($resql);
3730
   		if ($local==1) return $obj->localtax1;
3731
   		elseif ($local==2) return $obj->localtax2;
3732
	}
3733
	
3734
	return 0;
3735
}
3736
3737
3738
/**
3739
 * Return true if LocalTax (1 or 2) is unique.
3740
 * Example: If localtax1 is 5 on line with highest common vat rate, return true
3741
 * Example: If localtax1 is 5:8:15 on line with highest common vat rate, return false
3742
 *
3743
 * @param   int 	$local	Local tax to test (1 or 2)
3744
 * @return  boolean 		True if LocalTax have multiple values, False if not
3745
 */
3746
function isOnlyOneLocalTax($local)
3747
{
3748
	$tax=get_localtax_by_third($local);
3749
3750
	$valors=explode(":", $tax);
3751
3752
	if (count($valors)>1)
3753
	{
3754
		return false;
3755
	}
3756
	else
3757
	{
3758
		return true;
3759
	}
3760
}
3761
3762
/**
3763
 * Get values of localtaxes (1 or 2) for company country for the common vat with the highest value
3764
 *
3765
 * @param	int		$local 	LocalTax to get
3766
 * @return	number			Values of localtax
3767
 */
3768
function get_localtax_by_third($local)
3769
{
3770
	global $db, $mysoc;
3771
	$sql ="SELECT t.localtax1, t.localtax2 ";
3772
	$sql.=" FROM ".MAIN_DB_PREFIX."c_tva as t inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=t.fk_pays";
3773
	$sql.=" WHERE c.code = '".$mysoc->country_code."' AND t.active = 1 AND t.taux=(";
3774
	$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";
3775
	$sql.="  WHERE c.code = '".$mysoc->country_code."' AND tt.active = 1";
3776
	$sql.="  )";
3777
3778
	$resql=$db->query($sql);
3779
	if ($resql)
3780
	{
3781
		$obj = $db->fetch_object($resql);
3782
		if ($local==1) return $obj->localtax1;
3783
		elseif ($local==2) return $obj->localtax2;
3784
	}
3785
3786
	return 0;
3787
3788
}
3789
3790
3791
/**
3792
 *  Get vat rate and npr from id.
3793
 *  You can call getLocalTaxesFromRate after to get other fields 
3794
 *
3795
 *  @param	int      $vatrowid			Line ID into vat rate table.
3796
 *  @return	array    	  				array(localtax_type1(1-6 / 0 if not found), rate of localtax1, ...)
3797
 */
3798
function getTaxesFromId($vatrowid)
3799
{
3800
    global $db, $mysoc;
3801
3802
    dol_syslog("getTaxesFromId vatrowid=".$vatrowid);
3803
3804
    // Search local taxes
3805
    $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr";
3806
    $sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t";
3807
    $sql.= " WHERE t.rowid ='".$vatrowid."'";
3808
3809
    $resql=$db->query($sql);
3810
    if ($resql)
3811
    {
3812
        $obj = $db->fetch_object($resql);
3813
3814
        return array('rowid'=>$obj->rowid, 'code'=>$obj->code, 'rate'=>$obj->rate, 'npr'=>$obj->npr);
3815
    }
3816
    else dol_print_error($db);
3817
3818
    return array();
3819
}
3820
3821
/**
3822
 *  Get type and rate of localtaxes for a particular vat rate/country fo thirdparty
3823
 *  TODO
3824
 *  This function is ALSO called to retrieve type for building PDF. Such call of function must be removed.
3825
 *  Instead this function must be called when adding a line to get the array of localtax and type, and then
3826
 *  provide it to the function calcul_price_total.
3827
 *
3828
 *  @param	string  $vatrate			VAT 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.
3829
 *  @param	int		$local              Number of localtax (1 or 2, or 0 to return 1 & 2)
3830
 *  @param	Societe	$buyer         		Company object
3831
 *  @param	Societe	$seller        		Company object
3832
 *  @param  int     $firstparamisid     1 if first param is id into table (use this if you can)
3833
 *  @return	array    	  				array(localtax_type1(1-6 / 0 if not found), rate of localtax1, ...)
3834
 */
3835
function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
3836
{
3837
	global $db, $mysoc;
3838
3839
	dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
3840
3841
	$vatratecleaned = $vatrate;
3842
	if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg))      // If vat is "xx (yy)"
3843
	{
3844
	    $vatratecleaned = $reg[1];
3845
	    $vatratecode = $reg[2];
3846
	}
3847
	
3848
	// Search local taxes
3849
	$sql  = "SELECT t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy";
3850
	$sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
3851
	if ($firstparamisid) $sql.= " WHERE t.rowid ='".$vatrate."'";
3852
	else
3853
	{
3854
	    $sql.=", ".MAIN_DB_PREFIX."c_country as c";
3855
    	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 ??
3856
    	else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
3857
    	$sql.= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
3858
    	if ($vatratecode) $sql.= " AND t.code ='".$vatratecode."'";
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...
3859
	}
3860
	
3861
	$resql=$db->query($sql);
3862
	if ($resql)
3863
	{
3864
		$obj = $db->fetch_object($resql);
3865
		if ($local == 1)
3866
		{
3867
			if (! isOnlyOneLocalTax(1))
3868
			{
3869
				return array($obj->localtax1_type, get_localtax($vatrate, $local, $buyer, $seller), $obj->accountancy_code_sell,$obj->accountancy_code_buy);
3870
			}
3871
			else
3872
			{
3873
				return array($obj->localtax1_type, $obj->localtax1,$obj->accountancy_code_sell,$obj->accountancy_code_buy);
3874
			}
3875
		}
3876
		elseif ($local == 2)
3877
		{
3878
			if (! isOnlyOneLocalTax(2))
3879
			{
3880
				return array($obj->localtax2_type, get_localtax($vatrate, $local, $buyer, $seller),$obj->accountancy_code_sell,$obj->accountancy_code_buy);
3881
			}
3882
			else
3883
			{
3884
				return array($obj->localtax2_type, $obj->localtax2,$obj->accountancy_code_sell,$obj->accountancy_code_buy);
3885
			}
3886
		}
3887
		else
3888
		{
3889
			if(! isOnlyOneLocalTax(1))
3890
			{
3891
				if(! isOnlyOneLocalTax(2))
3892
				{
3893
					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);
3894
				}
3895
				else
3896
				{
3897
					return array($obj->localtax1_type, get_localtax($vatrate, 1, $buyer, $seller), $obj->localtax2_type, $obj->localtax2,$obj->accountancy_code_sell,$obj->accountancy_code_buy);
3898
				}
3899
			}
3900
			else
3901
			{
3902
				if(! isOnlyOneLocalTax(2))
3903
				{
3904
					return array($obj->localtax1_type, $obj->localtax1, $obj->localtax2_type,get_localtax($vatrate, 2, $buyer, $seller) ,$obj->accountancy_code_sell,$obj->accountancy_code_buy);
3905
				}
3906
				else
3907
				{
3908
					return array($obj->localtax1_type, $obj->localtax1, $obj->localtax2_type, $obj->localtax2,$obj->accountancy_code_sell,$obj->accountancy_code_buy);
3909
				}
3910
			}
3911
		}
3912
	}
3913
3914
	return 0;
3915
}
3916
3917
/**
3918
 *	Return vat rate of a product in a particular selling country or default country vat if product is unknown
3919
 *
3920
 *  @param	int			$idprod          	Id of product or 0 if not a predefined product
3921
 *  @param  Societe		$thirdparty_seller  Thirdparty with a ->country_code defined (FR, US, IT, ...)
3922
 *	@param	int			$idprodfournprice	Id product_fournisseur_price (for "supplier" order/invoice)
3923
 *  @return float					        Vat rate
3924
 *  @see get_product_localtax_for_country
3925
 */
3926
function get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice=0)
3927
{
3928
	global $db,$conf,$mysoc;
3929
3930
	require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3931
3932
	$ret=0;
3933
	$found=0;
3934
3935
	if ($idprod > 0)
3936
	{
3937
		// Load product
3938
		$product=new Product($db);
3939
		$result=$product->fetch($idprod);
3940
3941
		if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
3942
		{
3943
			if ($idprodfournprice > 0)     // We want vat for product for a "supplier" order or invoice
3944
			{
3945
				$product->get_buyprice($idprodfournprice,0,0,0);
3946
				$ret=$product->vatrate_supplier;
3947
			}
3948
			else
3949
			{
3950
				$ret=$product->tva_tx;    // Default vat of product we defined
3951
			}
3952
			$found=1;
3953
		}
3954
		else
3955
		{
3956
			// TODO Read default product vat according to countrycode and product
3957
3958
3959
		}
3960
	}
3961
3962
	if (! $found)
3963
	{
3964
		if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS))
3965
		{
3966
			// If vat of product for the country not found or not defined, we return higher vat of country.
3967
			$sql = "SELECT taux as vat_rate";
3968
			$sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
3969
			$sql.= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
3970
			$sql.= " ORDER BY t.taux DESC, t.recuperableonly ASC";
3971
			$sql.= $db->plimit(1);
3972
3973
			$resql=$db->query($sql);
3974
			if ($resql)
3975
			{
3976
				$obj=$db->fetch_object($resql);
3977
				if ($obj)
3978
				{
3979
					$ret=$obj->vat_rate;
3980
				}
3981
				$db->free($sql);
3982
			}
3983
			else dol_print_error($db);
3984
		}
3985
		else $ret=$conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;    // Forced value if autodetect fails
3986
	}
3987
3988
	dol_syslog("get_product_vat_for_country: ret=".$ret);
3989
	return $ret;
3990
}
3991
3992
/**
3993
 *	Return localtax vat rate of a product in a particular selling country or default country vat if product is unknown
3994
 *
3995
 *  @param	int		$idprod         		Id of product
3996
 *  @param  int		$local          		1 for localtax1, 2 for localtax 2
3997
 *  @param  Societe	$thirdparty_seller    	Thirdparty with a ->country_code defined (FR, US, IT, ...)
3998
 *  @return int             				<0 if KO, Vat rate if OK
3999
 *  @see get_product_vat_for_country
4000
 */
4001
function get_product_localtax_for_country($idprod, $local, $thirdparty_seller)
4002
{
4003
	global $db,$mysoc;
4004
4005
	if (! class_exists('Product')) {
4006
		require DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4007
	}
4008
4009
	$ret=0;
4010
	$found=0;
4011
4012
	if ($idprod > 0)
4013
	{
4014
		// Load product
4015
		$product=new Product($db);
4016
		$result=$product->fetch($idprod);
4017
4018
		if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
4019
		{
4020
			/* Not defined yet, so we don't use this
4021
			if ($local==1) $ret=$product->localtax1_tx;
4022
			elseif ($local==2) $ret=$product->localtax2_tx;
4023
			$found=1;
4024
			*/
4025
		}
4026
		else
4027
		{
4028
			// TODO Read default product vat according to countrycode and product
4029
4030
4031
		}
4032
	}
4033
4034
	if (! $found)
4035
	{
4036
		// If vat of product for the country not found or not defined, we return higher vat of country.
4037
		$sql = "SELECT taux as vat_rate, localtax1, localtax2";
4038
		$sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4039
		$sql.= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
4040
		$sql.= " ORDER BY t.taux DESC, t.recuperableonly ASC";
4041
		$sql.= $db->plimit(1);
4042
4043
		$resql=$db->query($sql);
4044
		if ($resql)
4045
		{
4046
			$obj=$db->fetch_object($resql);
4047
			if ($obj)
4048
			{
4049
				if ($local==1) $ret=$obj->localtax1;
4050
				elseif ($local==2) $ret=$obj->localtax2;
4051
			}
4052
		}
4053
		else dol_print_error($db);
4054
	}
4055
4056
	dol_syslog("get_product_localtax_for_country: ret=".$ret);
4057
	return $ret;
4058
}
4059
4060
/**
4061
 *	Function that return vat rate of a product line (according to seller, buyer and product vat rate)
4062
 *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
4063
 *	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
4064
 *	 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.
4065
 *	 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
4066
 *	 Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise avec num TVA) intra alors TVA par defaut=0. Fin de regle
4067
 *	 Sinon TVA proposee par defaut=0. Fin de regle.
4068
 *
4069
 *	@param	Societe		$thirdparty_seller    	Objet societe vendeuse
4070
 *	@param  Societe		$thirdparty_buyer   	Objet societe acheteuse
4071
 *	@param  int			$idprod					Id product
4072
 *	@param	int			$idprodfournprice		Id product_fournisseur_price (for supplier order/invoice)
4073
 *	@return float         				      	Vat rate to use, -1 if we can't guess it
4074
 *  @see get_default_npr, get_default_localtax
4075
 */
4076
function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
4077
{
4078
	global $conf;
4079
4080
	// Note: possible values for tva_assuj are 0/1 or franchise/reel
4081
	$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;
4082
4083
	$seller_country_code=$thirdparty_seller->country_code;
4084
	$seller_in_cee=$thirdparty_seller->isInEEC();
4085
4086
	$buyer_country_code=$thirdparty_buyer->country_code;
4087
	$buyer_in_cee=$thirdparty_buyer->isInEEC();
4088
4089
	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:''));
4090
4091
	// 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)
4092
	// we use the buyer VAT.
4093
	if (! empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC))
4094
	{
4095
		if ($seller_in_cee && $buyer_in_cee && ! $thirdparty_buyer->isACompany())
4096
		{
4097
			//print 'VATRULE 0';
4098
			return get_product_vat_for_country($idprod,$thirdparty_buyer,$idprodfournprice);
4099
		}
4100
	}
4101
4102
	// If seller does not use VAT
4103
	if (! $seller_use_vat)
4104
	{
4105
		//print 'VATRULE 1';
4106
		return 0;
4107
	}
4108
4109
	// 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.
4110
4111
	// Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
4112
	if (($seller_country_code == $buyer_country_code)
4113
	|| (in_array($seller_country_code,array('FR,MC')) && in_array($buyer_country_code,array('FR','MC')))) // Warning ->country_code not always defined
4114
	{
4115
		//print 'VATRULE 2';
4116
		return get_product_vat_for_country($idprod,$thirdparty_seller,$idprodfournprice);
4117
	}
4118
4119
	// 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.
4120
	// Not supported
4121
4122
	// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
4123
	// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
4124
	if (($seller_in_cee && $buyer_in_cee))
4125
	{
4126
		$isacompany=$thirdparty_buyer->isACompany();
4127
		if ($isacompany)
4128
		{
4129
			//print 'VATRULE 3';
4130
			return 0;
4131
		}
4132
		else
4133
		{
4134
			//print 'VATRULE 4';
4135
			return get_product_vat_for_country($idprod,$thirdparty_seller,$idprodfournprice);
4136
		}
4137
	}
4138
4139
	// Sinon la TVA proposee par defaut=0. Fin de regle.
4140
	// Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
4141
	//print 'VATRULE 5';
4142
	return 0;
4143
}
4144
4145
4146
/**
4147
 *	Fonction qui renvoie si tva doit etre tva percue recuperable
4148
 *
4149
 *	@param	Societe		$thirdparty_seller    	Thirdparty seller
4150
 *	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
4151
 *  @param  int			$idprod                 Id product
4152
 *  @param	int			$idprodfournprice		Id supplier price for product
4153
 *	@return float       			        	0 or 1
4154
 *  @see get_default_tva, get_default_localtax
4155
 */
4156
function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
4157
{
4158
	global $db;
4159
4160
	if ($idprodfournprice > 0)
4161
	{
4162
		if (! class_exists('ProductFournisseur'))
4163
			require DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
4164
		$prodprice = new ProductFournisseur($db);
4165
		$prodprice->fetch_product_fournisseur_price($idprodfournprice);
4166
		return $prodprice->fourn_tva_npr;
4167
	}
4168
	elseif ($idprod > 0)
4169
	{
4170
		if (! class_exists('Product'))
4171
			require DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4172
		$prod = new Product($db);
4173
		$prod->fetch($idprod);
4174
		return $prod->tva_npr;
4175
	}
4176
4177
	return 0;
4178
}
4179
4180
/**
4181
 *	Function that return localtax of a product line (according to seller, buyer and product vat rate)
4182
 *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
4183
 *	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
4184
 *	 Sinon TVA proposee par defaut=0. Fin de regle.
4185
 *
4186
 *	@param	Societe		$thirdparty_seller    	Thirdparty seller
4187
 *	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
4188
 *  @param	int			$local					Localtax to process (1 or 2)
4189
 *	@param  int			$idprod					Id product
4190
 *	@return integer        				       	localtax, -1 si ne peut etre determine
4191
 *  @see get_default_tva, get_default_npr
4192
 */
4193
function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
4194
{
4195
	global $mysoc;
4196
4197
	if (!is_object($thirdparty_seller)) return -1;
4198
	if (!is_object($thirdparty_buyer)) return -1;
4199
4200
	if ($local==1) // Localtax 1
4201
	{
4202
		if ($mysoc->country_code == 'ES')
4203
		{
4204
			if (is_numeric($thirdparty_buyer->localtax1_assuj) && ! $thirdparty_buyer->localtax1_assuj) return 0;
4205
		}
4206
		else
4207
		{
4208
			// Si vendeur non assujeti a Localtax1, localtax1 par default=0
4209
			if (is_numeric($thirdparty_seller->localtax1_assuj) && ! $thirdparty_seller->localtax1_assuj) return 0;
4210
			if (! is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj=='localtax1off') return 0;
4211
		}
4212
	}
4213
	elseif ($local==2) //I Localtax 2
4214
	{
4215
		// Si vendeur non assujeti a Localtax2, localtax2 par default=0
4216
		if (is_numeric($thirdparty_seller->localtax2_assuj) && ! $thirdparty_seller->localtax2_assuj) return 0;
4217
		if (! is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj=='localtax2off') return 0;
4218
	}
4219
4220
	if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code)
4221
	{
4222
		return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
4223
	}
4224
4225
	return 0;
4226
}
4227
4228
/**
4229
 *	Return yes or no in current language
4230
 *
4231
 *	@param	string	$yesno			Value to test (1, 'yes', 'true' or 0, 'no', 'false')
4232
 *	@param	integer	$case			1=Yes/No, 0=yes/no, 2=Disabled checkbox, 3=Disabled checkbox + Yes/No
4233
 *	@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.
4234
 *	@return	string					HTML string
4235
 */
4236
function yn($yesno, $case=1, $color=0)
4237
{
4238
	global $langs;
4239
	$result='unknown';
4240
	if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') 	// A mettre avant test sur no a cause du == 0
4241
	{
4242
		$result=$langs->trans('yes');
4243
		if ($case == 1 || $case == 3) $result=$langs->trans("Yes");
4244
		if ($case == 2) $result='<input type="checkbox" value="1" checked disabled>';
4245
		if ($case == 3) $result='<input type="checkbox" value="1" checked disabled> '.$result;
4246
4247
		$classname='ok';
4248
	}
4249
	elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false')
4250
	{
4251
		$result=$langs->trans("no");
4252
		if ($case == 1 || $case == 3) $result=$langs->trans("No");
4253
		if ($case == 2) $result='<input type="checkbox" value="0" disabled>';
4254
		if ($case == 3) $result='<input type="checkbox" value="0" disabled> '.$result;
4255
4256
		if ($color == 2) $classname='ok';
4257
		else $classname='error';
4258
	}
4259
	if ($color) return '<font class="'.$classname.'">'.$result.'</font>';
0 ignored issues
show
Bug introduced by
The variable $classname 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...
4260
	return $result;
4261
}
4262
4263
4264
/**
4265
 *	Return a path to have a directory according to object.
4266
 *  New usage:       $conf->product->multidir_output[$object->entity].'/'.get_exdir(0, 0, 0, 1, $object, 'modulepart')
4267
 *  Old usage:       '015' with level 3->"0/1/5/", '015' with level 1->"5/", 'ABC-1' with level 3 ->"0/0/1/" 
4268
 *
4269
 *	@param	string	$num            Id of object (deprecated, $object will be used in future)
4270
 *	@param  int		$level		    Level of subdirs to return (1, 2 or 3 levels). (deprecated, global option will be used in future)
4271
 * 	@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)
4272
 *  @param  int		$withoutslash   0=With slash at end (except if '/', we return ''), 1=without slash at end
4273
 *  @param	Object	$object			Object
4274
 *  @param	string	$modulepart		Type of object ('invoice_supplier, 'donation', 'invoice', ...')
4275
 *  @return	string					Dir to use ending. Example '' or '1/' or '1/2/'
4276
 */
4277
function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart)
4278
{
4279
	global $conf;
4280
4281
	$path = '';
4282
4283
	$arrayforoldpath=array('cheque','user','category','holiday','shipment','supplier_invoice','invoice_supplier','mailing');
4284
	if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) $arrayforoldpath[]='product';	
4285
	if (! empty($level) && in_array($modulepart, $arrayforoldpath))
4286
	{
4287
		// This part should be removed once all code is using "get_exdir" to forge path, with all parameters provided
4288
		if (empty($alpha)) $num = preg_replace('/([^0-9])/i','',$num);
4289
		else $num = preg_replace('/^.*\-/i','',$num);
4290
		$num = substr("000".$num, -$level);
4291
		if ($level == 1) $path = substr($num,0,1);
4292
		if ($level == 2) $path = substr($num,1,1).'/'.substr($num,0,1);
4293
		if ($level == 3) $path = substr($num,2,1).'/'.substr($num,1,1).'/'.substr($num,0,1);
4294
	}
4295
	else
4296
	{
4297
		// TODO
4298
		// We will enhance here a common way of forging path for document storage
4299
		// Here, object->id, object->ref and object->modulepart are required.
4300
        if (in_array($modulepart, array('thirdparty','contact','member')))
4301
        {
4302
            $path=$object->ref?$object->ref:$object->id;
4303
        }
4304
	}
4305
4306
	if (empty($withoutslash) && ! empty($path)) $path.='/';
4307
	return $path;
4308
}
4309
4310
/**
4311
 *	Creation of a directory (this can create recursive subdir)
4312
 *
4313
 *	@param	string	$dir		Directory to create (Separator must be '/'. Example: '/mydir/mysubdir')
4314
 *	@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)
4315
 *  @param	int		$newmask	Mask for new file (Defaults to $conf->global->MAIN_UMASK or 0755 if unavailable). Example: '0444'
4316
 *	@return int         		< 0 if KO, 0 = already exists, > 0 if OK
4317
 */
4318
function dol_mkdir($dir, $dataroot='', $newmask=null)
4319
{
4320
	global $conf;
4321
4322
	dol_syslog("functions.lib::dol_mkdir: dir=".$dir,LOG_INFO);
4323
4324
	$dir_osencoded=dol_osencode($dir);
4325
	if (@is_dir($dir_osencoded)) return 0;
4326
4327
	$nberr=0;
4328
	$nbcreated=0;
4329
4330
	$ccdir='';
4331
	if (! empty($dataroot)) {
4332
		// Remove data root from loop
4333
		$dir = str_replace($dataroot.'/', '', $dir);
4334
		$ccdir = $dataroot.'/';
4335
	}
4336
4337
	$cdir = explode("/", $dir);
4338
	$num=count($cdir);
4339
	for ($i = 0; $i < $num; $i++)
4340
	{
4341
		if ($i > 0) $ccdir .= '/'.$cdir[$i];
4342
		else $ccdir .= $cdir[$i];
4343
		if (preg_match("/^.:$/",$ccdir,$regs)) continue;	// Si chemin Windows incomplet, on poursuit par rep suivant
4344
4345
		// Attention, le is_dir() peut echouer bien que le rep existe.
4346
		// (ex selon config de open_basedir)
4347
		if ($ccdir)
4348
		{
4349
			$ccdir_osencoded=dol_osencode($ccdir);
4350
			if (! @is_dir($ccdir_osencoded))
4351
			{
4352
				dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.",LOG_DEBUG);
4353
4354
				umask(0);
4355
				$dirmaskdec=octdec($newmask);
4356
				if (empty($newmask)) {
4357
					$dirmaskdec = empty( $conf->global->MAIN_UMASK ) ? octdec( '0755' ) : octdec( $conf->global->MAIN_UMASK );
4358
				}
4359
				$dirmaskdec |= octdec('0111');  // Set x bit required for directories
4360
				if (! @mkdir($ccdir_osencoded, $dirmaskdec))
4361
				{
4362
					// Si le is_dir a renvoye une fausse info, alors on passe ici.
4363
					dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.",LOG_WARNING);
4364
					$nberr++;
4365
				}
4366
				else
4367
				{
4368
					dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created",LOG_DEBUG);
4369
					$nberr=0;	// On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
4370
					$nbcreated++;
4371
				}
4372
			}
4373
			else
4374
			{
4375
				$nberr=0;	// On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
4376
			}
4377
		}
4378
	}
4379
	return ($nberr ? -$nberr : $nbcreated);
4380
}
4381
4382
4383
/**
4384
 *	Return picto saying a field is required
4385
 *
4386
 *	@return  string		Chaine avec picto obligatoire
4387
 */
4388
function picto_required()
4389
{
4390
	return '<span class="fieldrequired">*</span>';
4391
}
4392
4393
4394
/**
4395
 *	Clean a string from all HTML tags and entities
4396
 *
4397
 *	@param	string	$StringHtml			String to clean
4398
 *	@param	integer	$removelinefeed		1=Replace also all lines feeds by a space, 0=Only last one are removed
4399
 *  @param  string	$pagecodeto      	Encoding of input/output string
4400
 *	@return string	    				String cleaned
4401
 *
4402
 * 	@see		dol_escape_htmltag
4403
 */
4404
function dol_string_nohtmltag($StringHtml,$removelinefeed=1,$pagecodeto='UTF-8')
4405
{
4406
	$pattern = "/<[^<>]+>/";
4407
	$temp = dol_html_entity_decode($StringHtml,ENT_COMPAT,$pagecodeto);
4408
	$temp = preg_replace($pattern,"",$temp);
4409
4410
	// Supprime aussi les retours
4411
	if ($removelinefeed) $temp=str_replace(array("\r\n","\r","\n")," ",$temp);
4412
4413
	// et les espaces doubles
4414
	while(strpos($temp,"  "))
4415
	{
4416
		$temp = str_replace("  "," ",$temp);
4417
	}
4418
	$CleanString = trim($temp);
4419
	return $CleanString;
4420
}
4421
4422
4423
/**
4424
 * Return first line of text. Cut will depends if content is HTML or not.
4425
 *
4426
 * @param 	string	$text		Input text
4427
 * @return	string				Output text
4428
 * @see dol_nboflines_bis
4429
 */
4430
function dolGetFirstLineOfText($text)
4431
{
4432
	if (dol_textishtml($text))
4433
	{
4434
		$firstline=preg_replace('/<br[^>]*>.*$/s','',$text);		// The s pattern modifier means the . can match newline characters
4435
		$firstline=preg_replace('/<div[^>]*>.*$/s','',$firstline);	// The s pattern modifier means the . can match newline characters
4436
		
4437
	}
4438
	else
4439
	{
4440
    	$firstline=preg_replace('/[\n\r].*/','',$text);
4441
	}
4442
    return $firstline.((strlen($firstline) != strlen($text))?'...':'');
4443
}
4444
4445
4446
/**
4447
 * Replace CRLF in string with a HTML BR tag
4448
 *
4449
 * @param	string	$stringtoencode		String to encode
4450
 * @param	int     $nl2brmode			0=Adding br before \n, 1=Replacing \n by br
4451
 * @param   bool	$forxml             false=Use <br>, true=Use <br />
4452
 * @return	string						String encoded
4453
 * @see dol_nboflines, dolGetFirstLineOfText
4454
 */
4455
function dol_nl2br($stringtoencode,$nl2brmode=0,$forxml=false)
4456
{
4457
	if (!$nl2brmode) {
4458
		return nl2br($stringtoencode, $forxml);
4459
	} else {
4460
		$ret=preg_replace('/(\r\n|\r|\n)/i', ($forxml?'<br />':'<br>'), $stringtoencode);
4461
		return $ret;
4462
	}
4463
}
4464
4465
4466
/**
4467
 *	This function is called to encode a string into a HTML string but differs from htmlentities because
4468
 * 	a detection is done before to see if text is already HTML or not. Also, all entities but &,<,> are converted.
4469
 *  This permits to encode special chars to entities with no double encoding for already encoded HTML strings.
4470
 * 	This function also remove last EOL or BR if $removelasteolbr=1 (default).
4471
 *  For PDF usage, you can show text by 2 ways:
4472
 *              - writeHTMLCell -> param must be encoded into HTML.
4473
 *              - MultiCell -> param must not be encoded into HTML.
4474
 *              Because writeHTMLCell convert also \n into <br>, if function
4475
 *              is used to build PDF, nl2brmode must be 1.
4476
 *
4477
 *	@param	string	$stringtoencode		String to encode
4478
 *	@param	int		$nl2brmode			0=Adding br before \n, 1=Replacing \n by br (for use with FPDF writeHTMLCell function for example)
4479
 *  @param  string	$pagecodefrom       Pagecode stringtoencode is encoded
4480
 *  @param	int		$removelasteolbr	1=Remove last br or lasts \n (default), 0=Do nothing
4481
 *  @return	string						String encoded
4482
 */
4483
function dol_htmlentitiesbr($stringtoencode,$nl2brmode=0,$pagecodefrom='UTF-8',$removelasteolbr=1)
4484
{
4485
	$newstring=$stringtoencode;
4486
	if (dol_textishtml($stringtoencode))	// Check if text is already HTML or not
4487
	{
4488
		$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.
4489
		if ($removelasteolbr) $newstring=preg_replace('/<br>$/i','',$newstring);	// Remove last <br> (remove only last one)
4490
		$newstring=strtr($newstring,array('&'=>'__and__','<'=>'__lt__','>'=>'__gt__','"'=>'__dquot__'));
4491
		$newstring=dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom);	// Make entity encoding
4492
		$newstring=strtr($newstring,array('__and__'=>'&','__lt__'=>'<','__gt__'=>'>','__dquot__'=>'"'));
4493
	}
4494
	else
4495
	{
4496
		if ($removelasteolbr) $newstring=preg_replace('/(\r\n|\r|\n)$/i','',$newstring);	// Remove last \n (may remove several)
4497
		$newstring=dol_nl2br(dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom),$nl2brmode);
4498
	}
4499
	// Other substitutions that htmlentities does not do
4500
	//$newstring=str_replace(chr(128),'&euro;',$newstring);	// 128 = 0x80. Not in html entity table.     // Seems useles with TCPDF. Make bug with UTF8 languages
4501
	return $newstring;
4502
}
4503
4504
/**
4505
 *	This function is called to decode a HTML string (it decodes entities and br tags)
4506
 *
4507
 *	@param	string	$stringtodecode		String to decode
4508
 *	@param	string	$pagecodeto			Page code for result
4509
 *	@return	string						String decoded
4510
 */
4511
function dol_htmlentitiesbr_decode($stringtodecode,$pagecodeto='UTF-8')
4512
{
4513
	$ret=dol_html_entity_decode($stringtodecode,ENT_COMPAT,$pagecodeto);
4514
	$ret=preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i',"<br>",$ret);
4515
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i',"\r\n",$ret);
4516
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i',"\n",$ret);
4517
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i',"\n",$ret);
4518
	return $ret;
4519
}
4520
4521
/**
4522
 *	This function remove all ending \n and br at end
4523
 *
4524
 *	@param	string	$stringtodecode		String to decode
4525
 *	@return	string						String decoded
4526
 */
4527
function dol_htmlcleanlastbr($stringtodecode)
4528
{
4529
	$ret=preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i',"",$stringtodecode);
4530
	return $ret;
4531
}
4532
4533
/**
4534
 * Replace html_entity_decode functions to manage errors
4535
 *
4536
 * @param   string	$a		Operand a
4537
 * @param   string	$b		Operand b (ENT_QUOTES=convert simple and double quotes)
4538
 * @param   string	$c		Operand c
4539
 * @return  string			String decoded
4540
 */
4541
function dol_html_entity_decode($a,$b,$c='UTF-8')
4542
{
4543
	return html_entity_decode($a,$b,$c);
4544
}
4545
4546
/**
4547
 * Replace htmlentities functions to manage errors http://php.net/manual/en/function.htmlentities.php
4548
 * Goal of this function is to be sure to have default values of htmlentities that match what we need.
4549
 *
4550
 * @param   string  $string         The input string.
4551
 * @param   int     $flags          Flags(see PHP doc above)
4552
 * @param   string  $encoding       Encoding
4553
 * @param   bool    $double_encode  When double_encode is turned off PHP will not encode existing html entities
4554
 * @return  string  $ret            Encoded string
4555
 */
4556
function dol_htmlentities($string, $flags=null, $encoding='UTF-8', $double_encode=false)
4557
{
4558
	return htmlentities($string, $flags, $encoding, $double_encode);
4559
}
4560
4561
4562
/**
4563
 *	Check if a string is a correct iso string
4564
 *	If not, it will we considered not HTML encoded even if it is by FPDF.
4565
 *	Example, if string contains euro symbol that has ascii code 128
4566
 *
4567
 *	@param	string	$s      String to check
4568
 *	@return	int     		0 if bad iso, 1 if good iso
4569
 */
4570
function dol_string_is_good_iso($s)
4571
{
4572
	$len=dol_strlen($s);
4573
	$ok=1;
4574
	for($scursor=0;$scursor<$len;$scursor++)
4575
	{
4576
		$ordchar=ord($s{$scursor});
4577
		//print $scursor.'-'.$ordchar.'<br>';
4578
		if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) { $ok=0; break; }
4579
		if ($ordchar > 126 && $ordchar < 160) { $ok=0; break; }
4580
	}
4581
	return $ok;
4582
}
4583
4584
4585
/**
4586
 *	Return nb of lines of a clear text
4587
 *
4588
 *	@param	string	$s			String to check
4589
 * 	@param	int     $maxchar	Not yet used
4590
 *	@return	int					Number of lines
4591
 *  @see	dol_nboflines_bis, dolGetFirstLineOfText
4592
 */
4593
function dol_nboflines($s,$maxchar=0)
4594
{
4595
	if ($s == '') return 0;
4596
	$arraystring=explode("\n",$s);
4597
	$nb=count($arraystring);
4598
4599
	return $nb;
4600
}
4601
4602
4603
/**
4604
 *	Return nb of lines of a formated text with \n and <br> (we can't have both \n and br)
4605
 *
4606
 *	@param	string	$text      		Text
4607
 *	@param	int		$maxlinesize  	Largeur de ligne en caracteres (ou 0 si pas de limite - defaut)
4608
 * 	@param	string	$charset		Give the charset used to encode the $text variable in memory.
4609
 *	@return int						Number of lines
4610
 *	@see	dol_nboflines, dolGetFirstLineOfText
4611
 */
4612
function dol_nboflines_bis($text,$maxlinesize=0,$charset='UTF-8')
4613
{
4614
	$repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
4615
	if (dol_textishtml($text)) $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
4616
4617
	$text = strtr($text, $repTable);
4618
	if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; }	// /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
4619
	else $pattern = '/(<br[^>]*>)/U';							// /U is to have UNGREEDY regex to limit to one html tag.
4620
	$a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
4621
4622
	$nblines = (int) floor((count($a)+1)/2);
4623
	// count possible auto line breaks
4624
	if($maxlinesize)
4625
	{
4626
		foreach ($a as $line)
4627
		{
4628
			if (dol_strlen($line)>$maxlinesize)
4629
			{
4630
				//$line_dec = html_entity_decode(strip_tags($line));
4631
				$line_dec = html_entity_decode($line);
4632
				if(dol_strlen($line_dec)>$maxlinesize)
4633
				{
4634
					$line_dec=wordwrap($line_dec,$maxlinesize,'\n',true);
4635
					$nblines+=substr_count($line_dec,'\n');
4636
				}
4637
			}
4638
		}
4639
	}
4640
	return $nblines;
4641
}
4642
4643
/**
4644
 *	 Same function than microtime in PHP 5 but compatible with PHP4
4645
 *
4646
 * @return		float		Time (millisecondes) with microsecondes in decimal part
4647
 * @deprecated Dolibarr does not support PHP4, you should use native function
4648
 * @see microtime()
4649
 */
4650
function dol_microtime_float()
4651
{
4652
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
4653
4654
	return microtime(true);
4655
}
4656
4657
/**
4658
 *	Return if a text is a html content
4659
 *
4660
 *	@param	string	$msg		Content to check
4661
 *	@param	int		$option		0=Full detection, 1=Fast check
4662
 *	@return	boolean				true/false
4663
 *	@see	dol_concatdesc
4664
 */
4665
function dol_textishtml($msg,$option=0)
4666
{
4667
	if ($option == 1)
4668
	{
4669
		if (preg_match('/<html/i',$msg))				return true;
4670
		elseif (preg_match('/<body/i',$msg))			return true;
4671
		elseif (preg_match('/<br/i',$msg))				return true;
4672
		return false;
4673
	}
4674
	else
4675
	{
4676
		if (preg_match('/<html/i',$msg))				return true;
4677
		elseif (preg_match('/<body/i',$msg))			return true;
4678
		elseif (preg_match('/<(b|em|i|u)>/i',$msg))		return true;
4679
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i',$msg)) 	  return true;
4680
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*>/i',$msg)) return true;
4681
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/>/i',$msg)) return true;
4682
		elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i',$msg)) return true;	// must accept <img src="http://example.com/aaa.png" />
4683
		elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i',$msg)) return true;	// must accept <a href="http://example.com/aaa.png" />
4684
		elseif (preg_match('/<h[0-9]>/i',$msg))			return true;
4685
		elseif (preg_match('/&[A-Z0-9]{1,6};/i',$msg))	return true;    // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
4686
		elseif (preg_match('/&#[0-9]{2,3};/i',$msg))	return true;    // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
4687
		return false;
4688
	}
4689
}
4690
4691
/**
4692
 *  Concat 2 descriptions with a new line between them (second operand after first one with appropriate new line separator)
4693
 *  text1 html + text2 html => text1 + '<br>' + text2
4694
 *  text1 html + text2 txt  => text1 + '<br>' + dol_nl2br(text2)
4695
 *  text1 txt  + text2 html => dol_nl2br(text1) + '<br>' + text2
4696
 *  text1 txt  + text2 txt  => text1 + '\n' + text2
4697
 *
4698
 *  @param	string	$text1		Text 1
4699
 *  @param	string	$text2		Text 2
4700
 *  @param  bool	$forxml     false=Use <br>, true=Use <br />
4701
 *  @return	string				Text 1 + new line + Text2
4702
 *  @see    dol_textishtml
4703
 */
4704
function dol_concatdesc($text1,$text2,$forxml=false)
4705
{
4706
	$ret='';
4707
	$ret.= (! dol_textishtml($text1) && dol_textishtml($text2))?dol_nl2br($text1, 0, $forxml):$text1;
4708
	$ret.= (! empty($text1) && ! empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2))?($forxml?"<br \>\n":"<br>\n") : "\n") : "";
4709
	$ret.= (dol_textishtml($text1) && ! dol_textishtml($text2))?dol_nl2br($text2, 0, $forxml):$text2;
4710
	return $ret;
4711
}
4712
4713
/**
4714
 *  Make substition into a string replacing key with vals from $substitutionarray (oldval=>newval)
4715
 *
4716
 *  @param	string	$chaine      			Source string in which we must do substitution
4717
 *  @param  array	$substitutionarray		Array with key->val to substitute
4718
 * 	@return string  		    			Output string after subsitutions
4719
 *  @see	complete_substitutions_array
4720
 */
4721
function make_substitutions($chaine,$substitutionarray)
4722
{
4723
	global $conf;
4724
4725
	if (! is_array($substitutionarray)) return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
4726
4727
	// Make substitition
4728
	foreach ($substitutionarray as $key => $value)
4729
	{
4730
		if ($key == '__SIGNATURE__' && (! empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) $value='';
4731
		$chaine=str_replace("$key","$value",$chaine);	// We must keep the " to work when value is 123.5 for example
4732
	}
4733
4734
	return $chaine;
4735
}
4736
4737
/**
4738
 *  Complete the $substitutionarray with more entries
4739
 *
4740
 *  @param  array		$substitutionarray		Array substitution old value => new value value
4741
 *  @param  Translate	$outputlangs            If we want substitution from special constants, we provide a language
4742
 *  @param  object		$object                 If we want substitution from special constants, we provide data in a source object
4743
 *  @param  Mixed		$parameters       		Add more parameters (useful to pass product lines)
4744
 *  @param  string      $callfunc               What is the name of the custom function that will be called? (default: completesubstitutionarray)
4745
 *  @return	void
4746
 *  @see 	make_substitutions
4747
 */
4748
function complete_substitutions_array(&$substitutionarray,$outputlangs,$object='',$parameters=null,$callfunc="completesubstitutionarray")
4749
{
4750
	global $conf,$user;
4751
4752
	require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4753
4754
	// Check if there is external substitution to do asked by plugins
4755
	$dirsubstitutions=array_merge(array(),(array) $conf->modules_parts['substitutions']);
4756
4757
	foreach($dirsubstitutions as $reldir)
4758
	{
4759
		$dir=dol_buildpath($reldir,0);
4760
4761
		// Check if directory exists
4762
		if (! dol_is_dir($dir)) continue;
4763
4764
		$substitfiles=dol_dir_list($dir,'files',0,'functions_');
4765
		foreach($substitfiles as $substitfile)
4766
		{
4767
			if (preg_match('/functions_(.*)\.lib\.php/i',$substitfile['name'],$reg))
4768
			{
4769
				$module=$reg[1];
4770
4771
				dol_syslog("Library functions_".$substitfile['name']." found into ".$dir);
4772
				// Include the user's functions file
4773
				require_once $dir.$substitfile['name'];
4774
				// Call the user's function, and only if it is defined
4775
				$function_name=$module."_".$callfunc;
4776
				if (function_exists($function_name)) $function_name($substitutionarray,$outputlangs,$object,$parameters);
4777
			}
4778
		}
4779
	}
4780
}
4781
4782
/**
4783
 *    Format output for start and end date
4784
 *
4785
 *    @param	int	$date_start    Start date
4786
 *    @param    int	$date_end      End date
4787
 *    @param    string		$format        Output format
4788
 *    @param	Translate	$outputlangs   Output language
4789
 *    @return	void
4790
 */
4791
function print_date_range($date_start,$date_end,$format = '',$outputlangs='')
4792
{
4793
	print get_date_range($date_start,$date_end,$format,$outputlangs);
4794
}
4795
4796
/**
4797
 *    Format output for start and end date
4798
 *
4799
 *    @param	int			$date_start    		Start date
4800
 *    @param    int			$date_end      		End date
4801
 *    @param    string		$format        		Output format
4802
 *    @param	Translate	$outputlangs   		Output language
4803
 *    @param	integer		$withparenthesis	1=Add parenthesis, 0=non parenthesis
4804
 *    @return	string							String
4805
 */
4806
function get_date_range($date_start,$date_end,$format = '',$outputlangs='', $withparenthesis=1)
4807
{
4808
	global $langs;
4809
4810
	$out='';
4811
4812
	if (! is_object($outputlangs)) $outputlangs=$langs;
4813
4814
	if ($date_start && $date_end)
4815
	{
4816
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFromTo',dol_print_date($date_start, $format, false, $outputlangs),dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
4817
	}
4818
	if ($date_start && ! $date_end)
4819
	{
4820
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFrom',dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis?')':'');
4821
	}
4822
	if (! $date_start && $date_end)
4823
	{
4824
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateUntil',dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
4825
	}
4826
4827
	return $out;
4828
}
4829
4830
/**
4831
 * Return firstname and lastname in correct order
4832
 *
4833
 * @param	string	$firstname		Firstname
4834
 * @param	string	$lastname		Lastname
4835
 * @param	int		$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname
4836
 * @return	string					Firstname + lastname or Lastname + firstname
4837
 */
4838
function dolGetFirstLastname($firstname,$lastname,$nameorder=-1)
4839
{
4840
	global $conf;
4841
4842
	$ret='';
4843
	// If order not defined, we use the setup
4844
	if ($nameorder < 0) $nameorder=(empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION));
4845
	if ($nameorder && ((string) $nameorder != '2'))
4846
	{
4847
        $ret.=$firstname;
4848
		if ($firstname && $lastname) $ret.=' ';
4849
		$ret.=$lastname;
4850
	}
4851
	else if ($nameorder == 2)
4852
	{
4853
	   $ret.=$firstname;
4854
	}
4855
	else
4856
	{
4857
		$ret.=$lastname;
4858
		if ($firstname && $lastname) $ret.=' ';
4859
		$ret.=$firstname;
4860
	}
4861
	return $ret;
4862
}
4863
4864
4865
/**
4866
 *	Set event message in dol_events session object. Will be output by calling dol_htmloutput_events.
4867
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
4868
 *  Note: Prefer to use setEventMessages instead.
4869
 *
4870
 *	@param	mixed	$mesgs			Message string or array
4871
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
4872
 *  @return	void
4873
 *  @see	dol_htmloutput_events
4874
 */
4875
function setEventMessage($mesgs, $style='mesgs')
4876
{
4877
	//dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);		This is not deprecated, it is used by setEventMessages function
4878
	if (! is_array($mesgs))		// If mesgs is a string
4879
	{
4880
		if ($mesgs) $_SESSION['dol_events'][$style][] = $mesgs;
4881
	}
4882
	else						// If mesgs is an array
4883
	{
4884
		foreach($mesgs as $mesg)
4885
		{
4886
			if ($mesg) $_SESSION['dol_events'][$style][] = $mesg;
4887
		}
4888
	}
4889
}
4890
4891
/**
4892
 *	Set event messages in dol_events session object. Will be output by calling dol_htmloutput_events.
4893
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
4894
 *
4895
 *	@param	string	$mesg			Message string
4896
 *	@param	array	$mesgs			Message array
4897
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
4898
 *  @return	void
4899
 *  @see	dol_htmloutput_events
4900
 */
4901
function setEventMessages($mesg, $mesgs, $style='mesgs')
4902
{
4903
	if (! in_array((string) $style, array('mesgs','warnings','errors'))) dol_print_error('','Bad parameter style='.$style.' for setEventMessages');
4904
	if (empty($mesgs)) setEventMessage($mesg, $style);
4905
	else
4906
	{
4907
		if (! empty($mesg) && ! in_array($mesg, $mesgs)) setEventMessage($mesg, $style);	// Add message string if not already into array
4908
		setEventMessage($mesgs, $style);
4909
	}
4910
}
4911
4912
/**
4913
 *	Print formated messages to output (Used to show messages on html output).
4914
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function, so there is
4915
 *  no need to call it explicitely.
4916
 *
4917
 *  @return	void
4918
 *  @see    dol_htmloutput_mesg
4919
 */
4920
function dol_htmloutput_events()
4921
{
4922
	// Show mesgs
4923
	if (isset($_SESSION['dol_events']['mesgs'])) {
4924
		dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
4925
		unset($_SESSION['dol_events']['mesgs']);
4926
	}
4927
4928
	// Show errors
4929
	if (isset($_SESSION['dol_events']['errors'])) {
4930
		dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
4931
		unset($_SESSION['dol_events']['errors']);
4932
	}
4933
4934
	// Show warnings
4935
	if (isset($_SESSION['dol_events']['warnings'])) {
4936
		dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
4937
		unset($_SESSION['dol_events']['warnings']);
4938
	}
4939
}
4940
4941
/**
4942
 *	Get formated messages to output (Used to show messages on html output).
4943
 *  This include also the translation of the message key.
4944
 *
4945
 *	@param	string		$mesgstring		Message string or message key
4946
 *	@param	string[]	$mesgarray      Array of message strings or message keys
4947
 *  @param  string		$style          Style of message output ('ok' or 'error')
4948
 *  @param  int			$keepembedded   Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
4949
 *	@return	string						Return html output
4950
 *
4951
 *  @see    dol_print_error
4952
 *  @see    dol_htmloutput_errors
4953
 *  @see    setEventMessages
4954
 */
4955
function get_htmloutput_mesg($mesgstring='',$mesgarray='', $style='ok', $keepembedded=0)
4956
{
4957
	global $conf, $langs;
4958
4959
	$ret='';
4960
	$out='';
4961
	$divstart=$divend='';
4962
4963
	// If inline message with no format, we add it.
4964
	if ((empty($conf->use_javascript_ajax) || ! empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && ! preg_match('/<div class=".*">/i',$out))
4965
	{
4966
		$divstart='<div class="'.$style.'">';
4967
		$divend='</div>';
4968
	}
4969
4970
	if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring)
4971
	{
4972
		$langs->load("errors");
4973
		$out.=$divstart;
4974
		if (is_array($mesgarray) && count($mesgarray))
4975
		{
4976
			foreach($mesgarray as $message)
4977
			{
4978
				$ret++;
4979
				$out.= $langs->trans($message);
4980
				if ($ret < count($mesgarray)) $out.= "<br>\n";
4981
			}
4982
		}
4983
		if ($mesgstring)
4984
		{
4985
			$langs->load("errors");
4986
			$ret++;
4987
			$out.= $langs->trans($mesgstring);
4988
		}
4989
		$out.=$divend;
4990
	}
4991
4992
	if ($out)
4993
	{
4994
		if (! empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded))
4995
		{
4996
			$return = '<script type="text/javascript">
4997
					$(document).ready(function() {
4998
						var block = '.(! empty($conf->global->MAIN_USE_JQUERY_BLOCKUI)?"true":"false").'
4999
						if (block) {
5000
							$.dolEventValid("","'.dol_escape_js($out).'");
5001
						} else {
5002
							/* jnotify(message, preset of message type, keepmessage) */
5003
							$.jnotify("'.dol_escape_js($out).'",
5004
							"'.($style=="ok" ? 3000 : $style).'",
5005
							'.($style=="ok" ? "false" : "true").',
5006
							{ remove: function (){} } );
5007
						}
5008
					});
5009
				</script>';
5010
		}
5011
		else
5012
		{
5013
			$return = $out;
5014
		}
5015
	}
5016
5017
	return $return;
0 ignored issues
show
Bug introduced by
The variable $return 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...
5018
}
5019
5020
/**
5021
 *  Get formated error messages to output (Used to show messages on html output).
5022
 *
5023
 *  @param	string	$mesgstring         Error message
5024
 *  @param  array	$mesgarray          Error messages array
5025
 *  @param  int		$keepembedded       Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
5026
 *  @return string                		Return html output
5027
 *
5028
 *  @see    dol_print_error
5029
 *  @see    dol_htmloutput_mesg
5030
 */
5031
function get_htmloutput_errors($mesgstring='', $mesgarray='', $keepembedded=0)
5032
{
5033
	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 5031 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...
5034
}
5035
5036
/**
5037
 *	Print formated messages to output (Used to show messages on html output).
5038
 *
5039
 *	@param	string		$mesgstring		Message string or message key
5040
 *	@param	string[]	$mesgarray      Array of message strings or message keys
5041
 *  @param  string      $style          Which style to use ('ok', 'warning', 'error')
5042
 *  @param  int         $keepembedded   Set to 1 if message must be kept embedded into its html place (this disable jnotify)
5043
 *  @return	void
5044
 *
5045
 *  @see    dol_print_error
5046
 *  @see    dol_htmloutput_errors
5047
 *  @see    setEventMessages
5048
 */
5049
function dol_htmloutput_mesg($mesgstring='',$mesgarray='', $style='ok', $keepembedded=0)
5050
{
5051
	if (empty($mesgstring) && (! is_array($mesgarray) || count($mesgarray) == 0)) return;
5052
5053
	$iserror=0;
5054
	$iswarning=0;
5055
	if (is_array($mesgarray))
5056
	{
5057
		foreach($mesgarray as $val)
5058
		{
5059
			if ($val && preg_match('/class="error"/i',$val)) { $iserror++; break; }
5060
			if ($val && preg_match('/class="warning"/i',$val)) { $iswarning++; break; }
5061
		}
5062
	}
5063
	else if ($mesgstring && preg_match('/class="error"/i',$mesgstring)) $iserror++;
5064
	else if ($mesgstring && preg_match('/class="warning"/i',$mesgstring)) $iswarning++;
5065
	if ($style=='error') $iserror++;
5066
	if ($style=='warning') $iswarning++;
5067
5068
	if ($iserror || $iswarning)
5069
	{
5070
		// Remove div from texts
5071
		$mesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$mesgstring);
5072
		$mesgstring=preg_replace('/<div class="(error|warning)">/','',$mesgstring);
5073
		$mesgstring=preg_replace('/<\/div>/','',$mesgstring);
5074
		// Remove div from texts array
5075
		if (is_array($mesgarray))
5076
		{
5077
			$newmesgarray=array();
5078
			foreach($mesgarray as $val)
5079
			{
5080
				$tmpmesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$val);
5081
				$tmpmesgstring=preg_replace('/<div class="(error|warning)">/','',$tmpmesgstring);
5082
				$tmpmesgstring=preg_replace('/<\/div>/','',$tmpmesgstring);
5083
				$newmesgarray[]=$tmpmesgstring;
5084
			}
5085
			$mesgarray=$newmesgarray;
5086
		}
5087
		print get_htmloutput_mesg($mesgstring,$mesgarray,($iserror?'error':'warning'),$keepembedded);
5088
	}
5089
	else print get_htmloutput_mesg($mesgstring,$mesgarray,'ok',$keepembedded);
5090
}
5091
5092
/**
5093
 *  Print formated error messages to output (Used to show messages on html output).
5094
 *
5095
 *  @param	string	$mesgstring          Error message
5096
 *  @param  array	$mesgarray           Error messages array
5097
 *  @param  int		$keepembedded        Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
5098
 *  @return	void
5099
 *
5100
 *  @see    dol_print_error
5101
 *  @see    dol_htmloutput_mesg
5102
 */
5103
function dol_htmloutput_errors($mesgstring='', $mesgarray='', $keepembedded=0)
5104
{
5105
	dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
1 ignored issue
show
Bug introduced by
It seems like $mesgarray defined by parameter $mesgarray on line 5103 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...
5106
}
5107
5108
/**
5109
 * 	Advanced sort array by second index function, which produces ascending (default)
5110
 *  or descending output and uses optionally natural case insensitive sorting (which
5111
 *  can be optionally case sensitive as well).
5112
 *
5113
 *  @param      array		$array      		Array to sort (array of array('key','otherkey1','otherkey2'...))
5114
 *  @param      string		$index				Key in array to use for sorting criteria
5115
 *  @param      int			$order				Sort order ('asc' or 'desc')
5116
 *  @param      int			$natsort			1=use "natural" sort (natsort), 0=use "standard" sort (asort)
5117
 *  @param      int			$case_sensitive		1=sort is case sensitive, 0=not case sensitive
5118
 *  @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.
5119
 *  @return     array							Sorted array
5120
 */
5121
function dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
5122
{
5123
	// Clean parameters
5124
	$order=strtolower($order);
5125
5126
	$sizearray=count($array);
5127
	if (is_array($array) && $sizearray>0)
5128
	{
5129
		foreach(array_keys($array) as $key) $temp[$key]=$array[$key][$index];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$temp was never initialized. Although not strictly required by PHP, it is generally a good practice to add $temp = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
5130
5131
		if (!$natsort) ($order=='asc') ? asort($temp) : arsort($temp);
5132
		else
5133
		{
5134
			($case_sensitive) ? natsort($temp) : natcasesort($temp);
5135
			if($order!='asc') $temp=array_reverse($temp,TRUE);
5136
		}
5137
5138
		$sorted = array();
5139
5140
		foreach(array_keys($temp) as $key)
5141
		{
5142
			(is_numeric($key) && empty($keepindex)) ? $sorted[]=$array[$key] : $sorted[$key]=$array[$key];
5143
		}
5144
5145
		return $sorted;
5146
	}
5147
	return $array;
5148
}
5149
5150
5151
/**
5152
 *      Check if a string is in UTF8
5153
 *
5154
 *      @param	string	$str        String to check
5155
 * 		@return	boolean				True if string is UTF8 or ISO compatible with UTF8, False if not (ISO with special char or Binary)
5156
 */
5157
function utf8_check($str)
5158
{
5159
	// We must use here a binary strlen function (so not dol_strlen)
5160
	$strLength = dol_strlen($str);
5161
	for ($i=0; $i<$strLength; $i++)
5162
	{
5163
		if (ord($str[$i]) < 0x80) continue; // 0bbbbbbb
5164
		elseif ((ord($str[$i]) & 0xE0) == 0xC0) $n=1; // 110bbbbb
5165
		elseif ((ord($str[$i]) & 0xF0) == 0xE0) $n=2; // 1110bbbb
5166
		elseif ((ord($str[$i]) & 0xF8) == 0xF0) $n=3; // 11110bbb
5167
		elseif ((ord($str[$i]) & 0xFC) == 0xF8) $n=4; // 111110bb
5168
		elseif ((ord($str[$i]) & 0xFE) == 0xFC) $n=5; // 1111110b
5169
		else return false; // Does not match any model
5170
		for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ?
5171
			if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
5172
			return false;
5173
		}
5174
	}
5175
	return true;
5176
}
5177
5178
5179
/**
5180
 *      Return a string encoded into OS filesystem encoding. This function is used to define
5181
 * 	    value to pass to filesystem PHP functions.
5182
 *
5183
 *      @param	string	$str        String to encode (UTF-8)
5184
 * 		@return	string				Encoded string (UTF-8, ISO-8859-1)
5185
 */
5186
function dol_osencode($str)
5187
{
5188
	global $conf;
5189
5190
	$tmp=ini_get("unicode.filesystem_encoding");						// Disponible avec PHP 6.0
5191
	if (empty($tmp) && ! empty($_SERVER["WINDIR"])) $tmp='iso-8859-1';	// By default for windows
5192
	if (empty($tmp)) $tmp='utf-8';										// By default for other
5193
	if (! empty($conf->global->MAIN_FILESYSTEM_ENCODING)) $tmp=$conf->global->MAIN_FILESYSTEM_ENCODING;
5194
5195
	if ($tmp == 'iso-8859-1') return utf8_decode($str);
5196
	return $str;
5197
}
5198
5199
5200
/**
5201
 *      Return an id or code from a code or id.
5202
 *      Store also Code-Id into a cache to speed up next request on same key.
5203
 *
5204
 * 		@param	DoliDB	$db			Database handler
5205
 * 		@param	string	$key		Code or Id to get Id or Code
5206
 * 		@param	string	$tablename	Table name without prefix
5207
 * 		@param	string	$fieldkey	Field for code
5208
 * 		@param	string	$fieldid	Field for id
5209
 *      @return int					<0 if KO, Id of code if OK
5210
 *      @see $langs->getLabelFromKey
5211
 */
5212
function dol_getIdFromCode($db,$key,$tablename,$fieldkey='code',$fieldid='id')
5213
{
5214
	global $cache_codes;
5215
5216
	// If key empty
5217
	if ($key == '') return '';
5218
5219
	// Check in cache
5220
	if (isset($cache_codes[$tablename][$key]))	// Can be defined to 0 or ''
5221
	{
5222
		return $cache_codes[$tablename][$key];   // Found in cache
5223
	}
5224
5225
	$sql = "SELECT ".$fieldid." as valuetoget";
5226
	$sql.= " FROM ".MAIN_DB_PREFIX.$tablename;
5227
	$sql.= " WHERE ".$fieldkey." = '".$key."'";
5228
	dol_syslog('dol_getIdFromCode', LOG_DEBUG);
5229
	$resql = $db->query($sql);
5230
	if ($resql)
5231
	{
5232
		$obj = $db->fetch_object($resql);
5233
		if ($obj) $cache_codes[$tablename][$key]=$obj->valuetoget;
5234
		else $cache_codes[$tablename][$key]='';
5235
		$db->free($resql);
5236
		return $cache_codes[$tablename][$key];
5237
	}
5238
	else
5239
	{
5240
		return -1;
5241
	}
5242
}
5243
5244
/**
5245
 * Verify if condition in string is ok or not
5246
 *
5247
 * @param 	string		$strRights		String with condition to check
5248
 * @return 	boolean						True or False. Return true if strRights is ''
5249
 */
5250
function verifCond($strRights)
5251
{
5252
	global $user,$conf,$langs;
5253
	global $leftmenu;
5254
	global $rights;    // To export to dol_eval function
5255
5256
	//print $strRights."<br>\n";
5257
	$rights = true;
5258
	if ($strRights != '')
5259
	{
5260
		//$tab_rights = explode('&&', $strRights);
5261
		//$i = 0;
5262
		//while (($i < count($tab_rights)) && ($rights == true)) {
5263
		$str = 'if(!(' . $strRights . ')) { $rights = false; }';
5264
		dol_eval($str);
5265
		//	$i++;
5266
		//}
5267
	}
5268
	return $rights;
5269
}
5270
5271
/**
5272
 * Replace eval function to add more security.
5273
 * This function is called by verifCond() or trans() and transnoentitiesnoconv().
5274
 *
5275
 * @param 	string	$s				String to evaluate
5276
 * @param	int		$returnvalue	0=No return (used to execute eval($a=something)). 1=Value of eval is returned (used to eval($something)).
5277
 * @return	mixed					Nothing or return of eval
5278
 */
5279
function dol_eval($s,$returnvalue=0)
5280
{
5281
	// Only global variables can be changed by eval function and returned to caller
5282
	global $langs, $user, $conf;
5283
	global $leftmenu;
5284
	global $rights;
5285
	global $object;
5286
    global $soc;
5287
5288
	//print $s."<br>\n";
5289
	if ($returnvalue) return @eval('return '.$s.';');
1 ignored issue
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...
5290
	else @eval($s);
1 ignored issue
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...
5291
}
5292
5293
/**
5294
 * Return if var element is ok
5295
 *
5296
 * @param   string      $element    Variable to check
5297
 * @return  boolean                 Return true of variable is not empty
5298
 */
5299
function dol_validElement($element)
5300
{
5301
	return (trim($element) != '');
5302
}
5303
5304
/**
5305
 * 	Return img flag of country for a language code or country code
5306
 *
5307
 * 	@param	string	$codelang	Language code (en_IN, fr_CA...) or Country code (IN, FR)
5308
 * 	@return	string				HTML img string with flag.
5309
 */
5310
function picto_from_langcode($codelang)
5311
{
5312
	global $langs;
5313
5314
	if (empty($codelang)) return '';
5315
5316
	if (empty($codelang)) return '';
5317
5318
	if ($codelang == 'auto')
5319
	{
5320
		return img_picto_common($langs->trans('AutoDetectLang'), 'flags/int.png');
5321
	}
5322
5323
	$langtocountryflag = array(
5324
		'ar_AR' => '',
5325
		'ca_ES' => 'catalonia',
5326
		'da_DA' => 'dk',
5327
		'fr_CA' => 'mq',
5328
		'sv_SV' => 'se'
5329
	);
5330
5331
	if (isset($langtocountryflag[$codelang])) $flagImage = $langtocountryflag[$codelang];
5332
	else
5333
	{
5334
		$tmparray = explode('_', $codelang);
5335
		$flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
5336
	}
5337
5338
	return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png');
5339
}
5340
5341
/**
5342
 *  Complete or removed entries into a head array (used to build tabs). 
5343
 *  For example, with value added by external modules. Such values are declared into $conf->modules_parts['tab'].
5344
 *  Or by change using hook completeTabsHead
5345
 *
5346
 *  @param	Conf			$conf           Object conf
5347
 *  @param  Translate		$langs          Object langs
5348
 *  @param  object|null		$object         Object object
5349
 *  @param  array			$head          	Object head
5350
 *  @param  int				$h				New position to fill
5351
 *  @param  string			$type           Value for object where objectvalue can be
5352
 *                              			'thirdparty'       to add a tab in third party view
5353
 *		                        	      	'intervention'     to add a tab in intervention view
5354
 *     		                    	     	'supplier_order'   to add a tab in supplier order view
5355
 *          		            	        'supplier_invoice' to add a tab in supplier invoice view
5356
 *                  		    	        'invoice'          to add a tab in customer invoice view
5357
 *                          			    'order'            to add a tab in customer order view
5358
 *                      			        'product'          to add a tab in product view
5359
 *                              			'propal'           to add a tab in propal view
5360
 *                              			'user'             to add a tab in user view
5361
 *                              			'group'            to add a tab in group view
5362
 * 		        	               	     	'member'           to add a tab in fundation member view
5363
 *      		                        	'categories_x'	   to add a tab in category view ('x': type of category (0=product, 1=supplier, 2=customer, 3=member)
5364
 *      									'ecm'			   to add a tab for another ecm view
5365
 *                                          'stock'            to add a tab for warehouse view
5366
 *  @param  string		$mode  	        	'add' to complete head, 'remove' to remove entries
5367
 *	@return	void
5368
 */
5369
function complete_head_from_modules($conf,$langs,$object,&$head,&$h,$type,$mode='add')
5370
{
5371
	global $hookmanager;
5372
	
5373
	if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type]))
5374
	{
5375
		foreach ($conf->modules_parts['tabs'][$type] as $value)
5376
		{
5377
			$values=explode(':',$value);
5378
5379
			if ($mode == 'add' && ! preg_match('/^\-/',$values[1]))
5380
			{
5381
				if (count($values) == 6)       // new declaration with permissions:  $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
5382
				{
5383
					if ($values[0] != $type) continue;
5384
5385
					if (verifCond($values[4]))
5386
					{
5387
						if ($values[3]) $langs->load($values[3]);
5388
						if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
5389
						{
5390
							$substitutionarray=array();
5391
							complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 5369 can also be of type null; however, complete_substitutions_array() does only seem to accept string|object, 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...
5392
							$label=make_substitutions($reg[1], $substitutionarray);
5393
						}
5394
						else $label=$langs->trans($values[2]);
5395
5396
						$head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && ! empty($object->id))?$object->id:''), $values[5]), 1);
5397
						$head[$h][1] = $label;
5398
						$head[$h][2] = str_replace('+','',$values[1]);
5399
						$h++;
5400
					}
5401
				}
5402
				else if (count($values) == 5)       // deprecated
5403
				{
5404
					dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
5405
5406
					if ($values[0] != $type) continue;
5407
					if ($values[3]) $langs->load($values[3]);
5408
					if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
5409
					{
5410
						$substitutionarray=array();
5411
						complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
0 ignored issues
show
Bug introduced by
It seems like $object defined by parameter $object on line 5369 can also be of type null; however, complete_substitutions_array() does only seem to accept string|object, 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...
5412
						$label=make_substitutions($reg[1], $substitutionarray);
5413
					}
5414
					else $label=$langs->trans($values[2]);
5415
5416
					$head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && ! empty($object->id))?$object->id:''), $values[4]), 1);
5417
					$head[$h][1] = $label;
5418
					$head[$h][2] = str_replace('+','',$values[1]);
5419
					$h++;
5420
				}
5421
			}
5422
			else if ($mode == 'remove' && preg_match('/^\-/',$values[1]))
5423
			{
5424
				if ($values[0] != $type) continue;
5425
				$tabname=str_replace('-','',$values[1]);
5426
				foreach($head as $key => $val)
5427
				{
5428
					$condition = (! empty($values[3]) ? verifCond($values[3]) : 1);
5429
					if ($head[$key][2]==$tabname && $condition)
5430
					{
5431
						unset($head[$key]);
5432
						break;
5433
					}
5434
				}
5435
			}
5436
		}
5437
	}
5438
	
5439
	// No need to make a return $head. Var is modified as a reference
5440
	if (! empty($hookmanaer))
0 ignored issues
show
Bug introduced by
The variable $hookmanaer does not exist. Did you mean $hookmanager?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
5441
	{
5442
		$parameters=array('object' => $object, 'mode' => $mode, 'head'=>$head);
5443
		$reshook=$hookmanager->executeHooks('completeTabsHead',$parameters);
5444
		if ($reshook > 0)
5445
		{
5446
			$head = $hookmanager->resArray;
5447
		}
5448
	}
5449
}
5450
5451
/**
5452
 * Print common footer :
5453
 * 		conf->global->MAIN_HTML_FOOTER
5454
 * 		conf->global->MAIN_GOOGLE_AN_ID
5455
 * 		conf->global->MAIN_SHOW_TUNING_INFO or $_SERVER["MAIN_SHOW_TUNING_INFO"]
5456
 * 		conf->logbuffer
5457
 *
5458
 * @param	string	$zone	'private' (for private pages) or 'public' (for public pages)
5459
 * @return	void
5460
 */
5461
function printCommonFooter($zone='private')
5462
{
5463
	global $conf, $hookmanager;
5464
	global $micro_start_time;
5465
5466
	if ($zone == 'private') print "\n".'<!-- Common footer for private page -->'."\n";
5467
	else print "\n".'<!-- Common footer for public page -->'."\n";
5468
5469
	if (! empty($conf->global->MAIN_HTML_FOOTER)) print $conf->global->MAIN_HTML_FOOTER."\n";
5470
5471
	print "\n";
5472
	if (! empty($conf->use_javascript_ajax))
5473
	{
5474
		print '<!-- Reposition management (does not work if a redirect is done after action of submission) -->'."\n";
5475
    	print '<script type="text/javascript" language="javascript">jQuery(document).ready(function() {'."\n";
5476
    	
5477
    	print '<!-- If page_y set, we set scollbar with it -->'."\n";
5478
    	print "page_y=getParameterByName('page_y', 0);";
5479
    	print "if (page_y > 0) $('html, body').scrollTop(page_y);\n";
5480
    	
5481
    	print '<!-- Set handler to add page_y param on some a href links -->'."\n";
5482
    	print 'jQuery(".reposition").click(function() {
5483
    	           var page_y = $(document).scrollTop();
5484
    	           /*alert(page_y);*/
5485
    	           this.href=this.href+\'&page_y=\'+page_y;
5486
    	           });'."\n";
5487
    	print '});'."\n";
5488
    	
5489
    	print '<!-- Set handler to switch left menu page -->'."\n";
5490
    	print 'jQuery(".menuhider").click(function() {';
5491
    	print "  $('.side-nav').toggle();";
5492
    	if ($conf->theme == 'md') print "  $('.login_block').toggle();";
5493
    	print '});'."\n";
5494
    	
5495
    	print '</script>'."\n";
5496
	}
5497
	
5498
	// Google Analytics (need Google module)
5499
	if (! empty($conf->google->enabled) && ! empty($conf->global->MAIN_GOOGLE_AN_ID))
5500
	{
5501
		if (($conf->dol_use_jmobile != 4))
5502
		{
5503
			print "\n";
5504
			print '<script type="text/javascript">'."\n";
5505
			print '  var _gaq = _gaq || [];'."\n";
5506
			print '  _gaq.push([\'_setAccount\', \''.$conf->global->MAIN_GOOGLE_AN_ID.'\']);'."\n";
5507
			print '  _gaq.push([\'_trackPageview\']);'."\n";
5508
			print ''."\n";
5509
			print '  (function() {'."\n";
5510
			print '    var ga = document.createElement(\'script\'); ga.type = \'text/javascript\'; ga.async = true;'."\n";
5511
			print '    ga.src = (\'https:\' == document.location.protocol ? \'https://ssl\' : \'http://www\') + \'.google-analytics.com/ga.js\';'."\n";
5512
			print '    var s = document.getElementsByTagName(\'script\')[0]; s.parentNode.insertBefore(ga, s);'."\n";
5513
			print '  })();'."\n";
5514
			print '</script>'."\n";
5515
		}
5516
	}
5517
5518
	// End of tuning
5519
	if (! empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || ! empty($conf->global->MAIN_SHOW_TUNING_INFO))
5520
	{
5521
		print "\n".'<script type="text/javascript">'."\n";
5522
		print 'window.console && console.log("';
5523
		if (! empty($conf->global->MEMCACHED_SERVER)) print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
5524
		print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED)?$conf->global->MAIN_OPTIMIZE_SPEED:'off');
5525
		if (! empty($micro_start_time))   // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
5526
		{
5527
			$micro_end_time = microtime(true);
5528
			print ' - Build time: '.ceil(1000*($micro_end_time-$micro_start_time)).' ms';
5529
		}
5530
		if (function_exists("memory_get_usage"))
5531
		{
5532
			print ' - Mem: '.memory_get_usage();
5533
		}
5534
		if (function_exists("xdebug_memory_usage"))
5535
		{
5536
			print ' - XDebug time: '.ceil(1000*xdebug_time_index()).' ms';
5537
			print ' - XDebug mem: '.xdebug_memory_usage();
5538
			print ' - XDebug mem peak: '.xdebug_peak_memory_usage();
5539
		}
5540
		if (function_exists("zend_loader_file_encoded"))
5541
		{
5542
			print ' - Zend encoded file: '.(zend_loader_file_encoded()?'yes':'no');
5543
		}
5544
		print '");'."\n";
5545
		print '</script>'."\n";
5546
5547
		// Add Xdebug coverage of code
5548
		if (defined('XDEBUGCOVERAGE'))
5549
		{
5550
			print_r(xdebug_get_code_coverage());
5551
		}
5552
	}
5553
5554
	// If there is some logs in buffer to show
5555
	if (count($conf->logbuffer))
5556
	{
5557
		print "\n";
5558
		print "<!-- Start of log output\n";
5559
		//print '<div class="hidden">'."\n";
5560
		foreach($conf->logbuffer as $logline)
5561
		{
5562
			print $logline."<br>\n";
5563
		}
5564
		//print '</div>'."\n";
5565
		print "End of log output -->\n";
5566
	}
5567
5568
	$parameters=array();
5569
	$reshook=$hookmanager->executeHooks('printCommonFooter',$parameters);    // Note that $action and $object may have been modified by some hooks
5570
}
5571
5572
/**
5573
 * Split a string with 2 keys into key array.
5574
 * For example: "A=1;B=2;C=2" is exploded into array('A'=>1,'B'=>2,'C'=>3)
5575
 *
5576
 * @param 	string	$string		String to explode
5577
 * @param 	string	$delimiter	Delimiter between each couple of data
5578
 * @param 	string	$kv			Delimiter between key and value
5579
 * @return	array				Array of data exploded
5580
 */
5581
function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
5582
{
5583
	if ($a = explode($delimiter, $string))
5584
	{
5585
		foreach ($a as $s) { // each part
5586
			if ($s) {
5587
				if ($pos = strpos($s, $kv)) { // key/value delimiter
5588
					$ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ka was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ka = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
5589
				} else { // key delimiter not found
5590
					$ka[] = trim($s);
0 ignored issues
show
Bug introduced by
The variable $ka 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...
5591
				}
5592
			}
5593
		}
5594
		return $ka;
5595
	}
5596
	return array();
5597
}
5598
5599
5600
/**
5601
 * Set focus onto field with selector
5602
 *
5603
 * @param 	string	$selector	Selector ('#id')
5604
 * @return	string				HTML code to set focus
5605
 */
5606
function dol_set_focus($selector)
5607
{
5608
	print '<!-- Set focus onto a specific field -->'."\n";
5609
	print '<script type="text/javascript" language="javascript">jQuery(document).ready(function() { jQuery("'.$selector.'").focus(); });</script>'."\n";
5610
}
5611
5612
5613
/**
5614
 * Return getmypid() or random PID when function is disabled
5615
 * Some web hosts disable this php function for security reasons
5616
 * and sometimes we can't redeclare function
5617
 *
5618
 * @return	int
5619
 */
5620
function dol_getmypid()
5621
{
5622
    if (! function_exists('getmypid')) {
5623
        return mt_rand(1,32768);
5624
    } else {
5625
        return getmypid();
5626
    }
5627
}
5628
5629
5630
/**
5631
 * Generate natural SQL search string for a criteria (this criteria can be tested on one or several fields)
5632
 *
5633
 * @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)
5634
 * @param 	string 			$value 		The value to look for.
5635
 *                          		    If param $mode is 0, can contains several keywords separated with a space or |
5636
 *                                         like "keyword1 keyword2" = We want record field like keyword1 AND field like keyword2
5637
 *                                         or like "keyword1|keyword2" = We want record field like keyword1 OR field like keyword2
5638
 *                             			If param $mode is 1, can contains an operator <, > or = like "<10" or ">=100.5 < 1000"
5639
 *                             			If param $mode is 2, can contains a list of id separated by comma like "1,3,4"
5640
 * @param	integer			$mode		0=value is list of keywords, 1=value is a numeric test (Example ">5.5 <10"), 2=value is a list of id separated with comma (Example '1,3,4')
5641
 * @param	integer			$nofirstand	1=Do now output the first 'AND'
5642
 * @return 	string 			$res 		The statement to append to the SQL query
5643
 */
5644
function natural_search($fields, $value, $mode=0, $nofirstand=0)
5645
{
5646
    global $db,$langs;
5647
5648
    if ($mode == 0)
5649
    {
5650
    	$value=preg_replace('/\*/','%',$value);	// Replace * with %
5651
    }
5652
    if ($mode == 1)
5653
    {
5654
    	$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
5655
    }
5656
    $crits = explode(' ', $value);
5657
    $res = '';
5658
    if (! is_array($fields)) $fields = array($fields);
5659
5660
    $nboffields = count($fields);
5661
    $end2 = count($crits);
5662
    $j = 0;
5663
    foreach ($crits as $crit)
5664
    {
5665
        $i = 0; $i2 = 0;
5666
        $newres = '';
5667
        foreach ($fields as $field)
5668
        {
5669
            if ($mode == 1)
5670
            {
5671
            	$operator='=';
5672
            	$newcrit = preg_replace('/([<>=]+)/','',trim($crit));
5673
5674
            	preg_match('/([<>=]+)/',trim($crit), $reg);
5675
            	if ($reg[1])
5676
            	{
5677
            		$operator = $reg[1];
5678
            	}
5679
            	if ($newcrit != '')
5680
            	{
5681
            		$numnewcrit = price2num($newcrit);
5682
            		if (is_numeric($numnewcrit))
5683
            		{
5684
            			$newres .= ($i2 > 0 ? ' OR ' : '') . $field . ' '.$operator.' '.$numnewcrit;
5685
            		}
5686
            		else
5687
            		{
5688
            			$newres .= ($i2 > 0 ? ' OR ' : '') . '1 = 2';	// force false
5689
            		}
5690
            		$i2++;	// a criteria was added to string
5691
            	}
5692
            }
5693
            else if ($mode == 2)
5694
            {
5695
				$newres .= ($i2 > 0 ? ' OR ' : '') . $field . " IN (" . $db->escape(trim($crit)) . ")";
5696
            	$i2++;	// a criteria was added to string
5697
            }
5698
            else    // $mode=0
5699
			{
5700
				$textcrit = '';
5701
				$tmpcrits = explode('|',$crit);
5702
				$i3 = 0;
5703
				foreach($tmpcrits as $tmpcrit)
5704
				{
5705
	            	$newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '') . $field . " LIKE '";
5706
5707
	            	$tmpcrit=trim($tmpcrit);
5708
	            	$tmpcrit2=$tmpcrit;
5709
	            	$tmpbefore='%'; $tmpafter='%';
5710
	            	if (preg_match('/^[\^\$]/', $tmpcrit)) 
5711
	            	{ 
5712
	            	    $tmpbefore='';
5713
	            	    $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2); 
5714
	            	}
5715
					if (preg_match('/[\^\$]$/', $tmpcrit)) 
5716
	            	{ 
5717
	            	    $tmpafter='';
5718
	            	    $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2); 
5719
	            	}
5720
	            	$newres .= $tmpbefore;
5721
	            	$newres .= $db->escape($tmpcrit2);
5722
	            	$newres .= $tmpafter;
5723
	            	$newres .= "'";
5724
	            	if (empty($tmpcrit2))
5725
	            	{
5726
	            	    $newres .= ' OR ' . $field . " IS NULL";
5727
	            	}
5728
	            	$i3++;
5729
				}
5730
				$i2++;	// a criteria was added to string
5731
            }
5732
            $i++;
5733
        }
5734
        if ($newres) $res = $res . ($res ? ' AND ' : '') . ($i2 > 1 ? '(' : '') .$newres . ($i2 > 1 ? ')' : '');
5735
        $j++;
5736
    }
5737
    $res = ($nofirstand?"":" AND ")."(" . $res . ")";
5738
    //print 'xx'.$res.'yy';
5739
    return $res;
5740
}
5741
5742
/**
5743
 * Return the filename of file to get the thumbs
5744
 *
5745
 * @param   string  $file           Original filename (full or relative path)
5746
 * @param   string  $extName        Extension to differenciate thumb file name ('', '_small', '_mini')
5747
 * @param   string  $extImgTarget   Force image extension for thumbs. Use '' to keep same extension than original image (default).
5748
 * @return  string                  New file name (full or relative path, including the thumbs/)
5749
 */
5750
function getImageFileNameForSize($file, $extName, $extImgTarget='')
5751
{
5752
	$dirName = dirname($file);
5753
	if ($dirName == '.') $dirName='';
5754
5755
    $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp)$/i','',$file);	// We remove extension, whatever is its case
5756
	$fileName = basename($fileName);
5757
5758
	if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpg$/i',$file)?'.jpg':'');
5759
    if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpeg$/i',$file)?'.jpeg':'');
5760
    if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.gif$/i',$file)?'.gif':'');
5761
    if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.png$/i',$file)?'.png':'');
5762
    if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.bmp$/i',$file)?'.bmp':'');
5763
5764
    if (! $extImgTarget) return $file;
5765
5766
    $subdir='';
5767
    if ($extName) $subdir = 'thumbs/';
5768
5769
    return ($dirName?$dirName.'/':'').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
5770
}
5771
5772
5773
/**
5774
 * Return URL we can use for advanced preview links
5775
 *
5776
 * @param   string    $modulepart     propal, facture, facture_fourn, ...
5777
 * @param   string    $relativepath   Relative path of docs
5778
 * @return  string                    Output string with HTML
5779
 */
5780
function getAdvancedPreviewUrl($modulepart, $relativepath)
5781
{
5782
    global $conf;
5783
    
5784
    if (empty($conf->use_javascript_ajax)) return '';
5785
5786
    $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css');
5787
    //$mime_preview[]='vnd.oasis.opendocument.presentation';
5788
    //$mime_preview[]='archive';
5789
    $num_mime = array_search(dol_mimetype($relativepath, '', 1), $mime_preview);
5790
5791
    if ($num_mime !== false) return 'javascript:document_preview(\''.dol_escape_js(DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&amp;attachment=0&amp;file='.$relativepath).'\', \''.dol_mimetype($relativepath).'\', \''.dol_escape_js('Preview').'\')';
5792
    else return '';
5793
}
5794
5795
5796
/**
5797
 *	Return mime type of a file
5798
 *
5799
 *	@param	string	$file		Filename we looking for MIME type
5800
 *  @param  string	$default    Default mime type if extension not found in known list
5801
 * 	@param	int		$mode    	0=Return full mime, 1=otherwise short mime string, 2=image for mime type, 3=source language
5802
 *	@return string 		    	Return a mime type family (text/xxx, application/xxx, image/xxx, audio, video, archive)
5803
 *  @see    image_format_supported (images.lib.php)
5804
 */
5805
function dol_mimetype($file,$default='application/octet-stream',$mode=0)
5806
{
5807
    $mime=$default;
5808
    $imgmime='other.png';
5809
    $srclang='';
5810
5811
    $tmpfile=preg_replace('/\.noexe$/','',$file);
5812
5813
    // Text files
5814
    if (preg_match('/\.txt$/i',$tmpfile))         			   { $mime='text/plain'; $imgmime='text.png'; }
5815
    if (preg_match('/\.rtx$/i',$tmpfile))                      { $mime='text/richtext'; $imgmime='text.png'; }
5816
    if (preg_match('/\.csv$/i',$tmpfile))					   { $mime='text/csv'; $imgmime='text.png'; }
5817
    if (preg_match('/\.tsv$/i',$tmpfile))					   { $mime='text/tab-separated-values'; $imgmime='text.png'; }
5818
    if (preg_match('/\.(cf|conf|log)$/i',$tmpfile))            { $mime='text/plain'; $imgmime='text.png'; }
5819
    if (preg_match('/\.ini$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='ini'; }
5820
    if (preg_match('/\.css$/i',$tmpfile))                      { $mime='text/css'; $imgmime='css.png'; $srclang='css'; }
5821
    // Certificate files
5822
    if (preg_match('/\.(crt|cer|key|pub)$/i',$tmpfile))        { $mime='text/plain'; $imgmime='text.png'; }
5823
    // HTML/XML
5824
    if (preg_match('/\.(html|htm|shtml)$/i',$tmpfile))         { $mime='text/html'; $imgmime='html.png'; $srclang='html'; }
5825
    if (preg_match('/\.(xml|xhtml)$/i',$tmpfile))              { $mime='text/xml'; $imgmime='other.png'; $srclang='xml'; }
5826
    // Languages
5827
    if (preg_match('/\.bas$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='bas'; }
5828
    if (preg_match('/\.(c)$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='c'; }
5829
    if (preg_match('/\.(cpp)$/i',$tmpfile))                    { $mime='text/plain'; $imgmime='text.png'; $srclang='cpp'; }
5830
    if (preg_match('/\.(h)$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='h'; }
5831
    if (preg_match('/\.(java|jsp)$/i',$tmpfile))               { $mime='text/plain'; $imgmime='text.png'; $srclang='java'; }
5832
    if (preg_match('/\.php([0-9]{1})?$/i',$tmpfile))           { $mime='text/plain'; $imgmime='php.png'; $srclang='php'; }
5833
    if (preg_match('/\.phtml$/i',$tmpfile))                    { $mime='text/plain'; $imgmime='php.png'; $srclang='php'; }
5834
    if (preg_match('/\.(pl|pm)$/i',$tmpfile))                  { $mime='text/plain'; $imgmime='pl.png'; $srclang='perl'; }
5835
    if (preg_match('/\.sql$/i',$tmpfile))                      { $mime='text/plain'; $imgmime='text.png'; $srclang='sql'; }
5836
    if (preg_match('/\.js$/i',$tmpfile))                       { $mime='text/x-javascript'; $imgmime='jscript.png'; $srclang='js'; }
5837
    // Open office
5838
    if (preg_match('/\.odp$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.presentation'; $imgmime='ooffice.png'; }
5839
    if (preg_match('/\.ods$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.spreadsheet'; $imgmime='ooffice.png'; }
5840
    if (preg_match('/\.odt$/i',$tmpfile))                      { $mime='application/vnd.oasis.opendocument.text'; $imgmime='ooffice.png'; }
5841
    // MS Office
5842
    if (preg_match('/\.mdb$/i',$tmpfile))					   { $mime='application/msaccess'; $imgmime='mdb.png'; }
5843
    if (preg_match('/\.doc(x|m)?$/i',$tmpfile))				   { $mime='application/msword'; $imgmime='doc.png'; }
5844
    if (preg_match('/\.dot(x|m)?$/i',$tmpfile))				   { $mime='application/msword'; $imgmime='doc.png'; }
5845
    if (preg_match('/\.xlt(x)?$/i',$tmpfile))				   { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; }
5846
    if (preg_match('/\.xla(m)?$/i',$tmpfile))				   { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; }
5847
    if (preg_match('/\.xls$/i',$tmpfile))			           { $mime='application/vnd.ms-excel'; $imgmime='xls.png'; }
5848
    if (preg_match('/\.xls(b|m|x)$/i',$tmpfile))			   { $mime='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; $imgmime='xls.png'; }
5849
    if (preg_match('/\.pps(m|x)?$/i',$tmpfile))				   { $mime='application/vnd.ms-powerpoint'; $imgmime='ppt.png'; }
5850
    if (preg_match('/\.ppt(m|x)?$/i',$tmpfile))				   { $mime='application/x-mspowerpoint'; $imgmime='ppt.png'; }
5851
    // Other
5852
    if (preg_match('/\.pdf$/i',$tmpfile))                      { $mime='application/pdf'; $imgmime='pdf.png'; }
5853
    // Scripts
5854
    if (preg_match('/\.bat$/i',$tmpfile))                      { $mime='text/x-bat'; $imgmime='script.png'; $srclang='dos'; }
5855
    if (preg_match('/\.sh$/i',$tmpfile))                       { $mime='text/x-sh'; $imgmime='script.png'; $srclang='bash'; }
5856
    if (preg_match('/\.ksh$/i',$tmpfile))                      { $mime='text/x-ksh'; $imgmime='script.png'; $srclang='bash'; }
5857
    if (preg_match('/\.bash$/i',$tmpfile))                     { $mime='text/x-bash'; $imgmime='script.png'; $srclang='bash'; }
5858
    // Images
5859
    if (preg_match('/\.ico$/i',$tmpfile))                      { $mime='image/x-icon'; $imgmime='image.png'; }
5860
    if (preg_match('/\.(jpg|jpeg)$/i',$tmpfile))			   { $mime='image/jpeg'; $imgmime='image.png'; }
5861
    if (preg_match('/\.png$/i',$tmpfile))					   { $mime='image/png'; $imgmime='image.png'; }
5862
    if (preg_match('/\.gif$/i',$tmpfile))					   { $mime='image/gif'; $imgmime='image.png'; }
5863
    if (preg_match('/\.bmp$/i',$tmpfile))					   { $mime='image/bmp'; $imgmime='image.png'; }
5864
    if (preg_match('/\.(tif|tiff)$/i',$tmpfile))			   { $mime='image/tiff'; $imgmime='image.png'; }
5865
    // Calendar
5866
    if (preg_match('/\.vcs$/i',$tmpfile))					   { $mime='text/calendar'; $imgmime='other.png'; }
5867
    if (preg_match('/\.ics$/i',$tmpfile))					   { $mime='text/calendar'; $imgmime='other.png'; }
5868
    // Other
5869
    if (preg_match('/\.torrent$/i',$tmpfile))				   { $mime='application/x-bittorrent'; $imgmime='other.png'; }
5870
    // Audio
5871
    if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i',$tmpfile)) { $mime='audio'; $imgmime='audio.png'; }
5872
    // Video
5873
    if (preg_match('/\.ogv$/i',$tmpfile))                      { $mime='video/ogg'; $imgmime='video.png'; }
5874
    if (preg_match('/\.webm$/i',$tmpfile))                     { $mime='video/webm'; $imgmime='video.png'; }
5875
    if (preg_match('/\.avi$/i',$tmpfile))                      { $mime='video/x-msvideo'; $imgmime='video.png'; }
5876
    if (preg_match('/\.divx$/i',$tmpfile))                     { $mime='video/divx'; $imgmime='video.png'; }
5877
    if (preg_match('/\.xvid$/i',$tmpfile))                     { $mime='video/xvid'; $imgmime='video.png'; }
5878
    if (preg_match('/\.(wmv|mpg|mpeg)$/i',$tmpfile))           { $mime='video'; $imgmime='video.png'; }
5879
    // Archive
5880
    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, ...
5881
    // Exe
5882
    if (preg_match('/\.(exe|com)$/i',$tmpfile))                { $mime='application/octet-stream'; $imgmime='other.png'; }
5883
    // Lib
5884
    if (preg_match('/\.(dll|lib|o|so|a)$/i',$tmpfile))         { $mime='library'; $imgmime='library.png'; }
5885
    // Err
5886
    if (preg_match('/\.err$/i',$tmpfile))                      { $mime='error'; $imgmime='error.png'; }
5887
5888
    // Return string
5889
    if ($mode == 1)
5890
    {
5891
        $tmp=explode('/',$mime);
5892
        return (! empty($tmp[1])?$tmp[1]:$tmp[0]);
5893
    }
5894
    if ($mode == 2)
5895
    {
5896
        return $imgmime;
5897
    }
5898
    if ($mode == 3)
5899
    {
5900
        return $srclang;
5901
    }
5902
5903
    return $mime;
5904
}
5905
5906