Completed
Branch develop (73c7aa)
by
unknown
27:27
created

functions.lib.php ➔ dolGetFirstLineOfText()   C

Complexity

Conditions 13
Paths 44

Size

Total Lines 48
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 29
nc 44
nop 2
dl 0
loc 48
rs 5.0877
c 0
b 0
f 0

How to fix   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-2017	Regis Houssin				<[email protected]>
9
 * Copyright (C) 2008		Raphael Bertrand (Resultic)	<[email protected]>
10
 * Copyright (C) 2010-2016	Juanjo Menent				<[email protected]>
11
 * Copyright (C) 2013		Cédric Salvador				<[email protected]>
12
 * Copyright (C) 2013-2017	Alexandre Spangaro			<[email protected]>
13
 * Copyright (C) 2014		Cédric GROSS					<[email protected]>
14
 * Copyright (C) 2014-2015	Marcos García				<[email protected]>
15
 * Copyright (C) 2015		Jean-François Ferry			<[email protected]>
16
 *
17
 * This program is free software; you can redistribute it and/or modify
18
 * it under the terms of the GNU General Public License as published by
19
 * the Free Software Foundation; either version 3 of the License, or
20
 * (at your option) any later version.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
 * GNU General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU General Public License
28
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29
 * or see http://www.gnu.org/
30
 */
31
32
/**
33
 *	\file			htdocs/core/lib/functions.lib.php
34
 *	\brief			A set of functions for Dolibarr
35
 *					This file contains all frequently used functions.
36
 */
37
38
include_once DOL_DOCUMENT_ROOT .'/core/lib/json.lib.php';
39
40
/**
41
 * Function to return value of a static property when class
42
 * name is dynamically defined (not hard coded).
43
 * This is because $myclass::$myvar works from PHP 5.3.0+ only
44
 *
45
 * @param	string 	$class		Class name
46
 * @param 	string 	$member		Name of property
47
 * @return 	mixed				Return value of static property
48
 * @deprecated Dolibarr now requires 5.3.0+, use $class::$property syntax
49
 * @see https://php.net/manual/language.oop5.static.php
50
 */
51
function getStaticMember($class, $member)
52
{
53
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
54
55
	// This part is deprecated. Uncomment if for php 5.2.*, and comment next isset class::member
56
	/*if (version_compare(phpversion(), '5.3.0', '<'))
57
	{
58
		if (is_object($class)) $class = get_class($class);
59
		$classObj = new ReflectionClass($class);
60
		$result = null;
61
62
		$found=0;
63
		foreach($classObj->getStaticProperties() as $prop => $value)
64
		{
65
			if ($prop == $member)
66
			{
67
				$result = $value;
68
				$found++;
69
				break;
70
			}
71
		}
72
73
		if ($found) return $result;
74
	}*/
75
76
	if (isset($class::$member)) return $class::$member;
77
	dol_print_error('','Try to get a static member "'.$member.'" in class "'.$class.'" that does not exists or is not static.');
78
	return null;
79
}
80
81
82
/**
83
 * Return a DoliDB instance (database handler).
84
 *
85
 * @param   string	$type		Type of database (mysql, pgsql...)
86
 * @param	string	$host		Address of database server
87
 * @param	string	$user		Nom de l'utilisateur autorise
88
 * @param	string	$pass		Mot de passe
89
 * @param	string	$name		Nom de la database
90
 * @param	int		$port		Port of database server
91
 * @return	DoliDB				A DoliDB instance
92
 */
93
function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
94
{
95
	require_once DOL_DOCUMENT_ROOT ."/core/db/".$type.'.class.php';
96
97
	$class='DoliDB'.ucfirst($type);
98
	$dolidb=new $class($type, $host, $user, $pass, $name, $port);
99
	return $dolidb;
100
}
101
102
/**
103
 * 	Get list of entity id to use.
104
 *
105
 * 	@param	string	$element	Current element
106
 *                              'societe', 'socpeople', 'actioncomm', 'agenda', 'resource',
107
 *                              'product', 'productprice', 'stock',
108
 *                              'propal', 'supplier_proposal', 'facture', 'facture_fourn',
109
 *                              'categorie', 'bank_account', 'bank_account', 'adherent', 'user',
110
 *                              'commande', 'commande_fournisseur', 'expedition', 'intervention', 'survey',
111
 *                              'contract', 'tax', 'expensereport', 'holiday', 'multicurrency', 'project',
112
 *                              'email_template', 'event',
113
 * 	@param	int	     $shared	1=Return id of current entity + shared entities (default), 0=Return id of current entity only
114
 * 	@return	mixed				Entity id(s) to use
115
 */
116
function getEntity($element, $shared=1)
117
{
118
	global $conf, $mc;
119
120
	// For backward compatibilty
121
	if ($element == 'actioncomm') $element='agenda';
122
	if ($element == 'fichinter')  $element='intervention';
123
	if ($element == 'categorie')  $element='category';
124
125
	if (is_object($mc))
126
	{
127
		return $mc->getEntity($element, $shared);
128
	}
129
	else
130
	{
131
		$out='';
132
		$addzero = array('user', 'usergroup', 'email_template', 'default_values');
133
		if (in_array($element, $addzero)) $out.= '0,';
134
		$out.= $conf->entity;
135
		return $out;
136
	}
137
}
138
139
/**
140
 * Return information about user browser
141
 *
142
 * Returns array with the following format:
143
 * array(
144
 *  'browsername' => Browser name (firefox|chrome|iceweasel|epiphany|safari|opera|ie|unknown)
145
 *  'browserversion' => Browser version. Empty if unknown
146
 *  'browseros' => Set with mobile OS (android|blackberry|ios|palm|symbian|webos|maemo|windows|unknown)
147
 *  'layout' => (tablet|phone|classic)
148
 *  'phone' => empty if not mobile, (android|blackberry|ios|palm|unknown) if mobile
149
 *  'tablet' => true/false
150
 * )
151
 *
152
 * @param string $user_agent Content of $_SERVER["HTTP_USER_AGENT"] variable
153
 * @return	array Check function documentation
154
 */
155
function getBrowserInfo($user_agent)
156
{
157
	include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
158
159
	$name='unknown';
160
	$version='';
161
	$os='unknown';
162
	$phone = '';
163
164
	$detectmobile = new Mobile_Detect(null, $user_agent);
165
	$tablet = $detectmobile->isTablet();
166
167
	if ($detectmobile->isMobile()) {
168
169
		$phone = 'unknown';
170
171
		// If phone/smartphone, we set phone os name.
172
		if ($detectmobile->is('AndroidOS')) {
173
			$os = $phone = 'android';
174
		} elseif ($detectmobile->is('BlackBerryOS')) {
175
			$os = $phone = 'blackberry';
176
		} elseif ($detectmobile->is('iOS')) {
177
			$os = 'ios';
178
			$phone = 'iphone';
179
		} elseif ($detectmobile->is('PalmOS')) {
180
			$os = $phone = 'palm';
181
		} elseif ($detectmobile->is('SymbianOS')) {
182
			$os = 'symbian';
183
		} elseif ($detectmobile->is('webOS')) {
184
			$os = 'webos';
185
		} elseif ($detectmobile->is('MaemoOS')) {
186
			$os = 'maemo';
187
		} elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
188
			$os = 'windows';
189
		}
190
	}
191
192
	// OS
193
	if (preg_match('/linux/i', $user_agent))	{ $os='linux'; }
194
	elseif (preg_match('/macintosh/i', $user_agent))	{ $os='macintosh'; }
195
196
	// Name
197
	if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg))      { $name='firefox';   $version=$reg[2]; }
198
	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
199
	elseif (preg_match('/chrome/i', $user_agent, $reg))                   { $name='chrome'; }
200
	elseif (preg_match('/iceweasel/i', $user_agent))                      { $name='iceweasel'; }
