Passed
Push — master ( 3cffbe...0f9140 )
by Alxarafe
23:50
created

Helpers/DolUtils.php (1 issue)

1
<?php
2
/* Copyright (C) 2000-2007	Rodolphe Quiedeville			<[email protected]>
3
 * Copyright (C) 2003		Jean-Louis Bergamo			<[email protected]>
4
 * Copyright (C) 2004-2018	Laurent Destailleur			<[email protected]>
5
 * Copyright (C) 2004		Sebastien Di Cintio			<[email protected]>
6
 * Copyright (C) 2004		Benoit Mortier				<[email protected]>
7
 * Copyright (C) 2004		Christophe Combelles			<[email protected]>
8
 * Copyright (C) 2005-2017	Regis Houssin				<[email protected]>
9
 * Copyright (C) 2008		Raphael Bertrand (Resultic)	<[email protected]>
10
 * Copyright (C) 2010-2018	Juanjo Menent				<[email protected]>
11
 * Copyright (C) 2013		Cédric Salvador				<[email protected]>
12
 * Copyright (C) 2013-2017	Alexandre Spangaro			<[email protected]>
13
 * Copyright (C) 2014		Cédric GROSS					<[email protected]>
14
 * Copyright (C) 2014-2015	Marcos García				<[email protected]>
15
 * Copyright (C) 2015		Jean-François Ferry			<[email protected]>
16
 * Copyright (C) 2018       Frédéric France             <[email protected]>
17
 * Copyright (C) 2018-2019  Alxarafe                    <[email protected]>
18
 *
19
 * This program is free software; you can redistribute it and/or modify
20
 * it under the terms of the GNU General Public License as published by
21
 * the Free Software Foundation; either version 3 of the License, or
22
 * (at your option) any later version.
23
 *
24
 * This program is distributed in the hope that it will be useful,
25
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27
 * GNU General Public License for more details.
28
 *
29
 * You should have received a copy of the GNU General Public License
30
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31
 * or see http://www.gnu.org/
32
 */
33
namespace Alixar\Helpers;
34
35
// include_once DOL_BASE_PATH . '/core/lib/json.lib.php';
36
use Alixar\Helpers\Security;
37
use Mobile_Detect;
38
39
class DolUtils
40
{
41
42
    /**
43
     * Function to return value of a static property when class
44
     * name is dynamically defined (not hard coded).
45
     * This is because $myclass::$myvar works from PHP 5.3.0+ only
46
     *
47
     * @param	string 	$class		Class name
48
     * @param 	string 	$member		Name of property
49
     * @return 	mixed				Return value of static property
50
     * @deprecated Dolibarr now requires 5.3.0+, use $class::$property syntax
51
     * @see https://php.net/manual/language.oop5.static.php
52
     */
53
    static function getStaticMember($class, $member)
54
    {
55
        DolUtils::dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
56
57
        // This part is deprecated. Uncomment if for php 5.2.*, and comment next isset class::member
58
        /* if (version_compare(phpversion(), '5.3.0', '<'))
59
          {
60
          if (is_object($class)) $class = get_class($class);
61
          $classObj = new ReflectionClass($class);
62
          $result = null;
63
64
          $found=0;
65
          foreach($classObj->getStaticProperties() as $prop => $value)
66
          {
67
          if ($prop == $member)
68
          {
69
          $result = $value;
70
          $found++;
71
          break;
72
          }
73
          }
74
75
          if ($found) return $result;
76
          } */
77
78
        if (isset($class::$member))
79
            return $class::$member;
80
        dol_print_error('', 'Try to get a static member "' . $member . '" in class "' . $class . '" that does not exists or is not static.');
81
        return null;
82
    }
83
84
    /**
85
     * Return a DoliDB instance (database handler).
86
     *
87
     * @param   string	$type		Type of database (mysql, pgsql...)
88
     * @param	string	$host		Address of database server
89
     * @param	string	$user		Nom de l'utilisateur autorise
90
     * @param	string	$pass		Mot de passe
91
     * @param	string	$name		Nom de la database
92
     * @param	int		$port		Port of database server
93
     * @return	DoliDB				A DoliDB instance
94
     */
95
    static function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
96
    {
97
        require_once DOL_BASE_PATH . "/core/db/" . $type . '.class.php';
98
99
        $class = 'DoliDB' . ucfirst($type);
100
        $dolidb = new $class($type, $host, $user, $pass, $name, $port);
101
        return $dolidb;
102
    }
103
104
    /**
105
     * 	Get list of entity id to use.
106
     *
107
     * 	@param	string	$element		Current element
108
     * 									'societe', 'socpeople', 'actioncomm', 'agenda', 'resource',
109
     * 									'product', 'productprice', 'stock',
110
     * 									'propal', 'supplier_proposal', 'invoice', 'facture_fourn', 'payment_various',
111
     * 									'categorie', 'bank_account', 'bank_account', 'adherent', 'user',
112
     * 									'commande', 'commande_fournisseur', 'expedition', 'intervention', 'survey',
113
     * 									'contract', 'tax', 'expensereport', 'holiday', 'multicurrency', 'project',
114
     * 									'email_template', 'event', 'donation'
115
     * 									'c_paiement', 'c_payment_term', ...
116
     * 	@param	int		$shared			0=Return id of current entity only,
117
     * 									1=Return id of current entity + shared entities (default)
118
     *  @param	object	$currentobject	Current object if needed
119
     * 	@return	mixed				Entity id(s) to use
120
     */
121
    static function getEntity($element, $shared = 1, $currentobject = null)
122
    {
123
        //// global Globals::$conf, $mc;
124
125
        if (is_object(Globals::$mc)) {
126
            return Globals::$mc->getEntity($element, $shared, $currentobject);
127
        } else {
128
            $out = '';
129
            $addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values');
130
            if (in_array($element, $addzero))
131
                $out .= '0,';
132
            $out .= Globals::$conf->entity;
133
            return $out;
134
        }
135
    }
136
137
    /**
138
     * Return information about user browser
139
     *
140
     * Returns array with the following format:
141
     * array(
142
     *  'browsername' => Browser name (firefox|chrome|iceweasel|epiphany|safari|opera|ie|unknown)
143
     *  'browserversion' => Browser version. Empty if unknown
144
     *  'browseros' => Set with mobile OS (android|blackberry|ios|palm|symbian|webos|maemo|windows|unknown)
145
     *  'layout' => (tablet|phone|classic)
146
     *  'phone' => empty if not mobile, (android|blackberry|ios|palm|unknown) if mobile
147
     *  'tablet' => true/false
148
     * )
149
     *
150
     * @param string $user_agent Content of $_SERVER["HTTP_USER_AGENT"] variable
151
     * @return	array Check function documentation
152
     */
153
    static function getBrowserInfo($user_agent)
154
    {
155
        //include_once BASE_PATH . '/vendor/mobiledetect/mobiledetectlib/Mobile_Detect.php';
156
157
        $name = 'unknown';
158
        $version = '';
159
        $os = 'unknown';
160
        $phone = '';
161
162
        $detectmobile = new Mobile_Detect(null, $user_agent);
163
        $tablet = $detectmobile->isTablet();
164
165
        if ($detectmobile->isMobile()) {
166
167
            $phone = 'unknown';
168
169
            // If phone/smartphone, we set phone os name.
170
            if ($detectmobile->is('AndroidOS')) {
171
                $os = $phone = 'android';
172
            } elseif ($detectmobile->is('BlackBerryOS')) {
173
                $os = $phone = 'blackberry';
174
            } elseif ($detectmobile->is('iOS')) {
175
                $os = 'ios';
176
                $phone = 'iphone';
177
            } elseif ($detectmobile->is('PalmOS')) {
178
                $os = $phone = 'palm';
179
            } elseif ($detectmobile->is('SymbianOS')) {
180
                $os = 'symbian';
181
            } elseif ($detectmobile->is('webOS')) {
182
                $os = 'webos';
183
            } elseif ($detectmobile->is('MaemoOS')) {
184
                $os = 'maemo';
185
            } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
186
                $os = 'windows';
187
            }
188
        }
189
190
        // OS
191
        if (preg_match('/linux/i', $user_agent)) {
192
            $os = 'linux';
193
        } elseif (preg_match('/macintosh/i', $user_agent)) {
194
            $os = 'macintosh';
195
        } elseif (preg_match('/windows/i', $user_agent)) {
196
            $os = 'windows';
197
        }
198
199
        // Name
200
        if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
201
            $name = 'firefox';
202
            $version = $reg[2];
203
        } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
204
            $name = 'edge';
205
            $version = $reg[2];
206
        } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
207
            $name = 'chrome';
208
            $version = $reg[2];
209
        }    // we can have 'chrome (Mozilla...) chrome x.y' in one string
210
        elseif (preg_match('/chrome/i', $user_agent, $reg)) {
211
            $name = 'chrome';
212
        } elseif (preg_match('/iceweasel/i', $user_agent)) {
213
            $name = 'iceweasel';
214
        } elseif (preg_match('/epiphany/i', $user_agent)) {
215
            $name = 'epiphany';
216
        } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
217
            $name = 'safari';
218
            $version = $reg[2];
219
        } // Safari is often present in string for mobile but its not.
220
        elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
221
            $name = 'opera';
222
            $version = $reg[2];
223
        } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
224
            $name = 'ie';
225
            $version = end($reg);
226
        }    // MS products at end
227
        elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
228
            $name = 'ie';
229
            $version = end($reg);
230
        }    // MS products at end
231
        elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
232
            $name = 'lynxlinks';
233
            $version = $reg[4];
234
        }
235
236
        if ($tablet) {
237
            $layout = 'tablet';
238
        } elseif ($phone) {
239
            $layout = 'phone';
240
        } else {
241
            $layout = 'classic';
242
        }
243
244
        return array(
245
            'browsername' => $name,
246
            'browserversion' => $version,
247
            'browseros' => $os,
248
            'layout' => $layout,
249
            'phone' => $phone,
250
            'tablet' => $tablet
251
        );
252
    }
253
254
    /**
255
     *  Function called at end of web php process
256
     *
257
     *  @return	void
258
     */
259
    static function dol_shutdown()
260
    {
261
        // global Globals::$conf, $user, Globals::$langs, $db;
262
        $disconnectdone = false;
263
        $depth = 0;
264
        if (is_object($db) && !empty($db->connected)) {
265
            $depth = $db->transaction_opened;
266
            $disconnectdone = $db->close();
267
        }
268
        DolUtils::dol_syslog("--- End access to " . $_SERVER["PHP_SELF"] . (($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was ' . $depth . ')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
269
    }
270
271
    /**
272
     * Return true if we are in a context of submitting a parameter
273
     *
274
     * @param 	string	$paramname		Name or parameter to test
275
     * @return 	boolean					True if we have just submit a POST or GET request with the parameter provided (even if param is empty)
276
     */
277
    static function GETPOSTISSET($paramname)
278
    {
279
        return (isset($_POST[$paramname]) || isset($_GET[$paramname]));
280
    }
281
282
    /**
283
     *  Return value of a param into GET or POST supervariable.
284
     *  Use the property $user->default_values[path]['creatform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder']
285
     *  Note: The property $user->default_values is loaded by main.php when loading the user.
286
     *
287
     *  @param  string  $paramname   Name of parameter to found
288
     *  @param  string  $check	     Type of check
289
     *                               ''=no check (deprecated)
290
     *                               'none'=no check (only for param that should have very rich content)
291
     *                               'int'=check it's numeric (integer or float)
292
     *                               'intcomma'=check it's integer+comma ('1,2,3,4...')
293
     *                               'alpha'=check it's text and sign
294
     *                               'aZ'=check it's a-z only
295
     *                               'aZ09'=check it's simple alpha string (recommended for keys)
296
     *                               'array'=check it's array
297
     *                               'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string)
298
     *                               'nohtml', 'alphanohtml'=check there is no html content
299
     *                               'custom'= custom filter specify $filter and $options)
300
     *  @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)
301
     *  @param  int     $filter      Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails)
302
     *  @param  mixed   $options     Options to pass to filter_var when $check is set to 'custom'
303
     *  @param	string	$noreplace	 Force disable of replacement of __xxx__ strings.
304
     *  @return string|string[]      Value found (string or array), or '' if check fails
305
     */
306
    static function GETPOST($paramname, $check = 'none', $method = 0, $filter = null, $options = null, $noreplace = 0)
307
    {
308
        // global $mysoc, $user, Globals::$conf;
309
310
        if (empty($paramname))
311
            return 'BadFirstParameterForDolUtils::GETPOST';
312
        if (empty($check)) {
313
            DolUtils::dol_syslog("Deprecated use of DolUtils::GETPOST, called with 1st param = " . $paramname . " and 2nd param is '', when calling page " . $_SERVER["PHP_SELF"], LOG_WARNING);
314
            // Enable this line to know who call the DolUtils::GETPOST with '' $check parameter.
315
            //var_dump(debug_backtrace()[0]);
316
        }
317
318
        if (empty($method))
319
            $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
320
        elseif ($method == 1)
321
            $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
322
        elseif ($method == 2)
323
            $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
324
        elseif ($method == 3)
325
            $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
326
        elseif ($method == 4)
327
            $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_COOKIE[$paramname]) ? $_COOKIE[$paramname] : ''));
328
        else
329
            return 'BadThirdParameterForDolUtils::GETPOST';
330
331
        if (empty($method) || $method == 3 || $method == 4) {
332
            $relativepathstring = $_SERVER["PHP_SELF"];
333
            // Clean $relativepathstring
334
            if (constant('DOL_BASE_URI'))
335
                $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_BASE_URI'), '/') . '/', '', $relativepathstring);
336
            $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
337
            $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
338
            //var_dump($relativepathstring);
339
            //var_dump($user->default_values);
340
            // Code for search criteria persistence.
341
            // Retrieve values if restore_lastsearch_values
342
            if (!empty($_GET['restore_lastsearch_values'])) {        // Use $_GET here and not DolUtils::GETPOST
343
                if (!empty($_SESSION['lastsearch_values_' . $relativepathstring])) { // If there is saved values
344
                    $tmp = json_decode($_SESSION['lastsearch_values_' . $relativepathstring], true);
345
                    if (is_array($tmp)) {
346
                        foreach ($tmp as $key => $val) {
347
                            if ($key == $paramname) { // We are on the requested parameter
348
                                $out = $val;
349
                                break;
350
                            }
351
                        }
352
                    }
353
                }
354
                // If there is saved contextpage, page or limit
355
                if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_' . $relativepathstring])) {
356
                    $out = $_SESSION['lastsearch_contextpage_' . $relativepathstring];
357
                } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_' . $relativepathstring])) {
358
                    $out = $_SESSION['lastsearch_page_' . $relativepathstring];
359
                } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_' . $relativepathstring])) {
360
                    $out = $_SESSION['lastsearch_limit_' . $relativepathstring];
361
                }
362
            }
363
            // Else, retreive default values if we are not doing a sort
364
            elseif (!isset($_GET['sortfield'])) { // If we did a click on a field to sort, we do no apply default values. Same if option MAIN_ENABLE_DEFAULT_VALUES is not set
365
                if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
366
                    // Search default value from $object->field
367
                    // global $object;
368
                    if (is_object($object) && isset($object->fields[$paramname]['default'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $object seems to be never defined.
Loading history...
369
                        $out = $object->fields[$paramname]['default'];
370
                    }
371
                }
372
                if (!empty(Globals::$conf->global->MAIN_ENABLE_DEFAULT_VALUES)) {
373
                    if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
374
                        // Now search in setup to overwrite default values
375
                        if (!empty($user->default_values)) {  // $user->default_values defined from menu 'Setup - Default values'
376
                            if (isset($user->default_values[$relativepathstring]['createform'])) {
377
                                foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
378
                                    $qualified = 0;
379
                                    if ($defkey != '_noquery_') {
380
                                        $tmpqueryarraytohave = explode('&', $defkey);
381
                                        $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
382
                                        $foundintru = 0;
383
                                        foreach ($tmpqueryarraytohave as $tmpquerytohave) {
384
                                            if (!in_array($tmpquerytohave, $tmpqueryarraywehave))
385
                                                $foundintru = 1;
386
                                        }
387
                                        if (!$foundintru)
388
                                            $qualified = 1;
389
                                        //var_dump($defkey.'-'.$qualified);
390
                                    } else
391
                                        $qualified = 1;
392
393
                                    if ($qualified) {
394
                                        //var_dump($user->default_values[$relativepathstring][$defkey]['createform']);
395
                                        if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
396
                                            $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
397
                                            break;
398
                                        }
399
                                    }
400
                                }
401
                            }
402
                        }
403
                    }
404
                    // Management of default search_filters and sort order
405
                    //elseif (preg_match('/list.php$/', $_SERVER["PHP_SELF"]) && ! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
406
                    elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
407
                        if (!empty($user->default_values)) {  // $user->default_values defined from menu 'Setup - Default values'
408
                            //var_dump($user->default_values[$relativepathstring]);
409
                            if ($paramname == 'sortfield' || $paramname == 'sortorder') {   // Sorted on which fields ? ASC or DESC ?
410
                                if (isset($user->default_values[$relativepathstring]['sortorder'])) { // Even if paramname is sortfield, data are stored into ['sortorder...']
411
                                    foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
412
                                        $qualified = 0;
413
                                        if ($defkey != '_noquery_') {
414
                                            $tmpqueryarraytohave = explode('&', $defkey);
415
                                            $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
416
                                            $foundintru = 0;
417
                                            foreach ($tmpqueryarraytohave as $tmpquerytohave) {
418
                                                if (!in_array($tmpquerytohave, $tmpqueryarraywehave))
419
                                                    $foundintru = 1;
420
                                            }
421
                                            if (!$foundintru)
422
                                                $qualified = 1;
423
                                            //var_dump($defkey.'-'.$qualified);
424
                                        } else
425
                                            $qualified = 1;
426
427
                                        if ($qualified) {
428
                                            $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "=");  // we accept _, -, . and ,
429
                                            foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
430
                                                if ($out)
431
                                                    $out .= ', ';
432
                                                if ($paramname == 'sortfield') {
433
                                                    $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
434
                                                }
435
                                                if ($paramname == 'sortorder') {
436
                                                    $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
437
                                                }
438
                                            }
439
                                            //break;	// No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
440
                                        }
441
                                    }
442
                                }
443
                            } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
444
                                foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
445
                                    $qualified = 0;
446
                                    if ($defkey != '_noquery_') {
447
                                        $tmpqueryarraytohave = explode('&', $defkey);
448
                                        $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
449
                                        $foundintru = 0;
450
                                        foreach ($tmpqueryarraytohave as $tmpquerytohave) {
451
                                            if (!in_array($tmpquerytohave, $tmpqueryarraywehave))
452
                                                $foundintru = 1;
453
                                        }
454
                                        if (!$foundintru)
455
                                            $qualified = 1;
456
                                        //var_dump($defkey.'-'.$qualified);
457
                                    } else
458
                                        $qualified = 1;
459
460
                                    if ($qualified) {
461
                                        if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
462
                                            // We made a search from quick search menu, do we still use default filter ?
463
                                            if (empty(Globals::$conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) {
464
                                                $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "=");  // we accept _, -, . and ,
465
                                                $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
466
                                            }
467
                                        } else {
468
                                            $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "=");  // we accept _, -, . and ,
469
                                            $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
470
                                        }
471
                                        break;
472
                                    }
473
                                }
474
                            }
475
                        }
476
                    }
477
                }
478
            }
479
        }
480
481
        // Substitution variables for DolUtils::GETPOST (used to get final url with variable parameters or final default value with variable paramaters)
482
        // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
483
        // 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.
484
        if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
485
            $maxloop = 20;
486
            $loopnb = 0;    // Protection against infinite loop
487
            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.
488
                $loopnb++;
489
                $newout = '';
490
491
                if ($reg[1] == 'DAY') {
492
                    $tmp = dol_getdate(dol_now(), true);
493
                    $newout = $tmp['mday'];
494
                } elseif ($reg[1] == 'MONTH') {
495
                    $tmp = dol_getdate(dol_now(), true);
496
                    $newout = $tmp['mon'];
497
                } elseif ($reg[1] == 'YEAR') {
498
                    $tmp = dol_getdate(dol_now(), true);
499
                    $newout = $tmp['year'];
500
                } elseif ($reg[1] == 'PREVIOUS_DAY') {
501
                    $tmp = dol_getdate(dol_now(), true);
502
                    $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
503
                    $newout = $tmp2['day'];
504
                } elseif ($reg[1] == 'PREVIOUS_MONTH') {
505
                    $tmp = dol_getdate(dol_now(), true);
506
                    $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
507
                    $newout = $tmp2['month'];
508
                } elseif ($reg[1] == 'PREVIOUS_YEAR') {
509
                    $tmp = dol_getdate(dol_now(), true);
510
                    $newout = ($tmp['year'] - 1);
511
                } elseif ($reg[1] == 'NEXT_DAY') {
512
                    $tmp = dol_getdate(dol_now(), true);
513
                    $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
514
                    $newout = $tmp2['day'];
515
                } elseif ($reg[1] == 'NEXT_MONTH') {
516
                    $tmp = dol_getdate(dol_now(), true);
517
                    $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
518
                    $newout = $tmp2['month'];
519
                } elseif ($reg[1] == 'NEXT_YEAR') {
520
                    $tmp = dol_getdate(dol_now(), true);
521
                    $newout = ($tmp['year'] + 1);
522
                } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
523
                    $newout = $mysoc->country_id;
524
                } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
525
                    $newout = $user->id;
526
                } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
527
                    $newout = $user->fk_user;
528
                } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
529
                    $newout = Globals::$conf->entity;
530
                } else
531
                    $newout = '';     // Key not found, we replace with empty string
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
                    
575
//var_dump('__'.$reg[1].'__ -> '.$newout);
576
                $out = preg_replace('/__' . preg_quote($reg[1], '/') . '__/', $newout, $out);
577
            }
578
        }
579
580
        // Check is done after replacement
581
        switch ($check) {
582
            case 'none':
583
                break;
584
            case 'int':    // Check param is a numeric value (integer but also float or hexadecimal)
585
                if (!is_numeric($out)) {
586
                    $out = '';
587
                }
588
                break;
589
            case 'intcomma':
590
                if (preg_match('/[^0-9,-]+/i', $out))
591
                    $out = '';
592
                break;
593
            case 'alpha':
594
                if (!is_array($out)) {
595
                    $out = trim($out);
596
                    // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
597
                    // '../' is dangerous because it allows dir transversals
598
                    if (preg_match('/"/', $out))
599
                        $out = '';
600
                    else if (preg_match('/\.\.\//', $out))
601
                        $out = '';
602
                }
603
                break;
604
            case 'san_alpha':
605
                $out = filter_var($out, FILTER_SANITIZE_STRING);
606
                break;
607
            case 'aZ':
608
                if (!is_array($out)) {
609
                    $out = trim($out);
610
                    if (preg_match('/[^a-z]+/i', $out))
611
                        $out = '';
612
                }
613
                break;
614
            case 'aZ09':
615
                if (!is_array($out)) {
616
                    $out = trim($out);
617
                    if (preg_match('/[^a-z0-9_\-\.]+/i', $out))
618
                        $out = '';
619
                }
620
                break;
621
            case 'aZ09comma':  // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh
622
                if (!is_array($out)) {
623
                    $out = trim($out);
624
                    if (preg_match('/[^a-z0-9_\-\.,]+/i', $out))
625
                        $out = '';
626
                }
627
                break;
628
            case 'array':
629
                if (!is_array($out) || empty($out))
630
                    $out = array();
631
                break;
632
            case 'nohtml':  // Recommended for most scalar parameters
633
                $out = dol_string_nohtmltag($out, 0);
634
                break;
635
            case 'alphanohtml': // Recommended for search parameters
636
                if (!is_array($out)) {
637
                    $out = trim($out);
638
                    // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
639
                    // '../' is dangerous because it allows dir transversals
640
                    if (preg_match('/"/', $out))
641
                        $out = '';
642
                    else if (preg_match('/\.\.\//', $out))
643
                        $out = '';
644
                    $out = dol_string_nohtmltag($out);
645
                }
646
                break;
647
            case 'custom':
648
                if (empty($filter))
649
                    return 'BadFourthParameterForDolUtils::GETPOST';
650
                $out = filter_var($out, $filter, $options);
651
                break;
652
        }
653
654
        // Code for search criteria persistence.
655
        // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
656
        if (empty($method) || $method == 3 || $method == 4) {
657
            if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
658
                //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
659
                // We save search key only if $out not empty that means:
660
                // - posted value not empty, or
661
                // - if posted value is empty and a default value exists that is not empty (it means we did a filter to an empty value when default was not).
662
663
                if ($out != '') {  // $out = '0' or 'abc', it is a search criteria to keep
664
                    $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
665
                }
666
            }
667
        }
668
669
        return $out;
670
    }
671
672
    /**
673
     *  Return a prefix to use for this Dolibarr instance, for session/cookie names or email id.
674
     *  The prefix for session is unique in a web context only and is unique for instance and avoid conflict
675
     *  between multi-instances, even when having two instances with one root dir or two instances in virtual servers.
676
     *  The prefix for email is unique if MAIL_PREFIX_FOR_EMAIL_ID is set to a value, otherwise value may be same than other instance.
677
     *
678
     *  @param  string  $mode                   '' (prefix for session name) or 'email' (prefix for email id)
679
     *  @return	string                          A calculated prefix
680
     */
681
    static function dol_getprefix($mode = '')
682
    {
683
        // If prefix is for email
684
        if ($mode == 'email') {
685
            if (empty(Globals::$conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) {
686
                return Security::dol_hash(DOL_DOCUMENT_ROOT . DOL_BASE_URI);
687
            }
688
            // If MAIL_PREFIX_FOR_EMAIL_ID is set (a value initialized with a random value is recommended)
689
            if (Globals::$conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') {
690
                return Globals::$conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
691
            }
692
            if (isset($_SERVER["SERVER_NAME"])) {
693
                return $_SERVER["SERVER_NAME"];
694
            }
695
696
            return Security::dol_hash(DOL_DOCUMENT_ROOT . DOL_BASE_URI);
697
        }
698
699
        if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
700
            return Security::dol_hash($_SERVER["SERVER_NAME"] . $_SERVER["DOCUMENT_ROOT"] . DOL_DOCUMENT_ROOT . DOL_BASE_URI);
701
702
            // Use this for a "readable" cookie name
703
            //return dol_sanitizeFileName($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_BASE_URI);
704
        }
705
        return Security::dol_hash(DOL_DOCUMENT_ROOT . DOL_BASE_URI);
706
    }
707
708
    /**
709
     * 	Make an include_once using default root and alternate root if it fails.
710
     *  To link to a core file, use include(DOL_DOCUMENT_ROOT.'/pathtofile')
711
     *  To link to a module file from a module file, use include './mymodulefile';
712
     *  To link to a module file from a core file, then this function can be used (call by hook / trigger / speciales pages)
713
     *
714
     * 	@param	string	$relpath	Relative path to file (Ie: mydir/myfile, ../myfile, ...)
715
     * 	@param	string	$classname	Class name (deprecated)
716
     *  @return bool                True if load is a success, False if it fails
717
     */
718
    static function dol_include_once($relpath, $classname = '')
719
    {
720
        // global Globals::$conf, Globals::$langs, $user, $mysoc;   // Do not remove this. They must be defined for files we include. Other globals var must be retreived with $GLOBALS['var']
721
722
        $fullpath = dol_buildpath($relpath);
723
724
        if (!file_exists($fullpath)) {
725
            DolUtils::dol_syslog('functions::dol_include_once Tried to load unexisting file: ' . $relpath, LOG_ERR);
726
            return false;
727
        }
728
729
        if (!empty($classname) && !class_exists($classname)) {
730
            return include $fullpath;
731
        } else {
732
            return include_once $fullpath;
733
        }
734
    }
735
736
    /**
737
     * 	Return path of url or filesystem. Can check into alternate dir or alternate dir + main dir depending on value of $returnemptyifnotfound.
738
     *
739
     * 	@param	string	$path						Relative path to file (if mode=0) or relative url (if mode=1). Ie: mydir/myfile, ../myfile
740
     *  @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)
741
     *  @param	int		$returnemptyifnotfound		0:If $type==0 and if file was not found into alternate dir, return default path into main dir (no test on it)
742
     *  											1:If $type==0 and if file was not found into alternate dir, return empty string
743
     *  											2:If $type==0 and if file was not found into alternate dir, test into main dir, return default path if found, empty string if not found
744
     *  @return string								Full filesystem path (if path=0), Full url path (if mode=1)
745
     */
746
    static function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
747
    {
748
        // global Globals::$conf;
749
750
        $path = preg_replace('/^\//', '', $path);
751
752
        if ($type == 0 /* empty($type) */) { // For a filesystem path
753
//$res = DOL_BASE_PATH . '' . $path;  // Standard default path
754
            $res = DOL_BASE_PATH . '/' . $path;  // Standard default path
755
            if (isset(Globals::$conf->file->dol_document_root)) {
756
                foreach (Globals::$conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
757
                    if ($key == 'main') {
758
                        continue;
759
                    }
760
                    if (file_exists($dirroot . '/' . $path)) {
761
                        $res = $dirroot . '/' . $path;
762
                        return $res;
763
                    }
764
                }
765
            }
766
            if ($returnemptyifnotfound) {        // Not found into alternate dir
767
                if ($returnemptyifnotfound == 1 || !file_exists($res))
768
                    return '';
769
            }
770
        }
771
        else {    // For an url path
772
// We try to get local path of file on filesystem from url
773
// Note that trying to know if a file on disk exist by forging path on disk from url
774
// works only for some web server and some setup. This is bugged when
775
// using proxy, rewriting, virtual path, etc...
776
            $res = '';
777
            if ($type == 1) {
778
                $res = /* DOL_BASE_URI */ DOL_BASE_URI . '/' . $path;   // Standard value
779
            }
780
            if ($type == 2) {
781
                $res = /* DOL_MAIN_URL_ROOT */ DOL_BASE_PATH . '/' . $path;  // Standard value
782
            }
783
            if ($type == 3) {
784
                $res = DOL_BASE_URI . '/' . $path;
785
            }
786
787
            foreach (Globals::$conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
788
                if ($key == 'main') {
789
                    if ($type == 3) {
790
                        // global $dolibarr_main_url_root;
791
                        // Define $urlwithroot
792
                        // $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_BASE_URI, '/') . '$/i', '', trim($dolibarr_main_url_root));
793
                        $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_BASE_URI, '/') . '$/i', '', trim(DOL_BASE_URI));
794
795
                        $urlwithroot = $urlwithouturlroot . DOL_BASE_URI;  // This is to use external domain name found into config file
796
                        //$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
797
798
                        $res = (preg_match('/^http/i', Globals::$conf->file->dol_url_root[$key]) ? '' : $urlwithroot) . '/' . $path;     // Test on start with http is for old conf syntax
799
                    }
800
                    continue;
801
                }
802
                preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs);    // Take part before '?'
803
                if (!empty($regs[1])) {
804
                    //print $key.'-'.$dirroot.'/'.$path.'-'.Globals::$conf->file->dol_url_root[$type].'<br>'."\n";
805
                    if (file_exists($dirroot . '/' . $regs[1])) {
806
                        if ($type == 1) {
807
                            $res = (preg_match('/^http/i', Globals::$conf->file->dol_url_root[$key]) ? '' : DOL_BASE_URI) . Globals::$conf->file->dol_url_root[$key] . '/' . $path;
808
                        }
809
                        if ($type == 2) {
810
                            $res = (preg_match('/^http/i', Globals::$conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT) . Globals::$conf->file->dol_url_root[$key] . '/' . $path;
811
                        }
812
                        if ($type == 3) {
813
                            // global $dolibarr_main_url_root;
814
                            // Define $urlwithroot
815
                            $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_BASE_URI, '/') . '$/i', '', trim($dolibarr_main_url_root));
816
                            $urlwithroot = $urlwithouturlroot . DOL_BASE_URI;  // This is to use external domain name found into config file
817
                            //$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
818
819
                            $res = (preg_match('/^http/i', Globals::$conf->file->dol_url_root[$key]) ? '' : $urlwithroot) . Globals::$conf->file->dol_url_root[$key] . '/' . $path;     // Test on start with http is for old conf syntax
820
                        }
821
                        break;
822
                    }
823
                }
824
            }
825
        }
826
827
        return $res;
828
    }
829
830
    /**
831
     * 	Create a clone of instance of object (new instance with same value for properties)
832
     *  With native = 0: Property that are reference are also new object (true clone). This means $this->db is not valid.
833
     *  With native = 1: Use PHP clone. Property that are reference are same pointer. This means $this->db is still valid.
834
     *
835
     * 	@param	object	$object		Object to clone
836
     *  @param	int		$native		Native method or true method
837
     * 	@return object				Object clone
838
     *  @see https://php.net/manual/language.oop5.cloning.php
839
     */
840
    static function dol_clone($object, $native = 0)
841
    {
842
//DolUtils::dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
843
844
        if (empty($native)) {
845
            $myclone = unserialize(serialize($object));
846
        } else {
847
            $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)
848
        }
849
850
        return $myclone;
851
    }
852
853
    /**
854
     * 	Optimize a size for some browsers (phone, smarphone, ...)
855
     *
856
     * 	@param	int		$size		Size we want
857
     * 	@param	string	$type		Type of optimizing:
858
     * 								'' = function used to define a size for truncation
859
     * 								'width' = function is used to define a width
860
     * 	@return int					New size after optimizing
861
     */
862
    static function dol_size($size, $type = '')
863
    {
864
        //global Globals::$conf;
865
        if (empty(Globals::$conf->dol_optimize_smallscreen))
866
            return $size;
867
        if ($type == 'width' && $size > 250)
868
            return 250;
869
        else
870
            return 10;
871
    }
872
873
    /**
874
     * 	Clean a string to use it as a file name
875
     *
876
     * 	@param	string	$str            String to clean
877
     * 	@param	string	$newstr			String to replace bad chars with
878
     *  @param	int	    $unaccent		1=Remove also accent (default), 0 do not remove them
879
     * 	@return string          		String cleaned (a-zA-Z_)
880
     *
881
     * 	@see        	dol_string_nospecial, dol_string_unaccent, dol_sanitizePathName
882
     */
883
    static function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
884
    {
885
        $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', '°');
886
        return dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
887
    }
888
889
    /**
890
     * 	Clean a string to use it as a path name
891
     *
892
     * 	@param	string	$str            String to clean
893
     * 	@param	string	$newstr			String to replace bad chars with
894
     *  @param	int	    $unaccent		1=Remove also accent (default), 0 do not remove them
895
     * 	@return string          		String cleaned (a-zA-Z_)
896
     *
897
     * 	@see        	dol_string_nospecial, dol_string_unaccent, dol_sanitizeFileName
898
     */
899
    static function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
900
    {
901
        $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°');
902
        return dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
903
    }
904
905
    /**
906
     * 	Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName
907
     *
908
     * 	@param	string	$str			String to clean
909
     * 	@return string   	       		Cleaned string
910
     *
911
     * 	@see    		dol_sanitizeFilename, dol_string_nospecial
912
     */
913
    static function dol_string_unaccent($str)
914
    {
915
        if (utf8_check($str)) {
916
// See http://www.utf8-chartable.de/
917
            $string = rawurlencode($str);
918
            $replacements = array(
919
                '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
920
                '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
921
                '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
922
                '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
923
                '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
924
                '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
925
                '%C3%A7' => 'c',
926
                '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
927
                '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
928
                '%C3%B1' => 'n',
929
                '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
930
                '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
931
                '%C3%BF' => 'y'
932
            );
933
            $string = strtr($string, $replacements);
934
            return rawurldecode($string);
935
        } else {
936
// See http://www.ascii-code.com/
937
            $string = strtr(
938
                $str, "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
939
			\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
940
			\xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
941
			\xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
942
			\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
943
			\xF9\xFA\xFB\xFC\xFD\xFF", "AAAAAAC
944
			EEEEIIIIDN
945
			OOOOOUUUY
946
			aaaaaaceeee
947
			iiiidnooooo
948
			uuuuyy"
949
            );
950
            $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"));
951
            return $string;
952
        }
953
    }
954
955
    /**
956
     * 	Clean a string from all punctuation characters to use it as a ref or login.
957
     *  This is a more complete static function than dol_sanitizeFileName.
958
     *
959
     * 	@param	string	$str            	String to clean
960
     * 	@param	string	$newstr				String to replace forbidden chars with
961
     *  @param  array	$badcharstoreplace  List of forbidden characters
962
     * 	@return string          			Cleaned string
963
     *
964
     * 	@see    		dol_sanitizeFilename, dol_string_unaccent
965
     */
966
    static function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '')
967
    {
968
        $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°');  // more complete than dol_sanitizeFileName
969
        $forbidden_chars_to_remove = array();
970
        if (is_array($badcharstoreplace))
971
            $forbidden_chars_to_replace = $badcharstoreplace;
972
//$forbidden_chars_to_remove=array("(",")");
973
974
        return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
975
    }
976
977
    /**
978
     * Encode string for xml usage
979
     *
980
     * @param 	string	$string		String to encode
981
     * @return	string				String encoded
982
     */
983
    static function dolEscapeXML($string)
984
    {
985
        return strtr($string, array('\'' => '&apos;', '"' => '&quot;', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;'));
986
    }
987
988
    /**
989
     *  Returns text escaped for inclusion into javascript code
990
     *
991
     *  @param      string		$stringtoescape		String to escape
992
     *  @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 \
993
     *  @param		int		$noescapebackslashn	0=Escape also \n. 1=Do not escape \n.
994
     *  @return     string     		 				Escaped string. Both ' and " are escaped into ' if they are escaped.
995
     */
996
    static function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
997
    {
998
// escape quotes and backslashes, newlines, etc.
999
        $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1000
//$substitjs['</']='<\/';	// We removed this. Should be useless.
1001
        if (empty($noescapebackslashn)) {
1002
            $substitjs["\n"] = '\\n';
1003
            $substitjs['\\'] = '\\\\';
1004
        }
1005
        if (empty($mode)) {
1006
            $substitjs["'"] = "\\'";
1007
            $substitjs['"'] = "\\'";
1008
        } else if ($mode == 1)
1009
            $substitjs["'"] = "\\'";
1010
        else if ($mode == 2) {
1011
            $substitjs['"'] = '\\"';
1012
        } else if ($mode == 3) {
1013
            $substitjs["'"] = "\\'";
1014
            $substitjs['"'] = "\\\"";
1015
        }
1016
        return strtr($stringtoescape, $substitjs);
1017
    }
1018
1019
    /**
1020
     *  Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
1021
     *
1022
     *  @param      string		$stringtoescape		String to escape
1023
     *  @param		int			$keepb				1=Preserve b tags (otherwise, remove them)
1024
     *  @param      int         $keepn              1=Preserve \r\n strings (otherwise, replace them with escaped value)
1025
     *  @return     string     				 		Escaped string
1026
     *  @see		dol_string_nohtmltag, dol_string_nospecial, dol_string_unaccent
1027
     */
1028
    static function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0)
1029
    {
1030
// escape quotes and backslashes, newlines, etc.
1031
        $tmp = html_entity_decode($stringtoescape, ENT_COMPAT, 'UTF-8');  // TODO Use htmlspecialchars_decode instead, that make only required change for html tags
1032
        if (!$keepb)
1033
            $tmp = strtr($tmp, array("<b>" => '', '</b>' => ''));
1034
        if (!$keepn)
1035
            $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
1036
        return htmlentities($tmp, ENT_COMPAT, 'UTF-8');      // TODO Use htmlspecialchars instead, that make only required change for html tags
1037
    }
1038
1039
    /**
1040
     * Convert a string to lower. Never use strtolower because it does not works with UTF8 strings.
1041
     *
1042
     * @param 	string		$utf8_string		String to encode
1043
     * @return 	string							String converted
1044
     */
1045
    static function dol_strtolower($utf8_string)
1046
    {
1047
        return mb_strtolower($utf8_string, "UTF-8");
1048
    }
1049
1050
    /**
1051
     * Convert a string to upper. Never use strtolower because it does not works with UTF8 strings.
1052
     *
1053
     * @param 	string		$utf8_string		String to encode
1054
     * @return 	string							String converted
1055
     */
1056
    static function dol_strtoupper($utf8_string)
1057
    {
1058
        return mb_strtoupper($utf8_string, "UTF-8");
1059
    }
1060
1061
    /**
1062
     * 	Write log message into outputs. Possible outputs can be:
1063
     * 	SYSLOG_HANDLERS = ["mod_syslog_file"]  		file name is then defined by SYSLOG_FILE
1064
     * 	SYSLOG_HANDLERS = ["mod_syslog_syslog"]  	facility is then defined by SYSLOG_FACILITY
1065
     *  Warning, syslog functions are bugged on Windows, generating memory protection faults. To solve
1066
     *  this, use logging to files instead of syslog (see setup of module).
1067
     *  Note: If constant 'SYSLOG_FILE_NO_ERROR' defined, we never output any error message when writing to log fails.
1068
     *  Note: You can get log message into html sources by adding parameter &logtohtml=1 (constant MAIN_LOGTOHTML must be set)
1069
     *  This static function works only if syslog module is enabled.
1070
     * 	This must not use any call to other static function calling DolUtils::dol_syslog (avoid infinite loop).
1071
     *
1072
     * 	@param  string		$message				Line to log. ''=Show nothing
1073
     *  @param  int			$level					Log level
1074
     * 												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
1075
     * 												On Linux   LOG_ERR=3, LOG_WARNING=4, LOG_INFO=6, LOG_DEBUG=7
1076
     *  @param	int			$ident					1=Increase ident of 1, -1=Decrease ident of 1
1077
     *  @param	string		$suffixinfilename		When output is a file, append this suffix into default log filename.
1078
     *  @param	string		$restricttologhandler	Output log only for this log handler
1079
     *  @return	void
1080
     */
1081
    static function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '')
1082
    {
1083
        // global Globals::$conf, $user;
1084
// If syslog module enabled
1085
        if (empty(Globals::$conf->syslog->enabled))
1086
            return;
1087
1088
        if ($ident < 0) {
1089
            foreach (Globals::$conf->loghandlers as $loghandlerinstance) {
1090
                $loghandlerinstance->setIdent($ident);
1091
            }
1092
        }
1093
1094
        if (!empty($message)) {
1095
// Test log level
1096
            $logLevels = array(LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG);
1097
            if (!in_array($level, $logLevels, true)) {
1098
                throw new Exception('Incorrect log level');
1099
            }
1100
            if ($level > Globals::$conf->global->SYSLOG_LEVEL)
1101
                return;
1102
1103
            $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1104
// If adding log inside HTML page is required
1105
            if (!empty($_REQUEST['logtohtml']) && (!empty(Globals::$conf->global->MAIN_ENABLE_LOG_TO_HTML) || !empty(Globals::$conf->global->MAIN_LOGTOHTML))) {   // MAIN_LOGTOHTML kept for backward compatibility
1106
                Globals::$conf->logbuffer[] = DolUtils::dol_print_date(time(), "%Y-%m-%d %H:%M:%S") . " " . $message;
1107
            }
1108
1109
//TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1110
// If html log tag enabled and url parameter log defined, we show output log on HTML comments
1111
            if (!empty(Globals::$conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) {
1112
                print "\n\n<!-- Log start\n";
1113
                print $message . "\n";
1114
                print "Log end -->\n";
1115
            }
1116
1117
            $data = array(
1118
                'message' => $message,
1119
                'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
1120
                'level' => $level,
1121
                'user' => ((is_object($user) && $user->id) ? $user->login : false),
1122
                'ip' => false
1123
            );
1124
1125
// This is when server run behind a reverse proxy
1126
            if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
1127
                $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'] . (empty($_SERVER["REMOTE_ADDR"]) ? '' : '->' . $_SERVER['REMOTE_ADDR']);
1128
// This is when server run normally on a server
1129
            else if (!empty($_SERVER["REMOTE_ADDR"]))
1130
                $data['ip'] = $_SERVER['REMOTE_ADDR'];
1131
// This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1132
            else if (!empty($_SERVER['SERVER_ADDR']))
1133
                $data['ip'] = $_SERVER['SERVER_ADDR'];
1134
// 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).
1135
            else if (!empty($_SERVER['COMPUTERNAME']))
1136
                $data['ip'] = $_SERVER['COMPUTERNAME'] . (empty($_SERVER['USERNAME']) ? '' : '@' . $_SERVER['USERNAME']);
1137
// 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).
1138
            else if (!empty($_SERVER['LOGNAME']))
1139
                $data['ip'] = '???@' . $_SERVER['LOGNAME'];
1140
// Loop on each log handler and send output
1141
            foreach (Globals::$conf->loghandlers as $loghandlerinstance) {
1142
                if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler)
1143
                    continue;
1144
                $loghandlerinstance->export($data, $suffixinfilename);
1145
            }
1146
            unset($data);
1147
        }
1148
1149
        if ($ident > 0) {
1150
            foreach (Globals::$conf->loghandlers as $loghandlerinstance) {
1151
                $loghandlerinstance->setIdent($ident);
1152
            }
1153
        }
1154
    }
1155
1156
    /**
1157
     * 	Show tab header of a card
1158
     *
1159
     * 	@param	array	$links				Array of tabs. Currently initialized by calling a static function xxx_admin_prepare_head
1160
     * 	@param	string	$active     		Active tab name (document', 'info', 'ldap', ....)
1161
     * 	@param  string	$title      		Title
1162
     * 	@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.
1163
     * 	@param	string	$picto				Add a picto on tab title
1164
     * 	@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.
1165
     *  @param	string	$morehtmlright		Add more html content on right of tabs title
1166
     *  @param	string	$morecss			More Css
1167
     * 	@return	void
1168
     */
1169
    static function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '')
1170
    {
1171
        print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss);
1172
    }
1173
1174
    /**
1175
     *  Show tab header of a card
1176
     *
1177
     * 	@param	array	$links				Array of tabs
1178
     * 	@param	string	$active     		Active tab name
1179
     * 	@param  string	$title      		Title
1180
     * 	@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.
1181
     * 	@param	string	$picto				Add a picto on tab title
1182
     * 	@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.
1183
     *  @param	string	$morehtmlright		Add more html content on right of tabs title
1184
     *  @param	string	$morecss			More Css
1185
     * 	@return	string
1186
     */
1187
    static function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '')
1188
    {
1189
        // global Globals::$conf, Globals::$langs, Globals::$hookManager;
1190
1191
        $out = "\n" . '<div class="tabs" data-role="controlgroup" data-type="horizontal">' . "\n";
1192
1193
        if ($morehtmlright) {
1194
            $out .= '<div class="inline-block floatright tabsElem">' . $morehtmlright . '</div>'; // Output right area first so when space is missing, text is in front of tabs and not under.
1195
        }
1196
1197
// Show title
1198
        $showtitle = 1;
1199
        if (!empty(Globals::$conf->dol_optimize_smallscreen))
1200
            $showtitle = 0;
1201
        if (!empty($title) && $showtitle) {
1202
            $limittitle = 30;
1203
            $out .= '<a class="tabTitle">';
1204
            if ($picto)
1205
                $out .= img_picto($title, ($pictoisfullpath ? '' : 'object_') . $picto, '', $pictoisfullpath) . ' ';
1206
            $out .= '<span class="tabTitleText">' . dol_trunc($title, $limittitle) . '</span>';
1207
            $out .= '</a>';
1208
        }
1209
1210
// Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
1211
        $maxkey = -1;
1212
        if (is_array($links) && !empty($links)) {
1213
            $keys = array_keys($links);
1214
            if (count($keys))
1215
                $maxkey = max($keys);
1216
        }
1217
1218
        if (!empty(Globals::$conf->dol_optimize_smallscreen))
1219
            Globals::$conf->global->MAIN_MAXTABS_IN_CARD = 2;
1220
1221
// Show tabs
1222
        $bactive = false;
1223
// if =0 we don't use the feature
1224
        $limittoshow = (empty(Globals::$conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : Globals::$conf->global->MAIN_MAXTABS_IN_CARD);
1225
        $displaytab = 0;
1226
        $nbintab = 0;
1227
        $popuptab = 0;
1228
        $outmore = '';
1229
        for ($i = 0; $i <= $maxkey; $i++) {
1230
            if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1231
// If active tab is already present
1232
                if ($i >= $limittoshow)
1233
                    $limittoshow--;
1234
            }
1235
        }
1236
1237
        for ($i = 0; $i <= $maxkey; $i++) {
1238
            if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1239
                $isactive = true;
1240
                $bactive = true;
1241
            } else {
1242
                $isactive = false;
1243
            }
1244
1245
            if ($i < $limittoshow || $isactive) {
1246
                $out .= '<div class="inline-block tabsElem' . ($isactive ? ' tabsElemActive' : '') . ((!$isactive && !empty(Globals::$conf->global->MAIN_HIDE_INACTIVETAB_ON_PRINT)) ? ' hideonprint' : '') . '"><!-- id tab = ' . (empty($links[$i][2]) ? '' : $links[$i][2]) . ' -->';
1247
                if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1248
                    if (!empty($links[$i][0])) {
1249
                        $out .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
1250
                    } else {
1251
                        $out .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
1252
                    }
1253
                } else if (!empty($links[$i][1])) {
1254
                    //print "x $i $active ".$links[$i][2]." z";
1255
                    if ($isactive) {
1256
                        $out .= '<a' . (!empty($links[$i][2]) ? ' id="' . $links[$i][2] . '"' : '') . ' class="tabactive tab inline-block' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">';
1257
                        $out .= $links[$i][1];
1258
                        $out .= '</a>' . "\n";
1259
                    } else {
1260
                        $out .= '<a' . (!empty($links[$i][2]) ? ' id="' . $links[$i][2] . '"' : '') . ' class="tabunactive tab inline-block' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">';
1261
                        $out .= $links[$i][1];
1262
                        $out .= '</a>' . "\n";
1263
                    }
1264
                }
1265
                $out .= '</div>';
1266
            } else {
1267
// The popup with the other tabs
1268
                if (!$popuptab) {
1269
                    $popuptab = 1;
1270
                    $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
1271
                }
1272
                $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
1273
                if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1274
                    if (!empty($links[$i][0]))
1275
                        $outmore .= '<a class="tabimage' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">' . $links[$i][1] . '</a>' . "\n";
1276
                    else
1277
                        $outmore .= '<span class="tabspan">' . $links[$i][1] . '</span>' . "\n";
1278
                }
1279
                else if (!empty($links[$i][1])) {
1280
                    $outmore .= '<a' . (!empty($links[$i][2]) ? ' id="' . $links[$i][2] . '"' : '') . ' class="wordwrap inline-block' . ($morecss ? ' ' . $morecss : '') . '" href="' . $links[$i][0] . '">';
1281
                    $outmore .= preg_replace('/([a-z])\/([a-z])/i', '\\1 / \\2', $links[$i][1]); // Replace x/y with x / y to allow wrap on long composed texts.
1282
                    $outmore .= '</a>' . "\n";
1283
                }
1284
                $outmore .= '</div>';
1285
1286
                $nbintab++;
1287
            }
1288
            $displaytab = $i;
1289
        }
1290
        if ($popuptab)
1291
            $outmore .= '</div>';
1292
1293
        if ($displaytab > $limittoshow) {
1294
            $left = (Globals::$langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
1295
            $right = (Globals::$langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
1296
1297
            $tabsname = str_replace("@", "", $picto);
1298
            $out .= '<div id="moretabs' . $tabsname . '" class="inline-block tabsElem">';
1299
            $out .= '<a href="#" class="tab moretab inline-block tabunactive reposition">' . Globals::$langs->trans("More") . '... (' . $nbintab . ')</a>';
1300
            $out .= '<div id="moretabsList' . $tabsname . '" style="position: absolute; ' . $left . ': -999em; text-align: ' . $left . '; margin:0px; padding:2px">';
1301
            $out .= $outmore;
1302
            $out .= '</div>';
1303
            $out .= '<div></div>';
1304
            $out .= "</div>\n";
1305
1306
            $out .= "<script>";
1307
            $out .= "$('#moretabs" . $tabsname . "').mouseenter( function() { console.log('mouseenter " . $left . "'); $('#moretabsList" . $tabsname . "').css('" . $left . "','auto');});";
1308
            $out .= "$('#moretabs" . $tabsname . "').mouseleave( function() { console.log('mouseleave " . $left . "'); $('#moretabsList" . $tabsname . "').css('" . $left . "','-999em');});";
1309
            $out .= "</script>";
1310
        }
1311
1312
        $out .= "</div>\n";
1313
1314
        if (!$notab || $notab == -1)
1315
            $out .= "\n" . '<div class="tabBar' . ($notab == -1 ? '' : ' tabBarWithBottom') . '">' . "\n";
1316
1317
        $parameters = array('tabname' => $active, 'out' => $out);
1318
        $reshook = Globals::$hookManager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
1319
        if ($reshook > 0) {
1320
            $out = Globals::$hookManager->resPrint;
1321
        }
1322
1323
        return $out;
1324
    }
1325
1326
    /**
1327
     *  Show tab footer of a card
1328
     *
1329
     *  @param	int		$notab       -1 or 0=Add tab footer, 1=no tab footer
1330
     *  @return	void
1331
     */
1332
    static function dol_fiche_end($notab = 0)
1333
    {
1334
        print dol_get_fiche_end($notab);
1335
    }
1336
1337
    /**
1338
     * 	Return tab footer of a card
1339
     *
1340
     * 	@param  int		$notab		-1 or 0=Add tab footer, 1=no tab footer
1341
     *  @return	string
1342
     */
1343
    static function dol_get_fiche_end($notab = 0)
1344
    {
1345
        if (!$notab || $notab == -1)
1346
            return "\n</div>\n";
1347
        else
1348
            return '';
1349
    }
1350
1351
    /**
1352
     *  Show tab footer of a card.
1353
     *  Note: $object->next_prev_filter can be set to restrict select to find next or previous record by $form->showrefnav.
1354
     *
1355
     *  @param	Object	$object			Object to show
1356
     *  @param	string	$paramid   		Name of parameter to use to name the id into the URL next/previous link
1357
     *  @param	string	$morehtml  		More html content to output just before the nav bar
1358
     *  @param	int		$shownav	  	Show Condition (navigation is shown if value is 1)
1359
     *  @param	string	$fieldid   		Nom du champ en base a utiliser pour select next et previous (we make the select max and min on this field). Use 'none' for no prev/next search.
1360
     *  @param	string	$fieldref   	Nom du champ objet ref (object->ref) a utiliser pour select next et previous
1361
     *  @param	string	$morehtmlref  	More html to show after ref
1362
     *  @param	string	$moreparam  	More param to add in nav link url.
1363
     * 	@param	int		$nodbprefix		Do not include DB prefix to forge table name
1364
     * 	@param	string	$morehtmlleft	More html code to show before ref
1365
     * 	@param	string	$morehtmlstatus	More html code to show under navigation arrows
1366
     *  @param  int     $onlybanner     Put this to 1, if the card will contains only a banner (this add css 'arearefnobottom' on div)
1367
     * 	@param	string	$morehtmlright	More html code to show before navigation arrows
1368
     *  @return	void
1369
     */
1370
    static function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
1371
    {
1372
        // global Globals::$conf, $form, $user, Globals::$langs;
1373
1374
        $error = 0;
1375
1376
        $maxvisiblephotos = 1;
1377
        $showimage = 1;
1378
        $entity = (empty($object->entity) ? Globals::$conf->entity : $object->entity);
1379
        $showbarcode = empty(Globals::$conf->barcode->enabled) ? 0 : ($object->barcode ? 1 : 0);
1380
        if (!empty(Globals::$conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance))
1381
            $showbarcode = 0;
1382
        $modulepart = 'unknown';
1383
1384
        if ($object->element == 'societe') {
1385
            $modulepart = 'societe';
1386
        }
1387
        if ($object->element == 'contact') {
1388
            $modulepart = 'contact';
1389
        }
1390
        if ($object->element == 'member') {
1391
            $modulepart = 'memberphoto';
1392
        }
1393
        if ($object->element == 'user') {
1394
            $modulepart = 'userphoto';
1395
        }
1396
        if ($object->element == 'product') {
1397
            $modulepart = 'product';
1398
        }
1399
1400
        if (class_exists("Imagick")) {
1401
            if ($object->element == 'propal') {
1402
                $modulepart = 'propal';
1403
            }
1404
            if ($object->element == 'commande') {
1405
                $modulepart = 'commande';
1406
            }
1407
            if ($object->element == 'facture') {
1408
                $modulepart = 'facture';
1409
            }
1410
            if ($object->element == 'fichinter') {
1411
                $modulepart = 'ficheinter';
1412
            }
1413
            if ($object->element == 'contrat') {
1414
                $modulepart = 'contract';
1415
            }
1416
            if ($object->element == 'supplier_proposal') {
1417
                $modulepart = 'supplier_proposal';
1418
            }
1419
            if ($object->element == 'order_supplier') {
1420
                $modulepart = 'supplier_order';
1421
            }
1422
            if ($object->element == 'invoice_supplier') {
1423
                $modulepart = 'supplier_invoice';
1424
            }
1425
            if ($object->element == 'expensereport') {
1426
                $modulepart = 'expensereport';
1427
            }
1428
        }
1429
1430
        if ($object->element == 'product') {
1431
            $width = 80;
1432
            $cssclass = 'photoref';
1433
            $showimage = $object->is_photo_available(Globals::$conf->product->multidir_output[$entity]);
1434
            $maxvisiblephotos = (isset(Globals::$conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? Globals::$conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5);
1435
            if (Globals::$conf->browser->layout == 'phone') {
1436
                $maxvisiblephotos = 1;
1437
            }
1438
            if ($showimage) {
1439
                $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $object->show_photos('product', Globals::$conf->product->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0) . '</div>';
1440
            } else {
1441
                if (!empty(Globals::$conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
1442
                    $nophoto = '';
1443
                    $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
1444
                }
1445
//elseif (Globals::$conf->browser->layout != 'phone') {    // Show no photo link
1446
                $nophoto = '/public/theme/common/nophoto.png';
1447
// $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="No photo" border="0"' . ($width ? ' width="' . $width . '"' : '') . ' src="' . DOL_BASE_URI . $nophoto . '"></div>';
1448
                $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="No photo" border="0"' . ($width ? ' width="' . $width . '"' : '') . ' src="' . DOL_BASE_URI . $nophoto . '"></div>';
1449
//}
1450
            }
1451
        } elseif ($object->element == 'ticket') {
1452
            $width = 80;
1453
            $cssclass = 'photoref';
1454
            $showimage = $object->is_photo_available(Globals::$conf->ticket->multidir_output[$entity] . '/' . $object->track_id);
1455
            $maxvisiblephotos = (isset(Globals::$conf->global->TICKETSUP_MAX_VISIBLE_PHOTO) ? Globals::$conf->global->TICKETSUP_MAX_VISIBLE_PHOTO : 2);
1456
            if (Globals::$conf->browser->layout == 'phone') {
1457
                $maxvisiblephotos = 1;
1458
            }
1459
            if ($showimage) {
1460
                $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $object->show_photos('ticket', Globals::$conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0) . '</div>';
1461
            } else {
1462
                if (!empty(Globals::$conf->global->TICKETSUP_NODISPLAYIFNOPHOTO)) {
1463
                    $nophoto = '';
1464
                    $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
1465
                }
1466
//elseif (Globals::$conf->browser->layout != 'phone') {    // Show no photo link
1467
                $nophoto = '/public/theme/common/nophoto.png';
1468
//$morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="No photo" border="0"' . ($width ? ' width="' . $width . '"' : '') . ' src="' . DOL_BASE_URI . $nophoto . '"></div>';
1469
                $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="No photo" border="0"' . ($width ? ' width="' . $width . '"' : '') . ' src="' . DOL_BASE_URI . $nophoto . '"></div>';
1470
//}
1471
            }
1472
        } else {
1473
            if ($showimage) {
1474
                if ($modulepart != 'unknown') {
1475
                    $phototoshow = '';
1476
                    // Check if a preview file is available
1477
                    if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
1478
                        $objectref = dol_sanitizeFileName($object->ref);
1479
                        $dir_output = (empty(Globals::$conf->$modulepart->multidir_output[$entity]) ? Globals::$conf->$modulepart->dir_output : Globals::$conf->$modulepart->multidir_output[$entity]) . "/";
1480
                        if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
1481
                            $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
1482
                            $subdir .= ((!empty($subdir) && !preg_match('/\/$/', $subdir)) ? '/' : '') . $objectref;  // the objectref dir is not included into get_exdir when used with level=2, so we add it at end
1483
                        } else {
1484
                            $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
1485
                        }
1486
                        if (empty($subdir))
1487
                            $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
1488
1489
                        $filepath = $dir_output . $subdir . "/";
1490
1491
                        $file = $filepath . $objectref . ".pdf";
1492
                        $relativepath = $subdir . '/' . $objectref . '.pdf';
1493
1494
                        // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
1495
                        $fileimage = $file . '_preview.png';              // If PDF has 1 page
1496
                        $fileimagebis = $file . '_preview-0.png';         // If PDF has more than one page
1497
                        $relativepathimage = $relativepath . '_preview.png';
1498
1499
                        // Si fichier PDF existe
1500
                        if (file_exists($file)) {
1501
                            $encfile = urlencode($file);
1502
                            // Conversion du PDF en image png si fichier png non existant
1503
                            if ((!file_exists($fileimage) || (filemtime($fileimage) < filemtime($file))) && (!file_exists($fileimagebis) || (filemtime($fileimagebis) < filemtime($file)))
1504
                            ) {
1505
                                if (empty(Globals::$conf->global->MAIN_DISABLE_PDF_THUMBS)) {  // If you experienc trouble with pdf thumb generation and imagick, you can disable here.
1506
                                    include_once DOL_BASE_PATH . '/core/lib/files.lib.php';
1507
                                    $ret = dol_convert_file($file, 'png', $fileimage);
1508
                                    if ($ret < 0)
1509
                                        $error++;
1510
                                }
1511
                            }
1512
1513
                            $heightforphotref = 70;
1514
                            if (!empty(Globals::$conf->dol_optimize_smallscreen))
1515
                                $heightforphotref = 60;
1516
                            // Si fichier png PDF d'1 page trouve
1517
                            if (file_exists($fileimage)) {
1518
                                $phototoshow = '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
1519
                                $phototoshow .= '<img height="' . $heightforphotref . '" class="photo photowithmargin photowithborder" src="' . DOL_BASE_URI . '/viewimage.php?modulepart=apercu' . $modulepart . '&amp;file=' . urlencode($relativepathimage) . '">';
1520
                                $phototoshow .= '</div></div>';
1521
                            }
1522
                            // Si fichier png PDF de plus d'1 page trouve
1523
                            elseif (file_exists($fileimagebis)) {
1524
                                $preview = preg_replace('/\.png/', '', $relativepathimage) . "-0.png";
1525
                                $phototoshow = '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
1526
                                $phototoshow .= '<img height="' . $heightforphotref . '" class="photo photowithmargin photowithborder" src="' . DOL_BASE_URI . '/viewimage.php?modulepart=apercu' . $modulepart . '&amp;file=' . urlencode($preview) . '"><p>';
1527
                                $phototoshow .= '</div></div>';
1528
                            }
1529
                        }
1530
                    } else if (!$phototoshow) {
1531
                        $phototoshow = $form->showphoto($modulepart, $object, 0, 0, 0, 'photoref', 'small', 1, 0, $maxvisiblephotos);
1532
                    }
1533
1534
                    if ($phototoshow) {
1535
                        $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
1536
                        $morehtmlleft .= $phototoshow;
1537
                        $morehtmlleft .= '</div>';
1538
                    }
1539
                }
1540
1541
                if (!$phototoshow) {      // Show No photo link (picto of pbject)
1542
                    $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
1543
                    if ($object->element == 'action') {
1544
                        $width = 80;
1545
                        $cssclass = 'photorefcenter';
1546
                        $nophoto = img_picto('', 'title_agenda', '', false, 1);
1547
                    } else {
1548
                        $width = 14;
1549
                        $cssclass = 'photorefcenter';
1550
                        $picto = $object->picto;
1551
                        if ($object->element == 'project' && !$object->public)
1552
                            $picto = 'project'; // instead of projectpub
1553
                        $nophoto = img_picto('', 'object_' . $picto, '', false, 1);
1554
                    }
1555
                    $morehtmlleft .= '<!-- No photo to show -->';
1556
                    $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="No photo" border="0"' . ($width ? ' width="' . $width . '"' : '') . ' src="' . $nophoto . '"></div></div>';
1557
1558
                    $morehtmlleft .= '</div>';
1559
                }
1560
            }
1561
        }
1562
1563
        if ($showbarcode)
1564
            $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">' . $form->showbarcode($object) . '</div>';
1565
1566
        if ($object->element == 'societe') {
1567
            if (!empty(Globals::$conf->use_javascript_ajax) && $user->rights->societe->creer && !empty(Globals::$conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1568
                $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
1569
            } else {
1570
                $morehtmlstatus .= $object->getLibStatut(6);
1571
            }
1572
        } elseif ($object->element == 'product') {
1573
//$morehtmlstatus.=Globals::$langs->trans("Status").' ('.Globals::$langs->trans("Sell").') ';
1574
            if (!empty(Globals::$conf->use_javascript_ajax) && $user->rights->produit->creer && !empty(Globals::$conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1575
                $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
1576
            } else {
1577
                $morehtmlstatus .= '<span class="statusrefsell">' . $object->getLibStatut(5, 0) . '</span>';
1578
            }
1579
            $morehtmlstatus .= ' &nbsp; ';
1580
//$morehtmlstatus.=Globals::$langs->trans("Status").' ('.Globals::$langs->trans("Buy").') ';
1581
            if (!empty(Globals::$conf->use_javascript_ajax) && $user->rights->produit->creer && !empty(Globals::$conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1582
                $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
1583
            } else {
1584
                $morehtmlstatus .= '<span class="statusrefbuy">' . $object->getLibStatut(5, 1) . '</span>';
1585
            }
1586
        } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan'))) {
1587
            $tmptxt = $object->getLibStatut(6, $object->totalpaye);
1588
            if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3) || Globals::$conf->browser->layout == 'phone')
1589
                $tmptxt = $object->getLibStatut(5, $object->totalpaye);
1590
            $morehtmlstatus .= $tmptxt;
1591
        }
1592
        elseif ($object->element == 'contrat' || $object->element == 'contract') {
1593
            if ($object->statut == 0)
1594
                $morehtmlstatus .= $object->getLibStatut(5);
1595
            else
1596
                $morehtmlstatus .= $object->getLibStatut(4);
1597
        }
1598
        elseif ($object->element == 'facturerec') {
1599
            if ($object->frequency == 0)
1600
                $morehtmlstatus .= $object->getLibStatut(2);
1601
            else
1602
                $morehtmlstatus .= $object->getLibStatut(5);
1603
        }
1604
        elseif ($object->element == 'project_task') {
1605
            $object->fk_statut = 1;
1606
            if ($object->progress > 0)
1607
                $object->fk_statut = 2;
1608
            if ($object->progress >= 100)
1609
                $object->fk_statut = 3;
1610
            $tmptxt = $object->getLibStatut(5);
1611
            $morehtmlstatus .= $tmptxt;  // No status on task
1612
        }
1613
        else { // Generic case
1614
            $tmptxt = $object->getLibStatut(6);
1615
            if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3) || Globals::$conf->browser->layout == 'phone')
1616
                $tmptxt = $object->getLibStatut(5);
1617
            $morehtmlstatus .= $tmptxt;
1618
        }
1619
1620
// Add if object was dispatched "into accountancy"
1621
        if (!empty(Globals::$conf->accounting->enabled) && in_array($object->element, array('bank', 'facture', 'invoice', 'invoice_supplier', 'expensereport'))) {
1622
            if (method_exists($object, 'getVentilExportCompta')) {
1623
                $accounted = $object->getVentilExportCompta();
1624
                Globals::$langs->load("accountancy");
1625
                $morehtmlstatus .= '</div><div class="statusref statusrefbis">' . ($accounted > 0 ? Globals::$langs->trans("Accounted") : Globals::$langs->trans("NotYetAccounted"));
1626
            }
1627
        }
1628
1629
// Add alias for thirdparty
1630
        if (!empty($object->name_alias))
1631
            $morehtmlref .= '<div class="refidno">' . $object->name_alias . '</div>';
1632
1633
// Add label
1634
        if ($object->element == 'product' || $object->element == 'bank_account' || $object->element == 'project_task') {
1635
            if (!empty($object->label))
1636
                $morehtmlref .= '<div class="refidno">' . $object->label . '</div>';
1637
        }
1638
1639
        if (method_exists($object, 'getBannerAddress') && $object->element != 'product' && $object->element != 'bookmark' && $object->element != 'ecm_directories' && $object->element != 'ecm_files') {
1640
            $morehtmlref .= '<div class="refidno">';
1641
            $morehtmlref .= $object->getBannerAddress('refaddress', $object);
1642
            $morehtmlref .= '</div>';
1643
        }
1644
        if (!empty(Globals::$conf->global->MAIN_SHOW_TECHNICAL_ID) && in_array($object->element, array('societe', 'contact', 'member', 'product'))) {
1645
            $morehtmlref .= '<div style="clear: both;"></div><div class="refidno">';
1646
            $morehtmlref .= Globals::$langs->trans("TechnicalID") . ': ' . $object->id;
1647
            $morehtmlref .= '</div>';
1648
        }
1649
1650
        print '<div class="' . ($onlybanner ? 'arearefnobottom ' : 'arearef ') . 'heightref valignmiddle" width="100%">';
1651
        print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
1652
        print '</div>';
1653
        print '<div class="underrefbanner clearboth"></div>';
1654
    }
1655
1656
    /**
1657
     * Show a string with the label tag dedicated to the HTML edit field.
1658
     *
1659
     * @param	string	$langkey		Translation key
1660
     * @param 	string	$fieldkey		Key of the html select field the text refers to
1661
     * @param	int		$fieldrequired	1=Field is mandatory
1662
     * @return string
1663
     * @deprecated Form::editfieldkey
1664
     */
1665
    static function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
1666
    {
1667
        // global Globals::$conf, Globals::$langs;
1668
        $ret = '';
1669
        if ($fieldrequired)
1670
            $ret .= '<span class="fieldrequired">';
1671
        if ((Globals::$conf->dol_use_jmobile != 4))
1672
            $ret .= '<label for="' . $fieldkey . '">';
1673
        $ret .= Globals::$langs->trans($langkey);
1674
        if ((Globals::$conf->dol_use_jmobile != 4))
1675
            $ret .= '</label>';
1676
        if ($fieldrequired)
1677
            $ret .= '</span>';
1678
        return $ret;
1679
    }
1680
1681
    /**
1682
     * Return string to add class property on html element with pair/impair.
1683
     *
1684
     * @param	string	$var			0 or 1
1685
     * @param	string	$moreclass		More class to add
1686
     * @return	string					String to add class onto HTML element
1687
     */
1688
    static function dol_bc($var, $moreclass = '')
1689
    {
1690
        // global $bc;
1691
        $ret = ' ' . $bc[$var];
1692
        if ($moreclass)
1693
            $ret = preg_replace('/class=\"/', 'class="' . $moreclass . ' ', $ret);
1694
        return $ret;
1695
    }
1696
1697
    /**
1698
     *      Return a formated address (part address/zip/town/state) according to country rules
1699
     *
1700
     *      @param  Object		$object			A company or contact object
1701
     * 	    @param	int			$withcountry		1=Add country into address string
1702
     *      @param	string		$sep				Separator to use to build string
1703
     *      @param	Translate	$outputlangs		Object lang that contains language for text translation.
1704
     *      @param	int		$mode		0=Standard output, 1=Remove address
1705
     *      @return string						Formated string
1706
     *      @see dol_print_address
1707
     */
1708
    static function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0)
1709
    {
1710
        // global Globals::$conf, Globals::$langs;
1711
1712
        $ret = '';
1713
        $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR');    // See also MAIN_FORCE_STATE_INTO_ADDRESS
1714
// Address
1715
        if (empty($mode)) {
1716
            $ret .= $object->address;
1717
        }
1718
// Zip/Town/State
1719
        if (in_array($object->country_code, array('AU', 'CA', 'US')) || !empty(Globals::$conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {    // US: title firstname name \n address lines \n town, state, zip \n country
1720
            $ret .= ($ret ? $sep : '' ) . $object->town;
1721
            if ($object->state) {
1722
                $ret .= ($ret ? ", " : '') . $object->state;
1723
            }
1724
            if ($object->zip)
1725
                $ret .= ($ret ? ", " : '') . $object->zip;
1726
        }
1727
        else if (in_array($object->country_code, array('GB', 'UK'))) { // UK: title firstname name \n address lines \n town state \n zip \n country
1728
            $ret .= ($ret ? $sep : '' ) . $object->town;
1729
            if ($object->state) {
1730
                $ret .= ($ret ? ", " : '') . $object->state;
1731
            }
1732
            if ($object->zip)
1733
                $ret .= ($ret ? $sep : '' ) . $object->zip;
1734
        }
1735
        else if (in_array($object->country_code, array('ES', 'TR'))) { // ES: title firstname name \n address lines \n zip town \n state \n country
1736
            $ret .= ($ret ? $sep : '' ) . $object->zip;
1737
            $ret .= ($object->town ? (($object->zip ? ' ' : '') . $object->town) : '');
1738
            if ($object->state) {
1739
                $ret .= "\n" . $object->state;
1740
            }
1741
        } else if (in_array($object->country_code, array('IT'))) { // IT: tile firstname name\n address lines \n zip (Code Departement) \n country
1742
            $ret .= ($ret ? $sep : '' ) . $object->zip;
1743
            $ret .= ($object->town ? (($object->zip ? ' ' : '') . $object->town) : '');
1744
            $ret .= ($object->departement_id ? (' (' . ($object->departement_id) . ')') : '');
1745
        } else {                                          // Other: title firstname name \n address lines \n zip town \n country
1746
            $ret .= $object->zip ? (($ret ? $sep : '' ) . $object->zip) : '';
1747
            $ret .= ($object->town ? (($object->zip ? ' ' : ($ret ? $sep : '' )) . $object->town) : '');
1748
            if ($object->state && in_array($object->country_code, $countriesusingstate)) {
1749
                $ret .= ($ret ? ", " : '') . $object->state;
1750
            }
1751
        }
1752
        if (!is_object($outputlangs))
1753
            $outputlangs = Globals::$langs;
1754
        if ($withcountry) {
1755
            Globals::$langs->load("dict");
1756
            $ret .= ($object->country_code ? ($ret ? $sep : '') . $outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country" . $object->country_code)) : '');
1757
        }
1758
1759
        return $ret;
1760
    }
1761
1762
    /**
1763
     * 	Format a string.
1764
     *
1765
     * 	@param	string	$fmt		Format of strftime static function (http://php.net/manual/fr/function.strftime.php)
1766
     *  @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)
1767
     *  @param	int		$is_gmt		See comment of timestamp parameter
1768
     * 	@return	string				A formatted string
1769
     */
1770
    static function dol_strftime($fmt, $ts = false, $is_gmt = false)
1771
    {
1772
        if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
1773
            return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts);
1774
        } else
1775
            return 'Error date into a not supported range';
1776
    }
1777
1778
    /**
1779
     * 	Output date in a string format according to outputlangs (or langs if not defined).
1780
     * 	Return charset is always UTF-8, except if encodetoouput is defined. In this case charset is output charset
1781
     *
1782
     * 	@param	int			$time			GM Timestamps date
1783
     * 	@param	string		$format      	Output date format (tag of strftime function)
1784
     * 										"%d %b %Y",
1785
     * 										"%d/%m/%Y %H:%M",
1786
     * 										"%d/%m/%Y %H:%M:%S",
1787
     *                                      "%B"=Long text of month, "%A"=Long text of day, "%b"=Short text of month, "%a"=Short text of day
1788
     * 										"day", "daytext", "dayhour", "dayhourldap", "dayhourtext", "dayrfc", "dayhourrfc", "...reduceformat"
1789
     * 	@param	string		$tzoutput		true or 'gmt' => string is for Greenwich location
1790
     * 										false or 'tzserver' => output string is for local PHP server TZ usage
1791
     * 										'tzuser' => output string is for user TZ (current browser TZ with current dst) => In a future, we should have same behaviour than 'tzuserrel'
1792
     *                                      'tzuserrel' => output string is for user TZ (current browser TZ with dst or not, depending on date position) (TODO not implemented yet)
1793
     * 	@param	Translate	$outputlangs	Object lang that contains language for text translation.
1794
     *  @param  boolean		$encodetooutput false=no convert into output pagecode
1795
     * 	@return string      				Formated date or '' if time is null
1796
     *
1797
     *  @see        dol_mktime, dol_stringtotime, dol_getdate
1798
     */
1799
    static function dol_print_date($time, $format = '', $tzoutput = 'tzserver', $outputlangs = '', $encodetooutput = false)
1800
    {
1801
        // global Globals::$conf, Globals::$langs;
1802
// Clean parameters
1803
        $to_gmt = false;
1804
        $offsettz = $offsetdst = 0;
1805
        if ($tzoutput) {
1806
            $to_gmt = true; // For backward compatibility
1807
            if (is_string($tzoutput)) {
1808
                if ($tzoutput == 'tzserver') {
1809
                    $to_gmt = false;
1810
                    $offsettzstring = @date_default_timezone_get();  // Example 'Europe/Berlin' or 'Indian/Reunion'
1811
                    $offsettz = 0;
1812
                    $offsetdst = 0;
1813
                } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
1814
                    $to_gmt = true;
1815
                    $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
1816
                    $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;  // Will not be used anymore
1817
                    $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
1818
                }
1819
            }
1820
        }
1821
        if (!is_object($outputlangs))
1822
            $outputlangs = Globals::$langs;
1823
        if (!$format)
1824
            $format = 'daytextshort';
1825
        $reduceformat = (!empty(Globals::$conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0;
1826
        $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
1827
        if ($formatwithoutreduce != $format) {
1828
            $format = $formatwithoutreduce;
1829
            $reduceformat = 1;
1830
        }  // so format 'dayreduceformat' is processed like day
1831
// Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
1832
// TODO Add format daysmallyear and dayhoursmallyear
1833
        if ($format == 'day')
1834
            $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : Globals::$conf->format_date_short);
1835
        else if ($format == 'hour')
1836
            $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : Globals::$conf->format_hour_short);
1837
        else if ($format == 'hourduration')
1838
            $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : Globals::$conf->format_hour_short_duration);
1839
        else if ($format == 'daytext')
1840
            $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : Globals::$conf->format_date_text);
1841
        else if ($format == 'daytextshort')
1842
// Notice: Undefined property: stdClass::$format_date_text_short in \alixar\dolibarr\htdocs\core\lib\functions.lib.php on line 1781
1843
            $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : Globals::$conf->format_date_text_short);
1844
        else if ($format == 'dayhour')
1845
            $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : Globals::$conf->format_date_hour_short ?? "%d/%m/%Y %H:%M");
1846
        else if ($format == 'dayhoursec')
1847
            $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : Globals::$conf->format_date_hour_sec_short);
1848
        else if ($format == 'dayhourtext')
1849
            $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : Globals::$conf->format_date_hour_text);
1850
        else if ($format == 'dayhourtextshort')
1851
            $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : Globals::$conf->format_date_hour_text_short);
1852
// Format not sensitive to language
1853
        else if ($format == 'dayhourlog')
1854
            $format = '%Y%m%d%H%M%S';
1855
        else if ($format == 'dayhourldap')
1856
            $format = '%Y%m%d%H%M%SZ';
1857
        else if ($format == 'dayhourxcard')
1858
            $format = '%Y%m%dT%H%M%SZ';
1859
        else if ($format == 'dayxcard')
1860
            $format = '%Y%m%d';
1861
        else if ($format == 'dayrfc')
1862
            $format = '%Y-%m-%d';             // DATE_RFC3339
1863
        else if ($format == 'dayhourrfc')
1864
            $format = '%Y-%m-%dT%H:%M:%SZ';   // DATETIME RFC3339
1865
        else if ($format == 'standard')
1866
            $format = '%Y-%m-%d %H:%M:%S';
1867
1868
        if ($reduceformat) {
1869
            $format = str_replace('%Y', '%y', $format);
1870
            $format = str_replace('yyyy', 'yy', $format);
1871
        }
1872
1873
// If date undefined or "", we return ""
1874
        if (DolUtils::dol_strlen($time) == 0)
1875
            return '';  // $time=0 allowed (it means 01/01/1970 00:00:00)
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
            
1920
// Clean format
1921
        if (preg_match('/%b/i', $format)) {  // There is some text to translate
1922
// We inhibate translation to text made by strftime functions. We will use trans instead later.
1923
            $format = str_replace('%b', '__b__', $format);
1924
            $format = str_replace('%B', '__B__', $format);
1925
        }
1926
        if (preg_match('/%a/i', $format)) {  // There is some text to translate
1927
// We inhibate translation to text made by strftime functions. We will use trans instead later.
1928
            $format = str_replace('%a', '__a__', $format);
1929
            $format = str_replace('%A', '__A__', $format);
1930
        }
1931
1932
// Analyze date
1933
        if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i', $time, $reg) || 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
1934
// TODO Remove this.
1935
// This part of code should not be used.
1936
            DolUtils::dol_syslog("Functions.lib::DolUtils::dol_print_date static function call with deprecated value of time in page " . $_SERVER["PHP_SELF"], LOG_ERR);
1937
// Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' or 'YYYYMMDDHHMMSS'
1938
            $syear = (!empty($reg[1]) ? $reg[1] : '');
1939
            $smonth = (!empty($reg[2]) ? $reg[2] : '');
1940
            $sday = (!empty($reg[3]) ? $reg[3] : '');
1941
            $shour = (!empty($reg[4]) ? $reg[4] : '');
1942
            $smin = (!empty($reg[5]) ? $reg[5] : '');
1943
            $ssec = (!empty($reg[6]) ? $reg[6] : '');
1944
1945
            $time = DolUtils::dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
1946
            $ret = adodb_strftime($format, $time + $offsettz + $offsetdst, $to_gmt);
1947
        } else {
1948
// Date is a timestamps
1949
            if ($time < 100000000000) { // Protection against bad date values
1950
                $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with static function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1951
1952
                $ret = adodb_strftime($format, $timetouse, $to_gmt);
1953
            } else
1954
                $ret = 'Bad value ' . $time . ' for date';
1955
        }
1956
1957
        if (preg_match('/__b__/i', $format)) {
1958
            $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with static function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1959
// Here ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs.
1960
            $month = adodb_strftime('%m', $timetouse);
1961
            $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
1962
            if ($encodetooutput) {
1963
                $monthtext = $outputlangs->transnoentities('Month' . $month);
1964
                $monthtextshort = $outputlangs->transnoentities('MonthShort' . $month);
1965
            } else {
1966
                $monthtext = $outputlangs->transnoentitiesnoconv('Month' . $month);
1967
                $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort' . $month);
1968
            }
1969
//print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
1970
            $ret = str_replace('__b__', $monthtextshort, $ret);
1971
            $ret = str_replace('__B__', $monthtext, $ret);
1972
//print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
1973
//return $ret;
1974
        }
1975
        if (preg_match('/__a__/i', $format)) {
1976
            $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with static function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1977
1978
            $w = adodb_strftime('%w', $timetouse);      // TODO Replace this with static function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1979
            $dayweek = $outputlangs->transnoentitiesnoconv('Day' . $w);
1980
            $ret = str_replace('__A__', $dayweek, $ret);
1981
            $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
1982
        }
1983
1984
        return $ret;
1985
    }
1986
1987
    /**
1988
     * 	Return an array with locale date info.
1989
     *  PHP getdate is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
1990
     *  WARNING: This static function always use PHP server timezone to return locale informations !!!
1991
     *  Usage must be avoid.
1992
     *  FIXME: Replace this with PHP date static function and a parameter $gm
1993
     *
1994
     * 	@param	int			$timestamp      Timestamp
1995
     * 	@param	boolean		$fast           Fast mode
1996
     * 	@return	array						Array of informations
1997
     * 										If no fast mode:
1998
     * 										'seconds' => $secs,
1999
     * 										'minutes' => $min,
2000
     * 										'hours' => $hour,
2001
     * 										'mday' => $day,
2002
     * 										'wday' => $dow,		0=sunday, 6=saturday
2003
     * 										'mon' => $month,
2004
     * 										'year' => $year,
2005
     * 										'yday' => floor($secsInYear/$_day_power),
2006
     * 										'weekday' => gmdate('l',$_day_power*(3+$dow)),
2007
     * 										'month' => gmdate('F',mktime(0,0,0,$month,2,1971)),
2008
     * 										If fast mode:
2009
     * 										'seconds' => $secs,
2010
     * 										'minutes' => $min,
2011
     * 										'hours' => $hour,
2012
     * 										'mday' => $day,
2013
     * 										'mon' => $month,
2014
     * 										'year' => $year,
2015
     * 										'yday' => floor($secsInYear/$_day_power),
2016
     * 										'leap' => $leaf,
2017
     * 										'ndays' => $ndays
2018
     * 	@see 								DolUtils::dol_print_date, dol_stringtotime, dol_mktime
2019
     */
2020
    static function dol_getdate($timestamp, $fast = false)
2021
    {
2022
        // global Globals::$conf;
2023
2024
        $usealternatemethod = false;
2025
        if ($timestamp <= 0)
2026
            $usealternatemethod = true;    // <= 1970
2027
        if ($timestamp >= 2145913200)
2028
            $usealternatemethod = true;  // >= 2038
2029
2030
        if ($usealternatemethod) {
2031
            $arrayinfo = adodb_getdate($timestamp, $fast);
2032
        } else {
2033
            $arrayinfo = getdate($timestamp);
2034
        }
2035
2036
        return $arrayinfo;
2037
    }
2038
2039
    /**
2040
     * 	Return a timestamp date built from detailed informations (by default a local PHP server timestamp)
2041
     * 	Replace static function mktime not available under Windows if year < 1970
2042
     * 	PHP mktime is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
2043
     *
2044
     * 	@param	int			$hour			Hour	(can be -1 for undefined)
2045
     * 	@param	int			$minute			Minute	(can be -1 for undefined)
2046
     * 	@param	int			$second			Second	(can be -1 for undefined)
2047
     * 	@param	int			$month			Month (1 to 12)
2048
     * 	@param	int			$day			Day (1 to 31)
2049
     * 	@param	int			$year			Year
2050
     * 	@param	mixed		$gm				True or 1 or 'gmt'=Input informations are GMT values
2051
     * 										False or 0 or 'server' = local to server TZ
2052
     * 										'user' = local to user TZ
2053
     * 										'tz,TimeZone' = use specified timezone
2054
     * 	@param	int			$check			0=No check on parameters (Can use day 32, etc...)
2055
     * 	@return	int|string					Date as a timestamp, '' or false if error
2056
     * 	@see 								DolUtils::dol_print_date, dol_stringtotime, dol_getdate
2057
     */
2058
    static function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = false, $check = 1)
2059
    {
2060
        // global Globals::$conf;
2061
//print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
2062
// Clean parameters
2063
        if ($hour == -1 || empty($hour))
2064
            $hour = 0;
2065
        if ($minute == -1 || empty($minute))
2066
            $minute = 0;
2067
        if ($second == -1 || empty($second))
2068
            $second = 0;
2069
2070
// Check parameters
2071
        if ($check) {
2072
            if (!$month || !$day)
2073
                return '';
2074
            if ($day > 31)
2075
                return '';
2076
            if ($month > 12)
2077
                return '';
2078
            if ($hour < 0 || $hour > 24)
2079
                return '';
2080
            if ($minute < 0 || $minute > 60)
2081
                return '';
2082
            if ($second < 0 || $second > 60)
2083
                return '';
2084
        }
2085
2086
        if (method_exists('DateTime', 'getTimestamp')) {
2087
            if (empty($gm) || $gm === 'server') {
2088
                $default_timezone = @date_default_timezone_get();  // Example 'Europe/Berlin'
2089
                $localtz = new \DateTimeZone($default_timezone);
2090
            } else if ($gm === 'user') {
2091
// We use dol_tz_string first because it is more reliable.
2092
                $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]);  // Example 'Europe/Berlin'
2093
                try {
2094
                    $localtz = new \DateTimeZone($default_timezone);
2095
                } catch (Exception $e) {
2096
                    DolUtils::dol_syslog("Warning dol_tz_string contains an invalid value " . $_SESSION["dol_tz_string"], LOG_WARNING);
2097
                    $default_timezone = @date_default_timezone_get();
2098
                }
2099
            } else if (strrpos($gm, "tz,") !== false) {
2100
                $timezone = str_replace("tz,", "", $gm);  // Example 'tz,Europe/Berlin'
2101
                try {
2102
                    $localtz = new \DateTimeZone($timezone);
2103
                } catch (Exception $e) {
2104
                    DolUtils::dol_syslog("Warning passed timezone contains an invalid value " . $timezone, LOG_WARNING);
2105
                }
2106
            }
2107
2108
            if (empty($localtz)) {
2109
                $localtz = new \DateTimeZone('UTC');
2110
            }
2111
//var_dump($localtz);
2112
//var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
2113
            $dt = new \DateTime(null, $localtz);
2114
            $dt->setDate($year, $month, $day);
2115
            $dt->setTime((int) $hour, (int) $minute, (int) $second);
2116
            $date = $dt->getTimestamp(); // should include daylight saving time
2117
//var_dump($date);
2118
            return $date;
2119
        } else {
2120
            dol_print_error('', 'PHP version must be 5.4+');
2121
            return '';
2122
        }
2123
    }
2124
2125
    /**
2126
     * 	Return date for now. In most cases, we use this static function without parameters (that means GMT time).
2127
     *
2128
     * 	@param	string		$mode	'gmt' => we return GMT timestamp,
2129
     * 								'tzserver' => we add the PHP server timezone
2130
     *  							'tzref' => we add the company timezone
2131
     * 								'tzuser' => we add the user timezone
2132
     * 	@return int   $date	Timestamp
2133
     */
2134
    static function dol_now($mode = 'gmt')
2135
    {
2136
        $ret = 0;
2137
2138
// Note that gmmktime and mktime return same value (GMT) when used without parameters
2139
//if ($mode == 'gmt') $ret=gmmktime(); // Strict Standards: gmmktime(): You should be using the time() static function instead
2140
        if ($mode == 'gmt')
2141
            $ret = time(); // Time for now at greenwich.
2142
        else if ($mode == 'tzserver') {  // Time for now with PHP server timezone added
2143
            require_once DOL_BASE_PATH . '/core/lib/date.lib.php';
2144
            $tzsecond = getServerTimeZoneInt('now');    // Contains tz+dayling saving time
2145
            $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
2146
        }
2147
        /* else if ($mode == 'tzref')				// Time for now with parent company timezone is added
2148
          {
2149
          require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2150
          $tzsecond=getParentCompanyTimeZoneInt();    // Contains tz+dayling saving time
2151
          $ret=dol_now('gmt')+($tzsecond*3600);
2152
          } */ else if ($mode == 'tzuser') {    // Time for now with user timezone added
2153
//print 'time: '.time().'-'.mktime().'-'.gmmktime();
2154
            $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
2155
            $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
2156
            $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
2157
        }
2158
2159
        return $ret;
2160
    }
2161
2162
    /**
2163
     * Return string with formated size
2164
     *
2165
     * @param	int		$size		Size to print
2166
     * @param	int		$shortvalue	Tell if we want long value to use another unit (Ex: 1.5Kb instead of 1500b)
2167
     * @param	int		$shortunit	Use short label of size unit (for example 'b' instead of 'bytes')
2168
     * @return	string				Link
2169
     */
2170
    static function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
2171
    {
2172
        // global Globals::$conf, Globals::$langs;
2173
        $level = 1024;
2174
2175
        if (!empty(Globals::$conf->dol_optimize_smallscreen))
2176
            $shortunit = 1;
2177
2178
// Set value text
2179
        if (empty($shortvalue) || $size < ($level * 10)) {
2180
            $ret = $size;
2181
            $textunitshort = Globals::$langs->trans("b");
2182
            $textunitlong = Globals::$langs->trans("Bytes");
2183
        } else {
2184
            $ret = round($size / $level, 0);
2185
            $textunitshort = Globals::$langs->trans("Kb");
2186
            $textunitlong = Globals::$langs->trans("KiloBytes");
2187
        }
2188
// Use long or short text unit
2189
        if (empty($shortunit)) {
2190
            $ret .= ' ' . $textunitlong;
2191
        } else {
2192
            $ret .= ' ' . $textunitshort;
2193
        }
2194
2195
        return $ret;
2196
    }
2197
2198
    /**
2199
     * Show Url link
2200
     *
2201
     * @param	string		$url		Url to show
2202
     * @param	string		$target		Target for link
2203
     * @param	int			$max		Max number of characters to show
2204
     * @param	int			$withpicto	With picto
2205
     * @return	string					HTML Link
2206
     */
2207
    static function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0)
2208
    {
2209
        // global Globals::$langs;
2210
2211
        if (empty($url))
2212
            return '';
2213
2214
        $link = '<a href="';
2215
        if (!preg_match('/^http/i', $url))
2216
            $link .= 'http://';
2217
        $link .= $url;
2218
        $link .= '"';
2219
        if ($target)
2220
            $link .= ' target="' . $target . '"';
2221
        $link .= '>';
2222
        if (!preg_match('/^http/i', $url))
2223
            $link .= 'http://';
2224
        $link .= dol_trunc($url, $max);
2225
        $link .= '</a>';
2226
        return '<div class="nospan float" style="margin-right: 10px">' . ($withpicto ? img_picto(Globals::$langs->trans("Url"), 'object_globe.png') . ' ' : '') . $link . '</div>';
2227
    }
2228
2229
    /**
2230
     * Show EMail link
2231
     *
2232
     * @param	string		$email			EMail to show (only email, without 'Name of recipient' before)
2233
     * @param 	int			$cid 			Id of contact if known
2234
     * @param 	int			$socid 			Id of third party if known
2235
     * @param 	int			$addlink		0=no link, 1=email has a html email link (+ link to create action if constant AGENDA_ADDACTIONFOREMAIL is on)
2236
     * @param	int			$max			Max number of characters to show
2237
     * @param	int			$showinvalid	Show warning if syntax email is wrong
2238
     * @param	int			$withpicto		Show picto
2239
     * @return	string						HTML Link
2240
     */
2241
    static function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
2242
    {
2243
        // global Globals::$conf, $user, Globals::$langs, Globals::$hookManager;
2244
2245
        $newemail = $email;
2246
2247
        if (empty($email))
2248
            return '&nbsp;';
2249
2250
        if (!empty($addlink)) {
2251
            $newemail = '<a style="text-overflow: ellipsis;" href="';
2252
            if (!preg_match('/^mailto:/i', $email))
2253
                $newemail .= 'mailto:';
2254
            $newemail .= $email;
2255
            $newemail .= '">';
2256
            $newemail .= dol_trunc($email, $max);
2257
            $newemail .= '</a>';
2258
            if ($showinvalid && !isValidEmail($email)) {
2259
                Globals::$langs->load("errors");
2260
                $newemail .= img_warning(Globals::$langs->trans("ErrorBadEMail", $email));
2261
            }
2262
2263
            if (($cid || $socid) && !empty(Globals::$conf->agenda->enabled) && $user->rights->agenda->myactions->create) {
2264
                $type = 'AC_EMAIL';
2265
                $link = '';
2266
                if (!empty(Globals::$conf->global->AGENDA_ADDACTIONFOREMAIL))
2267
                    $link = '<a href="' . DOL_BASE_URI . '/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode=' . $type . '&amp;contactid=' . $cid . '&amp;socid=' . $socid . '">' . img_object(Globals::$langs->trans("AddAction"), "calendar") . '</a>';
2268
                if ($link)
2269
                    $newemail = '<div>' . $newemail . ' ' . $link . '</div>';
2270
            }
2271
        }
2272
        else {
2273
            if ($showinvalid && !isValidEmail($email)) {
2274
                Globals::$langs->load("errors");
2275
                $newemail .= img_warning(Globals::$langs->trans("ErrorBadEMail", $email));
2276
            }
2277
        }
2278
2279
        $rep = '<div class="nospan float" style="margin-right: 10px">' . ($withpicto ? img_picto(Globals::$langs->trans("EMail"), 'object_email.png') . ' ' : '') . $newemail . '</div>';
2280
        if (Globals::$hookManager) {
2281
            $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
2282
            $reshook = Globals::$hookManager->executeHooks('printEmail', $parameters, $email);
2283
            $rep .= Globals::$hookManager->resPrint;
2284
        }
2285
2286
        return $rep;
2287
    }
2288
2289
    /**
2290
     * Show social network link
2291
     *
2292
     * @param	string		$value			Skype to show (only skype, without 'Name of recipient' before)
2293
     * @param	int 		$cid 			Id of contact if known
2294
     * @param	int 		$socid 			Id of third party if known
2295
     * @param	string 		$type			'skype','facebook',...
2296
     * @return	string						HTML Link
2297
     */
2298
    static function dol_print_socialnetworks($value, $cid, $socid, $type)
2299
    {
2300
        // global Globals::$conf, $user, Globals::$langs;
2301
2302
        $newskype = $value;
2303
2304
        if (empty($value))
2305
            return '&nbsp;';
2306
2307
        if (!empty($type)) {
2308
            $newskype = '<div class="divsocialnetwork inline-block valignmiddle">';
2309
            $newskype .= img_picto(Globals::$langs->trans(strtoupper($type)), $type . '.png', '', false, 0, 0, '', 'paddingright');
2310
            $newskype .= $value;
2311
            if ($type == 'skype') {
2312
                $newskype .= '&nbsp;';
2313
                $newskype .= '<a href="skype:';
2314
                $newskype .= $value;
2315
                $newskype .= '?call" alt="' . Globals::$langs->trans("Call") . '&nbsp;' . $value . '" title="' . Globals::$langs->trans("Call") . '&nbsp;' . $value . '">';
2316
                $newskype .= '<img src="' . DOL_BASE_URI . '/theme/common/skype_callbutton.png" border="0">';
2317
                $newskype .= '</a><a href="skype:';
2318
                $newskype .= $value;
2319
                $newskype .= '?chat" alt="' . Globals::$langs->trans("Chat") . '&nbsp;' . $value . '" title="' . Globals::$langs->trans("Chat") . '&nbsp;' . $value . '">';
2320
                $newskype .= '<img class="paddingleft" src="' . DOL_BASE_URI . '/theme/common/skype_chatbutton.png" border="0">';
2321
                $newskype .= '</a>';
2322
            }
2323
            if (($cid || $socid) && !empty(Globals::$conf->agenda->enabled) && $user->rights->agenda->myactions->create && $type == 'skype') {
2324
                $addlink = 'AC_SKYPE';
2325
                $link = '';
2326
                if (!empty(Globals::$conf->global->AGENDA_ADDACTIONFORSKYPE))
2327
                    $link = '<a href="' . DOL_BASE_URI . '/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode=' . $addlink . '&amp;contactid=' . $cid . '&amp;socid=' . $socid . '">' . img_object(Globals::$langs->trans("AddAction"), "calendar") . '</a>';
2328
                $newskype .= ($link ? ' ' . $link : '');
2329
            }
2330
            $newskype .= '</div>';
2331
        }
2332
        else {
2333
            Globals::$langs->load("errors");
2334
            $newskype .= img_warning(Globals::$langs->trans("ErrorBadSocialNetworkValue", $value));
2335
        }
2336
        return $newskype;
2337
    }
2338
2339
    /**
2340
     * 	Format phone numbers according to country
2341
     *
2342
     * 	@param  string  $phone          Phone number to format
2343
     * 	@param  string  $countrycode    Country code to use for formatting
2344
     * 	@param 	int		$cid 		    Id of contact if known
2345
     * 	@param 	int		$socid          Id of third party if known
2346
     * 	@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)
2347
     * 	@param 	string	$separ 		    Separation between numbers for a better visibility example : xx.xx.xx.xx.xx
2348
     *  @param	string  $withpicto      Show picto
2349
     *  @param	string	$titlealt	    Text to show on alt
2350
     *  @param  int     $adddivfloat    Add div float around phone.
2351
     * 	@return string 				    Formated phone number
2352
     */
2353
    static function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
2354
    {
2355
        // global Globals::$conf, $user, Globals::$langs, $mysoc, Globals::$hookManager;
2356
// Clean phone parameter
2357
        $phone = preg_replace("/[\s.-]/", "", trim($phone));
2358
        if (empty($phone)) {
2359
            return '';
2360
        }
2361
        if (empty($countrycode))
2362
            $countrycode = $mysoc->country_code;
2363
2364
// Short format for small screens
2365
        if (Globals::$conf->dol_optimize_smallscreen)
2366
            $separ = '';
2367
2368
        $newphone = $phone;
2369
        if (strtoupper($countrycode) == "FR") {
2370
// France
2371
            if (DolUtils::dol_strlen($phone) == 10) {
2372
                $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);
2373
            } elseif (DolUtils::dol_strlen($phone) == 7) {
2374
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 2);
2375
            } elseif (DolUtils::dol_strlen($phone) == 9) {
2376
                $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 2) . $separ . substr($newphone, 7, 2);
2377
            } elseif (DolUtils::dol_strlen($phone) == 11) {
2378
                $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);
2379
            } elseif (DolUtils::dol_strlen($phone) == 12) {
2380
                $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);
2381
            }
2382
        } elseif (strtoupper($countrycode) == "CA") {
2383
            if (DolUtils::dol_strlen($phone) == 10) {
2384
                $newphone = ($separ != '' ? '(' : '') . substr($newphone, 0, 3) . ($separ != '' ? ')' : '') . $separ . substr($newphone, 3, 3) . ($separ != '' ? '-' : '') . substr($newphone, 6, 4);
2385
            }
2386
        } elseif (strtoupper($countrycode) == "PT") {//Portugal
2387
            if (DolUtils::dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
2388
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
2389
            }
2390
        } elseif (strtoupper($countrycode) == "SR") {//Suriname
2391
            if (DolUtils::dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
2392
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3);
2393
            } elseif (DolUtils::dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
2394
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 4);
2395
            }
2396
        } elseif (strtoupper($countrycode) == "DE") {//Allemagne
2397
            if (DolUtils::dol_strlen($phone) == 14) {//ex:  +49_ABCD_EFGH_IJK
2398
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 4) . $separ . substr($newphone, 11, 3);
2399
            } elseif (DolUtils::dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
2400
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 4) . $separ . substr($newphone, 10, 3);
2401
            }
2402
        } elseif (strtoupper($countrycode) == "ES") {//Espagne
2403
            if (DolUtils::dol_strlen($phone) == 12) {//ex:  +34_ABC_DEF_GHI
2404
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
2405
            }
2406
        } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
2407
            if (DolUtils::dol_strlen($phone) == 12) {//ex :  +22 A BC_DE_FG_HI
2408
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 1) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
2409
            }
2410
        } elseif (strtoupper($countrycode) == "RO") {// Roumanie
2411
            if (DolUtils::dol_strlen($phone) == 12) {//ex :  +40 AB_CDE_FG_HI
2412
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
2413
            }
2414
        } elseif (strtoupper($countrycode) == "TR") {//Turquie
2415
            if (DolUtils::dol_strlen($phone) == 13) {//ex :  +90 ABC_DEF_GHIJ
2416
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
2417
            }
2418
        } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
2419
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
2420
                $newphone = substr($newphone, 0, 2) . $separ . substr($newphone, 2, 3) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 4);
2421
            }
2422
        } elseif (strtoupper($countrycode) == "MX") {//Mexique
2423
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
2424
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
2425
            } elseif (DolUtils::dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
2426
                $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);
2427
            } elseif (DolUtils::dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
2428
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 4);
2429
            }
2430
        } elseif (strtoupper($countrycode) == "ML") {//Mali
2431
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
2432
                $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);
2433
            }
2434
        } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
2435
            if (DolUtils::dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
2436
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
2437
            } elseif (DolUtils::dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
2438
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 1) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 3);
2439
            }
2440
        } elseif (strtoupper($countrycode) == "MU") {//Maurice
2441
            if (DolUtils::dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
2442
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2);
2443
            } elseif (DolUtils::dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
2444
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
2445
            }
2446
        } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
2447
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
2448
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
2449
            }
2450
        } elseif (strtoupper($countrycode) == "SY") {//Syrie
2451
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
2452
                $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);
2453
            } elseif (DolUtils::dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
2454
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 3);
2455
            }
2456
        } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
2457
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
2458
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 2);
2459
            } elseif (DolUtils::dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
2460
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
2461
            } elseif (DolUtils::dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
2462
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 4);
2463
            }
2464
        } elseif (strtoupper($countrycode) == "DZ") {//Algérie
2465
            if (DolUtils::dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
2466
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
2467
            }
2468
        } elseif (strtoupper($countrycode) == "BE") {//Belgique
2469
            if (DolUtils::dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
2470
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 3);
2471
            } elseif (DolUtils::dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
2472
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
2473
            }
2474
        } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
2475
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
2476
                $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);
2477
            }
2478
        } elseif (strtoupper($countrycode) == "CO") {//Colombie
2479
            if (DolUtils::dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
2480
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 2) . $separ . substr($newphone, 11, 2);
2481
            }
2482
        } elseif (strtoupper($countrycode) == "JO") {//Jordanie
2483
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
2484
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 1) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2);
2485
            }
2486
        } elseif (strtoupper($countrycode) == "MG") {//Madagascar
2487
            if (DolUtils::dol_strlen($phone) == 13) {//ex: +261_AB_CD_EF_GHI
2488
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 2) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 3);
2489
            }
2490
        } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
2491
            if (DolUtils::dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
2492
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 4) . $separ . substr($newphone, 7, 3) . $separ . substr($newphone, 10, 3);
2493
            }
2494
        } elseif (strtoupper($countrycode) == "CH") {//Suisse
2495
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
2496
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 2) . $separ . substr($newphone, 10, 2);
2497
            } elseif (DolUtils::dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
2498
                $newphone = $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 2) . $separ . substr($newphone, 5, 3) . $separ . substr($newphone, 8, 3) . $separ . substr($newphone, 11, 4);
2499
            }
2500
        } elseif (strtoupper($countrycode) == "TN") {//Tunisie
2501
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
2502
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 2) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
2503
            }
2504
        } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
2505
            if (DolUtils::dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI  (ABC=594 de nouveau)
2506
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2) . $separ . substr($newphone, 11, 2);
2507
            }
2508
        } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
2509
            if (DolUtils::dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI  (ABC=590 de nouveau)
2510
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2) . $separ . substr($newphone, 11, 2);
2511
            }
2512
        } elseif (strtoupper($countrycode) == "MQ") {//Martinique
2513
            if (DolUtils::dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI  (ABC=596 de nouveau)
2514
                $newphone = substr($newphone, 0, 4) . $separ . substr($newphone, 4, 3) . $separ . substr($newphone, 7, 2) . $separ . substr($newphone, 9, 2) . $separ . substr($newphone, 11, 2);
2515
            }
2516
        } elseif (strtoupper($countrycode) == "IT") {//Italie
2517
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
2518
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 3);
2519
            } elseif (DolUtils::dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
2520
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 3) . $separ . substr($newphone, 6, 3) . $separ . substr($newphone, 9, 2) . $separ . substr($newphone, 11, 2);
2521
            }
2522
        } elseif (strtoupper($countrycode) == "AU") {//Australie
2523
            if (DolUtils::dol_strlen($phone) == 12) {//ex: +61_A_BCDE_FGHI
2524
                $newphone = substr($newphone, 0, 3) . $separ . substr($newphone, 3, 1) . $separ . substr($newphone, 4, 4) . $separ . substr($newphone, 8, 4);
2525
            }
2526
        }
2527
        if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
2528
            if (Globals::$conf->browser->layout == 'phone' || (!empty(Globals::$conf->clicktodial->enabled) && !empty(Globals::$conf->global->CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS))) { // If phone or option for, we use link of phone
2529
                $newphone = '<a href="tel:' . $phone . '"';
2530
                $newphone .= '>' . $phone . '</a>';
2531
            } else if (!empty(Globals::$conf->clicktodial->enabled) && $addlink == 'AC_TEL') {  // If click to dial, we use click to dial url
2532
                if (empty($user->clicktodial_loaded))
2533
                    $user->fetch_clicktodial();
2534
2535
// Define urlmask
2536
                $urlmask = 'ErrorClickToDialModuleNotConfigured';
2537
                if (!empty(Globals::$conf->global->CLICKTODIAL_URL))
2538
                    $urlmask = Globals::$conf->global->CLICKTODIAL_URL;
2539
                if (!empty($user->clicktodial_url))
2540
                    $urlmask = $user->clicktodial_url;
2541
2542
                $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
2543
                $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
2544
                $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
2545
// This line is for backward compatibility
2546
                $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
2547
// Thoose lines are for substitution
2548
                $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
2549
                    '__PHONETO__' => urlencode($phone),
2550
                    '__LOGIN__' => $clicktodial_login,
2551
                    '__PASS__' => $clicktodial_password);
2552
                $url = make_substitutions($url, $substitarray);
2553
                $newphonesav = $newphone;
2554
                $newphone = '<a href="' . $url . '"';
2555
                if (!empty(Globals::$conf->global->CLICKTODIAL_FORCENEWTARGET))
2556
                    $newphone .= ' target="_blank"';
2557
                $newphone .= '>' . $newphonesav . '</a>';
2558
            }
2559
2560
//if (($cid || $socid) && ! empty(Globals::$conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2561
            if (!empty(Globals::$conf->agenda->enabled) && $user->rights->agenda->myactions->create) {
2562
                $type = 'AC_TEL';
2563
                $link = '';
2564
                if ($addlink == 'AC_FAX')
2565
                    $type = 'AC_FAX';
2566
                if (!empty(Globals::$conf->global->AGENDA_ADDACTIONFORPHONE))
2567
                    $link = '<a href="' . DOL_BASE_URI . '/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode=' . $type . ($cid ? '&amp;contactid=' . $cid : '') . ($socid ? '&amp;socid=' . $socid : '') . '">' . img_object(Globals::$langs->trans("AddAction"), "calendar") . '</a>';
2568
                if ($link)
2569
                    $newphone = '<div>' . $newphone . ' ' . $link . '</div>';
2570
            }
2571
        }
2572
2573
        if (empty($titlealt)) {
2574
            $titlealt = ($withpicto == 'fax' ? Globals::$langs->trans("Fax") : Globals::$langs->trans("Phone"));
2575
        }
2576
        $rep = '';
2577
2578
        if (Globals::$hookManager) {
2579
            $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
2580
            $reshook = Globals::$hookManager->executeHooks('printPhone', $parameters, $phone);
2581
            $rep .= Globals::$hookManager->resPrint;
2582
        }
2583
        if (empty($reshook)) {
2584
            $picto = '';
2585
            if ($withpicto) {
2586
                if ($withpicto == 'fax') {
2587
                    $picto = 'phoning_fax';
2588
                } elseif ($withpicto == 'phone') {
2589
                    $picto = 'phoning';
2590
                } elseif ($withpicto == 'mobile') {
2591
                    $picto = 'phoning_mobile';
2592
                } else {
2593
                    $picto = '';
2594
                }
2595
            }
2596
            if ($adddivfloat)
2597
                $rep .= '<div class="nospan float" style="margin-right: 10px">';
2598
            else
2599
                $rep .= '<span style="margin-right: 10px;">';
2600
            $rep .= ($withpicto ? img_picto($titlealt, 'object_' . $picto . '.png') . ' ' : '') . $newphone;
2601
            if ($adddivfloat)
2602
                $rep .= '</div>';
2603
            else
2604
                $rep .= '</span>';
2605
        }
2606
2607
        return $rep;
2608
    }
2609
2610
    /**
2611
     * 	Return an IP formated to be shown on screen
2612
     *
2613
     * 	@param	string	$ip			IP
2614
     * 	@param	int		$mode		0=return IP + country/flag, 1=return only country/flag, 2=return only IP
2615
     * 	@return string 				Formated IP, with country if GeoIP module is enabled
2616
     */
2617
    static function dol_print_ip($ip, $mode = 0)
2618
    {
2619
        // global Globals::$conf, Globals::$langs;
2620
2621
        $ret = '';
2622
2623
        if (empty($mode)) {
2624
            $ret .= $ip;
2625
        }
2626
2627
        echo ('<p>DOL_BASE_PATH: ' . DOL_BASE_PATH . '/DOL_BASE_URI: ' . DOL_BASE_URI . '</p>');
2628
2629
        if ($mode != 2) {
2630
            $countrycode = dolGetCountryCodeFromIp($ip);
2631
            if ($countrycode) { // If success, countrycode is us, fr, ...
2632
                if (file_exists(DOL_BASE_PATH . '/theme/common/flags/' . $countrycode . '.png')) {
2633
                    $ret .= ' ' . img_picto($countrycode . ' ' . Globals::$langs->trans("AccordingToGeoIPDatabase"), DOL_BASE_URI . '/theme/common/flags/' . $countrycode . '.png', '', 1);
2634
                } else
2635
                    $ret .= ' (' . $countrycode . ')';
2636
            }
2637
        }
2638
2639
        return $ret;
2640
    }
2641
2642
    /**
2643
     * Return the IP of remote user.
2644
     * Take HTTP_X_FORWARDED_FOR (defined when using proxy)
2645
     * Then HTTP_CLIENT_IP if defined (rare)
2646
     * Then REMOTE_ADDR (not way to be modified by user but may be wrong if using proxy)
2647
     *
2648
     * @return	string		Ip of remote user.
2649
     */
2650
    static function getUserRemoteIP()
2651
    {
2652
        $ip = empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? (empty($_SERVER['HTTP_CLIENT_IP']) ? (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']) : $_SERVER['HTTP_CLIENT_IP']) : $_SERVER['HTTP_X_FORWARDED_FOR'];
2653
        return $ip;
2654
    }
2655
2656
    /**
2657
     * 	Return a country code from IP. Empty string if not found.
2658
     *
2659
     * 	@param	string	$ip			IP
2660
     * 	@return string 				Country code ('us', 'fr', ...)
2661
     */
2662
    static function dolGetCountryCodeFromIp($ip)
2663
    {
2664
        // global Globals::$conf;
2665
2666
        $countrycode = '';
2667
2668
        if (!empty(Globals::$conf->geoipmaxmind->enabled)) {
2669
            $datafile = Globals::$conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
2670
//$ip='24.24.24.24';
2671
//$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)
2672
2673
            include_once DOL_BASE_PATH . '/core/class/dolgeoip.class.php';
2674
            $geoip = new DolGeoIP('country', $datafile);
2675
//print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
2676
//print "geoip_country_id_by_addr=".geoip_country_id_by_addr($geoip->gi,$ip)."\n";
2677
            $countrycode = $geoip->getCountryCodeFromIP($ip);
2678
        }
2679
2680
        return $countrycode;
2681
    }
2682
2683
    /**
2684
     *  Return country code for current user.
2685
     *  If software is used inside a local network, detection may fails (we need a public ip)
2686
     *
2687
     *  @return     string      Country code (fr, es, it, us, ...)
2688
     */
2689
    static function dol_user_country()
2690
    {
2691
        // global Globals::$conf, Globals::$langs, $user;
2692
//$ret=$user->xxx;
2693
        $ret = '';
2694
        if (!empty(Globals::$conf->geoipmaxmind->enabled)) {
2695
            $ip = getUserRemoteIP();
2696
            $datafile = Globals::$conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
2697
//$ip='24.24.24.24';
2698
//$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
2699
            include_once DOL_BASE_PATH . '/core/class/dolgeoip.class.php';
2700
            $geoip = new DolGeoIP('country', $datafile);
2701
            $countrycode = $geoip->getCountryCodeFromIP($ip);
2702
            $ret = $countrycode;
2703
        }
2704
        return $ret;
2705
    }
2706
2707
    /**
2708
     *  Format address string
2709
     *
2710
     *  @param	string	$address    Address
2711
     *  @param  int		$htmlid     Html ID (for example 'gmap')
2712
     *  @param  int		$mode       thirdparty|contact|member|other
2713
     *  @param  int		$id         Id of object
2714
     *  @param	int		$noprint	No output. Result is the static function return
2715
     *  @param  string  $charfornl  Char to use instead of nl2br. '' means we use a standad nl2br.
2716
     *  @return string|void			Nothing if noprint is 0, formatted address if noprint is 1
2717
     *  @see dol_format_address
2718
     */
2719
    static function dol_print_address($address, $htmlid, $mode, $id, $noprint = 0, $charfornl = '')
2720
    {
2721
        // global Globals::$conf, $user, Globals::$langs, Globals::$hookManager;
2722
2723
        $out = '';
2724
2725
        if ($address) {
2726
            if (Globals::$hookManager) {
2727
                $parameters = array('element' => $mode, 'id' => $id);
2728
                $reshook = Globals::$hookManager->executeHooks('printAddress', $parameters, $address);
2729
                $out .= Globals::$hookManager->resPrint;
2730
            }
2731
            if (empty($reshook)) {
2732
                if (empty($charfornl))
2733
                    $out .= nl2br($address);
2734
                else
2735
                    $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
2736
2737
                $showgmap = $showomap = 0;
2738
2739
// TODO Add a hook here
2740
                if (($mode == 'thirdparty' || $mode == 'societe') && !empty(Globals::$conf->google->enabled) && !empty(Globals::$conf->global->GOOGLE_ENABLE_GMAPS))
2741
                    $showgmap = 1;
2742
                if ($mode == 'contact' && !empty(Globals::$conf->google->enabled) && !empty(Globals::$conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS))
2743
                    $showgmap = 1;
2744
                if ($mode == 'member' && !empty(Globals::$conf->google->enabled) && !empty(Globals::$conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS))
2745
                    $showgmap = 1;
2746
                if (($mode == 'thirdparty' || $mode == 'societe') && !empty(Globals::$conf->openstreetmap->enabled) && !empty(Globals::$conf->global->OPENSTREETMAP_ENABLE_MAPS))
2747
                    $showomap = 1;
2748
                if ($mode == 'contact' && !empty(Globals::$conf->openstreetmap->enabled) && !empty(Globals::$conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS))
2749
                    $showomap = 1;
2750
                if ($mode == 'member' && !empty(Globals::$conf->openstreetmap->enabled) && !empty(Globals::$conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS))
2751
                    $showomap = 1;
2752
2753
                if ($showgmap) {
2754
                    $url = dol_buildpath('/google/gmaps.php?mode=' . $mode . '&id=' . $id, 1);
2755
                    $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '" class="valigntextbottom" src="' . DOL_BASE_URI . '/theme/common/gmap.png"></a>';
2756
                }
2757
                if ($showomap) {
2758
                    $url = dol_buildpath('/openstreetmap/maps.php?mode=' . $mode . '&id=' . $id, 1);
2759
                    $out .= ' <a href="' . $url . '" target="_gmaps"><img id="' . $htmlid . '_openstreetmap" class="valigntextbottom" src="' . DOL_BASE_URI . '/theme/common/gmap.png"></a>';
2760
                }
2761
            }
2762
        }
2763
        if ($noprint)
2764
            return $out;
2765
        else
2766
            print $out;
2767
    }
2768
2769
    /**
2770
     * 	Return true if email syntax is ok
2771
     *
2772
     * 	@param	    string		$address    			email (Ex: "[email protected]", "John Do <[email protected]>")
2773
     *  @param		int			$acceptsupervisorkey	If 1, the special string '__SUPERVISOREMAIL__' is also accepted as valid
2774
     * 	@return     boolean     						true if email syntax is OK, false if KO or empty string
2775
     */
2776
    static function isValidEmail($address, $acceptsupervisorkey = 0)
2777
    {
2778
        if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__')
2779
            return true;
2780
        if (filter_var($address, FILTER_VALIDATE_EMAIL))
2781
            return true;
2782
2783
        return false;
2784
    }
2785
2786
    /**
2787
     * 	Return if the domain name has a valid MX record.
2788
     *  WARNING: This need static function idn_to_ascii, checkdnsrr and getmxrr
2789
     *
2790
     * 	@param	    string		$domain	    			Domain name (Ex: "yahoo.com", "yhaoo.com", "dolibarr.fr")
2791
     * 	@return     int     							-1 if error (static function not available), 0=Not valid, 1=Valid
2792
     */
2793
    static function isValidMXRecord($domain)
2794
    {
2795
        if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
2796
            if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
2797
                return 0;
2798
            }
2799
            if (function_exists('getmxrr')) {
2800
                $mxhosts = array();
2801
                $weight = array();
2802
                getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
2803
                if (count($mxhosts) > 1)
2804
                    return 1;
2805
                if (count($mxhosts) == 1 && !empty($mxhosts[0]))
2806
                    return 1;
2807
2808
                return 0;
2809
            }
2810
        }
2811
        return -1;
2812
    }
2813
2814
    /**
2815
     *  Return true if phone number syntax is ok
2816
     *  TODO Decide what to do with this
2817
     *
2818
     *  @param	string		$phone		phone (Ex: "0601010101")
2819
     *  @return boolean     			true if phone syntax is OK, false if KO or empty string
2820
     */
2821
    static function isValidPhone($phone)
2822
    {
2823
        return true;
2824
    }
2825
2826
    /**
2827
     * Make a strlen call. Works even if mbstring module not enabled
2828
     *
2829
     * @param   string		$string				String to calculate length
2830
     * @param   string		$stringencoding		Encoding of string
2831
     * @return  int								Length of string
2832
     */
2833
    static function dol_strlen($string, $stringencoding = 'UTF-8')
2834
    {
2835
        if (function_exists('mb_strlen'))
2836
            return mb_strlen($string, $stringencoding);
2837
        else
2838
            return strlen($string);
2839
    }
2840
2841
    /**
2842
     * Make a substring. Works even if mbstring module is not enabled for better compatibility.
2843
     *
2844
     * @param	string	$string				String to scan
2845
     * @param	string	$start				Start position
2846
     * @param	int		$length				Length (in nb of characters or nb of bytes depending on trunconbytes param)
2847
     * @param   string	$stringencoding		Page code used for input string encoding
2848
     * @param	int		$trunconbytes		1=Length is max of bytes instead of max of characters
2849
     * @return  string						substring
2850
     */
2851
    static function dol_substr($string, $start, $length, $stringencoding = '', $trunconbytes = 0)
2852
    {
2853
        // global Globals::$langs;
2854
2855
        if (empty($stringencoding))
2856
            $stringencoding = Globals::$langs->charset_output;
2857
2858
        $ret = '';
2859
        if (empty($trunconbytes)) {
2860
            if (function_exists('mb_substr')) {
2861
                $ret = mb_substr($string, $start, $length, $stringencoding);
2862
            } else {
2863
                $ret = substr($string, $start, $length);
2864
            }
2865
        } else {
2866
            if (function_exists('mb_strcut')) {
2867
                $ret = mb_strcut($string, $start, $length, $stringencoding);
2868
            } else {
2869
                $ret = substr($string, $start, $length);
2870
            }
2871
        }
2872
        return $ret;
2873
    }
2874
2875
    /**
2876
     * 	Truncate a string to a particular length adding '...' if string larger than length.
2877
     * 	If length = max length+1, we do no truncate to avoid having just 1 char replaced with '...'.
2878
     *  MAIN_DISABLE_TRUNC=1 can disable all truncings
2879
     *
2880
     * 	@param	string	$string				String to truncate
2881
     * 	@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 ...)
2882
     * 	@param	string	$trunc				Where to trunc: right, left, middle (size must be a 2 power), wrap
2883
     * 	@param	string	$stringencoding		Tell what is source string encoding
2884
     *  @param	int		$nodot				Truncation do not add ... after truncation. So it's an exact truncation.
2885
     *  @param  int     $display            Trunc is used to display data and can be changed for small screen. TODO Remove this param (must be dealt with CSS)
2886
     * 	@return string						Truncated string. WARNING: length is never higher than $size if $nodot is set, but can be 3 chars higher otherwise.
2887
     */
2888
    static function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
2889
    {
2890
        // global Globals::$conf;
2891
2892
        if ($size == 0 || !empty(Globals::$conf->global->MAIN_DISABLE_TRUNC))
2893
            return $string;
2894
2895
        if (empty($stringencoding))
2896
            $stringencoding = 'UTF-8';
2897
// reduce for small screen
2898
        if (Globals::$conf->dol_optimize_smallscreen == 1 && $display == 1)
2899
            $size = round($size / 3);
2900
2901
// We go always here
2902
        if ($trunc == 'right') {
2903
            $newstring = DolUtils::dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
2904
            if (DolUtils::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 ...
2905
                return dol_substr($newstring, 0, $size, $stringencoding) . ($nodot ? '' : '...');
2906
            else
2907
            //return 'u'.$size.'-'.$newstring.'-'.DolUtils::dol_strlen($newstring,$stringencoding).'-'.$string;
2908
                return $string;
2909
        }
2910
        elseif ($trunc == 'middle') {
2911
            $newstring = DolUtils::dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
2912
            if (DolUtils::dol_strlen($newstring, $stringencoding) > 2 && DolUtils::dol_strlen($newstring, $stringencoding) > ($size + 1)) {
2913
                $size1 = round($size / 2);
2914
                $size2 = round($size / 2);
2915
                return dol_substr($newstring, 0, $size1, $stringencoding) . '...' . dol_substr($newstring, DolUtils::dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
2916
            } else
2917
                return $string;
2918
        }
2919
        elseif ($trunc == 'left') {
2920
            $newstring = DolUtils::dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
2921
            if (DolUtils::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 ...
2922
                return '...' . dol_substr($newstring, DolUtils::dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
2923
            else
2924
                return $string;
2925
        }
2926
        elseif ($trunc == 'wrap') {
2927
            $newstring = DolUtils::dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
2928
            if (DolUtils::dol_strlen($newstring, $stringencoding) > ($size + 1))
2929
                return dol_substr($newstring, 0, $size, $stringencoding) . "\n" . dol_trunc(dol_substr($newstring, $size, DolUtils::dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
2930
            else
2931
                return $string;
2932
        } else
2933
            return 'BadParam3CallingDolTrunc';
2934
    }
2935
2936
    /**
2937
     * 	Show picto whatever it's its name (generic function)
2938
     *
2939
     * 	@param      string		$titlealt         	Text on title tag for tooltip. Not used if param notitle is set to 1.
2940
     * 	@param      string		$picto       		Name of image file to show ('filenew', ...)
2941
     * 												If no extension provided, we use '.png'. Image must be stored into theme/xxx/img directory.
2942
     *                                  			Example: picto.png                  if picto.png is stored into htdocs/theme/mytheme/img
2943
     *                                  			Example: picto.png@mymodule         if picto.png is stored into htdocs/mymodule/img
2944
     *                                  			Example: /mydir/mysubdir/picto.png  if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1)
2945
     * 	@param		string		$moreatt			Add more attribute on img tag (For example 'style="float: right"')
2946
     * 	@param		boolean|int	$pictoisfullpath	If true or 1, image path is a full path
2947
     * 	@param		int			$srconly			Return only content of the src attribute of img.
2948
     *  @param		int			$notitle			1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip.
2949
     *  @param		string		$alt				Force alt for bind people
2950
     *  @param		string		$morecss			Add more class css on img tag (For example 'myclascss'). Work only if $moreatt is empty.
2951
     *  @return     string       				    Return img tag
2952
     *  @see        #img_object, #img_picto_common
2953
     */
2954
    static function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '')
2955
    {
2956
        // global Globals::$conf, Globals::$langs;
2957
// We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_BASE_URI/theme/Globals::$conf->theme/img/$picto
2958
//$url = DOL_BASE_URI;
2959
        $url = DOL_BASE_URI;
2960
2961
        $theme = Globals::$conf->theme;
2962
        $path = 'theme/' . $theme;
2963
2964
// Define fullpathpicto to use into src
2965
        if ($pictoisfullpath) {
2966
// Clean parameters
2967
            if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
2968
                $picto .= '.png';
2969
            }
2970
            $fullpathpicto = $picto;
2971
        } else {
2972
            $pictowithoutext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
2973
2974
//if (in_array($picto, array('switch_off', 'switch_on', 'off', 'on')))
2975
            if (empty($srconly) && in_array($pictowithoutext, array(
2976
                    'bank', 'close_title', 'delete', 'edit', 'ellipsis-h', 'filter', 'grip', 'grip_title', 'list', 'listlight', 'off', 'on', 'play', 'playdisabled', 'printer', 'resize',
2977
                    'note', 'switch_off', 'switch_on', 'unlink', 'uparrow', '1downarrow', '1uparrow',
2978
                    'jabber', 'skype', 'twitter', 'facebook'
2979
                    )
2980
                )) {
2981
                $fakey = $pictowithoutext;
2982
                $facolor = '';
2983
                $fasize = '';
2984
                $marginleftonlyshort = 2;
2985
                if ($pictowithoutext == 'switch_off') {
2986
                    $fakey = 'fa-toggle-off';
2987
                    $facolor = '#999';
2988
                    $fasize = '2em';
2989
                } elseif ($pictowithoutext == 'switch_on') {
2990
                    $fakey = 'fa-toggle-on';
2991
                    $facolor = '#227722';
2992
                    $fasize = '2em';
2993
                } elseif ($pictowithoutext == 'off') {
2994
                    $fakey = 'fa-square-o';
2995
                    $fasize = '1.3em';
2996
                } elseif ($pictowithoutext == 'on') {
2997
                    $fakey = 'fa-check-square-o';
2998
                    $fasize = '1.3em';
2999
                } elseif ($pictowithoutext == 'bank') {
3000
                    $fakey = 'fa-bank';
3001
                    $facolor = '#444';
3002
                } elseif ($pictowithoutext == 'close_title') {
3003
                    $fakey = 'fa-window-close';
3004
                } elseif ($pictowithoutext == 'delete') {
3005
                    $fakey = 'fa-trash';
3006
                    $facolor = '#444';
3007
                } elseif ($pictowithoutext == 'edit') {
3008
                    $fakey = 'fa-pencil';
3009
                    $facolor = '#444';
3010
                } elseif ($pictowithoutext == 'filter') {
3011
                    $fakey = 'fa-' . $pictowithoutext;
3012
                } elseif ($pictowithoutext == 'grip_title' || $pictowithoutext == 'grip') {
3013
                    $fakey = 'fa-arrows';
3014
                } elseif ($pictowithoutext == 'listlight') {
3015
                    $fakey = 'fa-download';
3016
                    $facolor = '#999';
3017
                    $marginleftonlyshort = 1;
3018
                } elseif ($pictowithoutext == 'printer') {
3019
                    $fakey = 'fa-print';
3020
                    $fasize = '1.2em';
3021
                    $facolor = '#444';
3022
                } elseif ($pictowithoutext == 'resize') {
3023
                    $fakey = 'fa-crop';
3024
                    $facolor = '#444';
3025
                } elseif ($pictowithoutext == 'note') {
3026
                    $fakey = 'fa-sticky-note-o';
3027
                    $facolor = '#999';
3028
                    $marginleftonlyshort = 1;
3029
                } elseif ($pictowithoutext == 'uparrow') {
3030
                    $fakey = 'fa-mail-forward';
3031
                    $facolor = '#555';
3032
                } elseif ($pictowithoutext == '1uparrow') {
3033
                    $fakey = 'fa-caret-up';
3034
                    $marginleftonlyshort = 1;
3035
                } elseif ($pictowithoutext == '1downarrow') {
3036
                    $fakey = 'fa-caret-down';
3037
                    $marginleftonlyshort = 1;
3038
                } elseif ($pictowithoutext == 'unlink') {
3039
                    $fakey = 'fa-chain-broken';
3040
                    $facolor = '#555';
3041
                } elseif ($pictowithoutext == 'playdisabled') {
3042
                    $fakey = 'fa-play';
3043
                    $facolor = '#ccc';
3044
                } elseif ($pictowithoutext == 'play') {
3045
                    $fakey = 'fa-play';
3046
                    $facolor = '#444';
3047
                } elseif ($pictowithoutext == 'jabber') {
3048
                    $fakey = 'fa-comment-o';
3049
                } else {
3050
                    $fakey = 'fa-' . $pictowithoutext;
3051
                    $facolor = '#444';
3052
                    $marginleftonlyshort = 0;
3053
                }
3054
3055
                if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
3056
                    $morecss .= ($morecss ? ' ' : '') . $reg[1];
3057
                }
3058
                $enabledisablehtml = '<span class="fa ' . $fakey . ' ' . ($marginleftonlyshort ? ($marginleftonlyshort == 1 ? 'marginleftonlyshort' : 'marginleftonly') : '') . ' valignmiddle' . ($morecss ? ' ' . $morecss : '') . '" style="' . ($fasize ? ('font-size: ' . $fasize . ';') : '') . ($facolor ? (' color: ' . $facolor . ';') : '') . '" alt="' . DolUtils::dol_escape_htmltag($titlealt) . '"' . (($notitle || empty($title)) ? '' : ' title="' . DolUtils::dol_escape_htmltag($title) . '"') . ($moreatt ? ' ' . $moreatt : '') . '>';
3059
                if (!empty(Globals::$conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3060
                    $enabledisablehtml .= $titlealt;
3061
                }
3062
                $enabledisablehtml .= '</span>';
3063
3064
                return $enabledisablehtml;
3065
            }
3066
3067
            if (!empty(Globals::$conf->global->MAIN_OVERWRITE_THEME_PATH)) {
3068
                $path = Globals::$conf->global->MAIN_OVERWRITE_THEME_PATH . '/theme/' . $theme; // If the theme does not have the same name as the module
3069
            } else if (!empty(Globals::$conf->global->MAIN_OVERWRITE_THEME_RES)) {
3070
                $path = Globals::$conf->global->MAIN_OVERWRITE_THEME_RES . '/theme/' . Globals::$conf->global->MAIN_OVERWRITE_THEME_RES;  // To allow an external module to overwrite image resources whatever is activated theme
3071
            } else if (!empty(Globals::$conf->modules_parts['theme']) && array_key_exists($theme, Globals::$conf->modules_parts['theme'])) {
3072
                $path = $theme . '/theme/' . $theme; // If the theme have the same name as the module
3073
            }
3074
3075
// If we ask an image into $url/$mymodule/img (instead of default path)
3076
            if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
3077
                $picto = $regs[1];
3078
                $path = $regs[2]; // $path is $mymodule
3079
            }
3080
3081
// Clean parameters
3082
            if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
3083
                $picto .= '.png';
3084
            }
3085
// If alt path are defined, define url where img file is, according to physical path
3086
// ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
3087
            foreach (Globals::$conf->file->dol_document_root as $type => $dirroot) {
3088
                if ($type == 'main') {
3089
                    continue;
3090
                }
3091
// This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
3092
                if (file_exists($dirroot . '/' . $path . '/img/' . $picto)) {
3093
                    //$url = DOL_BASE_URI . Globals::$conf->file->dol_url_root[$type];
3094
                    $url = DOL_BASE_URI . Globals::$conf->file->dol_url_root[$type];
3095
                    break;
3096
                }
3097
            }
3098
3099
// $url is '' or '/custom', $path is current theme or
3100
            $fullpathpicto = $url . '/' . $path . '/img/' . $picto;
3101
        }
3102
3103
        if ($srconly) {
3104
            return $fullpathpicto;
3105
        }
3106
3107
// tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for bind people
3108
        return '<img src="' . $fullpathpicto . '" alt="' . DolUtils::dol_escape_htmltag($alt) . '"' . (($notitle || empty($titlealt)) ? '' : ' title="' . DolUtils::dol_escape_htmltag($titlealt) . '"') . ($moreatt ? ' ' . $moreatt : ' class="inline-block' . ($morecss ? ' ' . $morecss : '') . '"') . '>'; // Alt is used for accessibility, title for popup
3109
    }
3110
3111
    /**
3112
     * 	Show a picto called object_picto (generic function)
3113
     *
3114
     * 	@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.
3115
     * 	@param	string	$picto				Name of image to show object_picto (example: user, group, action, bill, contract, propal, product, ...)
3116
     * 										For external modules use imagename@mymodule to search into directory "img" of module.
3117
     * 	@param	string	$moreatt			Add more attribute on img tag (ie: class="datecallink")
3118
     * 	@param	int		$pictoisfullpath	If 1, image path is a full path
3119
     * 	@param	int		$srconly			Return only content of the src attribute of img.
3120
     *  @param	int		$notitle			1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip.
3121
     * 	@return	string						Return img tag
3122
     * 	@see	#img_picto, #img_picto_common
3123
     */
3124
    static function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0)
3125
    {
3126
        return img_picto($titlealt, 'object_' . $picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
3127
    }
3128
3129
    /**
3130
     * 	Show weather picto
3131
     *
3132
     * 	@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.
3133
     * 	@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.
3134
     * 	@param		string		$moreatt			Add more attribute on img tag
3135
     * 	@param		int			$pictoisfullpath	If 1, image path is a full path
3136
     * 	@return     string      					Return img tag
3137
     *  @see        #img_object, #img_picto
3138
     */
3139
    static function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0)
3140
    {
3141
        // global Globals::$conf;
3142
3143
        if (!preg_match('/(\.png|\.gif)$/i', $picto))
3144
            $picto .= '.png';
3145
3146
//$path = DOL_BASE_URI . '/theme/' . Globals::$conf->theme . '/img/weather/' . $picto;
3147
        $path = DOL_BASE_URI . '/theme/' . Globals::$conf->theme . '/img/weather/' . $picto;
3148
3149
        return img_picto($titlealt, $path, $moreatt, 1);
3150
    }
3151
3152
    /**
3153
     * 	Show picto (generic function)
3154
     *
3155
     * 	@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.
3156
     * 	@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.
3157
     * 	@param		string		$moreatt			Add more attribute on img tag
3158
     * 	@param		int			$pictoisfullpath	If 1, image path is a full path
3159
     * 	@return     string      					Return img tag
3160
     *  @see        #img_object, #img_picto
3161
     */
3162
    static function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0)
3163
    {
3164
        // global Globals::$conf;
3165
3166
        if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
3167
            $picto .= '.png';
3168
        }
3169
3170
        if ($pictoisfullpath) {
3171
            $path = $picto;
3172
        } else {
3173
            //$path = DOL_BASE_URI . '/theme/common/' . $picto;
3174
            $path = DOL_BASE_URI . '/theme/common/' . $picto;
3175
3176
            if (!empty(Globals::$conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) {
3177
                $themepath = DOL_BASE_PATH . 'theme/' . Globals::$conf->theme . '/img/' . $picto;
3178
3179
                if (file_exists($themepath)) {
3180
                    $path = $themepath;
3181
                }
3182
            }
3183
        }
3184
3185
        return self::img_picto($titlealt, $path, $moreatt, 1);
3186
    }
3187
3188
    /**
3189
     * 	Show logo action
3190
     *
3191
     * 	@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.
3192
     * 	@param  string		$numaction   	Action id or code to show
3193
     * 	@return string      				Return an img tag
3194
     */
3195
    static function img_action($titlealt, $numaction)
3196
    {
3197
        // global Globals::$conf, Globals::$langs;
3198
3199
        if (empty($titlealt) || $titlealt == 'default') {
3200
            if ($numaction == '-1' || $numaction == 'ST_NO') {
3201
                $numaction = -1;
3202
                $titlealt = Globals::$langs->transnoentitiesnoconv('ChangeDoNotContact');
3203
            } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
3204
                $numaction = 0;
3205
                $titlealt = Globals::$langs->transnoentitiesnoconv('ChangeNeverContacted');
3206
            } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
3207
                $numaction = 1;
3208
                $titlealt = Globals::$langs->transnoentitiesnoconv('ChangeToContact');
3209
            } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
3210
                $numaction = 2;
3211
                $titlealt = Globals::$langs->transnoentitiesnoconv('ChangeContactInProcess');
3212
            } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
3213
                $numaction = 3;
3214
                $titlealt = Globals::$langs->transnoentitiesnoconv('ChangeContactDone');
3215
            } else {
3216
                $titlealt = Globals::$langs->transnoentitiesnoconv('ChangeStatus ' . $numaction);
3217
                $numaction = 0;
3218
            }
3219
        }
3220
        if (!is_numeric($numaction))
3221
            $numaction = 0;
3222
3223
        return img_picto($titlealt, 'stcomm' . $numaction . '.png');
3224
    }
3225
3226
    /**
3227
     *  Show pdf logo
3228
     *
3229
     *  @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.
3230
     *  @param  int		    $size       Taille de l'icone : 3 = 16x16px , 2 = 14x14px
3231
     *  @return string      			Retourne tag img
3232
     */
3233
    static function img_pdf($titlealt = 'default', $size = 3)
3234
    {
3235
        // global Globals::$conf, Globals::$langs;
3236
3237
        if ($titlealt == 'default')
3238
            $titlealt = Globals::$langs->trans('Show');
3239
3240
        return img_picto($titlealt, 'pdf' . $size . '.png');
3241
    }
3242
3243
    /**
3244
     * 	Show logo +
3245
     *
3246
     * 	@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.
3247
     * 	@param  string	$other      Add more attributes on img
3248
     * 	@return string      		Return tag img
3249
     */
3250
    static function img_edit_add($titlealt = 'default', $other = '')
3251
    {
3252
        // global Globals::$conf, Globals::$langs;
3253
3254
        if ($titlealt == 'default')
3255
            $titlealt = Globals::$langs->trans('Add');
3256
3257
        return img_picto($titlealt, 'edit_add.png', $other);
3258
    }
3259
3260
    /**
3261
     * 	Show logo -
3262
     *
3263
     * 	@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.
3264
     * 	@param  string	$other      Add more attributes on img
3265
     * 	@return string      		Return tag img
3266
     */
3267
    static function img_edit_remove($titlealt = 'default', $other = '')
3268
    {
3269
        // global Globals::$conf, Globals::$langs;
3270
3271
        if ($titlealt == 'default')
3272
            $titlealt = Globals::$langs->trans('Remove');
3273
3274
        return img_picto($titlealt, 'edit_remove.png', $other);
3275
    }
3276
3277
    /**
3278
     * 	Show logo editer/modifier fiche
3279
     *
3280
     * 	@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.
3281
     * 	@param  integer	$float      Si il faut y mettre le style "float: right"
3282
     * 	@param  string	$other		Add more attributes on img
3283
     * 	@return string      		Return tag img
3284
     */
3285
    static function img_edit($titlealt = 'default', $float = 0, $other = 'class="pictoedit"')
3286
    {
3287
        // global Globals::$conf, Globals::$langs;
3288
3289
        if ($titlealt == 'default')
3290
            $titlealt = Globals::$langs->trans('Modify');
3291
3292
        return img_picto($titlealt, 'edit.png', ($float ? 'style="float: ' . (Globals::$langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right') . '"' : "") . ($other ? ' ' . $other : ''));
3293
    }
3294
3295
    /**
3296
     * 	Show logo view card
3297
     *
3298
     * 	@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.
3299
     * 	@param  integer	$float      Si il faut y mettre le style "float: right"
3300
     * 	@param  string	$other		Add more attributes on img
3301
     * 	@return string      		Return tag img
3302
     */
3303
    static function img_view($titlealt = 'default', $float = 0, $other = '')
3304
    {
3305
        // global Globals::$conf, Globals::$langs;
3306
3307
        if ($titlealt == 'default')
3308
            $titlealt = Globals::$langs->trans('View');
3309
3310
        $moreatt = ($float ? 'style="float: right" ' : '') . $other;
3311
3312
        return img_picto($titlealt, 'view.png', $moreatt);
3313
    }
3314
3315
    /**
3316
     *  Show delete logo
3317
     *
3318
     *  @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.
3319
     * 	@param  string	$other      Add more attributes on img
3320
     *  @return string      		Retourne tag img
3321
     */
3322
    static function img_delete($titlealt = 'default', $other = 'class="pictodelete"')
3323
    {
3324
        // global Globals::$conf, Globals::$langs;
3325
3326
        if ($titlealt == 'default')
3327
            $titlealt = Globals::$langs->trans('Delete');
3328
3329
        return img_picto($titlealt, 'delete.png', $other);
3330
//return '<span class="fa fa-trash fa-2x fa-fw" style="font-size: 1.7em;" title="'.$titlealt.'"></span>';
3331
    }
3332
3333
    /**
3334
     *  Show printer logo
3335
     *
3336
     *  @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.
3337
     *  @param  string  $other      Add more attributes on img
3338
     *  @return string              Retourne tag img
3339
     */
3340
    static function img_printer($titlealt = "default", $other = '')
3341
    {
3342
        // global Globals::$conf, Globals::$langs;
3343
        if ($titlealt == "default")
3344
            $titlealt = Globals::$langs->trans("Print");
3345
        return img_picto($titlealt, 'printer.png', $other);
3346
    }
3347
3348
    /**
3349
     *  Show split logo
3350
     *
3351
     *  @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.
3352
     * 	@param  string	$other      Add more attributes on img
3353
     *  @return string      		Retourne tag img
3354
     */
3355
    static function img_split($titlealt = 'default', $other = 'class="pictosplit"')
3356
    {
3357
        // global Globals::$conf, Globals::$langs;
3358
3359
        if ($titlealt == 'default')
3360
            $titlealt = Globals::$langs->trans('Split');
3361
3362
        return img_picto($titlealt, 'split.png', $other);
3363
    }
3364
3365
    /**
3366
     * 	Show help logo with cursor "?"
3367
     *
3368
     * 	@param	int              	$usehelpcursor		1=Use help cursor, 2=Use click pointer cursor, 0=No specific cursor
3369
     * 	@param	int|string	        $usealttitle		Text to use as alt title
3370
     * 	@return string            	           			Return tag img
3371
     */
3372
    static function img_help($usehelpcursor = 1, $usealttitle = 1)
3373
    {
3374
        // global Globals::$conf, Globals::$langs;
3375
3376
        if ($usealttitle) {
3377
            if (is_string($usealttitle))
3378
                $usealttitle = DolUtils::dol_escape_htmltag($usealttitle);
3379
            else
3380
                $usealttitle = Globals::$langs->trans('Info');
3381
        }
3382
3383
        return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;' . ($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')) . '"');
3384
    }
3385
3386
    /**
3387
     * 	Show info logo
3388
     *
3389
     * 	@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.
3390
     * 	@return string      		Return img tag
3391
     */
3392
    static function img_info($titlealt = 'default')
3393
    {
3394
        // global Globals::$conf, Globals::$langs;
3395
3396
        if ($titlealt == 'default')
3397
            $titlealt = Globals::$langs->trans('Informations');
3398
3399
        return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
3400
    }
3401
3402
    /**
3403
     * 	Show warning logo
3404
     *
3405
     * 	@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.
3406
     * 	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"'). If 1, add float: right. Can't be "class" attribute.
3407
     * 	@return string      		Return img tag
3408
     */
3409
    static function img_warning($titlealt = 'default', $moreatt = '')
3410
    {
3411
        // global Globals::$conf, Globals::$langs;
3412
3413
        if ($titlealt == 'default')
3414
            $titlealt = Globals::$langs->trans('Warning');
3415
3416
//return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
3417
        return img_picto($titlealt, 'warning.png', 'class="pictowarning valignmiddle"' . ($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' ' . $moreatt) : ''));
3418
    }
3419
3420
    /**
3421
     *  Show error logo
3422
     *
3423
     * 	@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.
3424
     * 	@return string      		Return img tag
3425
     */
3426
    static function img_error($titlealt = 'default')
3427
    {
3428
        // global Globals::$conf, Globals::$langs;
3429
3430
        if ($titlealt == 'default')
3431
            $titlealt = Globals::$langs->trans('Error');
3432
3433
        return img_picto($titlealt, 'error.png', 'class="valigntextbottom"');
3434
    }
3435
3436
    /**
3437
     * 	Show next logo
3438
     *
3439
     * 	@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.
3440
     * 	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3441
     * 	@return string      		Return img tag
3442
     */
3443
    static function img_next($titlealt = 'default', $moreatt = '')
3444
    {
3445
        // global Globals::$conf, Globals::$langs;
3446
3447
        if ($titlealt == 'default')
3448
            $titlealt = Globals::$langs->trans('Next');
3449
3450
//return img_picto($titlealt, 'next.png', $moreatt);
3451
        return '<span class="fa fa-chevron-right paddingright paddingleft" title="' . DolUtils::dol_escape_htmltag($titlealt) . '"></span>';
3452
    }
3453
3454
    /**
3455
     * 	Show previous logo
3456
     *
3457
     * 	@param	string	$titlealt   Text on alt and title of image. Alt only if param notitle is set to 1. If text is "TextA:TextB", use Text A on alt and Text B on title.
3458
     * 	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3459
     * 	@return string      		Return img tag
3460
     */
3461
    static function img_previous($titlealt = 'default', $moreatt = '')
3462
    {
3463
        // global Globals::$conf, Globals::$langs;
3464
3465
        if ($titlealt == 'default')
3466
            $titlealt = Globals::$langs->trans('Previous');
3467
3468
//return img_picto($titlealt, 'previous.png', $moreatt);
3469
        return '<span class="fa fa-chevron-left paddingright paddingleft" title="' . DolUtils::dol_escape_htmltag($titlealt) . '"></span>';
3470
    }
3471
3472
    /**
3473
     * 	Show down arrow logo
3474
     *
3475
     * 	@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.
3476
     * 	@param  int		$selected   Selected
3477
     *  @param	string	$moreclass	Add more CSS classes
3478
     * 	@return string      		Return img tag
3479
     */
3480
    static function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
3481
    {
3482
        // global Globals::$conf, Globals::$langs;
3483
3484
        if ($titlealt == 'default')
3485
            $titlealt = Globals::$langs->trans('Down');
3486
3487
        return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown' . ($moreclass ? " " . $moreclass : "") . '"');
3488
    }
3489
3490
    /**
3491
     * 	Show top arrow logo
3492
     *
3493
     * 	@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.
3494
     * 	@param  int		$selected	Selected
3495
     *  @param	string	$moreclass	Add more CSS classes
3496
     * 	@return string      		Return img tag
3497
     */
3498
    static function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
3499
    {
3500
        // global Globals::$conf, Globals::$langs;
3501
3502
        if ($titlealt == 'default')
3503
            $titlealt = Globals::$langs->trans('Up');
3504
3505
        return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup' . ($moreclass ? " " . $moreclass : "") . '"');
3506
    }
3507
3508
    /**
3509
     * 	Show left arrow logo
3510
     *
3511
     * 	@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.
3512
     * 	@param  int		$selected	Selected
3513
     * 	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3514
     * 	@return string      		Return img tag
3515
     */
3516
    static function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
3517
    {
3518
        // global Globals::$conf, Globals::$langs;
3519
3520
        if ($titlealt == 'default')
3521
            $titlealt = Globals::$langs->trans('Left');
3522
3523
        return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
3524
    }
3525
3526
    /**
3527
     * 	Show right arrow logo
3528
     *
3529
     * 	@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.
3530
     * 	@param  int		$selected	Selected
3531
     * 	@param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
3532
     * 	@return string      		Return img tag
3533
     */
3534
    static function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
3535
    {
3536
        // global Globals::$conf, Globals::$langs;
3537
3538
        if ($titlealt == 'default')
3539
            $titlealt = Globals::$langs->trans('Right');
3540
3541
        return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
3542
    }
3543
3544
    /**
3545
     * 	Show tick logo if allowed
3546
     *
3547
     * 	@param	string	$allow		Allow
3548
     * 	@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.
3549
     * 	@return string      		Return img tag
3550
     */
3551
    static function img_allow($allow, $titlealt = 'default')
3552
    {
3553
        // global Globals::$conf, Globals::$langs;
3554
3555
        if ($titlealt == 'default')
3556
            $titlealt = Globals::$langs->trans('Active');
3557
3558
        if ($allow == 1)
3559
            return img_picto($titlealt, 'tick.png');
3560
3561
        return '-';
3562
    }
3563
3564
    /**
3565
     * 	Return image of a credit card according to its brand name
3566
     *
3567
     * 	@param	string	$brand		Brand name of credit card
3568
     * 	@return string     			Return img tag
3569
     */
3570
    static function img_credit_card($brand)
3571
    {
3572
        if ($brand == 'Visa') {
3573
            $brand = 'cc-visa';
3574
        } elseif ($brand == 'MasterCard') {
3575
            $brand = 'cc-mastercard';
3576
        } elseif ($brand == 'American Express') {
3577
            $brand = 'cc-amex';
3578
        } elseif ($brand == 'Discover') {
3579
            $brand = 'cc-discover';
3580
        } elseif ($brand == 'JCB') {
3581
            $brand = 'cc-jcb';
3582
        } elseif ($brand == 'Diners Club') {
3583
            $brand = 'cc-diners-club';
3584
        } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
3585
            $brand = 'credit-card';
3586
        }
3587
3588
        return '<span class="fa fa-' . $brand . ' fa-2x fa-fw"></span>';
3589
    }
3590
3591
    /**
3592
     * 	Show MIME img of a file
3593
     *
3594
     * 	@param	string	$file		Filename
3595
     * 	@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.
3596
     *  @param	string	$morecss	More css
3597
     * 	@return string     			Return img tag
3598
     */
3599
    static function img_mime($file, $titlealt = '', $morecss = '')
3600
    {
3601
        require_once DOL_BASE_PATH . '/core/lib/files.lib.php';
3602
3603
        $mimetype = dol_mimetype($file, '', 1);
3604
        $mimeimg = dol_mimetype($file, '', 2);
3605
        $mimefa = dol_mimetype($file, '', 4);
3606
3607
        if (empty($titlealt))
3608
            $titlealt = 'Mime type: ' . $mimetype;
3609
3610
//return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
3611
        return '<i class="fa fa-' . $mimefa . ' paddingright"></i>';
3612
    }
3613
3614
    /**
3615
     * 	Show phone logo.
3616
     *  Use img_picto instead.
3617
     *
3618
     * 	@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.
3619
     * 	@param  int		$option		Option
3620
     * 	@return string      		Return img tag
3621
     *  @deprecated
3622
     *  @see img_picto
3623
     */
3624
    static function img_phone($titlealt = 'default', $option = 0)
3625
    {
3626
        DolUtils::dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
3627
3628
        // global Globals::$conf, Globals::$langs;
3629
3630
        if ($titlealt == 'default')
3631
            $titlealt = Globals::$langs->trans('Call');
3632
3633
        if ($option == 1)
3634
            $img = 'call';
3635
        else
3636
            $img = 'call_out';
3637
3638
        return img_picto($titlealt, $img);
3639
    }
3640
3641
    /**
3642
     *  Show search logo
3643
     *
3644
     *  @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.
3645
     * 	@param  string	$other      Add more attributes on img
3646
     *  @return string      		Retourne tag img
3647
     */
3648
    static function img_search($titlealt = 'default', $other = '')
3649
    {
3650
        // global Globals::$conf, Globals::$langs;
3651
3652
        if ($titlealt == 'default')
3653
            $titlealt = Globals::$langs->trans('Search');
3654
3655
        $img = img_picto($titlealt, 'search.png', $other, false, 1);
3656
3657
        $input = '<input type="image" class="liste_titre" name="button_search" src="' . $img . '" ';
3658
        $input .= 'value="' . DolUtils::dol_escape_htmltag($titlealt) . '" title="' . DolUtils::dol_escape_htmltag($titlealt) . '" >';
3659
3660
        return $input;
3661
    }
3662
3663
    /**
3664
     *  Show search logo
3665
     *
3666
     *  @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.
3667
     * 	@param  string	$other      Add more attributes on img
3668
     *  @return string      		Retourne tag img
3669
     */
3670
    static function img_searchclear($titlealt = 'default', $other = '')
3671
    {
3672
        // global Globals::$conf, Globals::$langs;
3673
3674
        if ($titlealt == 'default')
3675
            $titlealt = Globals::$langs->trans('Search');
3676
3677
        $img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
3678
3679
        $input = '<input type="image" class="liste_titre" name="button_removefilter" src="' . $img . '" ';
3680
        $input .= 'value="' . DolUtils::dol_escape_htmltag($titlealt) . '" title="' . DolUtils::dol_escape_htmltag($titlealt) . '" >';
3681
3682
        return $input;
3683
    }
3684
3685
    /**
3686
     * 	Show information for admin users or standard users
3687
     *
3688
     * 	@param	string	$text			Text info
3689
     * 	@param  integer	$infoonimgalt	Info is shown only on alt of star picto, otherwise it is show on output after the star picto
3690
     * 	@param	int		$nodiv			No div
3691
     *  @param  string  $admin          '1'=Info for admin users. '0'=Info for standard users (change only the look), 'xxx'=Other
3692
     *  @param	string	$morecss		More CSS
3693
     * 	@return	string					String with info text
3694
     */
3695
    static function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = '')
3696
    {
3697
        // global Globals::$conf, Globals::$langs;
3698
3699
        if ($infoonimgalt) {
3700
            return img_picto($text, 'info', 'class="hideonsmartphone' . ($morecss ? ' ' . $morecss : '') . '"');
3701
        }
3702
3703
        return ($nodiv ? '' : '<div class="' . (empty($admin) ? '' : ($admin == '1' ? 'info' : $admin)) . ' hideonsmartphone' . ($morecss ? ' ' . $morecss : '') . '">') . '<span class="fa fa-info-circle" title="' . DolUtils::dol_escape_htmltag($admin ? Globals::$langs->trans('InfoAdmin') : Globals::$langs->trans('Note')) . '"></span> ' . $text . ($nodiv ? '' : '</div>');
3704
    }
3705
3706
    /**
3707
     * 	Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remontee des bugs.
3708
     * 	On doit appeler cette fonction quand une erreur technique bloquante est rencontree.
3709
     * 	Toutefois, il faut essayer de ne l'appeler qu'au sein de pages php, les classes devant
3710
     * 	renvoyer leur erreur par l'intermediaire de leur propriete "error".
3711
     *
3712
     * 	@param	 	DoliDB	$db      	Database handler
3713
     * 	@param  	mixed	$error		String or array of errors strings to show
3714
     *  @param		array	$errors		Array of errors
3715
     * 	@return 	void
3716
     *  @see    	dol_htmloutput_errors
3717
     */
3718
    static function dol_print_error($dbError = '', $error = '', $errors = null)
3719
    {
3720
        // global Globals::$conf, Globals::$langs, $argv;
3721
        // global $dolibarr_main_prod;
3722
3723
        $out = '';
3724
        $syslog = '';
3725
3726
// Si erreur intervenue avant chargement langue
3727
        if (!Globals::$langs) {
3728
            //require_once DOL_BASE_PATH . '/core/class/translate.class.php';
3729
            Globals::$langs = new Translate('', Globals::$conf);
3730
            Globals::$langs->load("main");
3731
        }
3732
3733
// Load translation files required by the page
3734
        Globals::$langs->loadLangs(array('main', 'errors'));
3735
3736
        if ($_SERVER['DOCUMENT_ROOT']) {    // Mode web
3737
            $out .= Globals::$langs->trans("DolibarrHasDetectedError") . ".<br>\n";
3738
            if (!empty(Globals::$conf->global->MAIN_FEATURES_LEVEL)) {
3739
                $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";
3740
            }
3741
            $out .= Globals::$langs->trans("InformationToHelpDiagnose") . ":<br>\n";
3742
3743
            $out .= "<b>" . Globals::$langs->trans("Date") . ":</b> " . DolUtils::dol_print_date(time(), 'dayhourlog') . "<br>\n";
3744
            $out .= "<b>" . Globals::$langs->trans("Dolibarr") . ":</b> " . DOL_VERSION . "<br>\n";
3745
            if (isset(Globals::$conf->global->MAIN_FEATURES_LEVEL)) {
3746
                $out .= "<b>" . Globals::$langs->trans("LevelOfFeature") . ":</b> " . Globals::$conf->global->MAIN_FEATURES_LEVEL . "<br>\n";
3747
            }
3748
            if (function_exists("phpversion")) {
3749
                $out .= "<b>" . Globals::$langs->trans("PHP") . ":</b> " . phpversion() . "<br>\n";
3750
            }
3751
            $out .= "<b>" . Globals::$langs->trans("Server") . ":</b> " . $_SERVER["SERVER_SOFTWARE"] . "<br>\n";
3752
            if (function_exists("php_uname")) {
3753
                $out .= "<b>" . Globals::$langs->trans("OS") . ":</b> " . php_uname() . "<br>\n";
3754
            }
3755
            $out .= "<b>" . Globals::$langs->trans("UserAgent") . ":</b> " . $_SERVER["HTTP_USER_AGENT"] . "<br>\n";
3756
            $out .= "<br>\n";
3757
            $out .= "<b>" . Globals::$langs->trans("RequestedUrl") . ":</b> " . DolUtils::dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT, 'UTF-8') . "<br>\n";
3758
            $out .= "<b>" . Globals::$langs->trans("Referer") . ":</b> " . (isset($_SERVER["HTTP_REFERER"]) ? DolUtils::dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT, 'UTF-8') : '') . "<br>\n";
3759
            $out .= "<b>" . Globals::$langs->trans("MenuManager") . ":</b> " . (isset(Globals::$conf->standard_menu) ? Globals::$conf->standard_menu : '') . "<br>\n";
3760
            $out .= "<br>\n";
3761
            $syslog .= "url=" . DolUtils::dol_escape_htmltag($_SERVER["REQUEST_URI"]);
3762
            $syslog .= ", query_string=" . DolUtils::dol_escape_htmltag($_SERVER["QUERY_STRING"]);
3763
        } else {                              // Mode CLI
3764
            $out .= '> ' . Globals::$langs->transnoentities("ErrorInternalErrorDetected") . ":\n" . $argv[0] . "\n";
3765
            $syslog .= "pid=" . dol_getmypid();
3766
        }
3767
3768
        if (is_object($dbError)) {
3769
            $out .= '<pre>' . print_r($dbError, true) . '</pre>';
3770
            /*
3771
              if ($_SERVER['DOCUMENT_ROOT']) {  // Mode web
3772
              $out .= "<b>" . Globals::$langs->trans("DatabaseTypeManager") . ":</b> " . $dbError->type . "<br>\n";
3773
              $out .= "<b>" . Globals::$langs->trans("RequestLastAccessInError") . ":</b> " . ($dbError->lastqueryerror() ? DolUtils::dol_escape_htmltag($db->lastqueryerror()) : Globals::$langs->trans("ErrorNoRequestInError")) . "<br>\n";
3774
              $out .= "<b>" . Globals::$langs->trans("ReturnCodeLastAccessInError") . ":</b> " . ($dbError->lasterrno() ? DolUtils::dol_escape_htmltag($db->lasterrno()) : Globals::$langs->trans("ErrorNoRequestInError")) . "<br>\n";
3775
              $out .= "<b>" . Globals::$langs->trans("InformationLastAccessInError") . ":</b> " . ($dbError->lasterror() ? DolUtils::dol_escape_htmltag($db->lasterror()) : Globals::$langs->trans("ErrorNoRequestInError")) . "<br>\n";
3776
              $out .= "<br>\n";
3777
              } else {                            // Mode CLI
3778
              // No DolUtils::dol_escape_htmltag for output, we are in CLI mode
3779
              $out .= '> ' . Globals::$langs->transnoentities("DatabaseTypeManager") . ":\n" . $db->type . "\n";
3780
              $out .= '> ' . Globals::$langs->transnoentities("RequestLastAccessInError") . ":\n" . ($db->lastqueryerror() ? $db->lastqueryerror() : Globals::$langs->transnoentities("ErrorNoRequestInError")) . "\n";
3781
              $out .= '> ' . Globals::$langs->transnoentities("ReturnCodeLastAccessInError") . ":\n" . ($db->lasterrno() ? $db->lasterrno() : Globals::$langs->transnoentities("ErrorNoRequestInError")) . "\n";
3782
              $out .= '> ' . Globals::$langs->transnoentities("InformationLastAccessInError") . ":\n" . ($db->lasterror() ? $db->lasterror() : Globals::$langs->transnoentities("ErrorNoRequestInError")) . "\n";
3783
              }
3784
              $syslog .= ", sql=" . $db->lastquery();
3785
              $syslog .= ", db_error=" . $db->lasterror();
3786
             */
3787
        }
3788
3789
        if ($error || $errors) {
3790
            Globals::$langs->load("errors");
3791
3792
// Merge all into $errors array
3793
            if (is_array($error) && is_array($errors)) {
3794
                $errors = array_merge($error, $errors);
3795
            } elseif (is_array($error)) {
3796
                $errors = $error;
3797
            } elseif (is_array($errors)) {
3798
                $errors = array_merge(array($error), $errors);
3799
            } else {
3800
                $errors = array_merge(array($error));
3801
            }
3802
3803
            foreach ($errors as $msg) {
3804
                if (empty($msg)) {
3805
                    continue;
3806
                }
3807
                if ($_SERVER['DOCUMENT_ROOT']) {  // Mode web
3808
                    $out .= "<b>" . Globals::$langs->trans("Message") . ":</b> " . DolUtils::dol_escape_htmltag($msg) . "<br>\n";
3809
                } else {                        // Mode CLI
3810
                    $out .= '> ' . Globals::$langs->transnoentities("Message") . ":\n" . $msg . "\n";
3811
                }
3812
                $syslog .= ", msg=" . $msg;
3813
            }
3814
        }
3815
        if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
3816
            xdebug_print_function_stack();
3817
            $out .= '<b>XDebug informations:</b>' . "<br>\n";
3818
            $out .= 'File: ' . xdebug_call_file() . "<br>\n";
3819
            $out .= 'Line: ' . xdebug_call_line() . "<br>\n";
3820
            $out .= 'Function: ' . xdebug_call_function() . "<br>\n";
3821
            $out .= "<br>\n";
3822
        }
3823
3824
        if (empty($dolibarr_main_prod)) {
3825
            print $out;
3826
        } else {
3827
            print Globals::$langs->trans("DolibarrHasDetectedError") . '. ';
3828
            print Globals::$langs->trans("YouCanSetOptionDolibarrMainProdToZero");
3829
            define("MAIN_CORE_ERROR", 1);
3830
        }
3831
//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.';
3832
        DolUtils::dol_syslog("Error " . $syslog, LOG_ERR);
3833
    }
3834
3835
    /**
3836
     * Show a public email and error code to contact if technical error
3837
     *
3838
     * @param	string	$prefixcode		Prefix of public error code
3839
     * @param   string  $errormessage   Complete error message
3840
     * @param	array	$errormessages	Array of error messages
3841
     * @param	string	$morecss		More css
3842
     * @param	string	$email			Email
3843
     * @return	void
3844
     */
3845
    static function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
3846
    {
3847
        // global Globals::$langs, Globals::$conf;
3848
3849
        if (empty($email)) {
3850
            $email = Globals::$conf->global->MAIN_INFO_SOCIETE_MAIL;
3851
        }
3852
3853
        Globals::$langs->load("errors");
3854
        $now = dol_now();
3855
3856
        print '<br><div class="center login_main_message"><div class="' . $morecss . '">';
3857
        print Globals::$langs->trans("ErrorContactEMail", $email, $prefixcode . DolUtils::dol_print_date($now, '%Y%m%d'));
3858
        if ($errormessage) {
3859
            print '<br><br>' . $errormessage;
3860
        }
3861
        if (is_array($errormessages) && count($errormessages)) {
3862
            foreach ($errormessages as $mesgtoshow) {
3863
                print '<br><br>' . $mesgtoshow;
3864
            }
3865
        }
3866
        print '</div></div>';
3867
    }
3868
3869
    /**
3870
     * 	Show title line of an array
3871
     *
3872
     * 	@param	string	$name        Label of field
3873
     * 	@param	string	$file        Url used when we click on sort picto
3874
     * 	@param	string	$field       Field to use for new sorting
3875
     * 	@param	string	$begin       ("" by defaut)
3876
     * 	@param	string	$moreparam   Add more parameters on sort url links ("" by default)
3877
     * 	@param  string	$moreattrib  Options of attribute td ("" by defaut, example: 'align="center"')
3878
     * 	@param  string	$sortfield   Current field used to sort
3879
     * 	@param  string	$sortorder   Current sort order
3880
     *  @param	string	$prefix		 Prefix for css. Use space after prefix to add your own CSS tag.
3881
     *  @param	string	$tooltip	 Tooltip
3882
     * 	@return	void
3883
     */
3884
    static function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "")
3885
    {
3886
        print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip);
3887
    }
3888
3889
    /**
3890
     * 	Get title line of an array
3891
     *
3892
     * 	@param	string	$name        		Translation key of field
3893
     * 	@param	int		$thead		 		0=To use with standard table format, 1=To use inside <thead><tr>, 2=To use with <div>
3894
     * 	@param	string	$file        		Url used when we click on sort picto
3895
     * 	@param	string	$field       		Field to use for new sorting. Empty if this field is not sortable. Example "t.abc" or "t.abc,t.def"
3896
     * 	@param	string	$begin       		("" by defaut)
3897
     * 	@param	string	$moreparam   		Add more parameters on sort url links ("" by default)
3898
     * 	@param  string	$moreattrib  		Add more attributes on th ("" by defaut, example: 'align="center"'). To add more css class, use param $prefix.
3899
     * 	@param  string	$sortfield   		Current field used to sort (Ex: 'd.datep,d.id')
3900
     * 	@param  string	$sortorder   		Current sort order (Ex: 'asc,desc')
3901
     *  @param	string	$prefix		 		Prefix for css. Use space after prefix to add your own CSS tag, for example 'mycss '.
3902
     *  @param	string	$disablesortlink	1=Disable sort link
3903
     *  @param	string	$tooltip	 		Tooltip
3904
     * 	@return	string
3905
     */
3906
    static function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '')
3907
    {
3908
        // global Globals::$conf, Globals::$langs, $form;
3909
//print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
3910
3911
        $sortorder = strtoupper($sortorder);
3912
        $out = '';
3913
        $sortimg = '';
3914
3915
        $tag = 'th';
3916
        if ($thead == 2) {
3917
            $tag = 'div';
3918
        }
3919
3920
        $tmpsortfield = explode(',', $sortfield);
3921
        $sortfield1 = trim($tmpsortfield[0]);    // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
3922
        $tmpfield = explode(',', $field);
3923
        $field1 = trim($tmpfield[0]);            // If $field is 'd.datep,d.id', it becomes 'd.datep'
3924
//var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
3925
// If field is used as sort criteria we use a specific css class liste_titre_sel
3926
// Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
3927
        if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
3928
            $out .= '<' . $tag . ' class="' . $prefix . 'liste_titre_sel" ' . $moreattrib . '>';
3929
        } else {
3930
            $out .= '<' . $tag . ' class="' . $prefix . 'liste_titre" ' . $moreattrib . '>';
3931
        }
3932
3933
        if (empty($thead) && $field && empty($disablesortlink)) {    // If this is a sort field
3934
            $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', $moreparam);
3935
            $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
3936
            $options = preg_replace('/&+/i', '&', $options);
3937
            if (!preg_match('/^&/', $options)) {
3938
                $options = '&' . $options;
3939
            }
3940
3941
            $sortordertouseinlink = '';
3942
            if ($field1 != $sortfield1) { // We are on another field than current sorted field
3943
                if (preg_match('/^DESC/i', $sortorder)) {
3944
                    $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
3945
                } else {  // We reverse the var $sortordertouseinlink
3946
                    $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
3947
                }
3948
            } else {                        // We are on field that is the first current sorting criteria
3949
                if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
3950
                    $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
3951
                } else {
3952
                    $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
3953
                }
3954
            }
3955
            $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
3956
            $out .= '<a class="reposition" href="' . $file . '?sortfield=' . $field . '&sortorder=' . $sortordertouseinlink . '&begin=' . $begin . $options . '">';
3957
        }
3958
3959
        if ($tooltip) {
3960
            $out .= $form->textwithpicto(Globals::$langs->trans($name), Globals::$langs->trans($tooltip));
3961
        } else {
3962
            $out .= Globals::$langs->trans($name);
3963
        }
3964
3965
        if (empty($thead) && $field && empty($disablesortlink)) {    // If this is a sort field
3966
            $out .= '</a>';
3967
        }
3968
3969
        if (empty($thead) && $field) {    // If this is a sort field
3970
            $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', $moreparam);
3971
            $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
3972
            $options = preg_replace('/&+/i', '&', $options);
3973
            if (!preg_match('/^&/', $options)) {
3974
                $options = '&' . $options;
3975
            }
3976
3977
            if (!$sortorder || $field1 != $sortfield1) {
3978
//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
3979
//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
3980
            } else {
3981
                if (preg_match('/^DESC/', $sortorder)) {
3982
                    //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
3983
                    //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
3984
                    $sortimg .= '<span class="nowrap">' . img_up("Z-A", 0) . '</span>';
3985
                }
3986
                if (preg_match('/^ASC/', $sortorder)) {
3987
                    //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
3988
                    //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
3989
                    $sortimg .= '<span class="nowrap">' . img_down("A-Z", 0) . '</span>';
3990
                }
3991
            }
3992
        }
3993
3994
        $out .= $sortimg;
3995
3996
        $out .= '</' . $tag . '>';
3997
3998
        return $out;
3999
    }
4000
4001
    /**
4002
     * 	Show a title.
4003
     *
4004
     * 	@param	string	$title			Title to show
4005
     * 	@return	string					Title to show
4006
     *  @deprecated						Use load_fiche_titre instead
4007
     *  @see load_fiche_titre
4008
     */
4009
    static function print_titre($title)
4010
    {
4011
        DolUtils::dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
4012
4013
        print '<div class="titre">' . $title . '</div>';
4014
    }
4015
4016
    /**
4017
     * 	Show a title with picto
4018
     *
4019
     * 	@param	string	$title				Title to show
4020
     * 	@param	string	$mesg				Added message to show on right
4021
     * 	@param	string	$picto				Icon to use before title (should be a 32x32 transparent png file)
4022
     * 	@param	int		$pictoisfullpath	1=Icon name is a full absolute url of image
4023
     * 	@param	int		$id					To force an id on html objects
4024
     * 	@return	void
4025
     *  @deprecated Use print load_fiche_titre instead
4026
     */
4027
    static function print_fiche_titre($title, $mesg = '', $picto = 'title_generic.png', $pictoisfullpath = 0, $id = '')
4028
    {
4029
        print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
4030
    }
4031
4032
    /**
4033
     * 	Load a title with picto
4034
     *
4035
     * 	@param	string	$titre				Title to show
4036
     * 	@param	string	$morehtmlright		Added message to show on right
4037
     * 	@param	string	$picto				Icon to use before title (should be a 32x32 transparent png file)
4038
     * 	@param	int		$pictoisfullpath	1=Icon name is a full absolute url of image
4039
     * 	@param	string	$id					To force an id on html objects
4040
     *  @param  string  $morecssontable     More css on table
4041
     * 	@param	string	$morehtmlcenter		Added message to show on center
4042
     * 	@return	string
4043
     *  @see print_barre_liste
4044
     */
4045
    static function load_fiche_titre($titre, $morehtmlright = '', $picto = 'title_generic.png', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
4046
    {
4047
        // global Globals::$conf;
4048
4049
        $return = '';
4050
4051
        if ($picto == 'setup') {
4052
            $picto = 'title_generic.png';
4053
        }
4054
4055
        $return .= "\n";
4056
        $return .= '<table ' . ($id ? 'id="' . $id . '" ' : '') . 'summary="" class="centpercent notopnoleftnoright' . ($morecssontable ? ' ' . $morecssontable : '') . '" style="margin-bottom: 6px;"><tr>'; // maring bottom must be same than into print_barre_list
4057
        if ($picto) {
4058
            $return .= '<td class="nobordernopadding widthpictotitle opacityhigh" valign="middle">' . DolUtils::img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath) . '</td>';
4059
        }
4060
        $return .= '<td class="nobordernopadding valignmiddle">';
4061
        $return .= '<div class="titre inline-block">' . $titre . '</div>';
4062
        $return .= '</td>';
4063
        if (DolUtils::dol_strlen($morehtmlcenter)) {
4064
            $return .= '<td class="nobordernopadding" align="center" valign="middle">' . $morehtmlcenter . '</td>';
4065
        }
4066
        if (DolUtils::dol_strlen($morehtmlright)) {
4067
            $return .= '<td class="nobordernopadding titre_right wordbreak" align="right" valign="middle">' . $morehtmlright . '</td>';
4068
        }
4069
        $return .= '</tr></table>' . "\n";
4070
4071
        return $return;
4072
    }
4073
4074
    /**
4075
     * 	Print a title with navigation controls for pagination
4076
     *
4077
     * 	@param	string	    $titre				Title to show (required)
4078
     * 	@param	int   	    $page				Numero of page to show in navigation links (required)
4079
     * 	@param	string	    $file				Url of page (required)
4080
     * 	@param	string	    $options         	More parameters for links ('' by default, does not include sortfield neither sortorder). Value must be 'urlencoded' before calling function.
4081
     * 	@param	string    	$sortfield       	Field to sort on ('' by default)
4082
     * 	@param	string	    $sortorder       	Order to sort ('' by default)
4083
     * 	@param	string	    $morehtmlcenter     String in the middle ('' by default). We often find here string $massaction comming from $form->selectMassAction()
4084
     * 	@param	int		    $num				Number of records found by select with limit+1
4085
     * 	@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.
4086
     * 	@param	string	    $picto				Icon to use before title (should be a 32x32 transparent png file)
4087
     * 	@param	int		    $pictoisfullpath	1=Icon name is a full absolute url of image
4088
     *  @param	string	    $morehtmlright			More html to show
4089
     *  @param  string      $morecss            More css to the table
4090
     *  @param  int         $limit              Max number of lines (-1 = use default, 0 = no limit, > 0 = limit).
4091
     *  @param  int         $hideselectlimit    Force to hide select limit
4092
     *  @param  int         $hidenavigation     Force to hide all navigation tools
4093
     * 	@return	void
4094
     */
4095
    static function print_barre_liste($titre, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'title_generic.png', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $hideselectlimit = 0, $hidenavigation = 0)
4096
    {
4097
        // global Globals::$conf, Globals::$langs;
4098
4099
        $savlimit = $limit;
4100
        $savtotalnboflines = $totalnboflines;
4101
        $totalnboflines = abs($totalnboflines);
4102
4103
        if ($picto == 'setup')
4104
            $picto = 'title_setup.png';
4105
        if ((Globals::$conf->browser->name == 'ie') && $picto == 'title_generic.png')
4106
            $picto = 'title.gif';
4107
        if ($limit < 0)
4108
            $limit = Globals::$conf->liste_limit;
4109
        if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
4110
            $nextpage = 1;
4111
        } else {
4112
            $nextpage = 0;
4113
        }
4114
//print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage;
4115
4116
        print "\n";
4117
        print "<!-- Begin title '" . $titre . "' -->\n";
4118
        print '<table border="0" class="centpercent notopnoleftnoright' . ($morecss ? ' ' . $morecss : '') . '" style="margin-bottom: 6px;"><tr>'; // maring bottom must be same than into load_fiche_tire
4119
// Left
4120
//if ($picto && $titre) print '<td class="nobordernopadding hideonsmartphone" width="40" align="left" valign="middle">'.img_picto('', $picto, 'id="pictotitle"', $pictoisfullpath).'</td>';
4121
        print '<td class="nobordernopadding valignmiddle">';
4122
        if ($picto && $titre)
4123
            print img_picto('', $picto, 'class="hideonsmartphone valignmiddle opacityhigh pictotitle widthpictotitle"', $pictoisfullpath);
4124
        print '<div class="titre inline-block">' . $titre;
4125
        if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '')
4126
            print ' (' . $totalnboflines . ')';
4127
        print '</div></td>';
4128
4129
// Center
4130
        if ($morehtmlcenter) {
4131
            print '<td class="nobordernopadding center valignmiddle">' . $morehtmlcenter . '</td>';
4132
        }
4133
4134
// Right
4135
        print '<td class="nobordernopadding valignmiddle" align="right">';
4136
        if ($sortfield)
4137
            $options .= "&sortfield=" . urlencode($sortfield);
4138
        if ($sortorder)
4139
            $options .= "&sortorder=" . urlencode($sortorder);
4140
// Show navigation bar
4141
        $pagelist = '';
4142
        if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
4143
            if ($totalnboflines) { // If we know total nb of lines
4144
// Define nb of extra page links before and after selected page + ... + first or last
4145
                $maxnbofpage = (empty(Globals::$conf->dol_optimize_smallscreen) ? 4 : 1);
4146
4147
                if ($limit > 0)
4148
                    $nbpages = ceil($totalnboflines / $limit);
4149
                else
4150
                    $nbpages = 1;
4151
                $cpt = ($page - $maxnbofpage);
4152
                if ($cpt < 0) {
4153
                    $cpt = 0;
4154
                }
4155
4156
                if ($cpt >= 1) {
4157
                    $pagelist .= '<li' . ((Globals::$conf->dol_use_jmobile != 4) ? ' class="pagination"' : '') . '><a href="' . $file . '?page=0' . $options . '">1</a></li>';
4158
                    if ($cpt > 2)
4159
                        $pagelist .= '<li' . ( (Globals::$conf->dol_use_jmobile != 4) ? ' class="pagination"' : '') . '><span ' . ((Globals::$conf->dol_use_jmobile != 4) ? 'class="inactive"' : '') . '>...</span></li>';
4160
                    else if ($cpt == 2)
4161
                        $pagelist .= '<li' . ( (Globals::$conf->dol_use_jmobile != 4) ? ' class="pagination"' : '') . '><a href="' . $file . '?page=1' . $options . '">2</a></li>';
4162
                }
4163
4164
                do {
4165
                    if ($cpt == $page) {
4166
                        $pagelist .= '<li' . ((Globals::$conf->dol_use_jmobile != 4) ? ' class="pagination"' : '') . '><span ' . ((Globals::$conf->dol_use_jmobile != 4) ? 'class="active"' : '') . '>' . ($page + 1) . '</span></li>';
4167
                    } else {
4168
                        $pagelist .= '<li' . ((Globals::$conf->dol_use_jmobile != 4) ? ' class="pagination"' : '') . '><a href="' . $file . '?page=' . $cpt . $options . '">' . ($cpt + 1) . '</a></li>';
4169
                    }
4170
                    $cpt++;
4171
                } while ($cpt < $nbpages && $cpt <= $page + $maxnbofpage);
4172
4173
                if ($cpt < $nbpages) {
4174
                    if ($cpt < $nbpages - 2)
4175
                        $pagelist .= '<li' . ( (Globals::$conf->dol_use_jmobile != 4) ? ' class="pagination"' : '') . '><span ' . ((Globals::$conf->dol_use_jmobile != 4) ? 'class="inactive"' : '') . '>...</span></li>';
4176
                    else if ($cpt == $nbpages - 2)
4177
                        $pagelist .= '<li' . ( (Globals::$conf->dol_use_jmobile != 4) ? ' class="pagination"' : '') . '><a href="' . $file . '?page=' . ($nbpages - 2) . $options . '">' . ($nbpages - 1) . '</a></li>';
4178
                    $pagelist .= '<li' . ((Globals::$conf->dol_use_jmobile != 4) ? ' class="pagination"' : '') . '><a href="' . $file . '?page=' . ($nbpages - 1) . $options . '">' . $nbpages . '</a></li>';
4179
                }
4180
            }
4181
            else {
4182
                $pagelist .= '<li' . ((Globals::$conf->dol_use_jmobile != 4) ? ' class="pagination"' : '') . '><span ' . ((Globals::$conf->dol_use_jmobile != 4) ? 'class="active"' : '') . '>' . ($page + 1) . "</li>";
4183
            }
4184
        }
4185
4186
        print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $hideselectlimit);  // output the div and ul for previous/last completed with page numbers into $pagelist
4187
4188
        print '</td>';
4189
4190
        print '</tr></table>' . "\n";
4191
        print "<!-- End title -->\n\n";
4192
    }
4193
4194
    /**
4195
     * 	Function to show navigation arrows into lists
4196
     *
4197
     * 	@param	int				$page				Number of page
4198
     * 	@param	string			$file				Page URL (in most cases provided with $_SERVER["PHP_SELF"])
4199
     * 	@param	string			$options         	Other url paramaters to propagate ("" by default, may include sortfield and sortorder)
4200
     * 	@param	integer			$nextpage	    	Do we show a next page button
4201
     * 	@param	string			$betweenarrows		HTML content to show between arrows. MUST contains '<li> </li>' tags or '<li><span> </span></li>'.
4202
     *  @param	string			$afterarrows		HTML content to show after arrows. Must NOT contains '<li> </li>' tags.
4203
     *  @param  int             $limit              Max nb of record to show  (-1 = no combo with limit, 0 = no limit, > 0 = limit)
4204
     * 	@param	int		        $totalnboflines		Total number of records/lines for all pages (if known)
4205
     *  @param  int             $hideselectlimit    Force to hide select limit
4206
     * 	@return	void
4207
     */
4208
    static function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0)
4209
    {
4210
        // global Globals::$conf, Globals::$langs;
4211
4212
        print '<div class="pagination"><ul>';
4213
        if ((int) $limit >= 0 && empty($hideselectlimit)) {
4214
            $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000,5000:5000';
4215
//$pagesizechoices.=',0:'.Globals::$langs->trans("All");     // Not yet supported
4216
//$pagesizechoices.=',2:2';
4217
            if (!empty(Globals::$conf->global->MAIN_PAGESIZE_CHOICES))
4218
                $pagesizechoices = Globals::$conf->global->MAIN_PAGESIZE_CHOICES;
4219
4220
            print '<li class="pagination">';
4221
            print '<select class="flat selectlimit" name="limit" title="' . DolUtils::dol_escape_htmltag(Globals::$langs->trans("MaxNbOfRecordPerPage")) . '">';
4222
            $tmpchoice = explode(',', $pagesizechoices);
4223
            $tmpkey = $limit . ':' . $limit;
4224
            if (!in_array($tmpkey, $tmpchoice))
4225
                $tmpchoice[] = $tmpkey;
4226
            $tmpkey = Globals::$conf->liste_limit . ':' . Globals::$conf->liste_limit;
4227
            if (!in_array($tmpkey, $tmpchoice))
4228
                $tmpchoice[] = $tmpkey;
4229
            asort($tmpchoice, SORT_NUMERIC);
4230
            $found = false;
4231
            foreach ($tmpchoice as $val) {
4232
                $selected = '';
4233
                $tmp = explode(':', $val);
4234
                $key = $tmp[0];
4235
                $val = $tmp[1];
4236
                if ($key != '' && $val != '') {
4237
                    if ((int) $key == (int) $limit) {
4238
                        $selected = ' selected="selected"';
4239
                        $found = true;
4240
                    }
4241
                    print '<option name="' . $key . '"' . $selected . '>' . DolUtils::dol_escape_htmltag($val) . '</option>' . "\n";
4242
                }
4243
            }
4244
            print '</select>';
4245
            if (Globals::$conf->use_javascript_ajax) {
4246
                print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
4247
            		<script type="text/javascript">
4248
                	jQuery(document).ready(static function () {
4249
            	  		jQuery(".selectlimit").change(function() {
4250
                            console.log("Change limit. Send submit");
4251
                            $(this).parents(\'form:first\').submit();
4252
            	  		});
4253
                	});
4254
            		</script>
4255
                ';
4256
            }
4257
            print '</li>';
4258
        }
4259
        if ($page > 0) {
4260
            print '<li class="pagination"><a class="paginationprevious" href="' . $file . '?page=' . ($page - 1) . $options . '"><i class="fa fa-chevron-left" title="' . DolUtils::dol_escape_htmltag(Globals::$langs->trans("Previous")) . '"></i></a></li>';
4261
        }
4262
        if ($betweenarrows) {
4263
            print $betweenarrows;
4264
        }
4265
        if ($nextpage > 0) {
4266
            print '<li class="pagination"><a class="paginationnext" href="' . $file . '?page=' . ($page + 1) . $options . '"><i class="fa fa-chevron-right" title="' . DolUtils::dol_escape_htmltag(Globals::$langs->trans("Next")) . '"></i></a></li>';
4267
        }
4268
        if ($afterarrows) {
4269
            print '<li class="paginationafterarrows">';
4270
            print $afterarrows;
4271
            print '</li>';
4272
        }
4273
        print '</ul></div>' . "\n";
4274
    }
4275
4276
    /**
4277
     * 	Return a string with VAT rate label formated for view output
4278
     * 	Used into pdf and HTML pages
4279
     *
4280
     * 	@param	string	$rate			Rate value to format ('19.6', '19,6', '19.6%', '19,6%', '19.6 (CODEX)', ...)
4281
     *  @param	boolean	$addpercent		Add a percent % sign in output
4282
     * 	@param	int		$info_bits		Miscellaneous information on vat (0=Default, 1=French NPR vat)
4283
     * 	@param	int		$usestarfornpr	-1=Never show, 0 or 1=Use '*' for NPR vat rates
4284
     *  @return	string					String with formated amounts ('19,6' or '19,6%' or '8.5% (NPR)' or '8.5% *' or '19,6 (CODEX)')
4285
     */
4286
    static function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0)
4287
    {
4288
        $morelabel = '';
4289
4290
        if (preg_match('/%/', $rate)) {
4291
            $rate = str_replace('%', '', $rate);
4292
            $addpercent = true;
4293
        }
4294
        if (preg_match('/\((.*)\)/', $rate, $reg)) {
4295
            $morelabel = ' (' . $reg[1] . ')';
4296
            $rate = preg_replace('/\s*' . preg_quote($morelabel, '/') . '/', '', $rate);
4297
        }
4298
        if (preg_match('/\*/', $rate)) {
4299
            $rate = str_replace('*', '', $rate);
4300
            $info_bits |= 1;
4301
        }
4302
4303
// If rate is '9/9/9' we don't change it.  If rate is '9.000' we apply price()
4304
        if (!preg_match('/\//', $rate))
4305
            $ret = price($rate, 0, '', 0, 0) . ($addpercent ? '%' : '');
4306
        else {
4307
// TODO Split on / and output with a price2num to have clean numbers without ton of 000.
4308
            $ret = $rate . ($addpercent ? '%' : '');
4309
        }
4310
        if (($info_bits & 1) && $usestarfornpr >= 0)
4311
            $ret .= ' *';
4312
        $ret .= $morelabel;
4313
        return $ret;
4314
    }
4315
4316
    /**
4317
     * 		Function to format a value into an amount for visual output
4318
     * 		Function used into PDF and HTML pages
4319
     *
4320
     * 		@param	float		$amount			Amount to format
4321
     * 		@param	integer		$form			Type of format, HTML or not (not by default)
4322
     * 		@param	Translate	$outlangs		Object langs for output
4323
     * 		@param	int			$trunc			1=Truncate if there is more decimals than MAIN_MAX_DECIMALS_SHOWN (default), 0=Does not truncate. Deprecated because amount are rounded (to unit or total amount accurancy) before beeing inserted into database or after a computation, so this parameter should be useless.
4324
     * 		@param	int			$rounding		Minimum number of decimal to show. If 0, no change, if -1, we use min(Globals::$conf->global->MAIN_MAX_DECIMALS_UNIT,Globals::$conf->global->MAIN_MAX_DECIMALS_TOT)
4325
     * 		@param	int			$forcerounding	Force the number of decimal to forcerounding decimal (-1=do not force)
4326
     * 		@param	string		$currency_code	To add currency symbol (''=add nothing, 'auto'=Use default currency, 'XXX'=add currency symbols for XXX currency)
4327
     * 		@return	string						Chaine avec montant formate
4328
     *
4329
     * 		@see	price2num					Revert static function of price
4330
     */
4331
    static function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
4332
    {
4333
        // global Globals::$langs, Globals::$conf;
4334
// Clean parameters
4335
        if (empty($amount))
4336
            $amount = 0; // To have a numeric value if amount not defined or = ''
4337
        $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
4338
        if ($rounding < 0)
4339
            $rounding = min(Globals::$conf->global->MAIN_MAX_DECIMALS_UNIT, Globals::$conf->global->MAIN_MAX_DECIMALS_TOT);
4340
        $nbdecimal = $rounding;
4341
4342
// Output separators by default (french)
4343
        $dec = ',';
4344
        $thousand = ' ';
4345
4346
// If $outlangs not forced, we use use language
4347
        if (!is_object($outlangs))
4348
            $outlangs = Globals::$langs;
4349
4350
        if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal")
4351
            $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
4352
        if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand")
4353
            $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
4354
        if ($thousand == 'None')
4355
            $thousand = '';
4356
        else if ($thousand == 'Space')
4357
            $thousand = ' ';
4358
//print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
4359
//print "amount=".$amount."-";
4360
        $amount = str_replace(',', '.', $amount); // should be useless
4361
//print $amount."-";
4362
        $datas = explode('.', $amount);
4363
        $decpart = isset($datas[1]) ? $datas[1] : '';
4364
        $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
4365
//print "decpart=".$decpart."<br>";
4366
        $end = '';
4367
4368
// We increase nbdecimal if there is more decimal than asked (to not loose information)
4369
        if (DolUtils::dol_strlen($decpart) > $nbdecimal)
4370
            $nbdecimal = DolUtils::dol_strlen($decpart);
4371
// Si on depasse max
4372
        if ($trunc && $nbdecimal > Globals::$conf->global->MAIN_MAX_DECIMALS_SHOWN) {
4373
            $nbdecimal = Globals::$conf->global->MAIN_MAX_DECIMALS_SHOWN;
4374
            if (preg_match('/\.\.\./i', Globals::$conf->global->MAIN_MAX_DECIMALS_SHOWN)) {
4375
// Si un affichage est tronque, on montre des ...
4376
                $end = '...';
4377
            }
4378
        }
4379
4380
// If force rounding
4381
        if ($forcerounding >= 0)
4382
            $nbdecimal = $forcerounding;
4383
4384
// Format number
4385
        $output = number_format($amount, $nbdecimal, $dec, $thousand);
4386
        if ($form) {
4387
            $output = preg_replace('/\s/', '&nbsp;', $output);
4388
            $output = preg_replace('/\'/', '&#039;', $output);
4389
        }
4390
// Add symbol of currency if requested
4391
        $cursymbolbefore = $cursymbolafter = '';
4392
        if ($currency_code) {
4393
            if ($currency_code == 'auto')
4394
                $currency_code = Globals::$conf->currency;
4395
4396
            $listofcurrenciesbefore = array('USD', 'GBP', 'AUD', 'MXN', 'PEN', 'CNY');
4397
            if (in_array($currency_code, $listofcurrenciesbefore))
4398
                $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
4399
            else {
4400
                $tmpcur = $outlangs->getCurrencySymbol($currency_code);
4401
                $cursymbolafter .= ($tmpcur == $currency_code ? ' ' . $tmpcur : $tmpcur);
4402
            }
4403
        }
4404
        $output = $cursymbolbefore . $output . $end . ($cursymbolafter ? ' ' : '') . $cursymbolafter;
4405
4406
        return $output;
4407
    }
4408
4409
    /**
4410
     * 	Function that return a number with universal decimal format (decimal separator is '.') from an amount typed by a user.
4411
     * 	Function to use on each input amount before any numeric test or database insert
4412
     *
4413
     * 	@param	float	$amount			Amount to convert/clean
4414
     * 	@param	string	$rounding		''=No rounding
4415
     * 									'MU'=Round to Max unit price (MAIN_MAX_DECIMALS_UNIT)
4416
     * 									'MT'=Round to Max for totals with Tax (MAIN_MAX_DECIMALS_TOT)
4417
     * 									'MS'=Round to Max for stock quantity (MAIN_MAX_DECIMALS_STOCK)
4418
     * 									Numeric = Nb of digits for rounding
4419
     * 	@param	int		$alreadysqlnb	Put 1 if you know that content is already universal format number
4420
     * 	@return	string					Amount with universal numeric format (Example: '99.99999') or unchanged text if conversion fails. If amount is null or '', it returns ''.
4421
     *
4422
     * 	@see    price					Opposite static function of price2num
4423
     */
4424
    static function price2num($amount, $rounding = '', $alreadysqlnb = 0)
4425
    {
4426
        // global Globals::$langs, Globals::$conf;
4427
// Round PHP static function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
4428
// Numbers must be '1234.56'
4429
// Decimal delimiter for PHP and database SQL requests must be '.'
4430
        $dec = ',';
4431
        $thousand = ' ';
4432
        if (Globals::$langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal")
4433
            $dec = Globals::$langs->transnoentitiesnoconv("SeparatorDecimal");
4434
        if (Globals::$langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand")
4435
            $thousand = Globals::$langs->transnoentitiesnoconv("SeparatorThousand");
4436
        if ($thousand == 'None')
4437
            $thousand = '';
4438
        elseif ($thousand == 'Space')
4439
            $thousand = ' ';
4440
//print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
4441
// Convert value to universal number format (no thousand separator, '.' as decimal separator)
4442
        if ($alreadysqlnb != 1) { // If not a PHP number or unknown, we change format
4443
//print 'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
4444
// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
4445
// to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
4446
            if (is_numeric($amount)) {
4447
// We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
4448
                $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
4449
                $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
4450
                $nbofdec = max(0, DolUtils::dol_strlen($temps) - 2); // -2 to remove "0."
4451
                $amount = number_format($amount, $nbofdec, $dec, $thousand);
4452
            }
4453
//print "QQ".$amount.'<br>';
4454
// Now make replace (the main goal of function)
4455
            if ($thousand != ',' && $thousand != '.')
4456
                $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
4457
            $amount = str_replace(' ', '', $amount);  // To avoid spaces
4458
            $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
4459
            $amount = str_replace($dec, '.', $amount);
4460
        }
4461
4462
// Now, make a rounding if required
4463
        if ($rounding) {
4464
            $nbofdectoround = '';
4465
            if ($rounding == 'MU')
4466
                $nbofdectoround = Globals::$conf->global->MAIN_MAX_DECIMALS_UNIT;
4467
            elseif ($rounding == 'MT')
4468
                $nbofdectoround = Globals::$conf->global->MAIN_MAX_DECIMALS_TOT;
4469
            elseif ($rounding == 'MS')
4470
                $nbofdectoround = empty(Globals::$conf->global->MAIN_MAX_DECIMALS_STOCK) ? 5 : Globals::$conf->global->MAIN_MAX_DECIMALS_STOCK;
4471
            elseif (is_numeric($rounding))
4472
                $nbofdectoround = $rounding;
4473
//print "RR".$amount.' - '.$nbofdectoround.'<br>';
4474
            if (DolUtils::dol_strlen($nbofdectoround))
4475
                $amount = round($amount, $nbofdectoround); // $nbofdectoround can be 0.
4476
            else
4477
                return 'ErrorBadParameterProvidedToFunction';
4478
//print 'SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
4479
// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
4480
// to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
4481
            if (is_numeric($amount)) {
4482
// We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
4483
                $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
4484
                $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
4485
                $nbofdec = max(0, DolUtils::dol_strlen($temps) - 2); // -2 to remove "0."
4486
                $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand);  // Convert amount to format with dolibarr dec and thousand
4487
            }
4488
//print "TT".$amount.'<br>';
4489
// Always make replace because each math static function (like round) replace
4490
// with local values and we want a number that has a SQL string format x.y
4491
            if ($thousand != ',' && $thousand != '.')
4492
                $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
4493
            $amount = str_replace(' ', '', $amount);  // To avoid spaces
4494
            $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
4495
            $amount = str_replace($dec, '.', $amount);
4496
        }
4497
4498
        return $amount;
4499
    }
4500
4501
    /**
4502
     * Output a dimension with best unit
4503
     *
4504
     * @param   float       $dimension      Dimension
4505
     * @param   int         $unit           Unit of dimension (0, -3, ...)
4506
     * @param   string      $type           'weight', 'volume', ...
4507
     * @param   Translate   $outputlangs    Translate language object
4508
     * @param   int         $round          -1 = non rounding, x = number of decimal
4509
     * @param   string      $forceunitoutput    'no' or numeric (-3, -6, ...) compared to $unit
4510
     * @return  string                      String to show dimensions
4511
     */
4512
    static function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no')
4513
    {
4514
        require_once DOL_BASE_PATH . '/core/lib/product.lib.php';
4515
4516
        if (($forceunitoutput == 'no' && $dimension < 1 / 10000) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
4517
            $dimension = $dimension * 1000000;
4518
            $unit = $unit - 6;
4519
        } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
4520
            $dimension = $dimension * 1000;
4521
            $unit = $unit - 3;
4522
        } elseif (($forceunitoutput == 'no' && $dimension > 100000000) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
4523
            $dimension = $dimension / 1000000;
4524
            $unit = $unit + 6;
4525
        } elseif (($forceunitoutput == 'no' && $dimension > 100000) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
4526
            $dimension = $dimension / 1000;
4527
            $unit = $unit + 3;
4528
        }
4529
4530
        $ret = price($dimension, 0, $outputlangs, 0, 0, $round) . ' ' . measuring_units_string($unit, $type);
4531
4532
        return $ret;
4533
    }
4534
4535
    /**
4536
     * 	Return localtax rate for a particular vat, when selling a product with vat $vatrate, from a $thirdparty_buyer to a $thirdparty_seller
4537
     *  Note: This static function applies same rules than get_default_tva
4538
     *
4539
     * 	@param	float		$vatrate		        Vat rate. Can be '8.5' or '8.5 (VATCODEX)' for example
4540
     * 	@param  int			$local		         	Local tax to search and return (1 or 2 return only tax rate 1 or tax rate 2)
4541
     *  @param  Societe		$thirdparty_buyer    	Object of buying third party
4542
     *  @param	Societe		$thirdparty_seller		Object of selling third party ($mysoc if not defined)
4543
     *  @param	int			$vatnpr					If vat rate is NPR or not
4544
     * 	@return	mixed			   					0 if not found, localtax rate if found
4545
     *  @see get_default_tva
4546
     */
4547
    static function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
4548
    {
4549
        // global $db, Globals::$conf, $mysoc;
4550
4551
        if (empty($thirdparty_seller) || !is_object($thirdparty_seller))
4552
            $thirdparty_seller = $mysoc;
4553
4554
        DolUtils::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);
4555
4556
        $vatratecleaned = $vatrate;
4557
        if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) {      // If vat is "xx (yy)"
4558
            $vatratecleaned = trim($reg[1]);
4559
            $vatratecode = $reg[2];
4560
        }
4561
4562
        /* if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
4563
          {
4564
          return 0;
4565
          } */
4566
4567
// Some test to guess with no need to make database access
4568
        if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
4569
            if ($local == 1) {
4570
                if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0")
4571
                    return 0;
4572
                if ($thirdparty_seller->id == $mysoc->id) {
4573
                    if (!$thirdparty_buyer->localtax1_assuj)
4574
                        return 0;
4575
                }
4576
                else {
4577
                    if (!$thirdparty_seller->localtax1_assuj)
4578
                        return 0;
4579
                }
4580
            }
4581
4582
            if ($local == 2) {
4583
//if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
4584
                if (!$mysoc->localtax2_assuj)
4585
                    return 0;  // If main vat is 0, IRPF may be different than 0.
4586
                if ($thirdparty_seller->id == $mysoc->id) {
4587
                    if (!$thirdparty_buyer->localtax2_assuj)
4588
                        return 0;
4589
                }
4590
                else {
4591
                    if (!$thirdparty_seller->localtax2_assuj)
4592
                        return 0;
4593
                }
4594
            }
4595
        }
4596
        else {
4597
            if ($local == 1 && !$thirdparty_seller->localtax1_assuj)
4598
                return 0;
4599
            if ($local == 2 && !$thirdparty_seller->localtax2_assuj)
4600
                return 0;
4601
        }
4602
4603
// For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
4604
        if (in_array($mysoc->country_code, array('ES'))) {
4605
            Globals::$conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
4606
        }
4607
4608
// Search local taxes
4609
        if (!empty(Globals::$conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) {
4610
            if ($local == 1) {
4611
                if ($thirdparty_seller != $mysoc) {
4612
                    if (!isOnlyOneLocalTax($local)) {  // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
4613
                        return $thirdparty_seller->localtax1_value;
4614
                    }
4615
                } else {  // i am the seller
4616
                    if (!isOnlyOneLocalTax($local)) {  // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
4617
                        return Globals::$conf->global->MAIN_INFO_VALUE_LOCALTAX1;
4618
                    }
4619
                }
4620
            }
4621
            if ($local == 2) {
4622
                if ($thirdparty_seller != $mysoc) {
4623
                    if (!isOnlyOneLocalTax($local)) {  // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
4624
                        // TODO We should also return value defined on thirdparty only if defined
4625
                        return $thirdparty_seller->localtax2_value;
4626
                    }
4627
                } else {  // i am the seller
4628
                    if (in_array($mysoc->country_code, array('ES'))) {
4629
                        return $thirdparty_buyer->localtax2_value;
4630
                    } else {
4631
                        return Globals::$conf->global->MAIN_INFO_VALUE_LOCALTAX2;
4632
                    }
4633
                }
4634
            }
4635
        }
4636
4637
// By default, search value of local tax on line of common tax
4638
        $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
4639
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
4640
        $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $thirdparty_seller->country_code . "'";
4641
        $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
4642
        if ($vatratecode)
4643
            $sql .= " AND t.code ='" . $vatratecode . "'";  // If we have the code, we use it in priority
4644
        else
4645
            $sql .= " AND t.recuperableonly ='" . $vatnpr . "'";
4646
        DolUtils::dol_syslog("get_localtax", LOG_DEBUG);
4647
        $resql = $db->query($sql);
4648
4649
        if ($resql) {
4650
            $obj = $db->fetch_object($resql);
4651
            if ($local == 1)
4652
                return $obj->localtax1;
4653
            elseif ($local == 2)
4654
                return $obj->localtax2;
4655
        }
4656
4657
        return 0;
4658
    }
4659
4660
    /**
4661
     * Return true if LocalTax (1 or 2) is unique.
4662
     * Example: If localtax1 is 5 on line with highest common vat rate, return true
4663
     * Example: If localtax1 is 5:8:15 on line with highest common vat rate, return false
4664
     *
4665
     * @param   int 	$local	Local tax to test (1 or 2)
4666
     * @return  boolean 		True if LocalTax have multiple values, False if not
4667
     */
4668
    static function isOnlyOneLocalTax($local)
4669
    {
4670
        $tax = get_localtax_by_third($local);
4671
4672
        $valors = explode(":", $tax);
4673
4674
        if (count($valors) > 1) {
4675
            return false;
4676
        } else {
4677
            return true;
4678
        }
4679
    }
4680
4681
    /**
4682
     * Get values of localtaxes (1 or 2) for company country for the common vat with the highest value
4683
     *
4684
     * @param	int		$local 	LocalTax to get
4685
     * @return	number			Values of localtax
4686
     */
4687
    static function get_localtax_by_third($local)
4688
    {
4689
        // global $db, $mysoc;
4690
        $sql = "SELECT t.localtax1, t.localtax2 ";
4691
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t inner join " . MAIN_DB_PREFIX . "c_country as c ON c.rowid=t.fk_pays";
4692
        $sql .= " WHERE c.code = '" . $mysoc->country_code . "' AND t.active = 1 AND t.taux=(";
4693
        $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";
4694
        $sql .= "  WHERE c.code = '" . $mysoc->country_code . "' AND tt.active = 1";
4695
        $sql .= "  )";
4696
4697
        $resql = $db->query($sql);
4698
        if ($resql) {
4699
            $obj = $db->fetch_object($resql);
4700
            if ($local == 1)
4701
                return $obj->localtax1;
4702
            elseif ($local == 2)
4703
                return $obj->localtax2;
4704
        }
4705
4706
        return 0;
4707
    }
4708
4709
    /**
4710
     *  Get vat main information from Id.
4711
     *  You can call getLocalTaxesFromRate after to get other fields.
4712
     *
4713
     *  @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.
4714
     *  @param	Societe	    $buyer         		Company object
4715
     *  @param	Societe	    $seller        		Company object
4716
     *  @param  int         $firstparamisid     1 if first param is id into table (use this if you can)
4717
     *  @return	array       	  				array('rowid'=> , 'code'=> ...)
4718
     *  @see getLocalTaxesFromRate
4719
     */
4720
    static function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
4721
    {
4722
        // global $db, $mysoc;
4723
4724
        DolUtils::dol_syslog("getTaxesFromId vat id or rate = " . $vatrate);
4725
4726
// Search local taxes
4727
        $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy";
4728
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
4729
        if ($firstparamisid)
4730
            $sql .= " WHERE t.rowid = " . (int) $vatrate;
4731
        else {
4732
            $vatratecleaned = $vatrate;
4733
            $vatratecode = '';
4734
            if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) {      // If vat is "xx (yy)"
4735
                $vatratecleaned = $reg[1];
4736
                $vatratecode = $reg[2];
4737
            }
4738
4739
            $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
4740
            /* if ($mysoc->country_code == 'ES') $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$buyer->country_code."'";    // vat in spain use the buyer country ??
4741
              else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'"; */
4742
            $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $seller->country_code . "'";
4743
            $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
4744
            if ($vatratecode)
4745
                $sql .= " AND t.code = '" . $vatratecode . "'";
4746
        }
4747
4748
        $resql = $db->query($sql);
4749
        if ($resql) {
4750
            $obj = $db->fetch_object($resql);
4751
            if ($obj)
4752
                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);
4753
            else
4754
                return array();
4755
        } else
4756
            dol_print_error($db);
4757
4758
        return array();
4759
    }
4760
4761
    /**
4762
     *  Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
4763
     *  This does not take into account the seller setup if subject to vat or not, only country.
4764
     *  TODO
4765
     *  This static function is ALSO called to retrieve type for building PDF. Such call of static function must be removed.
4766
     *  Instead this static function must be called when adding a line to get the array of localtax and type, and then
4767
     *  provide it to the static function calcul_price_total.
4768
     *
4769
     *  @param	int|string  $vatrate			VAT ID or Rate+Code. Value can be value or the string with code into parenthesis or rowid if $firstparamisid is 1. Example: '8.5' or '8.5 (8.5NPR)' or 123.
4770
     *  @param	int		    $local              Number of localtax (1 or 2, or 0 to return 1 & 2)
4771
     *  @param	Societe	    $buyer         		Company object
4772
     *  @param	Societe	    $seller        		Company object
4773
     *  @param  int         $firstparamisid     1 if first param is ID into table instead of Rate+code (use this if you can)
4774
     *  @return	array    	    				array(localtax_type1(1-6/0 if not found), rate localtax1, localtax_type2, rate localtax2, accountancycodecust, accountancycodesupp)
4775
     *  @see getTaxesFromId
4776
     */
4777
    static function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
4778
    {
4779
        // global $db, $mysoc;
4780
4781
        DolUtils::dol_syslog("getLocalTaxesFromRate vatrate=" . $vatrate . " local=" . $local);
4782
4783
// Search local taxes
4784
        $sql = "SELECT t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy";
4785
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t";
4786
        if ($firstparamisid)
4787
            $sql .= " WHERE t.rowid = " . (int) $vatrate;
4788
        else {
4789
            $vatratecleaned = $vatrate;
4790
            $vatratecode = '';
4791
            if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) {      // If vat is "x.x (yy)"
4792
                $vatratecleaned = $reg[1];
4793
                $vatratecode = $reg[2];
4794
            }
4795
4796
            $sql .= ", " . MAIN_DB_PREFIX . "c_country as c";
4797
            if ($mysoc->country_code == 'ES')
4798
                $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $buyer->country_code . "'";    // local tax in spain use the buyer country ??
4799
            else
4800
                $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '" . $seller->country_code . "'";
4801
            $sql .= " AND t.taux = " . ((float) $vatratecleaned) . " AND t.active = 1";
4802
            if ($vatratecode)
4803
                $sql .= " AND t.code = '" . $vatratecode . "'";
4804
        }
4805
4806
        $resql = $db->query($sql);
4807
        if ($resql) {
4808
            $obj = $db->fetch_object($resql);
4809
            if ($local == 1) {
4810
                return array($obj->localtax1_type, get_localtax($vatrate, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4811
            } elseif ($local == 2) {
4812
                return array($obj->localtax2_type, get_localtax($vatrate, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4813
            } else {
4814
                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);
4815
            }
4816
        }
4817
4818
        return 0;
4819
    }
4820
4821
    /**
4822
     * 	Return vat rate of a product in a particular selling country or default country vat if product is unknown
4823
     *  Function called by get_default_tva
4824
     *
4825
     *  @param	int			$idprod          	Id of product or 0 if not a predefined product
4826
     *  @param  Societe		$thirdparty_seller  Thirdparty with a ->country_code defined (FR, US, IT, ...)
4827
     * 	@param	int			$idprodfournprice	Id product_fournisseur_price (for "supplier" proposal/order/invoice)
4828
     *  @return float|string   				    Vat rate to use with format 5.0 or '5.0 (XXX)'
4829
     *  @see get_product_localtax_for_country
4830
     */
4831
    static function get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice = 0)
4832
    {
4833
        // global $db, Globals::$conf, $mysoc;
4834
4835
        require_once DOL_BASE_PATH . '/product/class/product.class.php';
4836
4837
        $ret = 0;
4838
        $found = 0;
4839
4840
        if ($idprod > 0) {
4841
// Load product
4842
            $product = new Product($db);
4843
            $result = $product->fetch($idprod);
4844
4845
            if ($mysoc->country_code == $thirdparty_seller->country_code) { // If selling country is ours
4846
                if ($idprodfournprice > 0) {     // We want vat for product for a "supplier" object
4847
                    $product->get_buyprice($idprodfournprice, 0, 0, 0);
4848
                    $ret = $product->vatrate_supplier;
4849
                    if ($product->default_vat_code)
4850
                        $ret .= ' (' . $product->default_vat_code . ')';
4851
                }
4852
                else {
4853
                    $ret = $product->tva_tx;    // Default vat of product we defined
4854
                    if ($product->default_vat_code)
4855
                        $ret .= ' (' . $product->default_vat_code . ')';
4856
                }
4857
                $found = 1;
4858
            }
4859
            else {
4860
// TODO Read default product vat according to countrycode and product. Vat for couple countrycode/product is a feature not implemeted yet.
4861
// May be usefull/required if hidden option SERVICE_ARE_ECOMMERCE_200238EC is on
4862
            }
4863
        }
4864
4865
        if (!$found) {
4866
            if (empty(Globals::$conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
4867
// If vat of product for the country not found or not defined, we return the first higher vat of country.
4868
                $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
4869
                $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
4870
                $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='" . $thirdparty_seller->country_code . "'";
4871
                $sql .= " ORDER BY t.taux DESC, t.code ASC, t.recuperableonly ASC";
4872
                $sql .= $db->plimit(1);
4873
4874
                $resql = $db->query($sql);
4875
                if ($resql) {
4876
                    $obj = $db->fetch_object($resql);
4877
                    if ($obj) {
4878
                        $ret = $obj->vat_rate;
4879
                        if ($obj->default_vat_code)
4880
                            $ret .= ' (' . $obj->default_vat_code . ')';
4881
                    }
4882
                    $db->free($sql);
4883
                } else
4884
                    dol_print_error($db);
4885
            } else
4886
                $ret = Globals::$conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;    // Forced value if autodetect fails
4887
        }
4888
4889
        DolUtils::dol_syslog("get_product_vat_for_country: ret=" . $ret);
4890
        return $ret;
4891
    }
4892
4893
    /**
4894
     * 	Return localtax vat rate of a product in a particular selling country or default country vat if product is unknown
4895
     *
4896
     *  @param	int		$idprod         		Id of product
4897
     *  @param  int		$local          		1 for localtax1, 2 for localtax 2
4898
     *  @param  Societe	$thirdparty_seller    	Thirdparty with a ->country_code defined (FR, US, IT, ...)
4899
     *  @return int             				<0 if KO, Vat rate if OK
4900
     *  @see get_product_vat_for_country
4901
     */
4902
    static function get_product_localtax_for_country($idprod, $local, $thirdparty_seller)
4903
    {
4904
        // global $db, $mysoc;
4905
4906
        if (!class_exists('Product')) {
4907
            require_once DOL_BASE_PATH . 'product/class/product.class.php';
4908
        }
4909
4910
        $ret = 0;
4911
        $found = 0;
4912
4913
        if ($idprod > 0) {
4914
// Load product
4915
            $product = new Product($db);
4916
            $result = $product->fetch($idprod);
4917
4918
            if ($mysoc->country_code == $thirdparty_seller->country_code) { // If selling country is ours
4919
                /* Not defined yet, so we don't use this
4920
                  if ($local==1) $ret=$product->localtax1_tx;
4921
                  elseif ($local==2) $ret=$product->localtax2_tx;
4922
                  $found=1;
4923
                 */
4924
            } else {
4925
// TODO Read default product vat according to countrycode and product
4926
            }
4927
        }
4928
4929
        if (!$found) {
4930
// If vat of product for the country not found or not defined, we return higher vat of country.
4931
            $sql = "SELECT taux as vat_rate, localtax1, localtax2";
4932
            $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t, " . MAIN_DB_PREFIX . "c_country as c";
4933
            $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='" . $thirdparty_seller->country_code . "'";
4934
            $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
4935
            $sql .= $db->plimit(1);
4936
4937
            $resql = $db->query($sql);
4938
            if ($resql) {
4939
                $obj = $db->fetch_object($resql);
4940
                if ($obj) {
4941
                    if ($local == 1)
4942
                        $ret = $obj->localtax1;
4943
                    elseif ($local == 2)
4944
                        $ret = $obj->localtax2;
4945
                }
4946
            } else
4947
                dol_print_error($db);
4948
        }
4949
4950
        DolUtils::dol_syslog("get_product_localtax_for_country: ret=" . $ret);
4951
        return $ret;
4952
    }
4953
4954
    /**
4955
     * 	Function that return vat rate of a product line (according to seller, buyer and product vat rate)
4956
     *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
4957
     * 	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
4958
     * 	 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.
4959
     * 	 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
4960
     * 	 Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise avec num TVA) intra alors TVA par defaut=0. Fin de regle
4961
     * 	 Sinon TVA proposee par defaut=0. Fin de regle.
4962
     *
4963
     * 	@param	Societe		$thirdparty_seller    	Objet societe vendeuse
4964
     * 	@param  Societe		$thirdparty_buyer   	Objet societe acheteuse
4965
     * 	@param  int			$idprod					Id product
4966
     * 	@param	int			$idprodfournprice		Id product_fournisseur_price (for supplier order/invoice)
4967
     * 	@return float|string   				      	Vat rate to use with format 5.0 or '5.0 (XXX)', -1 if we can't guess it
4968
     *  @see get_default_npr, get_default_localtax
4969
     */
4970
    static function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
4971
    {
4972
        // global Globals::$conf;
4973
4974
        require_once DOL_BASE_PATH . '/core/lib/company.lib.php';
4975
4976
// Note: possible values for tva_assuj are 0/1 or franchise/reel
4977
        $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;
4978
4979
        $seller_country_code = $thirdparty_seller->country_code;
4980
        $seller_in_cee = isInEEC($thirdparty_seller);
4981
4982
        $buyer_country_code = $thirdparty_buyer->country_code;
4983
        $buyer_in_cee = isInEEC($thirdparty_buyer);
4984
4985
        DolUtils::dol_syslog("get_default_tva: seller use vat=" . $seller_use_vat . ", seller country=" . $seller_country_code . ", seller in cee=" . $seller_in_cee . ", buyer vat number=" . $thirdparty_buyer->tva_intra . " buyer country=" . $buyer_country_code . ", buyer in cee=" . $buyer_in_cee . ", idprod=" . $idprod . ", idprodfournprice=" . $idprodfournprice . ", SERVICE_ARE_ECOMMERCE_200238EC=" . (!empty(Globals::$conf->global->SERVICES_ARE_ECOMMERCE_200238EC) ? Globals::$conf->global->SERVICES_ARE_ECOMMERCE_200238EC : ''));
4986
4987
// 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)
4988
// we use the buyer VAT.
4989
        if (!empty(Globals::$conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {
4990
            if ($seller_in_cee && $buyer_in_cee && !$thirdparty_buyer->isACompany()) {
4991
//print 'VATRULE 0';
4992
                return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
4993
            }
4994
        }
4995
4996
// If seller does not use VAT
4997
        if (!$seller_use_vat) {
4998
//print 'VATRULE 1';
4999
            return 0;
5000
        }
5001
5002
// 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.
5003
// Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
5004
        if (($seller_country_code == $buyer_country_code) || (in_array($seller_country_code, array('FR,MC')) && in_array($buyer_country_code, array('FR', 'MC')))) { // Warning ->country_code not always defined
5005
//print 'VATRULE 2';
5006
            return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
5007
        }
5008
5009
// 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.
5010
// Not supported
5011
// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
5012
// Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
5013
        if (($seller_in_cee && $buyer_in_cee)) {
5014
            $isacompany = $thirdparty_buyer->isACompany();
5015
            if ($isacompany) {
5016
//print 'VATRULE 3';
5017
                return 0;
5018
            } else {
5019
//print 'VATRULE 4';
5020
                return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
5021
            }
5022
        }
5023
5024
// Si (vendeur en France et acheteur hors Communaute europeenne et acheteur particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
5025
        if (!empty(Globals::$conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee) && !$thirdparty_buyer->isACompany()) {
5026
            return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
5027
        }
5028
5029
// Sinon la TVA proposee par defaut=0. Fin de regle.
5030
// Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
5031
//print 'VATRULE 5';
5032
        return 0;
5033
    }
5034
5035
    /**
5036
     * 	Fonction qui renvoie si tva doit etre tva percue recuperable
5037
     *
5038
     * 	@param	Societe		$thirdparty_seller    	Thirdparty seller
5039
     * 	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
5040
     *  @param  int			$idprod                 Id product
5041
     *  @param	int			$idprodfournprice		Id supplier price for product
5042
     * 	@return float       			        	0 or 1
5043
     *  @see get_default_tva, get_default_localtax
5044
     */
5045
    static function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
5046
    {
5047
        // global $db;
5048
5049
        if ($idprodfournprice > 0) {
5050
            if (!class_exists('ProductFournisseur'))
5051
                require_once DOL_BASE_PATH . '/fourn/class/fournisseur.product.class.php';
5052
            $prodprice = new ProductFournisseur($db);
5053
            $prodprice->fetch_product_fournisseur_price($idprodfournprice);
5054
            return $prodprice->fourn_tva_npr;
5055
        }
5056
        elseif ($idprod > 0) {
5057
            if (!class_exists('Product'))
5058
                require_once DOL_BASE_PATH . 'product/class/product.class.php';
5059
            $prod = new Product($db);
5060
            $prod->fetch($idprod);
5061
            return $prod->tva_npr;
5062
        }
5063
5064
        return 0;
5065
    }
5066
5067
    /**
5068
     * 	Function that return localtax of a product line (according to seller, buyer and product vat rate)
5069
     *   Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle.
5070
     * 	 Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle.
5071
     * 	 Sinon TVA proposee par defaut=0. Fin de regle.
5072
     *
5073
     * 	@param	Societe		$thirdparty_seller    	Thirdparty seller
5074
     * 	@param  Societe		$thirdparty_buyer   	Thirdparty buyer
5075
     *  @param	int			$local					Localtax to process (1 or 2)
5076
     * 	@param  int			$idprod					Id product
5077
     * 	@return integer        				       	localtax, -1 si ne peut etre determine
5078
     *  @see get_default_tva, get_default_npr
5079
     */
5080
    static function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
5081
    {
5082
        // global $mysoc;
5083
5084
        if (!is_object($thirdparty_seller))
5085
            return -1;
5086
        if (!is_object($thirdparty_buyer))
5087
            return -1;
5088
5089
        if ($local == 1) { // Localtax 1
5090
            if ($mysoc->country_code == 'ES') {
5091
                if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj)
5092
                    return 0;
5093
            }
5094
            else {
5095
// Si vendeur non assujeti a Localtax1, localtax1 par default=0
5096
                if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj)
5097
                    return 0;
5098
                if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off')
5099
                    return 0;
5100
            }
5101
        }
5102
        elseif ($local == 2) { //I Localtax 2
5103
// Si vendeur non assujeti a Localtax2, localtax2 par default=0
5104
            if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj)
5105
                return 0;
5106
            if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off')
5107
                return 0;
5108
        }
5109
5110
        if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
5111
            return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
5112
        }
5113
5114
        return 0;
5115
    }
5116
5117
    /**
5118
     * 	Return yes or no in current language
5119
     *
5120
     * 	@param	string	$yesno			Value to test (1, 'yes', 'true' or 0, 'no', 'false')
5121
     * 	@param	integer	$case			1=Yes/No, 0=yes/no, 2=Disabled checkbox, 3=Disabled checkbox + Yes/No
5122
     * 	@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.
5123
     * 	@return	string					HTML string
5124
     */
5125
    static function yn($yesno, $case = 1, $color = 0)
5126
    {
5127
        // global Globals::$langs;
5128
        $result = 'unknown';
5129
        $classname = '';
5130
        if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') {  // A mettre avant test sur no a cause du == 0
5131
            $result = Globals::$langs->trans('yes');
5132
            if ($case == 1 || $case == 3)
5133
                $result = Globals::$langs->trans("Yes");
5134
            if ($case == 2)
5135
                $result = '<input type="checkbox" value="1" checked disabled>';
5136
            if ($case == 3)
5137
                $result = '<input type="checkbox" value="1" checked disabled> ' . $result;
5138
5139
            $classname = 'ok';
5140
        }
5141
        elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
5142
            $result = Globals::$langs->trans("no");
5143
            if ($case == 1 || $case == 3)
5144
                $result = Globals::$langs->trans("No");
5145
            if ($case == 2)
5146
                $result = '<input type="checkbox" value="0" disabled>';
5147
            if ($case == 3)
5148
                $result = '<input type="checkbox" value="0" disabled> ' . $result;
5149
5150
            if ($color == 2)
5151
                $classname = 'ok';
5152
            else
5153
                $classname = 'error';
5154
        }
5155
        if ($color)
5156
            return '<font class="' . $classname . '">' . $result . '</font>';
5157
        return $result;
5158
    }
5159
5160
    /**
5161
     * 	Return a path to have a the directory according to object where files are stored.
5162
     *  New usage:       Globals::$conf->module->multidir_output[$object->entity].'/'.get_exdir(0, 0, 0, 1, $object, $modulepart)
5163
     *         or:       Globals::$conf->module->dir_output.'/'.get_exdir(0, 0, 0, 1, $object, $modulepart)     if multidir_output not defined.
5164
     *  Example our with new usage:       $object is invoice -> 'INYYMM-ABCD'
5165
     *  Example our with old usage:       '015' with level 3->"0/1/5/", '015' with level 1->"5/", 'ABC-1' with level 3 ->"0/0/1/"
5166
     *
5167
     * 	@param	string	$num            Id of object (deprecated, $object will be used in future)
5168
     * 	@param  int		$level		    Level of subdirs to return (1, 2 or 3 levels). (deprecated,// global option will be used in future)
5169
     * 	@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)
5170
     *  @param  int		$withoutslash   0=With slash at end (except if '/', we return ''), 1=without slash at end
5171
     *  @param	Object	$object			Object
5172
     *  @param	string	$modulepart		Type of object ('invoice_supplier, 'donation', 'invoice', ...')
5173
     *  @return	string					Dir to use ending. Example '' or '1/' or '1/2/'
5174
     */
5175
    static function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart)
5176
    {
5177
        // global Globals::$conf;
5178
5179
        $path = '';
5180
5181
        $arrayforoldpath = array('cheque', 'user', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment');
5182
        if (!empty(Globals::$conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))
5183
            $arrayforoldpath[] = 'product';
5184
        if (!empty($level) && in_array($modulepart, $arrayforoldpath)) {
5185
// This part should be removed once all code is using "get_exdir" to forge path, with all parameters provided.
5186
            if (empty($alpha))
5187
                $num = preg_replace('/([^0-9])/i', '', $num);
5188
            else
5189
                $num = preg_replace('/^.*\-/i', '', $num);
5190
            $num = substr("000" . $num, -$level);
5191
            if ($level == 1)
5192
                $path = substr($num, 0, 1);
5193
            if ($level == 2)
5194
                $path = substr($num, 1, 1) . '/' . substr($num, 0, 1);
5195
            if ($level == 3)
5196
                $path = substr($num, 2, 1) . '/' . substr($num, 1, 1) . '/' . substr($num, 0, 1);
5197
        }
5198
        else {
5199
// TODO
5200
// We will enhance here a common way of forging path for document storage
5201
// Here, object->id, object->ref and modulepart are required.
5202
//var_dump($modulepart);
5203
            if (in_array($modulepart, array('thirdparty', 'contact', 'member', 'propal', 'proposal', 'commande', 'order', 'facture', 'invoice',
5204
                    'supplier_order', 'supplier_proposal', 'shipment', 'contract', 'expensereport'))) {
5205
                $path = ($object->ref ? $object->ref : $object->id);
5206
            }
5207
        }
5208
5209
        if (empty($withoutslash) && !empty($path))
5210
            $path .= '/';
5211
5212
        return $path;
5213
    }
5214
5215
    /**
5216
     * 	Creation of a directory (this can create recursive subdir)
5217
     *
5218
     * 	@param	string	$dir		Directory to create (Separator must be '/'. Example: '/mydir/mysubdir')
5219
     * 	@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)
5220
     *  @param	int		$newmask	Mask for new file (Defaults to Globals::$conf->global->MAIN_UMASK or 0755 if unavailable). Example: '0444'
5221
     * 	@return int         		< 0 if KO, 0 = already exists, > 0 if OK
5222
     */
5223
    static function dol_mkdir($dir, $dataroot = '', $newmask = null)
5224
    {
5225
        // global Globals::$conf;
5226
5227
        DolUtils::dol_syslog("functions.lib::dol_mkdir: dir=" . $dir, LOG_INFO);
5228
5229
        $dir_osencoded = dol_osencode($dir);
5230
        if (@is_dir($dir_osencoded))
5231
            return 0;
5232
5233
        $nberr = 0;
5234
        $nbcreated = 0;
5235
5236
        $ccdir = '';
5237
        if (!empty($dataroot)) {
5238
// Remove data root from loop
5239
            $dir = str_replace($dataroot . '/', '', $dir);
5240
            $ccdir = $dataroot . '/';
5241
        }
5242
5243
        $cdir = explode("/", $dir);
5244
        $num = count($cdir);
5245
        for ($i = 0; $i < $num; $i++) {
5246
            if ($i > 0)
5247
                $ccdir .= '/' . $cdir[$i];
5248
            else
5249
                $ccdir .= $cdir[$i];
5250
            if (preg_match("/^.:$/", $ccdir, $regs))
5251
                continue; // Si chemin Windows incomplet, on poursuit par rep suivant
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
                
5296
// Attention, le is_dir() peut echouer bien que le rep existe.
5297
// (ex selon config de open_basedir)
5298
            if ($ccdir) {
5299
                $ccdir_osencoded = dol_osencode($ccdir);
5300
                if (!@is_dir($ccdir_osencoded)) {
5301
                    DolUtils::dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
5302
5303
                    umask(0);
5304
                    $dirmaskdec = octdec($newmask);
5305
                    if (empty($newmask)) {
5306
                        $dirmaskdec = empty(Globals::$conf->global->MAIN_UMASK) ? octdec('0755') : octdec(Globals::$conf->global->MAIN_UMASK);
5307
                    }
5308
                    $dirmaskdec |= octdec('0111');  // Set x bit required for directories
5309
                    if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
5310
                        // Si le is_dir a renvoye une fausse info, alors on passe ici.
5311
                        DolUtils::dol_syslog("functions.lib::dol_mkdir: Fails to create directory '" . $ccdir . "' or directory already exists.", LOG_WARNING);
5312
                        $nberr++;
5313
                    } else {
5314
                        DolUtils::dol_syslog("functions.lib::dol_mkdir: Directory '" . $ccdir . "' created", LOG_DEBUG);
5315
                        $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
5316
                        $nbcreated++;
5317
                    }
5318
                } else {
5319
                    $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
5320
                }
5321
            }
5322
        }
5323
        return ($nberr ? -$nberr : $nbcreated);
5324
    }
5325
5326
    /**
5327
     * 	Return picto saying a field is required
5328
     *
5329
     * 	@return  string		Chaine avec picto obligatoire
5330
     */
5331
    static function picto_required()
5332
    {
5333
        return '<span class="fieldrequired">*</span>';
5334
    }
5335
5336
    /**
5337
     * 	Clean a string from all HTML tags and entities.
5338
     *  This static function differs from strip_tags because:
5339
     *  - <br> are replaced with \n if removelinefeed=0 or 1
5340
     *  - if entities are found, they are decoded BEFORE the strip
5341
     *  - you can decide to convert line feed into a space
5342
     *
5343
     * 	@param	string	$stringtoclean		String to clean
5344
     * 	@param	integer	$removelinefeed		1=Replace all new lines by 1 space, 0=Only ending new lines are removed others are replaced with \n, 2=Ending new lines are removed but others are kept with a same number of \n than nb of <br> when there is both "...<br>\n..."
5345
     *  @param  string	$pagecodeto      	Encoding of input/output string
5346
     *  @param	integer	$strip_tags			0=Use internal strip, 1=Use strip_tags() php static function (bugged when text contains a < char that is not for a html tag)
5347
     * 	@return string	    				String cleaned
5348
     *
5349
     * 	@see	DolUtils::dol_escape_htmltag strip_tags dol_string_onlythesehtmltags dol_string_neverthesehtmltags
5350
     */
5351
    static function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0)
5352
    {
5353
        if ($removelinefeed == 2)
5354
            $stringtoclean = preg_replace('/<br[^>]*>\n+/ims', '<br>', $stringtoclean);
5355
        $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
5356
5357
        if ($strip_tags) {
5358
            $temp = strip_tags($temp);
5359
        } else {
5360
            $pattern = "/<[^<>]+>/";
5361
// Exemple of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
5362
            $temp = preg_replace($pattern, "", $temp);    // pass 1
5363
// $temp after pass 1: <a href="/myurl" title="A title">0000-021
5364
            $temp = preg_replace($pattern, "", $temp);    // pass 2
5365
// $temp after pass 2: 0000-021
5366
        }
5367
5368
        $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
5369
5370
// Supprime aussi les retours
5371
        if ($removelinefeed == 1)
5372
            $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
5373
5374
// et les espaces doubles
5375
        while (strpos($temp, "  ")) {
5376
            $temp = str_replace("  ", " ", $temp);
5377
        }
5378
5379
        return trim($temp);
5380
    }
5381
5382
    /**
5383
     * 	Clean a string to keep only desirable HTML tags.
5384
     *
5385
     * 	@param	string	$stringtoclean		String to clean
5386
     * 	@return string	    				String cleaned
5387
     *
5388
     * 	@see	DolUtils::dol_escape_htmltag strip_tags dol_string_nohtmltag dol_string_neverthesehtmltags
5389
     */
5390
    static function dol_string_onlythesehtmltags($stringtoclean)
5391
    {
5392
        $allowed_tags = array(
5393
            "html", "head", "meta", "body", "article", "a", "b", "br", "div", "em", "font", "img", "ins", "hr", "i", "li", "link",
5394
            "ol", "p", "s", "section", "span", "strong", "title",
5395
            "table", "tr", "th", "td", "u", "ul"
5396
        );
5397
5398
        $allowed_tags_string = join("><", $allowed_tags);
5399
        $allowed_tags_string = preg_replace('/^>/', '', $allowed_tags_string);
5400
        $allowed_tags_string = preg_replace('/<$/', '', $allowed_tags_string);
5401
5402
        $temp = strip_tags($stringtoclean, $allowed_tags_string);
5403
5404
        return $temp;
5405
    }
5406
5407
    /**
5408
     * 	Clean a string from some undesirable HTML tags.
5409
     *
5410
     * 	@param	string	$stringtoclean		String to clean
5411
     *  @param	array	$disallowed_tags	Array of tags not allowed
5412
     * 	@return string	    				String cleaned
5413
     *
5414
     * 	@see	DolUtils::dol_escape_htmltag strip_tags dol_string_nohtmltag dol_string_onlythesehtmltags
5415
     */
5416
    static function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'))
5417
    {
5418
        $temp = $stringtoclean;
5419
        foreach ($disallowed_tags as $tagtoremove) {
5420
            $temp = preg_replace('/<\/?' . $tagtoremove . '>/', '', $temp);
5421
            $temp = preg_replace('/<\/?' . $tagtoremove . '\s+[^>]*>/', '', $temp);
5422
        }
5423
        return $temp;
5424
    }
5425
5426
    /**
5427
     * Return first line of text. Cut will depends if content is HTML or not.
5428
     *
5429
     * @param 	string	$text		Input text
5430
     * @param	int		$nboflines  Nb of lines to get (default is 1 = first line only)
5431
     * @return	string				Output text
5432
     * @see dol_nboflines_bis, dol_string_nohtmltag, DolUtils::dol_escape_htmltag
5433
     */
5434
    static function dolGetFirstLineOfText($text, $nboflines = 1)
5435
    {
5436
        if ($nboflines == 1) {
5437
            if (DolUtils::dol_textishtml($text)) {
5438
                $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text);  // The s pattern modifier means the . can match newline characters
5439
                $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
5440
            } else {
5441
                $firstline = preg_replace('/[\n\r].*/', '', $text);
5442
            }
5443
            return $firstline . ((strlen($firstline) != strlen($text)) ? '...' : '');
5444
        } else {
5445
            $ishtml = 0;
5446
            if (DolUtils::dol_textishtml($text)) {
5447
                $text = preg_replace('/\n/', '', $text);
5448
                $ishtml = 1;
5449
                $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5450
            } else {
5451
                $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5452
            }
5453
5454
            $text = strtr($text, $repTable);
5455
            if ($charset == 'UTF-8') {
5456
                $pattern = '/(<br[^>]*>)/Uu';
5457
            } // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
5458
            else
5459
                $pattern = '/(<br[^>]*>)/U';       // /U is to have UNGREEDY regex to limit to one html tag.
5460
            $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5461
5462
            $firstline = '';
5463
            $i = 0;
5464
            $nba = count($a); // 2x nb of lines in $a because $a contains also a line for each new line separator
5465
            while (($i < $nba) && ($i < ($nboflines * 2))) {
5466
                if ($i % 2 == 0)
5467
                    $firstline .= $a[$i];
5468
                elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1)))
5469
                    $firstline .= ($ishtml ? "<br>\n" : "\n");
5470
                $i++;
5471
            }
5472
            unset($a);
5473
            return $firstline . (($i < $nba) ? '...' : '');
5474
        }
5475
    }
5476
5477
    /**
5478
     * Replace CRLF in string with a HTML BR tag
5479
     *
5480
     * @param	string	$stringtoencode		String to encode
5481
     * @param	int     $nl2brmode			0=Adding br before \n, 1=Replacing \n by br
5482
     * @param   bool	$forxml             false=Use <br>, true=Use <br />
5483
     * @return	string						String encoded
5484
     * @see dol_nboflines, dolGetFirstLineOfText
5485
     */
5486
    static function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
5487
    {
5488
        if (!$nl2brmode) {
5489
            return nl2br($stringtoencode, $forxml);
5490
        } else {
5491
            $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
5492
            return $ret;
5493
        }
5494
    }
5495
5496
    /**
5497
     * 	This static function is called to encode a string into a HTML string but differs from htmlentities because
5498
     * 	a detection is done before to see if text is already HTML or not. Also, all entities but &,<,> are converted.
5499
     *  This permits to encode special chars to entities with no double encoding for already encoded HTML strings.
5500
     * 	This static function also remove last EOL or BR if $removelasteolbr=1 (default).
5501
     *  For PDF usage, you can show text by 2 ways:
5502
     *              - writeHTMLCell -> param must be encoded into HTML.
5503
     *              - MultiCell -> param must not be encoded into HTML.
5504
     *              Because writeHTMLCell convert also \n into <br>, if function
5505
     *              is used to build PDF, nl2brmode must be 1.
5506
     *
5507
     * 	@param	string	$stringtoencode		String to encode
5508
     * 	@param	int		$nl2brmode			0=Adding br before \n, 1=Replacing \n by br (for use with FPDF writeHTMLCell static function for example)
5509
     *  @param  string	$pagecodefrom       Pagecode stringtoencode is encoded
5510
     *  @param	int		$removelasteolbr	1=Remove last br or lasts \n (default), 0=Do nothing
5511
     *  @return	string						String encoded
5512
     */
5513
    static function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
5514
    {
5515
        $newstring = $stringtoencode;
5516
        if (DolUtils::dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
5517
            $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.
5518
            if ($removelasteolbr)
5519
                $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
5520
            $newstring = strtr($newstring, array('&' => '__and__', '<' => '__lt__', '>' => '__gt__', '"' => '__dquot__'));
5521
            $newstring = DolUtils::dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
5522
            $newstring = strtr($newstring, array('__and__' => '&', '__lt__' => '<', '__gt__' => '>', '__dquot__' => '"'));
5523
        }
5524
        else {
5525
            if ($removelasteolbr)
5526
                $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
5527
            $newstring = dol_nl2br(DolUtils::dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
5528
        }
5529
// Other substitutions that htmlentities does not do
5530
//$newstring=str_replace(chr(128),'&euro;',$newstring);	// 128 = 0x80. Not in html entity table.     // Seems useles with TCPDF. Make bug with UTF8 languages
5531
        return $newstring;
5532
    }
5533
5534
    /**
5535
     * 	This static function is called to decode a HTML string (it decodes entities and br tags)
5536
     *
5537
     * 	@param	string	$stringtodecode		String to decode
5538
     * 	@param	string	$pagecodeto			Page code for result
5539
     * 	@return	string						String decoded
5540
     */
5541
    static function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8')
5542
    {
5543
        $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT, $pagecodeto);
5544
        $ret = preg_replace('/' . "\r\n" . '<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
5545
        $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\r\n" . '/i', "\r\n", $ret);
5546
        $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>' . "\n" . '/i', "\n", $ret);
5547
        $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
5548
        return $ret;
5549
    }
5550
5551
    /**
5552
     * 	This static function remove all ending \n and br at end
5553
     *
5554
     * 	@param	string	$stringtodecode		String to decode
5555
     * 	@return	string						String decoded
5556
     */
5557
    static function dol_htmlcleanlastbr($stringtodecode)
5558
    {
5559
        $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|' . "\n" . '|' . "\r" . ')+$/i', "", $stringtodecode);
5560
        return $ret;
5561
    }
5562
5563
    /**
5564
     * Replace html_entity_decode functions to manage errors
5565
     *
5566
     * @param   string	$a		Operand a
5567
     * @param   string	$b		Operand b (ENT_QUOTES=convert simple and double quotes)
5568
     * @param   string	$c		Operand c
5569
     * @return  string			String decoded
5570
     */
5571
    static function dol_html_entity_decode($a, $b, $c = 'UTF-8')
5572
    {
5573
        return html_entity_decode($a, $b, $c);
5574
    }
5575
5576
    /**
5577
     * Replace htmlentities functions.
5578
     * Goal of this static function is to be sure to have default values of htmlentities that match what we need.
5579
     *
5580
     * @param   string  $string         The input string to encode
5581
     * @param   int     $flags          Flags (see PHP doc above)
5582
     * @param   string  $encoding       Encoding page code
5583
     * @param   bool    $double_encode  When double_encode is turned off, PHP will not encode existing html entities
5584
     * @return  string  $ret            Encoded string
5585
     */
5586
    static function dol_htmlentities($string, $flags = null, $encoding = 'UTF-8', $double_encode = false)
5587
    {
5588
        return htmlentities($string, $flags, $encoding, $double_encode);
5589
    }
5590
5591
    /**
5592
     * 	Check if a string is a correct iso string
5593
     * 	If not, it will we considered not HTML encoded even if it is by FPDF.
5594
     * 	Example, if string contains euro symbol that has ascii code 128
5595
     *
5596
     * 	@param	string	$s      String to check
5597
     * 	@return	int     		0 if bad iso, 1 if good iso
5598
     */
5599
    static function dol_string_is_good_iso($s)
5600
    {
5601
        $len = DolUtils::dol_strlen($s);
5602
        $ok = 1;
5603
        for ($scursor = 0; $scursor < $len; $scursor++) {
5604
            $ordchar = ord($s{$scursor});
5605
//print $scursor.'-'.$ordchar.'<br>';
5606
            if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
5607
                $ok = 0;
5608
                break;
5609
            }
5610
            if ($ordchar > 126 && $ordchar < 160) {
5611
                $ok = 0;
5612
                break;
5613
            }
5614
        }
5615
        return $ok;
5616
    }
5617
5618
    /**
5619
     * 	Return nb of lines of a clear text
5620
     *
5621
     * 	@param	string	$s			String to check
5622
     * 	@param	int     $maxchar	Not yet used
5623
     * 	@return	int					Number of lines
5624
     *  @see	dol_nboflines_bis, dolGetFirstLineOfText
5625
     */
5626
    static function dol_nboflines($s, $maxchar = 0)
5627
    {
5628
        if ($s == '')
5629
            return 0;
5630
        $arraystring = explode("\n", $s);
5631
        $nb = count($arraystring);
5632
5633
        return $nb;
5634
    }
5635
5636
    /**
5637
     * 	Return nb of lines of a formated text with \n and <br> (WARNING: string must not have mixed \n and br separators)
5638
     *
5639
     * 	@param	string	$text      		Text
5640
     * 	@param	int		$maxlinesize  	Largeur de ligne en caracteres (ou 0 si pas de limite - defaut)
5641
     * 	@param	string	$charset		Give the charset used to encode the $text variable in memory.
5642
     * 	@return int						Number of lines
5643
     * 	@see	dol_nboflines, dolGetFirstLineOfText
5644
     */
5645
    static function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
5646
    {
5647
        $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5648
        if (DolUtils::dol_textishtml($text))
5649
            $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5650
5651
        $text = strtr($text, $repTable);
5652
        if ($charset == 'UTF-8') {
5653
            $pattern = '/(<br[^>]*>)/Uu';
5654
        } // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
5655
        else
5656
            $pattern = '/(<br[^>]*>)/U';       // /U is to have UNGREEDY regex to limit to one html tag.
5657
        $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5658
5659
        $nblines = (int) floor((count($a) + 1) / 2);
5660
// count possible auto line breaks
5661
        if ($maxlinesize) {
5662
            foreach ($a as $line) {
5663
                if (DolUtils::dol_strlen($line) > $maxlinesize) {
5664
                    //$line_dec = html_entity_decode(strip_tags($line));
5665
                    $line_dec = html_entity_decode($line);
5666
                    if (DolUtils::dol_strlen($line_dec) > $maxlinesize) {
5667
                        $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
5668
                        $nblines += substr_count($line_dec, '\n');
5669
                    }
5670
                }
5671
            }
5672
        }
5673
5674
        unset($a);
5675
        return $nblines;
5676
    }
5677
5678
    /**
5679
     * 	 Same static function than microtime in PHP 5 but compatible with PHP4
5680
     *
5681
     * @return		float		Time (millisecondes) with microsecondes in decimal part
5682
     * @deprecated Dolibarr does not support PHP4, you should use native function
5683
     * @see microtime()
5684
     */
5685
    static function dol_microtime_float()
5686
    {
5687
        DolUtils::dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
5688
5689
        return microtime(true);
5690
    }
5691
5692
    /**
5693
     * 	Return if a text is a html content
5694
     *
5695
     * 	@param	string	$msg		Content to check
5696
     * 	@param	int		$option		0=Full detection, 1=Fast check
5697
     * 	@return	boolean				true/false
5698
     * 	@see	dol_concatdesc
5699
     */
5700
    static function dol_textishtml($msg, $option = 0)
5701
    {
5702
        if ($option == 1) {
5703
            if (preg_match('/<html/i', $msg))
5704
                return true;
5705
            elseif (preg_match('/<body/i', $msg))
5706
                return true;
5707
            elseif (preg_match('/<br/i', $msg))
5708
                return true;
5709
            return false;
5710
        }
5711
        else {
5712
            if (preg_match('/<html/i', $msg))
5713
                return true;
5714
            elseif (preg_match('/<body/i', $msg))
5715
                return true;
5716
            elseif (preg_match('/<(b|em|i|u)>/i', $msg))
5717
                return true;
5718
            elseif (preg_match('/<br\/>/i', $msg))
5719
                return true;
5720
            elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i', $msg))
5721
                return true;
5722
            elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*>/i', $msg))
5723
                return true;
5724
            elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/>/i', $msg))
5725
                return true;
5726
            elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg))
5727
                return true; // must accept <img src="http://example.com/aaa.png" />
5728
            elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg))
5729
                return true; // must accept <a href="http://example.com/aaa.png" />
5730
            elseif (preg_match('/<h[0-9]>/i', $msg))
5731
                return true;
5732
            elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg))
5733
                return true;    // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
5734
            elseif (preg_match('/&#[0-9]{2,3};/i', $msg))
5735
                return true;    // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
5736
5737
            return false;
5738
        }
5739
    }
5740
5741
    /**
5742
     *  Concat 2 descriptions with a new line between them (second operand after first one with appropriate new line separator)
5743
     *  text1 html + text2 html => text1 + '<br>' + text2
5744
     *  text1 html + text2 txt  => text1 + '<br>' + dol_nl2br(text2)
5745
     *  text1 txt  + text2 html => dol_nl2br(text1) + '<br>' + text2
5746
     *  text1 txt  + text2 txt  => text1 + '\n' + text2
5747
     *
5748
     *  @param  string  $text1          Text 1
5749
     *  @param  string  $text2          Text 2
5750
     *  @param  bool    $forxml         false=Use <br>instead of \n if html content detected, true=Use <br /> instead of \n if html content detected
5751
     *  @param  bool    $invert         invert order of description lines if CONF CHANGE_ORDER_CONCAT_DESCRIPTION is active
5752
     *  @return string                  Text 1 + new line + Text2
5753
     *  @see    DolUtils::dol_textishtml
5754
     */
5755
    static function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
5756
    {
5757
        if (!empty($invert)) {
5758
            $tmp = $text1;
5759
            $text1 = $text2;
5760
            $text2 = $tmp;
5761
        }
5762
5763
        $ret = '';
5764
        $ret .= (!DolUtils::dol_textishtml($text1) && DolUtils::dol_textishtml($text2)) ? dol_nl2br($text1, 0, $forxml) : $text1;
5765
        $ret .= (!empty($text1) && !empty($text2)) ? ((DolUtils::dol_textishtml($text1) || DolUtils::dol_textishtml($text2)) ? ($forxml ? "<br \>\n" : "<br>\n") : "\n") : "";
5766
        $ret .= (DolUtils::dol_textishtml($text1) && !DolUtils::dol_textishtml($text2)) ? dol_nl2br($text2, 0, $forxml) : $text2;
5767
        return $ret;
5768
    }
5769
5770
    /**
5771
     * Return array of possible common substitutions. This includes several families like: 'system', 'mycompany', 'object', 'objectamount', 'date', 'user'
5772
     *
5773
     * @param	Translate	$outputlangs	Output language
5774
     * @param   int         $onlykey        1=Do not calculate some heavy values of keys (performance enhancement when we need only the keys), 2=Values are trunc and html sanitized (to use for help tooltip)
5775
     * @param   array       $exclude        Array of family keys we want to exclude. For example array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...)
5776
     * @param   Object      $object         Object for keys on object
5777
     * @return	array						Array of substitutions
5778
     * @see setSubstitFromObject
5779
     */
5780
    static function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null)
5781
    {
5782
        // global $db, Globals::$conf, $mysoc, $user, $extrafields;
5783
5784
        $substitutionarray = array();
5785
5786
        if (empty($exclude) || !in_array('user', $exclude)) {
5787
// Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
5788
// this will include signature content first and then replace var found into content of signature
5789
            $signature = $user->signature;
5790
            $substitutionarray = array_merge($substitutionarray, array(
5791
                '__USER_SIGNATURE__' => (string) (($signature && empty(Globals::$conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '')
5792
                )
5793
            );
5794
// For backward compatibility
5795
            if ($onlykey != 2) {
5796
                $substitutionarray['__SIGNATURE__'] = (string) (($signature && empty(Globals::$conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '');
5797
            }
5798
5799
            $substitutionarray = array_merge($substitutionarray, array(
5800
                '__USER_ID__' => (string) $user->id,
5801
                '__USER_LOGIN__' => (string) $user->login,
5802
                '__USER_LASTNAME__' => (string) $user->lastname,
5803
                '__USER_FIRSTNAME__' => (string) $user->firstname,
5804
                '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
5805
                '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
5806
                '__USER_REMOTE_IP__' => (string) getUserRemoteIP()
5807
                )
5808
            );
5809
        }
5810
        if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc)) {
5811
            $substitutionarray = array_merge($substitutionarray, array(
5812
                '__MYCOMPANY_NAME__' => $mysoc->name,
5813
                '__MYCOMPANY_EMAIL__' => $mysoc->email,
5814
                '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
5815
                '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
5816
                '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
5817
                '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
5818
                '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
5819
                '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
5820
                '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
5821
                '__MYCOMPANY_FULLADDRESS__' => $mysoc->getFullAddress(1, ', '),
5822
                '__MYCOMPANY_ADDRESS__' => $mysoc->address,
5823
                '__MYCOMPANY_ZIP__' => $mysoc->zip,
5824
                '__MYCOMPANY_TOWN__' => $mysoc->town,
5825
                '__MYCOMPANY_COUNTRY__' => $mysoc->country,
5826
                '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
5827
                '__MYCOMPANY_CURRENCY_CODE__' => Globals::$conf->currency
5828
            ));
5829
        }
5830
5831
        if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude))) {
5832
            if ($onlykey) {
5833
                $substitutionarray['__ID__'] = '__ID__';
5834
                $substitutionarray['__REF__'] = '__REF__';
5835
                $substitutionarray['__REFCLIENT__'] = '__REFCLIENT__';
5836
                $substitutionarray['__REFSUPPLIER__'] = '__REFSUPPLIER__';
5837
                $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
5838
5839
                $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
5840
                $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
5841
                $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
5842
                $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
5843
5844
                if (is_object($object) && $object->element == 'member') {
5845
                    $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
5846
                    $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
5847
                    $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
5848
                    $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
5849
                }
5850
                $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
5851
                $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
5852
                $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
5853
5854
                $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
5855
                $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
5856
                $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
5857
                $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
5858
5859
                $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
5860
                $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
5861
                $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
5862
                $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
5863
                $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
5864
                $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
5865
                $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a a service';
5866
5867
                $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
5868
                $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
5869
                $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
5870
5871
                if (is_object($object) && $object->element == 'shipping') {
5872
                    $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tacking number';
5873
                    $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
5874
                }
5875
            } else {
5876
                $substitutionarray['__ID__'] = $object->id;
5877
                $substitutionarray['__REF__'] = $object->ref;
5878
                $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : ''));
5879
                $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : '');
5880
5881
// TODO Remove this
5882
                $msgishtml = 0;
5883
5884
                $birthday = DolUtils::dol_print_date($object->birth, 'day');
5885
5886
                $substitutionarray['__MEMBER_ID__'] = $object->id;
5887
                if (method_exists($object, 'getCivilityLabel'))
5888
                    $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
5889
                $substitutionarray['__MEMBER_FIRSTNAME__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->firstname) : $object->firstname;
5890
                $substitutionarray['__MEMBER_LASTNAME__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->lastname) : $object->lastname;
5891
                if (method_exists($object, 'getFullName'))
5892
                    $substitutionarray['__MEMBER_FULLNAME__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->getFullName($outputlangs)) : $object->getFullName($outputlangs);
5893
                $substitutionarray['__MEMBER_COMPANY__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->societe) : $object->societe;
5894
                $substitutionarray['__MEMBER_ADDRESS__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->address) : $object->address;
5895
                $substitutionarray['__MEMBER_ZIP__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->zip) : $object->zip;
5896
                $substitutionarray['__MEMBER_TOWN__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->town) : $object->town;
5897
                $substitutionarray['__MEMBER_COUNTRY__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->country) : $object->country;
5898
                $substitutionarray['__MEMBER_EMAIL__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->email) : $object->email;
5899
                $substitutionarray['__MEMBER_BIRTH__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($birthday) : $birthday;
5900
                $substitutionarray['__MEMBER_PHOTO__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->photo) : $object->photo;
5901
                $substitutionarray['__MEMBER_LOGIN__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->login) : $object->login;
5902
                $substitutionarray['__MEMBER_PASSWORD__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->pass) : $object->pass;
5903
                $substitutionarray['__MEMBER_PHONE__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->phone) : $object->phone;
5904
                $substitutionarray['__MEMBER_PHONEPRO__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->phone_perso) : $object->phone_perso;
5905
                $substitutionarray['__MEMBER_PHONEMOBILE__'] = $msgishtml ? DolUtils::dol_htmlentitiesbr($object->phone_mobile) : $object->phone_mobile;
5906
                $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = DolUtils::dol_print_date($object->first_subscription_date, 'dayrfc');
5907
                $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = DolUtils::dol_print_date($object->first_subscription_date_start, 'dayrfc');
5908
                $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = DolUtils::dol_print_date($object->first_subscription_date_end, 'dayrfc');
5909
                $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = DolUtils::dol_print_date($object->last_subscription_date, 'dayrfc');
5910
                $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = DolUtils::dol_print_date($object->last_subscription_date_start, 'dayrfc');
5911
                $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = DolUtils::dol_print_date($object->last_subscription_date_end, 'dayrfc');
5912
5913
                if (is_object($object) && $object->element == 'societe') {
5914
                    $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
5915
                    $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
5916
                    $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
5917
                    $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
5918
                } elseif (is_object($object->thirdparty) && $object->thirdparty->id > 0) {
5919
                    $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
5920
                    $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
5921
                    $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
5922
                    $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
5923
                }
5924
5925
                if (is_object($object->projet) && $object->projet->id > 0) {
5926
                    $substitutionarray['__PROJECT_ID__'] = (is_object($object->projet) ? $object->projet->id : '');
5927
                    $substitutionarray['__PROJECT_REF__'] = (is_object($object->projet) ? $object->projet->ref : '');
5928
                    $substitutionarray['__PROJECT_NAME__'] = (is_object($object->projet) ? $object->projet->title : '');
5929
                }
5930
5931
                if (is_object($object) && $object->element == 'shipping') {
5932
                    $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
5933
                    $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
5934
                }
5935
5936
                if (is_object($object) && $object->element == 'contrat' && is_array($object->lines)) {
5937
                    $dateplannedstart = '';
5938
                    $datenextexpiration = '';
5939
                    foreach ($object->lines as $line) {
5940
                        if ($line->date_ouverture_prevue > $dateplannedstart)
5941
                            $dateplannedstart = $line->date_ouverture_prevue;
5942
                        if ($line->statut == 4 && $line->date_fin_prevue && (!$datenextexpiration || $line->date_fin_prevue < $datenextexpiration))
5943
                            $datenextexpiration = $line->date_fin_prevue;
5944
                    }
5945
                    $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = DolUtils::dol_print_date($dateplannedstart, 'dayrfc');
5946
                    $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = DolUtils::dol_print_date($dateplannedstart, 'standard');
5947
                    $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = DolUtils::dol_print_date($datenextexpiration, 'dayrfc');
5948
                    $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = DolUtils::dol_print_date($datenextexpiration, 'standard');
5949
                }
5950
5951
// Create dynamic tags for __EXTRAFIELD_FIELD__
5952
                if ($object->table_element && $object->id > 0) {
5953
                    if (!is_object($extrafields))
5954
                        $extrafields = new ExtraFields($db);
5955
                    $extrafields->fetch_name_optionals_label($object->table_element, true);
5956
5957
                    if ($object->fetch_optionals() > 0) {
5958
                        if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
5959
                            foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
5960
                                $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = $object->array_options['options_' . $key];
5961
                            }
5962
                        }
5963
                    }
5964
                }
5965
5966
// Complete substitution array with the url to make online payment
5967
                $paymenturl = '';
5968
                if (empty($substitutionarray['__REF__'])) {
5969
                    $paymenturl = '';
5970
                } else {
5971
                    // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
5972
                    require_once DOL_BASE_PATH . '/core/lib/payments.lib.php';
5973
                    $outputlangs->loadLangs(array('paypal', 'other'));
5974
                    $typeforonlinepayment = 'free';
5975
                    if (is_object($object) && $object->element == 'commande')
5976
                        $typeforonlinepayment = 'order';
5977
                    if (is_object($object) && $object->element == 'facture')
5978
                        $typeforonlinepayment = 'invoice';
5979
                    if (is_object($object) && $object->element == 'member')
5980
                        $typeforonlinepayment = 'member';
5981
                    $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__']);
5982
                    $paymenturl = $url;
5983
                }
5984
5985
                $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ? str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
5986
                $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
5987
5988
                if (!empty(Globals::$conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal') {
5989
                    $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
5990
                } else
5991
                    $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
5992
                if (!empty(Globals::$conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'commande') {
5993
                    $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
5994
                } else
5995
                    $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
5996
                if (!empty(Globals::$conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'facture') {
5997
                    $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
5998
                } else
5999
                    $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
6000
            }
6001
        }
6002
        if (empty($exclude) || !in_array('objectamount', $exclude)) {
6003
            $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? DolUtils::dol_print_date($object->date, 'day', 0, $outputlangs) : '') : '';
6004
            $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? DolUtils::dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : '') : '';
6005
6006
            $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
6007
            $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
6008
            $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? ($object->total_vat ? $object->total_vat : $object->total_tva) : '';
6009
            if ($onlykey != 2 || $mysoc->useLocalTax(1))
6010
                $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
6011
            if ($onlykey != 2 || $mysoc->useLocalTax(2))
6012
                $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
6013
6014
            $substitutionarray['__AMOUNT_FORMATED__'] = is_object($object) ? price($object->total_ttc, 0, $outputlangs, 0, 0, -1, Globals::$conf->currency) : '';
6015
            $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object) ? price($object->total_ht, 0, $outputlangs, 0, 0, -1, Globals::$conf->currency) : '';
6016
            $substitutionarray['__AMOUNT_VAT_FORMATED__'] = is_object($object) ? ($object->total_vat ? price($object->total_vat, 0, $outputlangs, 0, 0, -1, Globals::$conf->currency) : price($object->total_tva, 0, $outputlangs, 0, 0, -1, Globals::$conf->currency)) : '';
6017
            if ($onlykey != 2 || $mysoc->useLocalTax(1))
6018
                $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = is_object($object) ? price($object->total_localtax1, 0, $outputlangs, 0, 0, -1, Globals::$conf->currency) : '';
6019
            if ($onlykey != 2 || $mysoc->useLocalTax(2))
6020
                $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = is_object($object) ? price($object->total_localtax2, 0, $outputlangs, 0, 0, -1, Globals::$conf->currency) : '';
6021
6022
// TODO Add keys for foreign multicurrency
6023
// For backward compatibility
6024
            if ($onlykey != 2) {
6025
                $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
6026
                $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
6027
                $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? ($object->total_vat ? $object->total_vat : $object->total_tva) : '';
6028
            }
6029
        }
6030
6031
//var_dump($substitutionarray['__AMOUNT_FORMATED__']);
6032
        if (empty($exclude) || !in_array('date', $exclude)) {
6033
            include_once DOL_BASE_PATH . '/core/lib/date.lib.php';
6034
6035
            $tmp = dol_getdate(dol_now(), true);
6036
            $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
6037
            $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
6038
            $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
6039
            $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
6040
6041
            $substitutionarray = array_merge($substitutionarray, array(
6042
                '__DAY__' => (string) $tmp['mday'],
6043
                '__DAY_TEXT__' => $outputlangs->trans('Day' . $tmp['wday']), // Monday
6044
                '__DAY_TEXT_SHORT__' => $outputlangs->trans($tmp['weekday'] . 'Min'), // Mon
6045
                '__DAY_TEXT_MIN__' => $outputlangs->trans('Short' . $tmp['weekday']), // M
6046
                '__MONTH__' => (string) $tmp['mon'],
6047
                '__MONTH_TEXT__' => $outputlangs->trans('Month' . sprintf("%02d", $tmp['mon'])),
6048
                '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort' . sprintf("%02d", $tmp['mon'])),
6049
                '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort' . sprintf("%02d", $tmp['mon'])),
6050
                '__YEAR__' => (string) $tmp['year'],
6051
                '__PREVIOUS_DAY__' => (string) $tmp2['day'],
6052
                '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
6053
                '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
6054
                '__NEXT_DAY__' => (string) $tmp4['day'],
6055
                '__NEXT_MONTH__' => (string) $tmp5['month'],
6056
                '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
6057
            ));
6058
        }
6059
6060
        if (!empty(Globals::$conf->multicompany->enabled)) {
6061
            $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => Globals::$conf->entity));
6062
        }
6063
        if (empty($exclude) || !in_array('system', $exclude)) {
6064
            $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
6065
            $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
6066
            $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
6067
        }
6068
6069
        return $substitutionarray;
6070
    }
6071
6072
    /**
6073
     *  Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newval),
6074
     *  and texts like __(TranslationKey|langfile)__ and __[ConstantKey]__ are also replaced.
6075
     *  Example of usage:
6076
     *  $substitutionarray = getCommonSubstitutionArray(Globals::$langs, 0, null, $thirdparty);
6077
     *  complete_substitutions_array($substitutionarray, Globals::$langs, $thirdparty);
6078
     *  $mesg = make_substitutions($mesg, $substitutionarray, Globals::$langs);
6079
     *
6080
     *  @param	string		$text	      			Source string in which we must do substitution
6081
     *  @param  array		$substitutionarray		Array with key->val to substitute. Example: array('__MYKEY__' => 'MyVal', ...)
6082
     *  @param	Translate	$outputlangs			Output language
6083
     * 	@return string  		    				Output string after substitutions
6084
     *  @see	complete_substitutions_array, getCommonSubstitutionArray
6085
     */
6086
    static function make_substitutions($text, $substitutionarray, $outputlangs = null)
6087
    {
6088
        // global Globals::$conf, Globals::$langs;
6089
6090
        if (!is_array($substitutionarray))
6091
            return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
6092
6093
        if (empty($outputlangs))
6094
            $outputlangs = Globals::$langs;
6095
6096
// Make substitution for language keys
6097
        if (is_object($outputlangs)) {
6098
            while (preg_match('/__\(([^\)]+)\)__/', $text, $reg)) {
6099
                $msgishtml = 0;
6100
                if (DolUtils::dol_textishtml($text, 1))
6101
                    $msgishtml = 1;
6102
6103
// If key is __(TranslationKey|langfile)__, then force load of langfile.lang
6104
                $tmp = explode('|', $reg[1]);
6105
                if (!empty($tmp[1]))
6106
                    $outputlangs->load($tmp[1]);
6107
6108
                $text = preg_replace('/__\(' . preg_quote($reg[1], '/') . '\)__/', $msgishtml ? DolUtils::dol_htmlentitiesbr($outputlangs->transnoentitiesnoconv($reg[1])) : $outputlangs->transnoentitiesnoconv($reg[1]), $text);
6109
            }
6110
        }
6111
6112
// Make substitution for constant keys. Must be after the substitution of translation, so if text of translation contains a constant,
6113
// it is also converted.
6114
        while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
6115
            $msgishtml = 0;
6116
            if (DolUtils::dol_textishtml($text, 1))
6117
                $msgishtml = 1;
6118
6119
            $keyfound = $reg[1];
6120
            if (preg_match('/(_pass|password|secret|_key|key$)/i', $keyfound))
6121
                $newval = '*****forbidden*****';
6122
            else
6123
                $newval = empty(Globals::$conf->global->$keyfound) ? '' : Globals::$conf->global->$keyfound;
6124
            $text = preg_replace('/__\[' . preg_quote($keyfound, '/') . '\]__/', $msgishtml ? DolUtils::dol_htmlentitiesbr($newval) : $newval, $text);
6125
        }
6126
6127
// Make substitition for array $substitutionarray
6128
        foreach ($substitutionarray as $key => $value) {
6129
            if ($key == '__SIGNATURE__' && (!empty(Globals::$conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)))
6130
                $value = '';  // Protection
6131
            if ($key == '__USER_SIGNATURE__' && (!empty(Globals::$conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)))
6132
                $value = ''; // Protection
6133
6134
            $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
6135
        }
6136
6137
        return $text;
6138
    }
6139
6140
    /**
6141
     *  Complete the $substitutionarray with more entries coming from external module that had set the "substitutions=1" into module_part array.
6142
     *  In this case, method completesubstitutionarray provided by module is called.
6143
     *
6144
     *  @param  array		$substitutionarray		Array substitution old value => new value value
6145
     *  @param  Translate	$outputlangs            Output language
6146
     *  @param  Object		$object                 Source object
6147
     *  @param  mixed		$parameters       		Add more parameters (useful to pass product lines)
6148
     *  @param  string      $callfunc               What is the name of the custom static function that will be called? (default: completesubstitutionarray)
6149
     *  @return	void
6150
     *  @see 	make_substitutions
6151
     */
6152
    static function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
6153
    {
6154
        // global Globals::$conf, $user;
6155
6156
        require_once DOL_BASE_PATH . '/core/lib/files.lib.php';
6157
6158
// Add a substitution key for each extrafields, using key __EXTRA_XXX__
6159
// TODO Remove this. Already available into the getCommonSubstitutionArray used to build the substitution array.
6160
        /* if (is_object($object) && is_array($object->array_options))
6161
          {
6162
          foreach($object->array_options as $key => $val)
6163
          {
6164
          $keyshort=preg_replace('/^(options|extra)_/','',$key);
6165
          $substitutionarray['__EXTRAFIELD_'.$keyshort.'__']=$val;
6166
          // For backward compatibiliy
6167
          $substitutionarray['%EXTRA_'.$keyshort.'%']=$val;
6168
          }
6169
          } */
6170
6171
// Check if there is external substitution to do, requested by plugins
6172
        $dirsubstitutions = array_merge(array(), (array) Globals::$conf->modules_parts['substitutions']);
6173
6174
        foreach ($dirsubstitutions as $reldir) {
6175
            $dir = dol_buildpath($reldir, 0);
6176
6177
// Check if directory exists
6178
            if (!dol_is_dir($dir))
6179
                continue;
6180
6181
            $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
6182
            foreach ($substitfiles as $substitfile) {
6183
                if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
6184
                    $module = $reg[1];
6185
6186
                    DolUtils::dol_syslog("Library " . $substitfile['name'] . " found into " . $dir);
6187
                    // Include the user's functions file
6188
                    require_once $dir . $substitfile['name'];
6189
                    // Call the user's function, and only if it is defined
6190
                    $function_name = $module . "_" . $callfunc;
6191
                    if (function_exists($function_name))
6192
                        $function_name($substitutionarray, $outputlangs, $object, $parameters);
6193
                }
6194
            }
6195
        }
6196
    }
6197
6198
    /**
6199
     *    Format output for start and end date
6200
     *
6201
     *    @param	int	$date_start    Start date
6202
     *    @param    int	$date_end      End date
6203
     *    @param    string		$format        Output format
6204
     *    @param	Translate	$outputlangs   Output language
6205
     *    @return	void
6206
     */
6207
    static function print_date_range($date_start, $date_end, $format = '', $outputlangs = '')
6208
    {
6209
        print get_date_range($date_start, $date_end, $format, $outputlangs);
6210
    }
6211
6212
    /**
6213
     *    Format output for start and end date
6214
     *
6215
     *    @param	int			$date_start    		Start date
6216
     *    @param    int			$date_end      		End date
6217
     *    @param    string		$format        		Output format
6218
     *    @param	Translate	$outputlangs   		Output language
6219
     *    @param	integer		$withparenthesis	1=Add parenthesis, 0=non parenthesis
6220
     *    @return	string							String
6221
     */
6222
    static function get_date_range($date_start, $date_end, $format = '', $outputlangs = '', $withparenthesis = 1)
6223
    {
6224
        // global Globals::$langs;
6225
6226
        $out = '';
6227
6228
        if (!is_object($outputlangs))
6229
            $outputlangs = Globals::$langs;
6230
6231
        if ($date_start && $date_end) {
6232
            $out .= ($withparenthesis ? ' (' : '') . $outputlangs->transnoentitiesnoconv('DateFromTo', DolUtils::dol_print_date($date_start, $format, false, $outputlangs), DolUtils::dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
6233
        }
6234
        if ($date_start && !$date_end) {
6235
            $out .= ($withparenthesis ? ' (' : '') . $outputlangs->transnoentitiesnoconv('DateFrom', DolUtils::dol_print_date($date_start, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
6236
        }
6237
        if (!$date_start && $date_end) {
6238
            $out .= ($withparenthesis ? ' (' : '') . $outputlangs->transnoentitiesnoconv('DateUntil', DolUtils::dol_print_date($date_end, $format, false, $outputlangs)) . ($withparenthesis ? ')' : '');
6239
        }
6240
6241
        return $out;
6242
    }
6243
6244
    /**
6245
     * Return firstname and lastname in correct order
6246
     *
6247
     * @param	string	$firstname		Firstname
6248
     * @param	string	$lastname		Lastname
6249
     * @param	int		$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname
6250
     * @return	string					Firstname + lastname or Lastname + firstname
6251
     */
6252
    static function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
6253
    {
6254
        // global Globals::$conf;
6255
6256
        $ret = '';
6257
// If order not defined, we use the setup
6258
        if ($nameorder < 0)
6259
            $nameorder = (empty(Globals::$conf->global->MAIN_FIRSTNAME_NAME_POSITION) ? 1 : 0);
6260
        if ($nameorder && ((string) $nameorder != '2')) {
6261
            $ret .= $firstname;
6262
            if ($firstname && $lastname)
6263
                $ret .= ' ';
6264
            $ret .= $lastname;
6265
        }
6266
        else if ($nameorder == 2) {
6267
            $ret .= $firstname;
6268
        } else {
6269
            $ret .= $lastname;
6270
            if ($firstname && $lastname)
6271
                $ret .= ' ';
6272
            $ret .= $firstname;
6273
        }
6274
        return $ret;
6275
    }
6276
6277
    /**
6278
     * 	Set event message in dol_events session object. Will be output by calling dol_htmloutput_events.
6279
     *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
6280
     *  Note: Prefer to use setEventMessages instead.
6281
     *
6282
     * 	@param	mixed	$mesgs			Message string or array
6283
     *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
6284
     *  @return	void
6285
     *  @see	dol_htmloutput_events
6286
     */
6287
    static function setEventMessage($mesgs, $style = 'mesgs')
6288
    {
6289
//DolUtils::dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);		This is not deprecated, it is used by setEventMessages function
6290
        if (!is_array($mesgs)) {  // If mesgs is a string
6291
            if ($mesgs)
6292
                $_SESSION['dol_events'][$style][] = $mesgs;
6293
        }
6294
        else {      // If mesgs is an array
6295
            foreach ($mesgs as $mesg) {
6296
                if ($mesg)
6297
                    $_SESSION['dol_events'][$style][] = $mesg;
6298
            }
6299
        }
6300
    }
6301
6302
    /**
6303
     * 	Set event messages in dol_events session object. Will be output by calling dol_htmloutput_events.
6304
     *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function.
6305
     *
6306
     * 	@param	string	$mesg			Message string
6307
     * 	@param	array	$mesgs			Message array
6308
     *  @param  string	$style      	Which style to use ('mesgs' by default, 'warnings', 'errors')
6309
     *  @return	void
6310
     *  @see	dol_htmloutput_events
6311
     */
6312
    static function setEventMessages($mesg, $mesgs, $style = 'mesgs')
6313
    {
6314
        if (empty($mesg) && empty($mesgs)) {
6315
            DolUtils::dol_syslog("Try to add a message in stack with empty message", LOG_WARNING);
6316
        } else {
6317
            if (!in_array((string) $style, array('mesgs', 'warnings', 'errors')))
6318
                dol_print_error('', 'Bad parameter style=' . $style . ' for setEventMessages');
6319
            if (empty($mesgs))
6320
                setEventMessage($mesg, $style);
6321
            else {
6322
                if (!empty($mesg) && !in_array($mesg, $mesgs))
6323
                    setEventMessage($mesg, $style); // Add message string if not already into array
6324
                setEventMessage($mesgs, $style);
6325
            }
6326
        }
6327
    }
6328
6329
    /**
6330
     * 	Print formated messages to output (Used to show messages on html output).
6331
     *  Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function, so there is
6332
     *  no need to call it explicitely.
6333
     *
6334
     *  @param	int		$disabledoutputofmessages	Clear all messages stored into session without diplaying them
6335
     *  @return	void
6336
     *  @see    									dol_htmloutput_mesg
6337
     */
6338
    static function dol_htmloutput_events($disabledoutputofmessages = 0)
6339
    {
6340
// Show mesgs
6341
        if (isset($_SESSION['dol_events']['mesgs'])) {
6342
            if (empty($disabledoutputofmessages))
6343
                dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
6344
            unset($_SESSION['dol_events']['mesgs']);
6345
        }
6346
6347
// Show errors
6348
        if (isset($_SESSION['dol_events']['errors'])) {
6349
            if (empty($disabledoutputofmessages))
6350
                dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
6351
            unset($_SESSION['dol_events']['errors']);
6352
        }
6353
6354
// Show warnings
6355
        if (isset($_SESSION['dol_events']['warnings'])) {
6356
            if (empty($disabledoutputofmessages))
6357
                dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
6358
            unset($_SESSION['dol_events']['warnings']);
6359
        }
6360
    }
6361
6362
    /**
6363
     * 	Get formated messages to output (Used to show messages on html output).
6364
     *  This include also the translation of the message key.
6365
     *
6366
     * 	@param	string		$mesgstring		Message string or message key
6367
     * 	@param	string[]	$mesgarray      Array of message strings or message keys
6368
     *  @param  string		$style          Style of message output ('ok' or 'error')
6369
     *  @param  int			$keepembedded   Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
6370
     * 	@return	string						Return html output
6371
     *
6372
     *  @see    dol_print_error
6373
     *  @see    dol_htmloutput_errors
6374
     *  @see    setEventMessages
6375
     */
6376
    static function get_htmloutput_mesg($mesgstring = '', $mesgarray = '', $style = 'ok', $keepembedded = 0)
6377
    {
6378
        // global Globals::$conf, Globals::$langs;
6379
6380
        $ret = 0;
6381
        $return = '';
6382
        $out = '';
6383
        $divstart = $divend = '';
6384
6385
// If inline message with no format, we add it.
6386
        if ((empty(Globals::$conf->use_javascript_ajax) || !empty(Globals::$conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
6387
            $divstart = '<div class="' . $style . ' clearboth">';
6388
            $divend = '</div>';
6389
        }
6390
6391
        if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
6392
            Globals::$langs->load("errors");
6393
            $out .= $divstart;
6394
            if (is_array($mesgarray) && count($mesgarray)) {
6395
                foreach ($mesgarray as $message) {
6396
                    $ret++;
6397
                    $out .= Globals::$langs->trans($message);
6398
                    if ($ret < count($mesgarray))
6399
                        $out .= "<br>\n";
6400
                }
6401
            }
6402
            if ($mesgstring) {
6403
                Globals::$langs->load("errors");
6404
                $ret++;
6405
                $out .= Globals::$langs->trans($mesgstring);
6406
            }
6407
            $out .= $divend;
6408
        }
6409
6410
        if ($out) {
6411
            if (!empty(Globals::$conf->use_javascript_ajax) && empty(Globals::$conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded)) {
6412
                $return = '<script type="text/javascript">
6413
					$(document).ready(function() {
6414
						var block = ' . (!empty(Globals::$conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false") . '
6415
						if (block) {
6416
							$.dolEventValid("","' . dol_escape_js($out) . '");
6417
						} else {
6418
							/* jnotify(message, preset of message type, keepmessage) */
6419
							$.jnotify("' . dol_escape_js($out) . '",
6420
							"' . ($style == "ok" ? 3000 : $style) . '",
6421
							' . ($style == "ok" ? "false" : "true") . ',
6422
							{ remove: static function (){} } );
6423
						}
6424
					});
6425
				</script>';
6426
            } else {
6427
                $return = $out;
6428
            }
6429
        }
6430
6431
        return $return;
6432
    }
6433
6434
    /**
6435
     *  Get formated error messages to output (Used to show messages on html output).
6436
     *
6437
     *  @param  string	$mesgstring         Error message
6438
     *  @param  array	$mesgarray          Error messages array
6439
     *  @param  int		$keepembedded       Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
6440
     *  @return string                		Return html output
6441
     *
6442
     *  @see    dol_print_error
6443
     *  @see    dol_htmloutput_mesg
6444
     */
6445
    static function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
6446
    {
6447
        return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
6448
    }
6449
6450
    /**
6451
     * 	Print formated messages to output (Used to show messages on html output).
6452
     *
6453
     * 	@param	string		$mesgstring		Message string or message key
6454
     * 	@param	string[]	$mesgarray      Array of message strings or message keys
6455
     * 	@param  string      $style          Which style to use ('ok', 'warning', 'error')
6456
     * 	@param  int         $keepembedded   Set to 1 if message must be kept embedded into its html place (this disable jnotify)
6457
     * 	@return	void
6458
     *
6459
     * 	@see    dol_print_error
6460
     * 	@see    dol_htmloutput_errors
6461
     * 	@see    setEventMessages
6462
     */
6463
    static function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
6464
    {
6465
        if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0))
6466
            return;
6467
6468
        $iserror = 0;
6469
        $iswarning = 0;
6470
        if (is_array($mesgarray)) {
6471
            foreach ($mesgarray as $val) {
6472
                if ($val && preg_match('/class="error"/i', $val)) {
6473
                    $iserror++;
6474
                    break;
6475
                }
6476
                if ($val && preg_match('/class="warning"/i', $val)) {
6477
                    $iswarning++;
6478
                    break;
6479
                }
6480
            }
6481
        } else if ($mesgstring && preg_match('/class="error"/i', $mesgstring))
6482
            $iserror++;
6483
        else if ($mesgstring && preg_match('/class="warning"/i', $mesgstring))
6484
            $iswarning++;
6485
        if ($style == 'error')
6486
            $iserror++;
6487
        if ($style == 'warning')
6488
            $iswarning++;
6489
6490
        if ($iserror || $iswarning) {
6491
// Remove div from texts
6492
            $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
6493
            $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
6494
            $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
6495
// Remove div from texts array
6496
            if (is_array($mesgarray)) {
6497
                $newmesgarray = array();
6498
                foreach ($mesgarray as $val) {
6499
                    $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
6500
                    $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
6501
                    $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
6502
                    $newmesgarray[] = $tmpmesgstring;
6503
                }
6504
                $mesgarray = $newmesgarray;
6505
            }
6506
            print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
6507
        } else
6508
            print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
6509
    }
6510
6511
    /**
6512
     *  Print formated error messages to output (Used to show messages on html output).
6513
     *
6514
     *  @param	string	$mesgstring          Error message
6515
     *  @param  array	$mesgarray           Error messages array
6516
     *  @param  int		$keepembedded        Set to 1 in error message must be kept embedded into its html place (this disable jnotify)
6517
     *  @return	void
6518
     *
6519
     *  @see    dol_print_error
6520
     *  @see    dol_htmloutput_mesg
6521
     */
6522
    static function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
6523
    {
6524
        dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
6525
    }
6526
6527
    /**
6528
     * 	Advanced sort array by second index function, which produces ascending (default)
6529
     *  or descending output and uses optionally natural case insensitive sorting (which
6530
     *  can be optionally case sensitive as well).
6531
     *
6532
     *  @param      array		$array      		Array to sort (array of array('key','otherkey1','otherkey2'...))
6533
     *  @param      string		$index				Key in array to use for sorting criteria
6534
     *  @param      int			$order				Sort order ('asc' or 'desc')
6535
     *  @param      int			$natsort			1=use "natural" sort (natsort), 0=use "standard" sort (asort)
6536
     *  @param      int			$case_sensitive		1=sort is case sensitive, 0=not case sensitive
6537
     *  @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.
6538
     *  @return     array							Sorted array
6539
     */
6540
    static function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
6541
    {
6542
// Clean parameters
6543
        $order = strtolower($order);
6544
6545
        if (is_array($array)) {
6546
            $sizearray = count($array);
6547
            if ($sizearray > 0) {
6548
                $temp = array();
6549
                foreach (array_keys($array) as $key)
6550
                    $temp[$key] = $array[$key][$index];
6551
6552
                if (!$natsort)
6553
                    ($order == 'asc') ? asort($temp) : arsort($temp);
6554
                else {
6555
                    ($case_sensitive) ? natsort($temp) : natcasesort($temp);
6556
                    if ($order != 'asc')
6557
                        $temp = array_reverse($temp, true);
6558
                }
6559
6560
                $sorted = array();
6561
6562
                foreach (array_keys($temp) as $key) {
6563
                    (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
6564
                }
6565
6566
                return $sorted;
6567
            }
6568
        }
6569
        return $array;
6570
    }
6571
6572
    /**
6573
     *      Check if a string is in UTF8
6574
     *
6575
     *      @param	string	$str        String to check
6576
     * 		@return	boolean				True if string is UTF8 or ISO compatible with UTF8, False if not (ISO with special char or Binary)
6577
     */
6578
    static function utf8_check($str)
6579
    {
6580
        // We must use here a binary strlen static function (so not DolUtils::dol_strlen)
6581
        $strLength = DolUtils::dol_strlen($str);
6582
        for ($i = 0; $i < $strLength; $i++) {
6583
            if (ord($str[$i]) < 0x80)
6584
                continue; // 0bbbbbbb
6585
            elseif ((ord($str[$i]) & 0xE0) == 0xC0)
6586
                $n = 1; // 110bbbbb
6587
            elseif ((ord($str[$i]) & 0xF0) == 0xE0)
6588
                $n = 2; // 1110bbbb
6589
            elseif ((ord($str[$i]) & 0xF8) == 0xF0)
6590
                $n = 3; // 11110bbb
6591
            elseif ((ord($str[$i]) & 0xFC) == 0xF8)
6592
                $n = 4; // 111110bb
6593
            elseif ((ord($str[$i]) & 0xFE) == 0xFC)
6594
                $n = 5; // 1111110b
6595
            else
6596
                return false; // Does not match any model
6597
            for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
6598
                if (( ++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
6599
                    return false;
6600
            }
6601
        }
6602
        return true;
6603
    }
6604
6605
    /**
6606
     *      Return a string encoded into OS filesystem encoding. This static function is used to define
6607
     * 	    value to pass to filesystem PHP functions.
6608
     *
6609
     *      @param	string	$str        String to encode (UTF-8)
6610
     * 		@return	string				Encoded string (UTF-8, ISO-8859-1)
6611
     */
6612
    function dol_osencode($str)
6613
    {
6614
        // global Globals::$conf;
6615
6616
        $tmp = ini_get("unicode.filesystem_encoding");      // Disponible avec PHP 6.0
6617
        if (empty($tmp) && !empty($_SERVER["WINDIR"]))
6618
            $tmp = 'iso-8859-1'; // By default for windows
6619
        if (empty($tmp))
6620
            $tmp = 'utf-8';          // By default for other
6621
        if (!empty(Globals::$conf->global->MAIN_FILESYSTEM_ENCODING))
6622
            $tmp = Globals::$conf->global->MAIN_FILESYSTEM_ENCODING;
6623
6624
        if ($tmp == 'iso-8859-1')
6625
            return utf8_decode($str);
6626
        return $str;
6627
    }
6628
6629
    /**
6630
     *      Return an id or code from a code or id.
6631
     *      Store also Code-Id into a cache to speed up next request on same key.
6632
     *
6633
     * 		@param	DoliDB	$db				Database handler
6634
     * 		@param	string	$key			Code or Id to get Id or Code
6635
     * 		@param	string	$tablename		Table name without prefix
6636
     * 		@param	string	$fieldkey		Field to search the key into
6637
     * 		@param	string	$fieldid		Field to get
6638
     *      @param  int		$entityfilter	Filter by entity
6639
     *      @return int						<0 if KO, Id of code if OK
6640
     *      @see Globals::$langs->getLabelFromKey
6641
     */
6642
    static function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0)
6643
    {
6644
        // global $cache_codes;
6645
// If key empty
6646
        if ($key == '')
6647
            return '';
6648
6649
// Check in cache
6650
        if (isset($cache_codes[$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
6651
            return $cache_codes[$tablename][$key][$fieldid];   // Found in cache
6652
        }
6653
6654
        DolUtils::dol_syslog('dol_getIdFromCode (value not found into cache)', LOG_DEBUG);
6655
6656
        $sql = "SELECT " . $fieldid . " as valuetoget";
6657
        $sql .= " FROM " . MAIN_DB_PREFIX . $tablename;
6658
        $sql .= " WHERE " . $fieldkey . " = '" . $db->escape($key) . "'";
6659
        if (!empty($entityfilter))
6660
            $sql .= " AND entity IN (" . getEntity($tablename) . ")";
6661
6662
        $resql = $db->query($sql);
6663
        if ($resql) {
6664
            $obj = $db->fetch_object($resql);
6665
            if ($obj)
6666
                $cache_codes[$tablename][$key][$fieldid] = $obj->valuetoget;
6667
            else
6668
                $cache_codes[$tablename][$key][$fieldid] = '';
6669
            $db->free($resql);
6670
            return $cache_codes[$tablename][$key][$fieldid];
6671
        }
6672
        else {
6673
            return -1;
6674
        }
6675
    }
6676
6677
    /**
6678
     * Verify if condition in string is ok or not
6679
     *
6680
     * @param 	string		$strRights		String with condition to check
6681
     * @return 	boolean						True or False. Return True if strRights is ''
6682
     */
6683
    static function verifCond($strRights)
6684
    {
6685
        // global $user, Globals::$conf, Globals::$langs;
6686
        // global $leftmenu;
6687
        // global $rights;    // To export to dol_eval function
6688
//print $strRights."<br>\n";
6689
        $rights = true;
6690
        if ($strRights != '') {
6691
            $str = 'if(!(' . $strRights . ')) { $rights = false; }';
6692
            self::dol_eval($str);  // The dol_eval must contains all the// global $xxx used into a condition
6693
        }
6694
        return $rights;
6695
    }
6696
6697
    /**
6698
     * Replace eval static function to add more security.
6699
     * This static function is called by verifCond() or trans() and transnoentitiesnoconv().
6700
     *
6701
     * @param 	string	$s				String to evaluate
6702
     * @param	int		$returnvalue	0=No return (used to execute eval($a=something)). 1=Value of eval is returned (used to eval($something)).
6703
     * @param   int     $hideerrors     1=Hide errors
6704
     * @return	mixed					Nothing or return of eval
6705
     */
6706
    static function dol_eval($s, $returnvalue = 0, $hideerrors = 1)
6707
    {
6708
// Only// global variables can be changed by eval static function and returned to caller
6709
        // global $db, Globals::$langs, $user, Globals::$conf, $website, $websitepage;
6710
        // global $action, $mainmenu, $leftmenu;
6711
        // global $rights;
6712
        // global $object;
6713
        // global $mysoc;
6714
        // global $obj;       // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
6715
        // global $soc;       // For backward compatibility
6716
//print $s."<br>\n";
6717
        if ($returnvalue) {
6718
            if ($hideerrors)
6719
                return @eval('return ' . $s . ';');
6720
            else
6721
                return eval('return ' . $s . ';');
6722
        }
6723
        else {
6724
            if ($hideerrors)
6725
                @eval($s);
6726
            else
6727
                eval($s);
6728
        }
6729
    }
6730
6731
    /**
6732
     * Return if var element is ok
6733
     *
6734
     * @param   string      $element    Variable to check
6735
     * @return  boolean                 Return true of variable is not empty
6736
     */
6737
    static function dol_validElement($element)
6738
    {
6739
        return (trim($element) != '');
6740
    }
6741
6742
    /**
6743
     * 	Return img flag of country for a language code or country code
6744
     *
6745
     * 	@param	string	$codelang	Language code (en_IN, fr_CA...) or Country code (IN, FR)
6746
     *  @param	string	$moreatt	Add more attribute on img tag (For example 'style="float: right"')
6747
     * 	@return	string				HTML img string with flag.
6748
     */
6749
    static function picto_from_langcode($codelang, $moreatt = '')
6750
    {
6751
        // global Globals::$langs;
6752
6753
        if (empty($codelang))
6754
            return '';
6755
6756
        if ($codelang == 'auto') {
6757
            return '<span class="fa fa-globe"></span>';
6758
        }
6759
6760
        $langtocountryflag = array(
6761
            'ar_AR' => '',
6762
            'ca_ES' => 'catalonia',
6763
            'da_DA' => 'dk',
6764
            'fr_CA' => 'mq',
6765
            'sv_SV' => 'se'
6766
        );
6767
6768
        if (isset($langtocountryflag[$codelang]))
6769
            $flagImage = $langtocountryflag[$codelang];
6770
        else {
6771
            $tmparray = explode('_', $codelang);
6772
            $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
6773
        }
6774
6775
        return img_picto_common($codelang, 'flags/' . strtolower($flagImage) . '.png', $moreatt);
6776
    }
6777
6778
    /**
6779
     * Return default language from country code
6780
     *
6781
     * @param 	string 	$countrycode	Country code like 'US', 'FR', 'CA', ...
6782
     * @return	string					Value of locale like 'en_US', 'fr_FR', ...
6783
     */
6784
    static function getLanguageCodeFromCountryCode($countrycode)
6785
    {
6786
        // global $mysoc;
6787
6788
        if (strtoupper($countrycode) == 'MQ')
6789
            return 'fr_CA';
6790
        if (strtoupper($countrycode) == 'SE')
6791
            return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
6792
        if (strtoupper($countrycode) == 'CH') {
6793
            if ($mysoc->country_code == 'FR')
6794
                return 'fr_CH';
6795
            if ($mysoc->country_code == 'DE')
6796
                return 'de_CH';
6797
        }
6798
6799
// Locale list taken from:
6800
// http://stackoverflow.com/questions/3191664/
6801
// list-of-all-locales-and-their-short-codes
6802
        $locales = array(
6803
            'af-ZA',
6804
            'am-ET',
6805
            'ar-AE',
6806
            'ar-BH',
6807
            'ar-DZ',
6808
            'ar-EG',
6809
            'ar-IQ',
6810
            'ar-JO',
6811
            'ar-KW',
6812
            'ar-LB',
6813
            'ar-LY',
6814
            'ar-MA',
6815
            'ar-OM',
6816
            'ar-QA',
6817
            'ar-SA',
6818
            'ar-SY',
6819
            'ar-TN',
6820
            'ar-YE',
6821
            'as-IN',
6822
            'ba-RU',
6823
            'be-BY',
6824
            'bg-BG',
6825
            'bn-BD',
6826
            'bn-IN',
6827
            'bo-CN',
6828
            'br-FR',
6829
            'ca-ES',
6830
            'co-FR',
6831
            'cs-CZ',
6832
            'cy-GB',
6833
            'da-DK',
6834
            'de-AT',
6835
            'de-CH',
6836
            'de-DE',
6837
            'de-LI',
6838
            'de-LU',
6839
            'dv-MV',
6840
            'el-GR',
6841
            'en-AU',
6842
            'en-BZ',
6843
            'en-CA',
6844
            'en-GB',
6845
            'en-IE',
6846
            'en-IN',
6847
            'en-JM',
6848
            'en-MY',
6849
            'en-NZ',
6850
            'en-PH',
6851
            'en-SG',
6852
            'en-TT',
6853
            'en-US',
6854
            'en-ZA',
6855
            'en-ZW',
6856
            'es-AR',
6857
            'es-BO',
6858
            'es-CL',
6859
            'es-CO',
6860
            'es-CR',
6861
            'es-DO',
6862
            'es-EC',
6863
            'es-ES',
6864
            'es-GT',
6865
            'es-HN',
6866
            'es-MX',
6867
            'es-NI',
6868
            'es-PA',
6869
            'es-PE',
6870
            'es-PR',
6871
            'es-PY',
6872
            'es-SV',
6873
            'es-US',
6874
            'es-UY',
6875
            'es-VE',
6876
            'et-EE',
6877
            'eu-ES',
6878
            'fa-IR',
6879
            'fi-FI',
6880
            'fo-FO',
6881
            'fr-BE',
6882
            'fr-CA',
6883
            'fr-CH',
6884
            'fr-FR',
6885
            'fr-LU',
6886
            'fr-MC',
6887
            'fy-NL',
6888
            'ga-IE',
6889
            'gd-GB',
6890
            'gl-ES',
6891
            'gu-IN',
6892
            'he-IL',
6893
            'hi-IN',
6894
            'hr-BA',
6895
            'hr-HR',
6896
            'hu-HU',
6897
            'hy-AM',
6898
            'id-ID',
6899
            'ig-NG',
6900
            'ii-CN',
6901
            'is-IS',
6902
            'it-CH',
6903
            'it-IT',
6904
            'ja-JP',
6905
            'ka-GE',
6906
            'kk-KZ',
6907
            'kl-GL',
6908
            'km-KH',
6909
            'kn-IN',
6910
            'ko-KR',
6911
            'ky-KG',
6912
            'lb-LU',
6913
            'lo-LA',
6914
            'lt-LT',
6915
            'lv-LV',
6916
            'mi-NZ',
6917
            'mk-MK',
6918
            'ml-IN',
6919
            'mn-MN',
6920
            'mr-IN',
6921
            'ms-BN',
6922
            'ms-MY',
6923
            'mt-MT',
6924
            'nb-NO',
6925
            'ne-NP',
6926
            'nl-BE',
6927
            'nl-NL',
6928
            'nn-NO',
6929
            'oc-FR',
6930
            'or-IN',
6931
            'pa-IN',
6932
            'pl-PL',
6933
            'ps-AF',
6934
            'pt-BR',
6935
            'pt-PT',
6936
            'rm-CH',
6937
            'ro-RO',
6938
            'ru-RU',
6939
            'rw-RW',
6940
            'sa-IN',
6941
            'se-FI',
6942
            'se-NO',
6943
            'se-SE',
6944
            'si-LK',
6945
            'sk-SK',
6946
            'sl-SI',
6947
            'sq-AL',
6948
            'sv-FI',
6949
            'sv-SE',
6950
            'sw-KE',
6951
            'ta-IN',
6952
            'te-IN',
6953
            'th-TH',
6954
            'tk-TM',
6955
            'tn-ZA',
6956
            'tr-TR',
6957
            'tt-RU',
6958
            'ug-CN',
6959
            'uk-UA',
6960
            'ur-PK',
6961
            'vi-VN',
6962
            'wo-SN',
6963
            'xh-ZA',
6964
            'yo-NG',
6965
            'zh-CN',
6966
            'zh-HK',
6967
            'zh-MO',
6968
            'zh-SG',
6969
            'zh-TW',
6970
            'zu-ZA',
6971
        );
6972
6973
        $buildprimarykeytotest = strtolower($countrycode) . '-' . strtoupper($countrycode);
6974
        if (in_array($buildprimarykeytotest, $locales))
6975
            return strtolower($countrycode) . '_' . strtoupper($countrycode);
6976
6977
        foreach ($locales as $locale) {
6978
            $locale_language = locale_get_primary_language($locale);
6979
            $locale_region = locale_get_region($locale);
6980
            if (strtoupper($countrycode) == $locale_region) {
6981
//var_dump($locale.'-'.$locale_language.'-'.$locale_region);
6982
                return strtolower($locale_language) . '_' . strtoupper($locale_region);
6983
            }
6984
        }
6985
6986
        return null;
6987
    }
6988
6989
    /**
6990
     *  Complete or removed entries into a head array (used to build tabs).
6991
     *  For example, with value added by external modules. Such values are declared into Globals::$conf->modules_parts['tab'].
6992
     *  Or by change using hook completeTabsHead
6993
     *
6994
     *  @param	Conf			$conf           Object conf
6995
     *  @param  Translate		Globals::$langs          Object langs
6996
     *  @param  object|null		$object         Object object
6997
     *  @param  array			$head          	Object head
6998
     *  @param  int				$h				New position to fill
6999
     *  @param  string			$type           Value for object where objectvalue can be
7000
     *                              			'thirdparty'       to add a tab in third party view
7001
     * 		                        	      	'intervention'     to add a tab in intervention view
7002
     *     		                    	     	'supplier_order'   to add a tab in supplier order view
7003
     *          		            	        'supplier_invoice' to add a tab in supplier invoice view
7004
     *                  		    	        'invoice'          to add a tab in customer invoice view
7005
     *                          			    'order'            to add a tab in customer order view
7006
     *                          				'contract'		   to add a tabl in contract view
7007
     *                      			        'product'          to add a tab in product view
7008
     *                              			'propal'           to add a tab in propal view
7009
     *                              			'user'             to add a tab in user view
7010
     *                              			'group'            to add a tab in group view
7011
     * 		        	               	     	'member'           to add a tab in fundation member view
7012
     *      		                        	'categories_x'	   to add a tab in category view ('x': type of category (0=product, 1=supplier, 2=customer, 3=member)
7013
     *      									'ecm'			   to add a tab for another ecm view
7014
     *                                          'stock'            to add a tab for warehouse view
7015
     *  @param  string		$mode  	        	'add' to complete head, 'remove' to remove entries
7016
     * 	@return	void
7017
     */
7018
    static function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add')
7019
    {
7020
        // global Globals::$hookManager;
7021
7022
        if (isset(Globals::$conf->modules_parts['tabs'][$type]) && is_array(Globals::$conf->modules_parts['tabs'][$type])) {
7023
            foreach (Globals::$conf->modules_parts['tabs'][$type] as $value) {
7024
7025
                $values = explode(':', $value);
7026
7027
                if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
7028
                    if (count($values) == 6) {       // new declaration with permissions:  $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
7029
                        if ($values[0] != $type)
7030
                            continue;
7031
7032
                        if (verifCond($values[4])) {
7033
                            if ($values[3])
7034
                                Globals::$langs->load($values[3]);
7035
                            if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
7036
                                $substitutionarray = array();
7037
                                complete_substitutions_array($substitutionarray, Globals::$langs, $object, array('needforkey' => $values[2]));
7038
                                $label = make_substitutions($reg[1], $substitutionarray);
7039
                            } else
7040
                                $label = Globals::$langs->trans($values[2]);
7041
7042
                            //$head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
7043
                            $head[$h][0] = BASE_URI . preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]);
7044
                            $head[$h][1] = $label;
7045
                            $head[$h][2] = str_replace('+', '', $values[1]);
7046
                            $h++;
7047
                        }
7048
                    }
7049
                    else if (count($values) == 5) {       // deprecated
7050
                        DolUtils::dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
7051
7052
                        if ($values[0] != $type)
7053
                            continue;
7054
                        if ($values[3])
7055
                            Globals::$langs->load($values[3]);
7056
                        if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
7057
                            $substitutionarray = array();
7058
                            complete_substitutions_array($substitutionarray, Globals::$langs, $object, array('needforkey' => $values[2]));
7059
                            $label = make_substitutions($reg[1], $substitutionarray);
7060
                        } else
7061
                            $label = Globals::$langs->trans($values[2]);
7062
7063
                        $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
7064
                        $head[$h][1] = $label;
7065
                        $head[$h][2] = str_replace('+', '', $values[1]);
7066
                        $h++;
7067
                    }
7068
                }
7069
                else if ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
7070
                    if ($values[0] != $type)
7071
                        continue;
7072
                    $tabname = str_replace('-', '', $values[1]);
7073
                    foreach ($head as $key => $val) {
7074
                        $condition = (!empty($values[3]) ? verifCond($values[3]) : 1);
7075
                        //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
7076
                        if ($head[$key][2] == $tabname && $condition) {
7077
                            unset($head[$key]);
7078
                            break;
7079
                        }
7080
                    }
7081
                }
7082
            }
7083
        }
7084
7085
// No need to make a return $head. Var is modified as a reference
7086
        if (!empty(Globals::$hookManager)) {
7087
            $parameters = array('object' => $object, 'mode' => $mode, 'head' => $head);
7088
            $reshook = Globals::$hookManager->executeHooks('completeTabsHead', $parameters);
7089
            if ($reshook > 0) {
7090
                $head = Globals::$hookManager->resArray;
7091
                $h = count($head);
7092
            }
7093
        }
7094
    }
7095
7096
    /**
7097
     * Print common footer :
7098
     * 		conf->global->MAIN_HTML_FOOTER
7099
     *      js for switch of menu hider
7100
     * 		js for conf->global->MAIN_GOOGLE_AN_ID
7101
     * 		js for conf->global->MAIN_SHOW_TUNING_INFO or $_SERVER["MAIN_SHOW_TUNING_INFO"]
7102
     * 		js for conf->logbuffer
7103
     *
7104
     * @param	string	$zone	'private' (for private pages) or 'public' (for public pages)
7105
     * @return	void
7106
     */
7107
    static function printCommonFooter($zone = 'private')
7108
    {
7109
        // global Globals::$conf, Globals::$hookManager, $user;
7110
        // global $action;
7111
        // global $micro_start_time;
7112
7113
        if ($zone == 'private')
7114
            print "\n" . '<!-- Common footer for private page -->' . "\n";
7115
        else
7116
            print "\n" . '<!-- Common footer for public page -->' . "\n";
7117
7118
// A div to store page_y POST parameter so we can read it using javascript
7119
        print "\n<!-- A div to store page_y POST paramater -->\n";
7120
        print '<div id="page_y" style="display: none;">' . filter_input(INPUT_POST, 'page_y') . '</div>' . "\n";
7121
7122
        $parameters = array();
7123
        $reshook = Globals::$hookManager->executeHooks('printCommonFooter', $parameters);    // Note that $action and $object may have been modified by some hooks
7124
        if (empty($reshook)) {
7125
            if (!empty(Globals::$conf->global->MAIN_HTML_FOOTER))
7126
                print Globals::$conf->global->MAIN_HTML_FOOTER . "\n";
7127
7128
            print "\n";
7129
            if (!empty(Globals::$conf->use_javascript_ajax)) {
7130
                print '<script type="text/javascript" language="javascript">' . "\n";
7131
                print 'jQuery(document).ready(function() {' . "\n";
7132
7133
                if ($zone == 'private' && empty(Globals::$conf->dol_use_jmobile)) {
7134
                    print "\n";
7135
                    print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */' . "\n";
7136
                    print 'jQuery(".menuhider").click(function() {';
7137
                    print '  console.log("We click on .menuhider");' . "\n";
7138
                    //print "  $('.side-nav').animate({width:'toggle'},200);\n";     // OK with eldy theme but not with md
7139
                    print "  $('.side-nav').toggle()\n";
7140
                    print "  $('.login_block').toggle()\n";
7141
                    print '});' . "\n";
7142
                }
7143
7144
// Management of focus and mandatory for fields
7145
                if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"])))) {
7146
                    print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */' . "\n";
7147
                    $relativepathstring = $_SERVER["PHP_SELF"];
7148
                    // Clean $relativepathstring
7149
                    if (constant('DOL_BASE_URI'))
7150
                        $relativepathstring = preg_replace('/^' . preg_quote(constant('DOL_BASE_URI'), '/') . '/', '', $relativepathstring);
7151
                    $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
7152
                    $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
7153
                    $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
7154
                    if (!empty($user->default_values[$relativepathstring]['focus'])) {
7155
                        foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
7156
                            $qualified = 0;
7157
                            if ($defkey != '_noquery_') {
7158
                                $tmpqueryarraytohave = explode('&', $defkey);
7159
                                $foundintru = 0;
7160
                                foreach ($tmpqueryarraytohave as $tmpquerytohave) {
7161
                                    if (!in_array($tmpquerytohave, $tmpqueryarraywehave))
7162
                                        $foundintru = 1;
7163
                                }
7164
                                if (!$foundintru)
7165
                                    $qualified = 1;
7166
                                //var_dump($defkey.'-'.$qualified);
7167
                            } else
7168
                                $qualified = 1;
7169
7170
                            if ($qualified) {
7171
                                foreach ($defval as $paramkey => $paramval) {
7172
                                    // Set focus on field
7173
                                    print 'jQuery("input[name=\'' . $paramkey . '\']").focus();' . "\n";
7174
                                    print 'jQuery("textarea[name=\'' . $paramkey . '\']").focus();' . "\n";
7175
                                    print 'jQuery("select[name=\'' . $paramkey . '\']").focus();' . "\n";  // Not really usefull, but we keep it in case of.
7176
                                }
7177
                            }
7178
                        }
7179
                    }
7180
                    if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
7181
                        foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
7182
                            $qualified = 0;
7183
                            if ($defkey != '_noquery_') {
7184
                                $tmpqueryarraytohave = explode('&', $defkey);
7185
                                $foundintru = 0;
7186
                                foreach ($tmpqueryarraytohave as $tmpquerytohave) {
7187
                                    if (!in_array($tmpquerytohave, $tmpqueryarraywehave))
7188
                                        $foundintru = 1;
7189
                                }
7190
                                if (!$foundintru)
7191
                                    $qualified = 1;
7192
                                //var_dump($defkey.'-'.$qualified);
7193
                            } else
7194
                                $qualified = 1;
7195
7196
                            if ($qualified) {
7197
                                foreach ($defval as $paramkey => $paramval) {
7198
                                    // Add property 'required' on input
7199
                                    print 'jQuery("input[name=\'' . $paramkey . '\']").prop(\'required\',true);' . "\n";
7200
                                    print 'jQuery("textarea[name=\'' . $paramkey . '\']").prop(\'required\',true);' . "\n";
7201
                                    print 'jQuery("select[name=\'' . $paramkey . '\']").prop(\'required\',true);' . "\n";  // required on a select works only if key is "", this does not happen in Dolibarr
7202
                                }
7203
                            }
7204
                        }
7205
                    }
7206
                }
7207
7208
                print '});' . "\n";
7209
7210
// Google Analytics
7211
// TODO Add a hook here
7212
                if (!empty(Globals::$conf->google->enabled) && !empty(Globals::$conf->global->MAIN_GOOGLE_AN_ID)) {
7213
                    if ((Globals::$conf->dol_use_jmobile != 4)) {
7214
                        print "\n";
7215
                        print "/* JS CODE TO ENABLE for google analtics tag */\n";
7216
                        print '  var _gaq = _gaq || [];' . "\n";
7217
                        print '  _gaq.push([\'_setAccount\', \'' . Globals::$conf->global->MAIN_GOOGLE_AN_ID . '\']);' . "\n";
7218
                        print '  _gaq.push([\'_trackPageview\']);' . "\n";
7219
                        print '' . "\n";
7220
                        print '  (function() {' . "\n";
7221
                        print '    var ga = document.createElement(\'script\'); ga.type = \'text/javascript\'; ga.async = true;' . "\n";
7222
                        print '    ga.src = (\'https:\' == document.location.protocol ? \'https://ssl\' : \'http://www\') + \'.google-analytics.com/ga.js\';' . "\n";
7223
                        print '    var s = document.getElementsByTagName(\'script\')[0]; s.parentNode.insertBefore(ga, s);' . "\n";
7224
                        print '  })();' . "\n";
7225
                    }
7226
                }
7227
7228
// End of tuning
7229
                if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || !empty(Globals::$conf->global->MAIN_SHOW_TUNING_INFO)) {
7230
                    print "\n";
7231
                    print "/* JS CODE TO ENABLE to add memory info */\n";
7232
                    print 'window.console && console.log("';
7233
                    if (!empty(Globals::$conf->global->MEMCACHED_SERVER))
7234
                        print 'MEMCACHED_SERVER=' . Globals::$conf->global->MEMCACHED_SERVER . ' - ';
7235
                    print 'MAIN_OPTIMIZE_SPEED=' . (isset(Globals::$conf->global->MAIN_OPTIMIZE_SPEED) ? Globals::$conf->global->MAIN_OPTIMIZE_SPEED : 'off');
7236
                    if (!empty($micro_start_time)) {   // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in// global variable.
7237
                        $micro_end_time = microtime(true);
7238
                        print ' - Build time: ' . ceil(1000 * ($micro_end_time - $micro_start_time)) . ' ms';
7239
                    }
7240
                    if (function_exists("memory_get_usage")) {
7241
                        print ' - Mem: ' . memory_get_usage();
7242
                    }
7243
                    if (function_exists("xdebug_memory_usage")) {
7244
                        print ' - XDebug time: ' . ceil(1000 * xdebug_time_index()) . ' ms';
7245
                        print ' - XDebug mem: ' . xdebug_memory_usage();
7246
                        print ' - XDebug mem peak: ' . xdebug_peak_memory_usage();
7247
                    }
7248
                    if (function_exists("zend_loader_file_encoded")) {
7249
                        print ' - Zend encoded file: ' . (zend_loader_file_encoded() ? 'yes' : 'no');
7250
                    }
7251
                    print '");' . "\n";
7252
                }
7253
7254
                print "\n" . '</script>' . "\n";
7255
            }
7256
7257
// Add Xdebug coverage of code
7258
            if (defined('XDEBUGCOVERAGE')) {
7259
                print_r(xdebug_get_code_coverage());
7260
            }
7261
7262
// If there is some logs in buffer to show
7263
            if (count(Globals::$conf->logbuffer)) {
7264
                print "\n";
7265
                print "<!-- Start of log output\n";
7266
//print '<div class="hidden">'."\n";
7267
                foreach (Globals::$conf->logbuffer as $logline) {
7268
                    print $logline . "<br>\n";
7269
                }
7270
//print '</div>'."\n";
7271
                print "End of log output -->\n";
7272
            }
7273
        }
7274
    }
7275
7276
    /**
7277
     * Split a string with 2 keys into key array.
7278
     * For example: "A=1;B=2;C=2" is exploded into array('A'=>1,'B'=>2,'C'=>3)
7279
     *
7280
     * @param 	string	$string		String to explode
7281
     * @param 	string	$delimiter	Delimiter between each couple of data
7282
     * @param 	string	$kv			Delimiter between key and value
7283
     * @return	array				Array of data exploded
7284
     */
7285
    static function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
7286
    {
7287
        if ($a = explode($delimiter, $string)) {
7288
            $ka = array();
7289
            foreach ($a as $s) { // each part
7290
                if ($s) {
7291
                    if ($pos = strpos($s, $kv)) { // key/value delimiter
7292
                        $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
7293
                    } else { // key delimiter not found
7294
                        $ka[] = trim($s);
7295
                    }
7296
                }
7297
            }
7298
            return $ka;
7299
        }
7300
        return array();
7301
    }
7302
7303
    /**
7304
     * Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag)
7305
     *
7306
     * @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.
7307
     * @return	string				HTML code to set focus
7308
     */
7309
    static function dol_set_focus($selector)
7310
    {
7311
        print "\n" . '<!-- Set focus onto a specific field -->' . "\n";
7312
        print '<script type="text/javascript" language="javascript">jQuery(document).ready(function() { jQuery("' . dol_escape_js($selector) . '").focus(); });</script>' . "\n";
7313
    }
7314
7315
    /**
7316
     * Return getmypid() or random PID when static function is disabled
7317
     * Some web hosts disable this php static function for security reasons
7318
     * and sometimes we can't redeclare function
7319
     *
7320
     * @return	int
7321
     */
7322
    static function dol_getmypid()
7323
    {
7324
        if (!function_exists('getmypid')) {
7325
            return mt_rand(1, 32768);
7326
        } else {
7327
            return getmypid();
7328
        }
7329
    }
7330
7331
    /**
7332
     * Generate natural SQL search string for a criteria (this criteria can be tested on one or several fields)
7333
     *
7334
     * @param   string|string[]	$fields 	String or array of strings, filled with the name of all fields in the SQL query we must check (combined with a OR). Example: array("p.field1","p.field2")
7335
     * @param   string 			$value 		The value to look for.
7336
     *                          		    If param $mode is 0, can contains several keywords separated with a space or |
7337
     *                                      like "keyword1 keyword2" = We want record field like keyword1 AND field like keyword2
7338
     *                                      or like "keyword1|keyword2" = We want record field like keyword1 OR field like keyword2
7339
     *                             			If param $mode is 1, can contains an operator <, > or = like "<10" or ">=100.5 < 1000"
7340
     *                             			If param $mode is 2, can contains a list of int id separated by comma like "1,3,4"
7341
     *                             			If param $mode is 3, can contains a list of string separated by comma like "a,b,c"
7342
     * @param	integer			$mode		0=value is list of keyword strings, 1=value is a numeric test (Example ">5.5 <10"), 2=value is a list of ID separated with comma (Example '1,3,4')
7343
     * 										3=value is list of string separated with comma (Example 'text 1,text 2'), 4=value is a list of ID separated with comma (Example '1,3,4') for search into a multiselect string ('1,2')
7344
     * @param	integer			$nofirstand	1=Do not output the first 'AND'
7345
     * @return 	string 			$res 		The statement to append to the SQL query
7346
     */
7347
    static function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
7348
    {
7349
        // global $db, Globals::$langs;
7350
7351
        $value = trim($value);
7352
7353
        if ($mode == 0) {
7354
            $value = preg_replace('/\*/', '%', $value); // Replace * with %
7355
        }
7356
        if ($mode == 1) {
7357
            $value = preg_replace('/([<>=]+)\s+([0-9' . preg_quote(Globals::$langs->trans("DecimalSeparator"), '/') . '\-])/', '\1\2', $value); // Clean string '< 10' into '<10' so we can the explode on space to get all tests to do
7358
        }
7359
7360
        $value = preg_replace('/\s*\|\s*/', '|', $value);
7361
7362
        $crits = explode(' ', $value);
7363
        $res = '';
7364
        if (!is_array($fields))
7365
            $fields = array($fields);
7366
7367
        $nboffields = count($fields);
7368
        $end2 = count($crits);
7369
        $j = 0;
7370
        foreach ($crits as $crit) {
7371
            $i = 0;
7372
            $i2 = 0;
7373
            $newres = '';
7374
            foreach ($fields as $field) {
7375
                if ($mode == 1) {
7376
                    $operator = '=';
7377
                    $newcrit = preg_replace('/([<>=]+)/', '', trim($crit));
7378
7379
                    preg_match('/([<>=]+)/', trim($crit), $reg);
7380
                    if ($reg[1]) {
7381
                        $operator = $reg[1];
7382
                    }
7383
                    if ($newcrit != '') {
7384
                        $numnewcrit = price2num($newcrit);
7385
                        if (is_numeric($numnewcrit)) {
7386
                            $newres .= ($i2 > 0 ? ' OR ' : '') . $field . ' ' . $operator . ' ' . $numnewcrit;
7387
                        } else {
7388
                            $newres .= ($i2 > 0 ? ' OR ' : '') . '1 = 2'; // force false
7389
                        }
7390
                        $i2++; // a criteria was added to string
7391
                    }
7392
                } else if ($mode == 2) {
7393
                    $newres .= ($i2 > 0 ? ' OR ' : '') . $field . " IN (" . $db->escape(trim($crit)) . ")";
7394
                    $i2++; // a criteria was added to string
7395
                } else if ($mode == 3) {
7396
                    $tmparray = explode(',', trim($crit));
7397
                    if (count($tmparray)) {
7398
                        $listofcodes = '';
7399
                        foreach ($tmparray as $val) {
7400
                            if ($val) {
7401
                                $listofcodes .= ($listofcodes ? ',' : '');
7402
                                $listofcodes .= "'" . $db->escape(trim($val)) . "'";
7403
                            }
7404
                        }
7405
                        $newres .= ($i2 > 0 ? ' OR ' : '') . $field . " IN (" . $listofcodes . ")";
7406
                        $i2++; // a criteria was added to string
7407
                    }
7408
                } else if ($mode == 4) {
7409
                    $tmparray = explode(',', trim($crit));
7410
                    if (count($tmparray)) {
7411
                        $listofcodes = '';
7412
                        foreach ($tmparray as $val) {
7413
                            if ($val) {
7414
                                $newres .= ($i2 > 0 ? ' OR (' : '(') . $field . ' LIKE \'' . $db->escape(trim($val)) . ',%\'';
7415
                                $newres .= ' OR ' . $field . ' = \'' . $db->escape(trim($val)) . '\'';
7416
                                $newres .= ' OR ' . $field . ' LIKE \'%,' . $db->escape(trim($val)) . '\'';
7417
                                $newres .= ' OR ' . $field . ' LIKE \'%,' . $db->escape(trim($val)) . ',%\'';
7418
                                $newres .= ')';
7419
                                $i2++;
7420
                            }
7421
                        }
7422
                    }
7423
                } else {    // $mode=0
7424
                    $textcrit = '';
7425
                    $tmpcrits = explode('|', $crit);
7426
                    $i3 = 0;
7427
                    foreach ($tmpcrits as $tmpcrit) {
7428
                        if (empty($tmpcrit))
7429
                            continue;
7430
7431
                        $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
7432
7433
                        if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
7434
                            $newres .= $field . " = " . (is_numeric(trim($tmpcrit)) ? trim($tmpcrit) : '0');
7435
                        } else {
7436
                            $newres .= $field . " LIKE '";
7437
7438
                            $tmpcrit = trim($tmpcrit);
7439
                            $tmpcrit2 = $tmpcrit;
7440
                            $tmpbefore = '%';
7441
                            $tmpafter = '%';
7442
                            if (preg_match('/^[\^\$]/', $tmpcrit)) {
7443
                                $tmpbefore = '';
7444
                                $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
7445
                            }
7446
                            if (preg_match('/[\^\$]$/', $tmpcrit)) {
7447
                                $tmpafter = '';
7448
                                $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
7449
                            }
7450
                            $newres .= $tmpbefore;
7451
                            $newres .= $db->escape($tmpcrit2);
7452
                            $newres .= $tmpafter;
7453
                            $newres .= "'";
7454
                            if ($tmpcrit2 == '') {
7455
                                $newres .= ' OR ' . $field . " IS NULL";
7456
                            }
7457
                        }
7458
7459
                        $i3++;
7460
                    }
7461
                    $i2++; // a criteria was added to string
7462
                }
7463
                $i++;
7464
            }
7465
            if ($newres)
7466
                $res = $res . ($res ? ' AND ' : '') . ($i2 > 1 ? '(' : '') . $newres . ($i2 > 1 ? ')' : '');
7467
            $j++;
7468
        }
7469
        $res = ($nofirstand ? "" : " AND ") . "(" . $res . ")";
7470
//print 'xx'.$res.'yy';
7471
        return $res;
7472
    }
7473
7474
    /**
7475
     * Return string with full Url. The file qualified is the one defined by relative path in $object->last_main_doc
7476
     *
7477
     * @param   Object	$object				Object
7478
     * @return	string						Url string
7479
     */
7480
    static function showDirectDownloadLink($object)
7481
    {
7482
        // global Globals::$conf, Globals::$langs;
7483
7484
        $out = '';
7485
        $url = $object->getLastMainDocLink($object->element);
7486
7487
        if ($url) {
7488
            $out .= img_picto('', 'object_globe.png') . ' ' . Globals::$langs->trans("DirectDownloadLink") . '<br>';
7489
            $out .= '<input type="text" id="directdownloadlink" class="quatrevingtpercent" value="' . $url . '">';
7490
            $out .= ajax_autoselect("directdownloadlink", 0);
7491
        }
7492
        return $out;
7493
    }
7494
7495
    /**
7496
     * Return the filename of file to get the thumbs
7497
     *
7498
     * @param   string  $file           Original filename (full or relative path)
7499
     * @param   string  $extName        Extension to differenciate thumb file name ('', '_small', '_mini')
7500
     * @param   string  $extImgTarget   Force image extension for thumbs. Use '' to keep same extension than original image (default).
7501
     * @return  string                  New file name (full or relative path, including the thumbs/)
7502
     */
7503
    static function getImageFileNameForSize($file, $extName, $extImgTarget = '')
7504
    {
7505
        $dirName = dirname($file);
7506
        if ($dirName == '.')
7507
            $dirName = '';
7508
7509
        $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp)$/i', '', $file); // We remove extension, whatever is its case
7510
        $fileName = basename($fileName);
7511
7512
        if (empty($extImgTarget))
7513
            $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
7514
        if (empty($extImgTarget))
7515
            $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
7516
        if (empty($extImgTarget))
7517
            $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
7518
        if (empty($extImgTarget))
7519
            $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
7520
        if (empty($extImgTarget))
7521
            $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
7522
7523
        if (!$extImgTarget)
7524
            return $file;
7525
7526
        $subdir = '';
7527
        if ($extName)
7528
            $subdir = 'thumbs/';
7529
7530
        return ($dirName ? $dirName . '/' : '') . $subdir . $fileName . $extName . $extImgTarget; // New filename for thumb
7531
    }
7532
7533
    /**
7534
     * Return URL we can use for advanced preview links
7535
     *
7536
     * @param   string    $modulepart     propal, facture, facture_fourn, ...
7537
     * @param   string    $relativepath   Relative path of docs.
7538
     * @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)
7539
     * @param	string	  $param		  More param on http links
7540
     * @return  string|array              Output string with href link or array with all components of link
7541
     */
7542
    static function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
7543
    {
7544
        // global Globals::$conf, Globals::$langs;
7545
7546
        if (empty(Globals::$conf->use_javascript_ajax))
7547
            return '';
7548
7549
        $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'svg+xml');
7550
//$mime_preview[]='vnd.oasis.opendocument.presentation';
7551
//$mime_preview[]='archive';
7552
        $num_mime = array_search(dol_mimetype($relativepath, '', 1), $mime_preview);
7553
7554
        if ($alldata == 1) {
7555
            if ($num_mime !== false)
7556
                return array('target' => '_blank', 'css' => 'documentpreview', 'url' => DOL_BASE_URI . '/document.php?modulepart=' . $modulepart . '&attachment=0&file=' . urlencode($relativepath) . ($param ? '&' . $param : ''), 'mime' => dol_mimetype($relativepath),);
7557
            else
7558
                return array();
7559
        }
7560
7561
// old behavior
7562
        if ($num_mime !== false)
7563
            return 'javascript:document_preview(\'' . dol_escape_js(DOL_BASE_URI . '/document.php?modulepart=' . $modulepart . '&attachment=0&file=' . urlencode($relativepath) . ($param ? '&' . $param : '')) . '\', \'' . dol_mimetype($relativepath) . '\', \'' . dol_escape_js(Globals::$langs->trans('Preview')) . '\')';
7564
        else
7565
            return '';
7566
    }
7567
7568
    /**
7569
     * Make content of an input box selected when we click into input field.
7570
     *
7571
     * @param string	$htmlname	Id of html object
7572
     * @param string	$addlink	Add a 'link to' after
7573
     * @return string
7574
     */
7575
    static function ajax_autoselect($htmlname, $addlink = '')
7576
    {
7577
        // global Globals::$langs;
7578
        $out = '<script type="text/javascript">
7579
               jQuery(document).ready(static function () {
7580
				    jQuery("#' . $htmlname . '").click(function() { jQuery(this).select(); } );
7581
				});
7582
		    </script>';
7583
        if ($addlink)
7584
            $out .= ' <a href="' . $addlink . '" target="_blank">' . Globals::$langs->trans("Link") . '</a>';
7585
        return $out;
7586
    }
7587
7588
    /**
7589
     * 	Return mime type of a file
7590
     *
7591
     * 	@param	string	$file		Filename we looking for MIME type
7592
     *  @param  string	$default    Default mime type if extension not found in known list
7593
     * 	@param	int		$mode    	0=Return full mime, 1=otherwise short mime string, 2=image for mime type, 3=source language, 4=css of font fa
7594
     * 	@return string 		    	Return a mime type family (text/xxx, application/xxx, image/xxx, audio, video, archive)
7595
     *  @see    image_format_supported (images.lib.php)
7596
     */
7597
    static function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
7598
    {
7599
        $mime = $default;
7600
        $imgmime = 'other.png';
7601
        $famime = 'file-o';
7602
        $srclang = '';
7603
7604
        $tmpfile = preg_replace('/\.noexe$/', '', $file);
7605
7606
// Text files
7607
        if (preg_match('/\.txt$/i', $tmpfile)) {
7608
            $mime = 'text/plain';
7609
            $imgmime = 'text.png';
7610
            $famime = 'file-text-o';
7611
        }
7612
        if (preg_match('/\.rtx$/i', $tmpfile)) {
7613
            $mime = 'text/richtext';
7614
            $imgmime = 'text.png';
7615
            $famime = 'file-text-o';
7616
        }
7617
        if (preg_match('/\.csv$/i', $tmpfile)) {
7618
            $mime = 'text/csv';
7619
            $imgmime = 'text.png';
7620
            $famime = 'file-text-o';
7621
        }
7622
        if (preg_match('/\.tsv$/i', $tmpfile)) {
7623
            $mime = 'text/tab-separated-values';
7624
            $imgmime = 'text.png';
7625
            $famime = 'file-text-o';
7626
        }
7627
        if (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
7628
            $mime = 'text/plain';
7629
            $imgmime = 'text.png';
7630
            $famime = 'file-text-o';
7631
        }
7632
        if (preg_match('/\.ini$/i', $tmpfile)) {
7633
            $mime = 'text/plain';
7634
            $imgmime = 'text.png';
7635
            $srclang = 'ini';
7636
            $famime = 'file-text-o';
7637
        }
7638
        if (preg_match('/\.css$/i', $tmpfile)) {
7639
            $mime = 'text/css';
7640
            $imgmime = 'css.png';
7641
            $srclang = 'css';
7642
            $famime = 'file-text-o';
7643
        }
7644
// Certificate files
7645
        if (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) {
7646
            $mime = 'text/plain';
7647
            $imgmime = 'text.png';
7648
            $famime = 'file-text-o';
7649
        }
7650
// HTML/XML
7651
        if (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) {
7652
            $mime = 'text/html';
7653
            $imgmime = 'html.png';
7654
            $srclang = 'html';
7655
            $famime = 'file-text-o';
7656
        }
7657
        if (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
7658
            $mime = 'text/xml';
7659
            $imgmime = 'other.png';
7660
            $srclang = 'xml';
7661
            $famime = 'file-text-o';
7662
        }
7663
// Languages
7664
        if (preg_match('/\.bas$/i', $tmpfile)) {
7665
            $mime = 'text/plain';
7666
            $imgmime = 'text.png';
7667
            $srclang = 'bas';
7668
            $famime = 'file-code-o';
7669
        }
7670
        if (preg_match('/\.(c)$/i', $tmpfile)) {
7671
            $mime = 'text/plain';
7672
            $imgmime = 'text.png';
7673
            $srclang = 'c';
7674
            $famime = 'file-code-o';
7675
        }
7676
        if (preg_match('/\.(cpp)$/i', $tmpfile)) {
7677
            $mime = 'text/plain';
7678
            $imgmime = 'text.png';
7679
            $srclang = 'cpp';
7680
            $famime = 'file-code-o';
7681
        }
7682
        if (preg_match('/\.(h)$/i', $tmpfile)) {
7683
            $mime = 'text/plain';
7684
            $imgmime = 'text.png';
7685
            $srclang = 'h';
7686
            $famime = 'file-code-o';
7687
        }
7688
        if (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
7689
            $mime = 'text/plain';
7690
            $imgmime = 'text.png';
7691
            $srclang = 'java';
7692
            $famime = 'file-code-o';
7693
        }
7694
        if (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
7695
            $mime = 'text/plain';
7696
            $imgmime = 'php.png';
7697
            $srclang = 'php';
7698
            $famime = 'file-code-o';
7699
        }
7700
        if (preg_match('/\.phtml$/i', $tmpfile)) {
7701
            $mime = 'text/plain';
7702
            $imgmime = 'php.png';
7703
            $srclang = 'php';
7704
            $famime = 'file-code-o';
7705
        }
7706
        if (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
7707
            $mime = 'text/plain';
7708
            $imgmime = 'pl.png';
7709
            $srclang = 'perl';
7710
            $famime = 'file-code-o';
7711
        }
7712
        if (preg_match('/\.sql$/i', $tmpfile)) {
7713
            $mime = 'text/plain';
7714
            $imgmime = 'text.png';
7715
            $srclang = 'sql';
7716
            $famime = 'file-code-o';
7717
        }
7718
        if (preg_match('/\.js$/i', $tmpfile)) {
7719
            $mime = 'text/x-javascript';
7720
            $imgmime = 'jscript.png';
7721
            $srclang = 'js';
7722
            $famime = 'file-code-o';
7723
        }
7724
// Open office
7725
        if (preg_match('/\.odp$/i', $tmpfile)) {
7726
            $mime = 'application/vnd.oasis.opendocument.presentation';
7727
            $imgmime = 'ooffice.png';
7728
            $famime = 'file-powerpoint-o';
7729
        }
7730
        if (preg_match('/\.ods$/i', $tmpfile)) {
7731
            $mime = 'application/vnd.oasis.opendocument.spreadsheet';
7732
            $imgmime = 'ooffice.png';
7733
            $famime = 'file-excel-o';
7734
        }
7735
        if (preg_match('/\.odt$/i', $tmpfile)) {
7736
            $mime = 'application/vnd.oasis.opendocument.text';
7737
            $imgmime = 'ooffice.png';
7738
            $famime = 'file-word-o';
7739
        }
7740
// MS Office
7741
        if (preg_match('/\.mdb$/i', $tmpfile)) {
7742
            $mime = 'application/msaccess';
7743
            $imgmime = 'mdb.png';
7744
            $famime = 'file-o';
7745
        }
7746
        if (preg_match('/\.doc(x|m)?$/i', $tmpfile)) {
7747
            $mime = 'application/msword';
7748
            $imgmime = 'doc.png';
7749
            $famime = 'file-word-o';
7750
        }
7751
        if (preg_match('/\.dot(x|m)?$/i', $tmpfile)) {
7752
            $mime = 'application/msword';
7753
            $imgmime = 'doc.png';
7754
            $famime = 'file-word-o';
7755
        }
7756
        if (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
7757
            $mime = 'application/vnd.ms-excel';
7758
            $imgmime = 'xls.png';
7759
            $famime = 'file-excel-o';
7760
        }
7761
        if (preg_match('/\.xla(m)?$/i', $tmpfile)) {
7762
            $mime = 'application/vnd.ms-excel';
7763
            $imgmime = 'xls.png';
7764
            $famime = 'file-excel-o';
7765
        }
7766
        if (preg_match('/\.xls$/i', $tmpfile)) {
7767
            $mime = 'application/vnd.ms-excel';
7768
            $imgmime = 'xls.png';
7769
            $famime = 'file-excel-o';
7770
        }
7771
        if (preg_match('/\.xls(b|m|x)$/i', $tmpfile)) {
7772
            $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
7773
            $imgmime = 'xls.png';
7774
            $famime = 'file-excel-o';
7775
        }
7776
        if (preg_match('/\.pps(m|x)?$/i', $tmpfile)) {
7777
            $mime = 'application/vnd.ms-powerpoint';
7778
            $imgmime = 'ppt.png';
7779
            $famime = 'file-powerpoint-o';
7780
        }
7781
        if (preg_match('/\.ppt(m|x)?$/i', $tmpfile)) {
7782
            $mime = 'application/x-mspowerpoint';
7783
            $imgmime = 'ppt.png';
7784
            $famime = 'file-powerpoint-o';
7785
        }
7786
// Other
7787
        if (preg_match('/\.pdf$/i', $tmpfile)) {
7788
            $mime = 'application/pdf';
7789
            $imgmime = 'pdf.png';
7790
            $famime = 'file-pdf-o';
7791
        }
7792
// Scripts
7793
        if (preg_match('/\.bat$/i', $tmpfile)) {
7794
            $mime = 'text/x-bat';
7795
            $imgmime = 'script.png';
7796
            $srclang = 'dos';
7797
            $famime = 'file-code-o';
7798
        }
7799
        if (preg_match('/\.sh$/i', $tmpfile)) {
7800
            $mime = 'text/x-sh';
7801
            $imgmime = 'script.png';
7802
            $srclang = 'bash';
7803
            $famime = 'file-code-o';
7804
        }
7805
        if (preg_match('/\.ksh$/i', $tmpfile)) {
7806
            $mime = 'text/x-ksh';
7807
            $imgmime = 'script.png';
7808
            $srclang = 'bash';
7809
            $famime = 'file-code-o';
7810
        }
7811
        if (preg_match('/\.bash$/i', $tmpfile)) {
7812
            $mime = 'text/x-bash';
7813
            $imgmime = 'script.png';
7814
            $srclang = 'bash';
7815
            $famime = 'file-code-o';
7816
        }
7817
// Images
7818
        if (preg_match('/\.ico$/i', $tmpfile)) {
7819
            $mime = 'image/x-icon';
7820
            $imgmime = 'image.png';
7821
            $famime = 'file-image-o';
7822
        }
7823
        if (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
7824
            $mime = 'image/jpeg';
7825
            $imgmime = 'image.png';
7826
            $famime = 'file-image-o';
7827
        }
7828
        if (preg_match('/\.png$/i', $tmpfile)) {
7829
            $mime = 'image/png';
7830
            $imgmime = 'image.png';
7831
            $famime = 'file-image-o';
7832
        }
7833
        if (preg_match('/\.gif$/i', $tmpfile)) {
7834
            $mime = 'image/gif';
7835
            $imgmime = 'image.png';
7836
            $famime = 'file-image-o';
7837
        }
7838
        if (preg_match('/\.bmp$/i', $tmpfile)) {
7839
            $mime = 'image/bmp';
7840
            $imgmime = 'image.png';
7841
            $famime = 'file-image-o';
7842
        }
7843
        if (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
7844
            $mime = 'image/tiff';
7845
            $imgmime = 'image.png';
7846
            $famime = 'file-image-o';
7847
        }
7848
        if (preg_match('/\.svg$/i', $tmpfile)) {
7849
            $mime = 'image/svg+xml';
7850
            $imgmime = 'image.png';
7851
            $famime = 'file-image-o';
7852
        }
7853
// Calendar
7854
        if (preg_match('/\.vcs$/i', $tmpfile)) {
7855
            $mime = 'text/calendar';
7856
            $imgmime = 'other.png';
7857
            $famime = 'file-text-o';
7858
        }
7859
        if (preg_match('/\.ics$/i', $tmpfile)) {
7860
            $mime = 'text/calendar';
7861
            $imgmime = 'other.png';
7862
            $famime = 'file-text-o';
7863
        }
7864
// Other
7865
        if (preg_match('/\.torrent$/i', $tmpfile)) {
7866
            $mime = 'application/x-bittorrent';
7867
            $imgmime = 'other.png';
7868
            $famime = 'file-o';
7869
        }
7870
// Audio
7871
        if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) {
7872
            $mime = 'audio';
7873
            $imgmime = 'audio.png';
7874
            $famime = 'file-audio-o';
7875
        }
7876
// Video
7877
        if (preg_match('/\.ogv$/i', $tmpfile)) {
7878
            $mime = 'video/ogg';
7879
            $imgmime = 'video.png';
7880
            $famime = 'file-video-o';
7881
        }
7882
        if (preg_match('/\.webm$/i', $tmpfile)) {
7883
            $mime = 'video/webm';
7884
            $imgmime = 'video.png';
7885
            $famime = 'file-video-o';
7886
        }
7887
        if (preg_match('/\.avi$/i', $tmpfile)) {
7888
            $mime = 'video/x-msvideo';
7889
            $imgmime = 'video.png';
7890
            $famime = 'file-video-o';
7891
        }
7892
        if (preg_match('/\.divx$/i', $tmpfile)) {
7893
            $mime = 'video/divx';
7894
            $imgmime = 'video.png';
7895
            $famime = 'file-video-o';
7896
        }
7897
        if (preg_match('/\.xvid$/i', $tmpfile)) {
7898
            $mime = 'video/xvid';
7899
            $imgmime = 'video.png';
7900
            $famime = 'file-video-o';
7901
        }
7902
        if (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
7903
            $mime = 'video';
7904
            $imgmime = 'video.png';
7905
            $famime = 'file-video-o';
7906
        }
7907
// Archive
7908
        if (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh)$/i', $tmpfile)) {
7909
            $mime = 'archive';
7910
            $imgmime = 'archive.png';
7911
            $famime = 'file-archive-o';
7912
        }    // application/xxx where zzz is zip, ...
7913
// Exe
7914
        if (preg_match('/\.(exe|com)$/i', $tmpfile)) {
7915
            $mime = 'application/octet-stream';
7916
            $imgmime = 'other.png';
7917
            $famime = 'file-o';
7918
        }
7919
// Lib
7920
        if (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) {
7921
            $mime = 'library';
7922
            $imgmime = 'library.png';
7923
            $famime = 'file-o';
7924
        }
7925
// Err
7926
        if (preg_match('/\.err$/i', $tmpfile)) {
7927
            $mime = 'error';
7928
            $imgmime = 'error.png';
7929
            $famime = 'file-text-o';
7930
        }
7931
7932
// Return string
7933
        if ($mode == 1) {
7934
            $tmp = explode('/', $mime);
7935
            return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
7936
        }
7937
        if ($mode == 2) {
7938
            return $imgmime;
7939
        }
7940
        if ($mode == 3) {
7941
            return $srclang;
7942
        }
7943
        if ($mode == 4) {
7944
            return $famime;
7945
        }
7946
        return $mime;
7947
    }
7948
7949
    /**
7950
     * Return value from dictionary
7951
     *
7952
     * @param string	$tablename		name of dictionary
7953
     * @param string	$field			the value to return
7954
     * @param int		$id				id of line
7955
     * @param bool		$checkentity	add filter on entity
7956
     * @param string	$rowidfield		name of the column rowid
7957
     * @return string
7958
     */
7959
    static function getDictvalue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
7960
    {
7961
        // global $dictvalues, $db, Globals::$langs;
7962
7963
        if (!isset($dictvalues[$tablename])) {
7964
            $dictvalues[$tablename] = array();
7965
            $sql = 'SELECT * FROM ' . $tablename . ' WHERE 1';
7966
            if ($checkentity)
7967
                $sql .= ' AND entity IN (0,' . getEntity($tablename) . ')';
7968
7969
            $resql = $db->query($sql);
7970
            if ($resql) {
7971
                while ($obj = $db->fetch_object($resql)) {
7972
                    $dictvalues[$tablename][$obj->{$rowidfield}] = $obj;
7973
                }
7974
            } else {
7975
                dol_print_error($db);
7976
            }
7977
        }
7978
7979
        if (!empty($dictvalues[$tablename][$id]))
7980
            return $dictvalues[$tablename][$id]->{$field}; // Found
7981
        else { // Not found
7982
            if ($id > 0)
7983
                return $id;
7984
            return '';
7985
        }
7986
    }
7987
7988
    /**
7989
     * 	Return true if the color is light
7990
     *
7991
     *  @param	string	$stringcolor		String with hex (FFFFFF) or comma RGB ('255,255,255')
7992
     *  @return	int							-1 : Error with argument passed |0 : color is dark | 1 : color is light
7993
     */
7994
    static function colorIsLight($stringcolor)
7995
    {
7996
        $res = -1;
7997
        if (!empty($stringcolor)) {
7998
            $res = 0;
7999
            $tmp = explode(',', $stringcolor);
8000
            if (count($tmp) > 1) {   // This is a comma RGB ('255','255','255')
8001
                $r = $tmp[0];
8002
                $g = $tmp[1];
8003
                $b = $tmp[2];
8004
            } else {
8005
                $hexr = $stringcolor[0] . $stringcolor[1];
8006
                $hexg = $stringcolor[2] . $stringcolor[3];
8007
                $hexb = $stringcolor[4] . $stringcolor[5];
8008
                $r = hexdec($hexr);
8009
                $g = hexdec($hexg);
8010
                $b = hexdec($hexb);
8011
            }
8012
            $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0;    // HSL algorithm
8013
            if ($bright > 0.6)
8014
                $res = 1;
8015
        }
8016
        return $res;
8017
    }
8018
8019
    /**
8020
     * Function to test if an entry is enabled or not
8021
     *
8022
     * @param	string		$type_user					0=We test for internal user, 1=We test for external user
8023
     * @param	array		$menuentry					Array for feature entry to test
8024
     * @param	array		$listofmodulesforexternal	Array with list of modules allowed to external users
8025
     * @return	int										0=Hide, 1=Show, 2=Show gray
8026
     */
8027
    static function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
8028
    {
8029
        // global Globals::$conf;
8030
//print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
8031
//print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
8032
        if (empty($menuentry['enabled']))
8033
            return 0; // Entry disabled by condition
8034
        if ($type_user && $menuentry['module']) {
8035
            $tmploops = explode('|', $menuentry['module']);
8036
            $found = 0;
8037
            foreach ($tmploops as $tmploop) {
8038
                if (in_array($tmploop, $listofmodulesforexternal)) {
8039
                    $found++;
8040
                    break;
8041
                }
8042
            }
8043
            if (!$found)
8044
                return 0; // Entry is for menus all excluded to external users
8045
        }
8046
        if (!$menuentry['perms'] && $type_user)
8047
            return 0;            // No permissions and user is external
8048
        if (!$menuentry['perms'] && !empty(Globals::$conf->global->MAIN_MENU_HIDE_UNAUTHORIZED))
8049
            return 0; // No permissions and option to hide when not allowed, even for internal user, is on
8050
        if (!$menuentry['perms'])
8051
            return 2;               // No permissions and user is external
8052
        return 1;
8053
    }
8054
8055
    /**
8056
     * Round to next multiple.
8057
     *
8058
     * @param 	double		$n		Number to round up
8059
     * @param 	integer		$x		Multiple. For example 60 to round up to nearest exact minute for a date with seconds.
8060
     * @return 	integer				Value rounded.
8061
     */
8062
    static function roundUpToNextMultiple($n, $x = 5)
8063
    {
8064
        return (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x;
8065
    }
8066
}
8067