201
	elseif (preg_match('/epiphany/i', $user_agent))                       { $name='epiphany';  }
202
	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.
203
	elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg))    { $name='opera';     $version=$reg[2]; }
204
	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
205
	elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) { $name='lynxlinks'; $version=$reg[4]; }
206
207
	if ($tablet) {
208
		$layout = 'tablet';
209
	} elseif ($phone) {
210
		$layout = 'phone';
211
	} else {
212
		$layout = 'classic';
213
	}
214
215
	return array(
216
		'browsername' => $name,
217
		'browserversion' => $version,
218
		'browseros' => $os,
219
		'layout' => $layout,
220
		'phone' => $phone,
221
		'tablet' => $tablet
222
	);
223
}
224
225
/**
226
 *  Function called at end of web php process
227
 *
228
 *  @return	void
229
 */
230
function dol_shutdown()
231
{
232
	global $conf,$user,$langs,$db;
233
	$disconnectdone=false; $depth=0;
234
	if (is_object($db) && ! empty($db->connected)) { $depth=$db->transaction_opened; $disconnectdone=$db->close(); }
235
	dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth)?' (Warn: db disconnection forced, transaction depth was '.$depth.')':''), (($disconnectdone && $depth)?LOG_WARNING:LOG_INFO));
236
}
237
238
239
/**
240
 *  Return value of a param into GET or POST supervariable.
241
 *  Use the property $user->default_values[path]['creatform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder']
242
 *  Note: The property $user->default_values is loaded by main.php when loading the user.
243
 *
244
 *  @param	string	$paramname   Name of parameter to found
245
 *  @param	string	$check	     Type of check
246
 *                                  ''=no check (deprecated)
247
 *                                  'none'=no check (only for param that should have very rich content)
248
 *                                  'int'=check it's numeric (integer or float)
249
 *                                  'alpha'=check it's text and sign
250
 *                                  'aZ'=check it's a-z only
251
 *                                  'aZ09'=check it's simple alpha string (recommended for keys)
252
 *                                  'array'=check it's array
253
 *                                  'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string)
254
 *                                  'nohtml', 'alphanohtml'=check there is no html content
255
 *                                  'custom'= custom filter specify $filter and $options)
256
 *  @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)
257
 *  @param  int     $filter      Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails)
258
 *  @param  mixed   $options     Options to pass to filter_var when $check is set to 'custom'.
259
 *  @return string|string[]      Value found (string or array), or '' if check fails
260
 *
261
 *  @TODO Set default value for check to alpha. Check all WYSIWYG edition (email and description...) is still ok with rich text.
262
 */
263
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...
264
{
265
    global $mysoc,$user,$conf;
266
267
    if (empty($paramname)) return 'BadFirstParameterForGETPOST';
268
269
    if (empty($method)) $out = isset($_GET[$paramname])?$_GET[$paramname]:(isset($_POST[$paramname])?$_POST[$paramname]:'');
270
	elseif ($method==1) $out = isset($_GET[$paramname])?$_GET[$paramname]:'';
271
	elseif ($method==2) $out = isset($_POST[$paramname])?$_POST[$paramname]:'';
272
	elseif ($method==3) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:'');
273
	elseif ($method==4) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:(isset($_COOKIE[$paramname])?$_COOKIE[$paramname]:''));
274
	else return 'BadThirdParameterForGETPOST';
275
276
	if (empty($method) || $method == 3 || $method == 4)
277
	{
278
    	$relativepathstring = $_SERVER["PHP_SELF"];
279
    	// Clean $relativepathstring
280
    	if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'),'/').'/', '', $relativepathstring);
281
    	$relativepathstring = preg_replace('/^\//', '', $relativepathstring);
282
    	$relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
283
    	//var_dump($relativepathstring);
284
285
        // Code for search criteria persistence.
286
    	// Retrieve values if restore_lastsearch_values is set and there is saved values
287
    	if (! empty($_GET['restore_lastsearch_values']) && ! empty($_SESSION['lastsearch_values_'.$relativepathstring]))        // Keep $_GET here
288
    	{
289
	        $tmp=json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
290
	        if (is_array($tmp))
291
	        {
292
	            foreach($tmp as $key => $val)
293
	            {
294
	                if ($key == $paramname)
295
	                {
296
	                    $out=$val;
297
	                    break;
298
	                }
299
	            }
300
	        }
301
    	}
302
	    // Else, retreive default values if we are not doing a sort
303
	    elseif (! isset($_GET['sortfield']) && ! empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES))	// If we did a click on a field to sort, we do no apply default values. Same if option MAIN_ENABLE_DEFAULT_VALUES is not set
304
	    {
305
	        if (! empty($_GET['action']) && $_GET['action'] == 'create' && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
306
	        {
307
	            if (! empty($user->default_values))		// $user->default_values defined from menu default values
308
	            {
309
	                //var_dump($user->default_values[$relativepathstring]['createform']);
310
	                if (isset($user->default_values[$relativepathstring]['createform'][$paramname])) $out = $user->default_values[$relativepathstring]['createform'][$paramname];
311
	            }
312
	        }
313
	        // Management of default search_filters and sort order
314
	        //elseif (preg_match('/list.php$/', $_SERVER["PHP_SELF"]) && ! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
315
	        elseif (! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
316
	        {
317
	            if (! empty($user->default_values))		// $user->default_values defined from menu default values
318
	            {
319
	                //var_dump($user->default_values[$relativepathstring]);
320
	                if ($paramname == 'sortfield')
321
	                {
322
	                    if (isset($user->default_values[$relativepathstring]['sortorder']))    // We will use the key of $user->default_values[path][sortorder]
323
	                    {
324
	                        $forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
325
	                        foreach($user->default_values[$relativepathstring]['sortorder'] as $key => $val)
326
	                        {
327
	                            if ($out) $out.=', ';
328
	                            $out.=dol_string_nospecial($key, '', $forbidden_chars_to_replace);
329
	                        }
330
	                    }
331
	                }
332
	                elseif ($paramname == 'sortorder')
333
	                {
334
	                    if (isset($user->default_values[$relativepathstring]['sortorder']))    // We will use the val of $user->default_values[path][sortorder]
335
	                    {
336
	                        $forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
337
	                        foreach($user->default_values[$relativepathstring]['sortorder'] as $key => $val)
338
	                        {
339
	                            if ($out) $out.=', ';
340
	                            $out.=dol_string_nospecial($val, '', $forbidden_chars_to_replace);
341
	                        }
342
	                    }
343
	                }
344
	                elseif (isset($user->default_values[$relativepathstring]['filters'][$paramname]))
345
	                {
346
	                    $forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","=");  // we accept _, -, . and ,
347
	                    $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$paramname], '', $forbidden_chars_to_replace);
348
	                }
349
	            }
350
	        }
351
	    }
352
353
	}
354
355
	if (empty($check) && ! empty($conf->global->MAIN_FEATURES_LEVEL) && $conf->global->MAIN_FEATURES_LEVEL >= 2)
356
	{
357
	   dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param not defined, when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
358
	   // Enable this line to know who call the GETPOST with empty $check parameter.
359
	   //var_dump(debug_backtrace()[0]);
360
	}
361
362
	if (! empty($check))
363
	{
364
	    // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable paramaters)
365
	    // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOUNTRYID__, __USERID__, __ENTITYID__, ...
366
	    // We do this only if var is a GET. If it is a POST, may be we want to post the text with vars as the setup text.
367
	    if (! is_array($out) && empty($_POST[$paramname]))
368
	    {
369
	        $maxloop=20; $loopnb=0;    // Protection against infinite loop
370
	        while (preg_match('/__([A-Z0-9]+_?[A-Z0-9]+)__/i', $out, $reg) && ($loopnb < $maxloop))    // Detect '__ABCDEF__' as key 'ABCDEF' and '__ABC_DEF__' as key 'ABC_DEF'. Detection is also correct when 2 vars are side by side.
371
	        {
372
	            $loopnb++; $newout = '';
373
374
    	        if ($reg[1] == 'DAY')                { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['mday']; }
375
    	        elseif ($reg[1] == 'MONTH')          { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['mon'];  }
376
    	        elseif ($reg[1] == 'YEAR')           { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['year']; }
377
    	    	elseif ($reg[1] == 'PREVIOUS_DAY')   { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; }
378
    	        elseif ($reg[1] == 'PREVIOUS_MONTH') { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_prev_month($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['month']; }
379
    	        elseif ($reg[1] == 'PREVIOUS_YEAR')  { $tmp=dol_getdate(dol_now(), true); $newout = ($tmp['year'] - 1); }
380
    	    	elseif ($reg[1] == 'NEXT_DAY')       { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; }
381
    	        elseif ($reg[1] == 'NEXT_MONTH')     { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_next_month($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['month']; }
382
    	        elseif ($reg[1] == 'NEXT_YEAR')      { $tmp=dol_getdate(dol_now(), true); $newout = ($tmp['year'] + 1); }
383
    	        elseif ($reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID')
384
    	        {
385
    	            $newout = $mysoc->country_id;
386
    	        }
387
    	        elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID')
388
    	        {
389
    	            $newout = $user->id;
390
    	        }
391
    	    	elseif ($reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID')
392
    	        {
393
    	            $newout = $user->fk_user;
394
    	        }
395
    	        elseif ($reg[1] == 'ENTITYID')
396
    	        {
397
    	            $newout = $conf->entity;
398
    	        }
399
    	        else $newout = '';     // Key not found, we replace with empty string
400
    	        //var_dump('__'.$reg[1].'__ -> '.$newout);
401
    	        $out = preg_replace('/__'.preg_quote($reg[1],'/').'__/', $newout, $out);
402
	        }
403
	    }
404
405
	    // Check is done after replacement
406
	    switch ($check)
407
	    {
408
	        case 'none':
409
	            break;
410
	        case 'int':    // Check param is a numeric value (integer but also float or hexadecimal)
411
	            if (! is_numeric($out)) { $out=''; }
412
	            break;
413
	        case 'intcomma':
414
	            if (preg_match('/[^0-9,]+/i',$out)) $out='';
415
	            break;
416
	        case 'alpha':
417
	            $out=trim($out);
418
	            // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
419
	            // '../' is dangerous because it allows dir transversals
420
	            if (preg_match('/"/',$out)) $out='';
421
	            else if (preg_match('/\.\.\//',$out)) $out='';
422
	            break;
423
	        case 'san_alpha':
424
	            $out=filter_var($out,FILTER_SANITIZE_STRING);
425
	            break;
426
	        case 'aZ':
427
	            $out=trim($out);
428
	            if (preg_match('/[^a-z]+/i',$out)) $out='';
429
	            break;
430
	        case 'aZ09':
431
	            $out=trim($out);
432
	            if (preg_match('/[^a-z0-9_\-\.]+/i',$out)) $out='';
433
	            break;
434
	        case 'array':
435
	            if (! is_array($out) || empty($out)) $out=array();
436
	            break;
437
			case 'nohtml':
438
			    $out=dol_string_nohtmltag($out);
439
				break;
440
			case 'alphanohtml':	// Recommended for search params
441
	            $out=trim($out);
442
	            // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
443
	            // '../' is dangerous because it allows dir transversals
444
	            if (preg_match('/"/',$out)) $out='';
445
	            else if (preg_match('/\.\.\//',$out)) $out='';
446
			    $out=dol_string_nohtmltag($out);
447
				break;
448
			case 'custom':
449
	            if (empty($filter)) return 'BadFourthParameterForGETPOST';
450
	            $out=filter_var($out, $filter, $options);
451
	            break;
452
	    }
453
	}
454
455
    // Code for search criteria persistence.
456
	// Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
457
	if (empty($method) || $method == 3 || $method == 4)
458
	{
459
	    //if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield", 'smonth', 'syear', 'month', 'year')))
460
	    if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder','sortfield')))
461
	    {
462
	        //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
463
464
	        // We save search key only if:
465
	        // - not empty, or
466
	        // - if value is empty and a default value exists that is not empty (it means we did a filter to an empty value when default was not).
467
468
	        //if (! empty($out) || ! empty($user->default_values[$relativepathstring]['filters'][$paramname]))
469
	        if (! empty($out))
470
	        {
471
                $user->lastsearch_values_tmp[$relativepathstring][$paramname]=$out;
0 ignored issues
show
Bug introduced by
The variable $relativepathstring does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

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

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

Loading history...
4813
		else $pattern = '/(<br[^>]*>)/U';							// /U is to have UNGREEDY regex to limit to one html tag.
4814
		$a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
4815
4816
		$firstline='';
4817
		$i=0;
4818
		$nba = count($a);	// 2x nb of lines in $a because $a contains also a line for each new line separator
4819
		while (($i < $nba) && ($i < ($nboflines * 2)))
4820
		{
4821
			if ($i % 2 == 0) $firstline .= $a[$i];
4822
			elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) $firstline .= ($ishtml?"<br>\n":"\n");
4823
			$i++;
4824
		}
4825
		unset($a);
4826
    	return $firstline.(($i < $nba)?'...':'');
4827
	}
4828
}
4829
4830
4831
/**
4832
 * Replace CRLF in string with a HTML BR tag
4833
 *
4834
 * @param	string	$stringtoencode		String to encode
4835
 * @param	int     $nl2brmode			0=Adding br before \n, 1=Replacing \n by br
4836
 * @param   bool	$forxml             false=Use <br>, true=Use <br />
4837
 * @return	string						String encoded
4838
 * @see dol_nboflines, dolGetFirstLineOfText
4839
 */
4840
function dol_nl2br($stringtoencode,$nl2brmode=0,$forxml=false)
4841
{
4842
	if (!$nl2brmode) {
4843
		return nl2br($stringtoencode, $forxml);
4844
	} else {
4845
		$ret=preg_replace('/(\r\n|\r|\n)/i', ($forxml?'<br />':'<br>'), $stringtoencode);
4846
		return $ret;
4847
	}
4848
}
4849
4850
4851
/**
4852
 *	This function is called to encode a string into a HTML string but differs from htmlentities because
4853
 * 	a detection is done before to see if text is already HTML or not. Also, all entities but &,<,> are converted.
4854
 *  This permits to encode special chars to entities with no double encoding for already encoded HTML strings.
4855
 * 	This function also remove last EOL or BR if $removelasteolbr=1 (default).
4856
 *  For PDF usage, you can show text by 2 ways:
4857
 *              - writeHTMLCell -> param must be encoded into HTML.
4858
 *              - MultiCell -> param must not be encoded into HTML.
4859
 *              Because writeHTMLCell convert also \n into <br>, if function
4860
 *              is used to build PDF, nl2brmode must be 1.
4861
 *
4862
 *	@param	string	$stringtoencode		String to encode
4863
 *	@param	int		$nl2brmode			0=Adding br before \n, 1=Replacing \n by br (for use with FPDF writeHTMLCell function for example)
4864
 *  @param  string	$pagecodefrom       Pagecode stringtoencode is encoded
4865
 *  @param	int		$removelasteolbr	1=Remove last br or lasts \n (default), 0=Do nothing
4866
 *  @return	string						String encoded
4867
 */
4868
function dol_htmlentitiesbr($stringtoencode,$nl2brmode=0,$pagecodefrom='UTF-8',$removelasteolbr=1)
4869
{
4870
	$newstring=$stringtoencode;
4871
	if (dol_textishtml($stringtoencode))	// Check if text is already HTML or not
4872
	{
4873
		$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.
4874
		if ($removelasteolbr) $newstring=preg_replace('/<br>$/i','',$newstring);	// Remove last <br> (remove only last one)
4875
		$newstring=strtr($newstring,array('&'=>'__and__','<'=>'__lt__','>'=>'__gt__','"'=>'__dquot__'));
4876
		$newstring=dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom);	// Make entity encoding
4877
		$newstring=strtr($newstring,array('__and__'=>'&','__lt__'=>'<','__gt__'=>'>','__dquot__'=>'"'));
4878
	}
4879
	else
4880
	{
4881
		if ($removelasteolbr) $newstring=preg_replace('/(\r\n|\r|\n)$/i','',$newstring);	// Remove last \n (may remove several)
4882
		$newstring=dol_nl2br(dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom),$nl2brmode);
4883
	}
4884
	// Other substitutions that htmlentities does not do
4885
	//$newstring=str_replace(chr(128),'&euro;',$newstring);	// 128 = 0x80. Not in html entity table.     // Seems useles with TCPDF. Make bug with UTF8 languages
4886
	return $newstring;
4887
}
4888
4889
/**
4890
 *	This function is called to decode a HTML string (it decodes entities and br tags)
4891
 *
4892
 *	@param	string	$stringtodecode		String to decode
4893
 *	@param	string	$pagecodeto			Page code for result
4894
 *	@return	string						String decoded
4895
 */
4896
function dol_htmlentitiesbr_decode($stringtodecode,$pagecodeto='UTF-8')
4897
{
4898
	$ret=dol_html_entity_decode($stringtodecode,ENT_COMPAT,$pagecodeto);
4899
	$ret=preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i',"<br>",$ret);
4900
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i',"\r\n",$ret);
4901
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i',"\n",$ret);
4902
	$ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i',"\n",$ret);
4903
	return $ret;
4904
}
4905
4906
/**
4907
 *	This function remove all ending \n and br at end
4908
 *
4909
 *	@param	string	$stringtodecode		String to decode
4910
 *	@return	string						String decoded
4911
 */
4912
function dol_htmlcleanlastbr($stringtodecode)
4913
{
4914
	$ret=preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i',"",$stringtodecode);
4915
	return $ret;
4916
}
4917
4918
/**
4919
 * Replace html_entity_decode functions to manage errors
4920
 *
4921
 * @param   string	$a		Operand a
4922
 * @param   string	$b		Operand b (ENT_QUOTES=convert simple and double quotes)
4923
 * @param   string	$c		Operand c
4924
 * @return  string			String decoded
4925
 */
4926
function dol_html_entity_decode($a,$b,$c='UTF-8')
4927
{
4928
	return html_entity_decode($a,$b,$c);
4929
}
4930
4931
/**
4932
 * Replace htmlentities functions to manage errors http://php.net/manual/en/function.htmlentities.php
4933
 * Goal of this function is to be sure to have default values of htmlentities that match what we need.
4934
 *
4935
 * @param   string  $string         The input string.
4936
 * @param   int     $flags          Flags(see PHP doc above)
4937
 * @param   string  $encoding       Encoding
4938
 * @param   bool    $double_encode  When double_encode is turned off PHP will not encode existing html entities
4939
 * @return  string  $ret            Encoded string
4940
 */
4941
function dol_htmlentities($string, $flags=null, $encoding='UTF-8', $double_encode=false)
4942
{
4943
	return htmlentities($string, $flags, $encoding, $double_encode);
4944
}
4945
4946
4947
/**
4948
 *	Check if a string is a correct iso string
4949
 *	If not, it will we considered not HTML encoded even if it is by FPDF.
4950
 *	Example, if string contains euro symbol that has ascii code 128
4951
 *
4952
 *	@param	string	$s      String to check
4953
 *	@return	int     		0 if bad iso, 1 if good iso
4954
 */
4955
function dol_string_is_good_iso($s)
4956
{
4957
	$len=dol_strlen($s);
4958
	$ok=1;
4959
	for($scursor=0;$scursor<$len;$scursor++)
4960
	{
4961
		$ordchar=ord($s{$scursor});
4962
		//print $scursor.'-'.$ordchar.'<br>';
4963
		if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) { $ok=0; break; }
4964
		if ($ordchar > 126 && $ordchar < 160) { $ok=0; break; }
4965
	}
4966
	return $ok;
4967
}
4968
4969
4970
/**
4971
 *	Return nb of lines of a clear text
4972
 *
4973
 *	@param	string	$s			String to check
4974
 * 	@param	int     $maxchar	Not yet used
4975
 *	@return	int					Number of lines
4976
 *  @see	dol_nboflines_bis, dolGetFirstLineOfText
4977
 */
4978
function dol_nboflines($s,$maxchar=0)
4979
{
4980
	if ($s == '') return 0;
4981
	$arraystring=explode("\n",$s);
4982
	$nb=count($arraystring);
4983
4984
	return $nb;
4985
}
4986
4987
4988
/**
4989
 *	Return nb of lines of a formated text with \n and <br> (WARNING: string must not have mixed \n and br separators)
4990
 *
4991
 *	@param	string	$text      		Text
4992
 *	@param	int		$maxlinesize  	Largeur de ligne en caracteres (ou 0 si pas de limite - defaut)
4993
 * 	@param	string	$charset		Give the charset used to encode the $text variable in memory.
4994
 *	@return int						Number of lines
4995
 *	@see	dol_nboflines, dolGetFirstLineOfText
4996
 */
4997
function dol_nboflines_bis($text,$maxlinesize=0,$charset='UTF-8')
4998
{
4999
	$repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5000
	if (dol_textishtml($text)) $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5001
5002
	$text = strtr($text, $repTable);
5003
	if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; }	// /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
5004
	else $pattern = '/(<br[^>]*>)/U';							// /U is to have UNGREEDY regex to limit to one html tag.
5005
	$a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5006
5007
	$nblines = (int) floor((count($a)+1)/2);
5008
	// count possible auto line breaks
5009
	if($maxlinesize)
5010
	{
5011
		foreach ($a as $line)
5012
		{
5013
			if (dol_strlen($line)>$maxlinesize)
5014
			{
5015
				//$line_dec = html_entity_decode(strip_tags($line));
5016
				$line_dec = html_entity_decode($line);
5017
				if(dol_strlen($line_dec)>$maxlinesize)
5018
				{
5019
					$line_dec=wordwrap($line_dec,$maxlinesize,'\n',true);
5020
					$nblines+=substr_count($line_dec,'\n');
5021
				}
5022
			}
5023
		}
5024
	}
5025
5026
	unset($a);
5027
	return $nblines;
5028
}
5029
5030
/**
5031
 *	 Same function than microtime in PHP 5 but compatible with PHP4
5032
 *
5033
 * @return		float		Time (millisecondes) with microsecondes in decimal part
5034
 * @deprecated Dolibarr does not support PHP4, you should use native function
5035
 * @see microtime()
5036
 */
5037
function dol_microtime_float()
5038
{
5039
	dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
5040
5041
	return microtime(true);
5042
}
5043
5044
/**
5045
 *	Return if a text is a html content
5046
 *
5047
 *	@param	string	$msg		Content to check
5048
 *	@param	int		$option		0=Full detection, 1=Fast check
5049
 *	@return	boolean				true/false
5050
 *	@see	dol_concatdesc
5051
 */
5052
function dol_textishtml($msg,$option=0)
5053
{
5054
	if ($option == 1)
5055
	{
5056
		if (preg_match('/<html/i',$msg))				return true;
5057
		elseif (preg_match('/<body/i',$msg))			return true;
5058
		elseif (preg_match('/<br/i',$msg))				return true;
5059
		return false;
5060
	}
5061
	else
5062
	{
5063
		if (preg_match('/<html/i',$msg))				return true;
5064
		elseif (preg_match('/<body/i',$msg))			return true;
5065
		elseif (preg_match('/<(b|em|i|u)>/i',$msg))		return true;
5066
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i',$msg)) 	  return true;
5067
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*>/i',$msg)) return true;
5068
		elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/>/i',$msg)) return true;
5069
		elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i',$msg)) return true;	// must accept <img src="http://example.com/aaa.png" />
5070
		elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i',$msg)) return true;	// must accept <a href="http://example.com/aaa.png" />
5071
		elseif (preg_match('/<h[0-9]>/i',$msg))			return true;
5072
		elseif (preg_match('/&[A-Z0-9]{1,6};/i',$msg))	return true;    // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
5073
		elseif (preg_match('/&#[0-9]{2,3};/i',$msg))	return true;    // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
5074
		return false;
5075
	}
5076
}
5077
5078
/**
5079
 *  Concat 2 descriptions with a new line between them (second operand after first one with appropriate new line separator)
5080
 *  text1 html + text2 html => text1 + '<br>' + text2
5081
 *  text1 html + text2 txt  => text1 + '<br>' + dol_nl2br(text2)
5082
 *  text1 txt  + text2 html => dol_nl2br(text1) + '<br>' + text2
5083
 *  text1 txt  + text2 txt  => text1 + '\n' + text2
5084
 *
5085
 *  @param	string	$text1		Text 1
5086
 *  @param	string	$text2		Text 2
5087
 *  @param  bool	$forxml     false=Use <br>, true=Use <br />
5088
 *  @return	string				Text 1 + new line + Text2
5089
 *  @see    dol_textishtml
5090
 */
5091
function dol_concatdesc($text1,$text2,$forxml=false)
5092
{
5093
	$ret='';
5094
	$ret.= (! dol_textishtml($text1) && dol_textishtml($text2))?dol_nl2br($text1, 0, $forxml):$text1;
5095
	$ret.= (! empty($text1) && ! empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2))?($forxml?"<br \>\n":"<br>\n") : "\n") : "";
5096
	$ret.= (dol_textishtml($text1) && ! dol_textishtml($text2))?dol_nl2br($text2, 0, $forxml):$text2;
5097
	return $ret;
5098
}
5099
5100
5101
/**
5102
 * Return array of possible common substitutions.
5103
 *
5104
 * @param	Translate	$outputlangs	Output language
5105
 * @param   int         $onlykey        Do not calculate heavy values of keys (performance enhancement when we need only the keys)
5106
 * @param   array       $exclude        Array of family keys we want to exclude. For example array('mycompany', 'objectamount', 'date', 'user', ...)
5107
 * @param   Object      $object         Object for keys on object
5108
 * @return	array						Array of substitutions
5109
 */
5110
function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
5111
{
5112
    global $conf, $mysoc, $user;
5113
5114
    $substitutionarray=array();
5115
5116
    if (empty($exclude) || ! in_array('mycompany', $exclude))
5117
    {
5118
        $substitutionarray=array_merge($substitutionarray, array(
5119
            '__MYCOMPANY_NAME__' => $mysoc->name,
5120
            '__MYCOMPANY_EMAIL__' => $mysoc->email,
5121
            '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
5122
            '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
5123
            '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
5124
            '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
5125
            '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
5126
            '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
5127
            '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
5128
            '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id
5129
        ));
5130
    }
5131
    if (empty($exclude) || ! in_array('objectamount', $exclude))
5132
    {
5133
        if (is_object($object))       // For backward compatibility
5134
        {
5135
            $substitutionarray['__TOTAL_TTC__']    =is_object($object)?$object->total_ttc:'';
5136
            $substitutionarray['__TOTAL_HT__']     =is_object($object)?$object->total_ht:'';
5137
            $substitutionarray['__TOTAL_VAT__']    =is_object($object)?($object->total_vat?$object->total_vat:$object->total_tva):'';
5138
        }
5139
        $substitutionarray['__AMOUNT__']       =is_object($object)?$object->total_ttc:'';
5140
        $substitutionarray['__AMOUNT_WO_TAX__']=is_object($object)?$object->total_ht:'';
5141
        $substitutionarray['__AMOUNT_VAT__']   =is_object($object)?($object->total_vat?$object->total_vat:$object->total_tva):'';
5142
    }
5143
5144
    if (empty($exclude) || ! in_array('date', $exclude))
5145
    {
5146
        include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
5147
5148
        if (! empty($onlykey))
5149
        {
5150
            $tmp=$tmp2=$tmp3=$tmp4=$tmp5=array();
5151
        }
5152
        else
5153
        {
5154
            $tmp=dol_getdate(dol_now(), true);
5155
            $tmp2=dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
5156
            $tmp3=dol_get_prev_month($tmp['mday'], $tmp['mon'], $tmp['year']);
5157
            $tmp4=dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
5158
            $tmp5=dol_get_next_month($tmp['mday'], $tmp['mon'], $tmp['year']);
5159
        }
5160
        $substitutionarray=array_merge($substitutionarray, array(
5161
            '__DAY__' => $tmp['mday'],
5162
            '__MONTH__' => $tmp['mon'],
5163
            '__YEAR__' => $tmp['year'],
5164
            '__PREVIOUS_DAY__' => $tmp2['day'],
5165
            '__PREVIOUS_MONTH__' => $tmp3['month'],
5166
            '__PREVIOUS_YEAR__' => ($tmp['year'] - 1),
5167
            '__NEXT_DAY__' => $tmp4['day'],
5168
            '__NEXT_MONTH__' => $tmp5['month'],
5169
            '__NEXT_YEAR__' => ($tmp['year'] + 1),
5170
        ));
5171
    }
5172
5173
    if (empty($exclude) || ! in_array('user', $exclude))
5174
    {
5175
        $substitutionarray=array_merge($substitutionarray, array(
5176
            '__USER_ID__' => $user->id,
5177
            '__USER_LOGIN__' => $user->login,
5178
            '__USER_LASTNAME__' => $user->lastname,
5179
            '__USER_FIRSTNAME__' => $user->firstname,
5180
            '__USER_FULLNAME__' => $user->getFullName($outputlangs),
5181
            '__USER_SUPERVISOR_ID__' => $user->fk_user
5182
        ));
5183
    }
5184
    if (! empty($conf->multicompany->enabled))
5185
    {
5186
        $substitutionarray=array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
5187
    }
5188
5189
    return $substitutionarray;
5190
}
5191
5192
/**
5193
 *  Make substition into a text string, replacing keys with vals from $substitutionarray (oldval=>newval).
5194
 *
5195
 *  @param	string		$text	      			Source string in which we must do substitution
5196
 *  @param  array		$substitutionarray		Array with key->val to substitute
5197
 *  @param	Translate	$outputlangs			Output language
5198
 * 	@return string  		    				Output string after substitutions
5199
 *  @see	complete_substitutions_array
5200
 */
5201
function make_substitutions($text, $substitutionarray, $outputlangs=null)
5202
{
5203
	global $conf, $langs;
5204
5205
	if (! is_array($substitutionarray)) return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
5206
5207
	if (empty($outputlangs)) $outputlangs=$langs;
5208
5209
	// Make substitution for language keys
5210
	if (is_object($outputlangs))
5211
	{
5212
		while (preg_match('/__\(([^\)]*)\)__/', $text, $reg))
5213
		{
5214
		    // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
5215
			$tmp=explode('|',$reg[1]);
5216
			if (! empty($tmp[1])) $outputlangs->load($tmp[1]);
5217
5218
			$msgishtml = 0;
5219
			if (dol_textishtml($text,1)) $msgishtml = 1;
5220
5221
			$text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $msgishtml?dol_htmlentitiesbr($outputlangs->transnoentitiesnoconv($reg[1])):$outputlangs->transnoentitiesnoconv($reg[1]), $text);
5222
		}
5223
	}
5224
5225
	// Make substitition for array $substitutionarray
5226
	foreach ($substitutionarray as $key => $value)
5227
	{
5228
		if ($key == '__SIGNATURE__' && (! empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) $value='';
5229
		$text=str_replace("$key","$value",$text);	// We must keep the " to work when value is 123.5 for example
5230
	}
5231
5232
	return $text;
5233
}
5234
5235
/**
5236
 *  Complete the $substitutionarray with more entries.
5237
 *  Can also add substitution keys coming from external module that had set the "substitutions=1" into module_part array. In this case, method completesubstitutionarray provided by module is called.
5238
 *
5239
 *  @param  array		$substitutionarray		Array substitution old value => new value value
5240
 *  @param  Translate	$outputlangs            Output language
5241
 *  @param  Object		$object                 Source object
5242
 *  @param  mixed		$parameters       		Add more parameters (useful to pass product lines)
5243
 *  @param  string      $callfunc               What is the name of the custom function that will be called? (default: completesubstitutionarray)
5244
 *  @return	void
5245
 *  @see 	make_substitutions
5246
 */
5247
function complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
5248
{
5249
	global $conf,$user;
5250
5251
	require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5252
5253
	// Add a substitution key for each object property
5254
	if (is_object($object))
5255
	{
5256
		// TODO
5257
	}
5258
5259
	// Add a substitution key for each extrafields, using key __EXTRA_XXX__
5260
	if (is_object($object) && is_array($object->array_options))
5261
	{
5262
		foreach($object->array_options as $key => $val)
5263
		{
5264
			$keyshort=preg_replace('/^(options|extra)_/','',$key);
5265
			$substitutionarray['__EXTRA_'.$keyshort.'__']=$val;
5266
			// For backward compatibiliy
5267
			$substitutionarray['%EXTRA_'.$keyshort.'%']=$val;
5268
		}
5269
	}
5270
5271
	// Check if there is external substitution to do, requested by plugins
5272
	$dirsubstitutions=array_merge(array(),(array) $conf->modules_parts['substitutions']);
5273
5274
	foreach($dirsubstitutions as $reldir)
5275
	{
5276
		$dir=dol_buildpath($reldir,0);
5277
5278
		// Check if directory exists
5279
		if (! dol_is_dir($dir)) continue;
5280
5281
		$substitfiles=dol_dir_list($dir,'files',0,'functions_');
5282
		foreach($substitfiles as $substitfile)
5283
		{
5284
			if (preg_match('/functions_(.*)\.lib\.php/i',$substitfile['name'],$reg))
5285
			{
5286
				$module=$reg[1];
5287
5288
				dol_syslog("Library functions_".$substitfile['name']." found into ".$dir);
5289
				// Include the user's functions file
5290
				require_once $dir.$substitfile['name'];
5291
				// Call the user's function, and only if it is defined
5292
				$function_name=$module."_".$callfunc;
5293
				if (function_exists($function_name)) $function_name($substitutionarray,$outputlangs,$object,$parameters);
5294
			}
5295
		}
5296
	}
5297
}
5298
5299
/**
5300
 *    Format output for start and end date
5301
 *
5302
 *    @param	int	$date_start    Start date
5303
 *    @param    int	$date_end      End date
5304
 *    @param    string		$format        Output format
5305
 *    @param	Translate	$outputlangs   Output language
5306
 *    @return	void
5307
 */
5308
function print_date_range($date_start,$date_end,$format = '',$outputlangs='')
5309
{
5310
	print get_date_range($date_start,$date_end,$format,$outputlangs);
5311
}
5312
5313
/**
5314
 *    Format output for start and end date
5315
 *
5316
 *    @param	int			$date_start    		Start date
5317
 *    @param    int			$date_end      		End date
5318
 *    @param    string		$format        		Output format
5319
 *    @param	Translate	$outputlangs   		Output language
5320
 *    @param	integer		$withparenthesis	1=Add parenthesis, 0=non parenthesis
5321
 *    @return	string							String
5322
 */
5323
function get_date_range($date_start,$date_end,$format = '',$outputlangs='', $withparenthesis=1)
5324
{
5325
	global $langs;
5326
5327
	$out='';
5328
5329
	if (! is_object($outputlangs)) $outputlangs=$langs;
5330
5331
	if ($date_start && $date_end)
5332
	{
5333
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFromTo',dol_print_date($date_start, $format, false, $outputlangs),dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
5334
	}
5335
	if ($date_start && ! $date_end)
5336
	{
5337
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFrom',dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis?')':'');
5338
	}
5339
	if (! $date_start && $date_end)
5340
	{
5341
		$out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateUntil',dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
5342
	}
5343
5344
	return $out;
5345
}
5346
5347
/**
5348
 * Return firstname and lastname in correct order
5349
 *
5350
 * @param	string	$firstname		Firstname
5351
 * @param	string	$lastname		Lastname
5352
 * @param	int		$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname
5353
 * @return	string					Firstname + lastname or Lastname + firstname
5354
 */
5355
function dolGetFirstLastname($firstname,$lastname,$nameorder=-1)
5356
{
5357
	global $conf;
5358
5359
	$ret='';
5360
	// If order not defined, we use the setup
5361
	if ($nameorder < 0) $nameorder=(empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION));
5362
	if ($nameorder && ((string) $nameorder != '2'))
5363
	{
5364
        $ret.=$firstname;
5365
		if ($firstname && $lastname) $ret.=' ';
5366
		$ret.=$lastname;
5367
	}
5368
	else if ($nameorder == 2)
5369
	{
5370
	   $ret.=$firstname;
5371
	}
5372
	else
5373
	{
5374
		$ret.=$lastname;
5375
		if ($firstname && $lastname) $ret.=' ';
5376
		$ret.=$firstname;
5377
	}
5378
	return $ret;
5379
}
5380
5381
5382
/**
5383
 *	Set event message in dol_events session object. Will be output by calling dol_htmloutput_events.
5384
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
5385
 *  Note: Prefer to use setEventMessages instead.
5386
 *
5387
 *	@param	mixed	$mesgs			Message string or array
5388
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
5389
 *  @return	void
5390
 *  @see	dol_htmloutput_events
5391
 */
5392
function setEventMessage($mesgs, $style='mesgs')
5393
{
5394
	//dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);		This is not deprecated, it is used by setEventMessages function
5395
	if (! is_array($mesgs))		// If mesgs is a string
5396
	{
5397
		if ($mesgs) $_SESSION['dol_events'][$style][] = $mesgs;
5398
	}
5399
	else						// If mesgs is an array
5400
	{
5401
		foreach($mesgs as $mesg)
5402
		{
5403
			if ($mesg) $_SESSION['dol_events'][$style][] = $mesg;
5404
		}
5405
	}
5406
}
5407
5408
/**
5409
 *	Set event messages in dol_events session object. Will be output by calling dol_htmloutput_events.
5410
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
5411
 *
5412
 *	@param	string	$mesg			Message string
5413
 *	@param	array	$mesgs			Message array
5414
 *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
5415
 *  @return	void
5416
 *  @see	dol_htmloutput_events
5417
 */
5418
function setEventMessages($mesg, $mesgs, $style='mesgs')
5419
{
5420
	if (! in_array((string) $style, array('mesgs','warnings','errors'))) dol_print_error('','Bad parameter style='.$style.' for setEventMessages');
5421
	if (empty($mesgs)) setEventMessage($mesg, $style);
5422
	else
5423
	{
5424
		if (! empty($mesg) && ! in_array($mesg, $mesgs)) setEventMessage($mesg, $style);	// Add message string if not already into array
5425
		setEventMessage($mesgs, $style);
5426
	}
5427
}
5428
5429
/**
5430
 *	Print formated messages to output (Used to show messages on html output).
5431
 *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function, so there is
5432
 *  no need to call it explicitely.
5433
 *
5434
 *  @return	void
5435
 *  @see    dol_htmloutput_mesg
5436
 */
5437
function dol_htmloutput_events()
5438
{
5439
	// Show mesgs
5440
	if (isset($_SESSION['dol_events']['mesgs'])) {
5441
		dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
5442
		unset($_SESSION['dol_events']['mesgs']);
5443
	}
5444
5445
	// Show errors
5446
	if (isset($_SESSION['dol_events']['errors'])) {
5447
		dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
5448
		unset($_SESSION['dol_events']['errors']);
5449
	}
5450
5451
	// Show warnings
5452
	if (isset($_SESSION['dol_events']['warnings'])) {
5453
		dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
5454
		unset($_SESSION['dol_events']['warnings']);
5455
	}
5456
}
5457
5458
/**
5459
 *	Get formated messages to output (Used to show messages on html output).
5460
 *  This include also the translation of the message key.
5461
 *
5462
 *	@param	string		$mesgstring		Message string or message key
5463
 *	@param	string[]	$mesgarray      Array of message strings or message keys
5464
 *  @param  string		$style          Style of message output ('ok' or 'error')
5465
 *  @param  int			$keepembedded   Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
5466
 *	@return	string						Return html output
5467
 *
5468
 *  @see    dol_print_error
5469
 *  @see    dol_htmloutput_errors
5470
 *  @see    setEventMessages
5471
 */
5472
function get_htmloutput_mesg($mesgstring='',$mesgarray='', $style='ok', $keepembedded=0)
5473
{
5474
	global $conf, $langs;
5475
5476
	$ret=0; $return='';
5477
	$out='';
5478
	$divstart=$divend='';
5479
5480
	// If inline message with no format, we add it.
5481
	if ((empty($conf->use_javascript_ajax) || ! empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && ! preg_match('/<div class=".*">/i',$out))
5482
	{
5483
		$divstart='<div class="'.$style.'">';
5484
		$divend='</div>';
5485
	}
5486
5487
	if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring)
5488
	{
5489
		$langs->load("errors");
5490
		$out.=$divstart;
5491
		if (is_array($mesgarray) && count($mesgarray))
5492
		{
5493
			foreach($mesgarray as $message)
5494
			{
5495
				$ret++;
5496
				$out.= $langs->trans($message);
5497
				if ($ret < count($mesgarray)) $out.= "<br>\n";
5498
			}
5499
		}
5500
		if ($mesgstring)
5501
		{
5502
			$langs->load("errors");
5503
			$ret++;
5504
			$out.= $langs->trans($mesgstring);
5505
		}
5506
		$out.=$divend;
5507
	}
5508
5509
	if ($out)
5510
	{
5511
		if (! empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded))
5512
		{
5513
			$return = '<script type="text/javascript">
5514
					$(document).ready(function() {
5515
						var block = '.(! empty($conf->global->MAIN_USE_JQUERY_BLOCKUI)?"true":"false").'
5516
						if (block) {
5517
							$.dolEventValid("","'.dol_escape_js($out).'");
5518
						} else {
5519
							/* jnotify(message, preset of message type, keepmessage) */
5520
							$.jnotify("'.dol_escape_js($out).'",
5521
							"'.($style=="ok" ? 3000 : $style).'",
5522
							'.($style=="ok" ? "false" : "true").',
5523
							{ remove: function (){} } );
5524
						}
5525
					});
5526
				</script>';
5527
		}
5528
		else
5529
		{
5530
			$return = $out;
5531
		}
5532
	}
5533
5534
	return $return;
5535
}
5536
5537
/**
5538
 *  Get formated error messages to output (Used to show messages on html output).
5539
 *
5540
 *  @param	string	$mesgstring         Error message
5541
 *  @param  array	$mesgarray          Error messages array
5542
 *  @param  int		$keepembedded       Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
5543
 *  @return string                		Return html output
5544
 *
5545
 *  @see    dol_print_error
5546
 *  @see    dol_htmloutput_mesg
5547
 */
5548
function get_htmloutput_errors($mesgstring='', $mesgarray='', $keepembedded=0)
5549
{
5550
	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 5548 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...
5551
}
5552
5553
/**
5554
 *	Print formated messages to output (Used to show messages on html output).
5555
 *
5556
 *	@param	string		$mesgstring		Message string or message key
5557
 *	@param	string[]	$mesgarray      Array of message strings or message keys
5558
 *  @param  string      $style          Which style to use ('ok', 'warning', 'error')
5559
 *  @param  int         $keepembedded   Set to 1 if message must be kept embedded into its html place (this disable jnotify)
5560
 *  @return	void
5561
 *
5562
 *  @see    dol_print_error
5563
 *  @see    dol_htmloutput_errors
5564
 *  @see    setEventMessages
5565
 */
5566
function dol_htmloutput_mesg($mesgstring='',$mesgarray='', $style='ok', $keepembedded=0)
5567
{
5568
	if (empty($mesgstring) && (! is_array($mesgarray) || count($mesgarray) == 0)) return;
5569
5570
	$iserror=0;
5571
	$iswarning=0;
5572
	if (is_array($mesgarray))
5573
	{
5574
		foreach($mesgarray as $val)
5575
		{
5576
			if ($val && preg_match('/class="error"/i',$val)) { $iserror++; break; }
5577
			if ($val && preg_match('/class="warning"/i',$val)) { $iswarning++; break; }
5578
		}
5579
	}
5580
	else if ($mesgstring && preg_match('/class="error"/i',$mesgstring)) $iserror++;
5581
	else if ($mesgstring && preg_match('/class="warning"/i',$mesgstring)) $iswarning++;
5582
	if ($style=='error') $iserror++;
5583
	if ($style=='warning') $iswarning++;
5584
5585
	if ($iserror || $iswarning)
5586
	{
5587
		// Remove div from texts
5588
		$mesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$mesgstring);
5589
		$mesgstring=preg_replace('/<div class="(error|warning)">/','',$mesgstring);
5590
		$mesgstring=preg_replace('/<\/div>/','',$mesgstring);
5591
		// Remove div from texts array
5592
		if (is_array($mesgarray))
5593
		{
5594
			$newmesgarray=array();
5595
			foreach($mesgarray as $val)
5596
			{
5597
				$tmpmesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$val);
5598
				$tmpmesgstring=preg_replace('/<div class="(error|warning)">/','',$tmpmesgstring);
5599
				$tmpmesgstring=preg_replace('/<\/div>/','',$tmpmesgstring);
5600
				$newmesgarray[]=$tmpmesgstring;
5601
			}
5602
			$mesgarray=$newmesgarray;
5603
		}
5604
		print get_htmloutput_mesg($mesgstring,$mesgarray,($iserror?'error':'warning'),$keepembedded);
5605
	}
5606
	else print get_htmloutput_mesg($mesgstring,$mesgarray,'ok',$keepembedded);
5607
}
5608
5609
/**
5610
 *  Print formated error messages to output (Used to show messages on html output).
5611
 *
5612
 *  @param	string	$mesgstring          Error message
5613
 *  @param  array	$mesgarray           Error messages array
5614
 *  @param  int		$keepembedded        Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
5615
 *  @return	void
5616
 *
5617
 *  @see    dol_print_error
5618
 *  @see    dol_htmloutput_mesg
5619
 */
5620
function dol_htmloutput_errors($mesgstring='', $mesgarray='', $keepembedded=0)
5621
{
5622
	dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
1 ignored issue
show
Bug introduced by
It seems like $mesgarray defined by parameter $mesgarray on line 5620 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...
5623
}
5624
5625
/**
5626
 * 	Advanced sort array by second index function, which produces ascending (default)
5627
 *  or descending output and uses optionally natural case insensitive sorting (which
5628
 *  can be optionally case sensitive as well).
5629
 *
5630
 *  @param      array		$array      		Array to sort (array of array('key','otherkey1','otherkey2'...))
5631
 *  @param      string		$index				Key in array to use for sorting criteria
5632
 *  @param      int			$order				Sort order ('asc' or 'desc')
5633
 *  @param      int			$natsort			1=use "natural" sort (natsort), 0=use "standard" sort (asort)
5634
 *  @param      int			$case_sensitive		1=sort is case sensitive, 0=not case sensitive
5635
 *  @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.
5636
 *  @return     array							Sorted array
5637
 */
5638
function dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
5639
{
5640
	// Clean parameters
5641
	$order=strtolower($order);
5642
5643
	$sizearray=count($array);
5644
	if (is_array($array) && $sizearray>0)
5645
	{
5646
        $temp = array();
5647
        foreach(array_keys($array) as $key) $temp[$key]=$array[$key][$index];
5648
5649
		if (!$natsort) ($order=='asc') ? asort($temp) : arsort($temp);
5650
		else
5651
		{
5652
			($case_sensitive) ? natsort($temp) : natcasesort($temp);
5653
			if($order!='asc') $temp=array_reverse($temp,TRUE);
5654
		}
5655
5656
		$sorted = array();
5657
5658
		foreach(array_keys($temp) as $key)
5659
		{
5660
			(is_numeric($key) && empty($keepindex)) ? $sorted[]=$array[$key] : $sorted[$key]=$array[$key];
5661
		}
5662
5663
		return $sorted;
5664
	}
5665
	return $array;
5666
}
5667
5668
5669
/**
5670
 *      Check if a string is in UTF8
5671
 *
5672
 *      @param	string	$str        String to check
5673
 * 		@return	boolean				True if string is UTF8 or ISO compatible with UTF8, False if not (ISO with special char or Binary)
5674
 */
5675
function utf8_check($str)
5676
{
5677
	// We must use here a binary strlen function (so not dol_strlen)
5678
	$strLength = dol_strlen($str);
5679
	for ($i=0; $i<$strLength; $i++)
5680
	{
5681
		if (ord($str[$i]) < 0x80) continue; // 0bbbbbbb
5682
		elseif ((ord($str[$i]) & 0xE0) == 0xC0) $n=1; // 110bbbbb
5683
		elseif ((ord($str[$i]) & 0xF0) == 0xE0) $n=2; // 1110bbbb
5684
		elseif ((ord($str[$i]) & 0xF8) == 0xF0) $n=3; // 11110bbb
5685
		elseif ((ord($str[$i]) & 0xFC) == 0xF8) $n=4; // 111110bb
5686
		elseif ((ord($str[$i]) & 0xFE) == 0xFC) $n=5; // 1111110b
5687
		else return false; // Does not match any model
5688
		for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ?
5689
			if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
5690
			return false;
5691
		}
5692
	}
5693
	return true;
5694
}
5695
5696
5697
/**
5698
 *      Return a string encoded into OS filesystem encoding. This function is used to define
5699
 * 	    value to pass to filesystem PHP functions.
5700
 *
5701
 *      @param	string	$str        String to encode (UTF-8)
5702
 * 		@return	string				Encoded string (UTF-8, ISO-8859-1)
5703
 */
5704
function dol_osencode($str)
5705
{
5706
	global $conf;
5707
5708
	$tmp=ini_get("unicode.filesystem_encoding");						// Disponible avec PHP 6.0
5709
	if (empty($tmp) && ! empty($_SERVER["WINDIR"])) $tmp='iso-8859-1';	// By default for windows
5710
	if (empty($tmp)) $tmp='utf-8';										// By default for other
5711
	if (! empty($conf->global->MAIN_FILESYSTEM_ENCODING)) $tmp=$conf->global->MAIN_FILESYSTEM_ENCODING;
5712
5713
	if ($tmp == 'iso-8859-1') return utf8_decode($str);
5714
	return $str;
5715
}
5716
5717
5718
/**
5719
 *      Return an id or code from a code or id.
5720
 *      Store also Code-Id into a cache to speed up next request on same key.
5721
 *
5722
 * 		@param	DoliDB	$db			Database handler
5723
 * 		@param	string	$key		Code or Id to get Id or Code
5724
 * 		@param	string	$tablename	Table name without prefix
5725
 * 		@param	string	$fieldkey	Field for code
5726
 * 		@param	string	$fieldid	Field for id
5727
 *      @return int					<0 if KO, Id of code if OK
5728
 *      @see $langs->getLabelFromKey
5729
 */
5730
function dol_getIdFromCode($db,$key,$tablename,$fieldkey='code',$fieldid='id')
5731
{
5732
	global $cache_codes;
5733
5734
	// If key empty
5735
	if ($key == '') return '';
5736
5737
	// Check in cache
5738
	if (isset($cache_codes[$tablename][$key]))	// Can be defined to 0 or ''
5739
	{
5740
		return $cache_codes[$tablename][$key];   // Found in cache
5741
	}
5742
5743
	$sql = "SELECT ".$fieldid." as valuetoget";
5744
	$sql.= " FROM ".MAIN_DB_PREFIX.$tablename;
5745
	$sql.= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
5746
	dol_syslog('dol_getIdFromCode', LOG_DEBUG);
5747
	$resql = $db->query($sql);
5748
	if ($resql)
5749
	{
5750
		$obj = $db->fetch_object($resql);
5751
		if ($obj) $cache_codes[$tablename][$key]=$obj->valuetoget;
5752
		else $cache_codes[$tablename][$key]='';
5753
		$db->free($resql);
5754
		return $cache_codes[$tablename][$key];
5755
	}
5756
	else
5757
	{
5758
		return -1;
5759
	}
5760
}
5761
5762
/**
5763
 * Verify if condition in string is ok or not
5764
 *
5765
 * @param 	string		$strRights		String with condition to check
5766
 * @return 	boolean						True or False. Return true if strRights is ''
5767
 */
5768
function verifCond($strRights)
5769
{
5770
	global $user,$conf,$langs;
5771
	global $leftmenu;
5772
	global $rights;    // To export to dol_eval function
5773
5774
	//print $strRights."<br>\n";
5775
	$rights = true;
5776
	if ($strRights != '')
5777
	{
5778
		//$tab_rights = explode('&&', $strRights);
5779
		//$i = 0;
5780
		//while (($i < count($tab_rights)) && ($rights == true)) {
5781
		$str = 'if(!(' . $strRights . ')) { $rights = false; }';
5782
		dol_eval($str);
5783
		//	$i++;
5784
		//}
5785
	}
5786
	return $rights;
5787
}
5788
5789
/**
5790
 * Replace eval function to add more security.
5791
 * This function is called by verifCond() or trans() and transnoentitiesnoconv().
5792
 *
5793
 * @param 	string	$s				String to evaluate
5794
 * @param	int		$returnvalue	0=No return (used to execute eval($a=something)). 1=Value of eval is returned (used to eval($something)).
5795
 * @param   int     $hideerrors     1=Hide errors
5796
 * @return	mixed					Nothing or return of eval
5797
 */
5798
function dol_eval($s, $returnvalue=0, $hideerrors=1)
5799
{
5800
	// Only global variables can be changed by eval function and returned to caller
5801
	global $db, $langs, $user, $conf;
5802
	global $mainmenu, $leftmenu;
5803
	global $rights;
5804
	global $object;
5805
	global $mysoc;
5806
5807
	global $obj;       // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
5808
	global $soc;       // For backward compatibility
5809
5810
	//print $s."<br>\n";
5811
	if ($returnvalue)
5812
	{
5813
	    if ($hideerrors) return @eval('return '.$s.';');
0 ignored issues
show
Coding Style introduced by
The function dol_eval() contains an eval expression.

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

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

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

Loading history...
5815
	}
5816
	else
5817
	{
5818
	    if ($hideerrors) @eval($s);
0 ignored issues
show
Coding Style introduced by
The function dol_eval() contains an eval expression.

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

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

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

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