1<?php 2/* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org> 3 * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org> 4 * Copyright (C) 2004-2018 Laurent Destailleur <eldy@users.sourceforge.net> 5 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org> 6 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be> 7 * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr> 8 * Copyright (C) 2005-2019 Regis Houssin <regis.houssin@inodbox.com> 9 * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr> 10 * Copyright (C) 2010-2018 Juanjo Menent <jmenent@2byte.es> 11 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr> 12 * Copyright (C) 2013-2017 Alexandre Spangaro <aspangaro@open-dsi.fr> 13 * Copyright (C) 2014 Cédric GROSS <c.gross@kreiz-it.fr> 14 * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com> 15 * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr> 16 * Copyright (C) 2018-2020 Frédéric France <frederic.france@netlogic.fr> 17 * Copyright (C) 2019 Thibault Foucart <support@ptibogxiv.net> 18 * Copyright (C) 2020 Open-Dsi <support@open-dsi.fr> 19 * 20 * This program is free software; you can redistribute it and/or modify 21 * it under the terms of the GNU General Public License as published by 22 * the Free Software Foundation; either version 3 of the License, or 23 * (at your option) any later version. 24 * 25 * This program is distributed in the hope that it will be useful, 26 * but WITHOUT ANY WARRANTY; without even the implied warranty of 27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 * GNU General Public License for more details. 29 * 30 * You should have received a copy of the GNU General Public License 31 * along with this program. If not, see <https://www.gnu.org/licenses/>. 32 * or see https://www.gnu.org/ 33 */ 34 35/** 36 * \file htdocs/core/lib/functions.lib.php 37 * \brief A set of functions for Dolibarr 38 * This file contains all frequently used functions. 39 */ 40 41include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php'; 42 43 44/** 45 * Return dolibarr global constant string value 46 * @param string $key key to return value, return '' if not set 47 * @return string 48 */ 49function getDolGlobalString($key) 50{ 51 global $conf; 52 // return $conf->global->$key ?? ''; 53 return (string) (empty($conf->global->$key) ? '' : $conf->global->$key); 54} 55 56/** 57 * Return dolibarr global constant int value 58 * @param string $key key to return value, return 0 if not set 59 * @return int 60 */ 61function getDolGlobalInt($key) 62{ 63 global $conf; 64 // return $conf->global->$key ?? 0; 65 return (int) (empty($conf->global->$key) ? 0 : $conf->global->$key); 66} 67 68/** 69 * Return a DoliDB instance (database handler). 70 * 71 * @param string $type Type of database (mysql, pgsql...) 72 * @param string $host Address of database server 73 * @param string $user Authorized username 74 * @param string $pass Password 75 * @param string $name Name of database 76 * @param int $port Port of database server 77 * @return DoliDB A DoliDB instance 78 */ 79function getDoliDBInstance($type, $host, $user, $pass, $name, $port) 80{ 81 require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php'; 82 83 $class = 'DoliDB'.ucfirst($type); 84 $dolidb = new $class($type, $host, $user, $pass, $name, $port); 85 return $dolidb; 86} 87 88/** 89 * Get list of entity id to use. 90 * 91 * @param string $element Current element 92 * 'societe', 'socpeople', 'actioncomm', 'agenda', 'resource', 93 * 'product', 'productprice', 'stock', 'bom', 'mo', 94 * 'propal', 'supplier_proposal', 'invoice', 'supplier_invoice', 'payment_various', 95 * 'categorie', 'bank_account', 'bank_account', 'adherent', 'user', 96 * 'commande', 'supplier_order', 'expedition', 'intervention', 'survey', 97 * 'contract', 'tax', 'expensereport', 'holiday', 'multicurrency', 'project', 98 * 'email_template', 'event', 'donation' 99 * 'c_paiement', 'c_payment_term', ... 100 * @param int $shared 0=Return id of current entity only, 101 * 1=Return id of current entity + shared entities (default) 102 * @param object $currentobject Current object if needed 103 * @return mixed Entity id(s) to use ( eg. entity IN ('.getEntity(elementname).')' ) 104 */ 105function getEntity($element, $shared = 1, $currentobject = null) 106{ 107 global $conf, $mc; 108 109 // fix different element names (France to English) 110 switch ($element) { 111 case 'contrat': $element = 'contract'; break; // "/contrat/class/contrat.class.php" 112 case 'order_supplier': $element = 'supplier_order'; break; // "/fourn/class/fournisseur.commande.class.php" 113 } 114 115 if (is_object($mc)) 116 { 117 return $mc->getEntity($element, $shared, $currentobject); 118 } else { 119 $out = ''; 120 $addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values'); 121 if (in_array($element, $addzero)) $out .= '0,'; 122 $out .= ((int) $conf->entity); 123 return $out; 124 } 125} 126 127/** 128 * Set entity id to use when to create an object 129 * 130 * @param object $currentobject Current object 131 * @return mixed Entity id to use ( eg. entity = '.setEntity($object) ) 132 */ 133function setEntity($currentobject) 134{ 135 global $conf, $mc; 136 137 if (is_object($mc) && method_exists($mc, 'setEntity')) 138 { 139 return $mc->setEntity($currentobject); 140 } else { 141 return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity); 142 } 143} 144 145/** 146 * Return if string has a name dedicated to store a secret 147 * 148 * @param string $keyname Name of key to test 149 * @return boolean True if key is used to store a secret 150 */ 151function isASecretKey($keyname) 152{ 153 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname); 154} 155 156/** 157 * Return information about user browser 158 * 159 * Returns array with the following format: 160 * array( 161 * 'browsername' => Browser name (firefox|chrome|iceweasel|epiphany|safari|opera|ie|unknown) 162 * 'browserversion' => Browser version. Empty if unknown 163 * 'browseros' => Set with mobile OS (android|blackberry|ios|palm|symbian|webos|maemo|windows|unknown) 164 * 'layout' => (tablet|phone|classic) 165 * 'phone' => empty if not mobile, (android|blackberry|ios|palm|unknown) if mobile 166 * 'tablet' => true/false 167 * ) 168 * 169 * @param string $user_agent Content of $_SERVER["HTTP_USER_AGENT"] variable 170 * @return array Check function documentation 171 */ 172function getBrowserInfo($user_agent) 173{ 174 include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php'; 175 176 $name = 'unknown'; 177 $version = ''; 178 $os = 'unknown'; 179 $phone = ''; 180 181 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent 182 183 $detectmobile = new Mobile_Detect(null, $user_agent); 184 $tablet = $detectmobile->isTablet(); 185 186 if ($detectmobile->isMobile()) { 187 $phone = 'unknown'; 188 189 // If phone/smartphone, we set phone os name. 190 if ($detectmobile->is('AndroidOS')) { 191 $os = $phone = 'android'; 192 } elseif ($detectmobile->is('BlackBerryOS')) { 193 $os = $phone = 'blackberry'; 194 } elseif ($detectmobile->is('iOS')) { 195 $os = 'ios'; 196 $phone = 'iphone'; 197 } elseif ($detectmobile->is('PalmOS')) { 198 $os = $phone = 'palm'; 199 } elseif ($detectmobile->is('SymbianOS')) { 200 $os = 'symbian'; 201 } elseif ($detectmobile->is('webOS')) { 202 $os = 'webos'; 203 } elseif ($detectmobile->is('MaemoOS')) { 204 $os = 'maemo'; 205 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) { 206 $os = 'windows'; 207 } 208 } 209 210 // OS 211 if (preg_match('/linux/i', $user_agent)) { $os = 'linux'; } elseif (preg_match('/macintosh/i', $user_agent)) { $os = 'macintosh'; } elseif (preg_match('/windows/i', $user_agent)) { $os = 'windows'; } 212 213 // Name 214 $reg = array(); 215 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'firefox'; $version = $reg[2]; } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'edge'; $version = $reg[2]; } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) { $name = 'chrome'; $version = $reg[2]; } // we can have 'chrome (Mozilla...) chrome x.y' in one string 216 elseif (preg_match('/chrome/i', $user_agent, $reg)) { $name = 'chrome'; } elseif (preg_match('/iceweasel/i', $user_agent)) { $name = 'iceweasel'; } elseif (preg_match('/epiphany/i', $user_agent)) { $name = 'epiphany'; } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'safari'; $version = $reg[2]; } // Safari is often present in string for mobile but its not. 217 elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'opera'; $version = $reg[2]; } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) { $name = 'ie'; $version = end($reg); } // MS products at end 218 elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) { $name = 'ie'; $version = end($reg); } // MS products at end 219 elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) { $name = 'lynxlinks'; $version = $reg[4]; } 220 221 if ($tablet) { 222 $layout = 'tablet'; 223 } elseif ($phone) { 224 $layout = 'phone'; 225 } else { 226 $layout = 'classic'; 227 } 228 229 return array( 230 'browsername' => $name, 231 'browserversion' => $version, 232 'browseros' => $os, 233 'layout' => $layout, 234 'phone' => $phone, 235 'tablet' => $tablet 236 ); 237} 238 239/** 240 * Function called at end of web php process 241 * 242 * @return void 243 */ 244function dol_shutdown() 245{ 246 global $conf, $user, $langs, $db; 247 $disconnectdone = false; $depth = 0; 248 if (is_object($db) && !empty($db->connected)) { $depth = $db->transaction_opened; $disconnectdone = $db->close(); } 249 dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ?LOG_WARNING:LOG_INFO)); 250} 251 252/** 253 * Return true if we are in a context of submitting the parameter $paramname 254 * 255 * @param string $paramname Name or parameter to test 256 * @return boolean True if we have just submit a POST or GET request with the parameter provided (even if param is empty) 257 */ 258function GETPOSTISSET($paramname) 259{ 260 $isset = false; 261 262 $relativepathstring = $_SERVER["PHP_SELF"]; 263 // Clean $relativepathstring 264 if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring); 265 $relativepathstring = preg_replace('/^\//', '', $relativepathstring); 266 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring); 267 //var_dump($relativepathstring); 268 //var_dump($user->default_values); 269 270 // Code for search criteria persistence. 271 // Retrieve values if restore_lastsearch_values 272 if (!empty($_GET['restore_lastsearch_values'])) // Use $_GET here and not GETPOST 273 { 274 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) // If there is saved values 275 { 276 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true); 277 if (is_array($tmp)) 278 { 279 foreach ($tmp as $key => $val) 280 { 281 if ($key == $paramname) // We are on the requested parameter 282 { 283 $isset = true; 284 break; 285 } 286 } 287 } 288 } 289 // If there is saved contextpage, page or limit 290 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) 291 { 292 $isset = true; 293 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) 294 { 295 $isset = true; 296 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) 297 { 298 $isset = true; 299 } 300 } else { 301 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here 302 } 303 304 return $isset; 305} 306 307/** 308 * Return value of a param into GET or POST supervariable. 309 * Use the property $user->default_values[path]['creatform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder'] 310 * Note: The property $user->default_values is loaded by main.php when loading the user. 311 * 312 * @param string $paramname Name of parameter to found 313 * @param string $check Type of check 314 * ''=no check (deprecated) 315 * 'none'=no check (only for param that should have very rich content) 316 * 'array', 'array:restricthtml' or 'array:aZ09' to check it's an array 317 * 'int'=check it's numeric (integer or float) 318 * 'intcomma'=check it's integer+comma ('1,2,3,4...') 319 * 'alpha'=Same than alphanohtml since v13 320 * 'alphanohtml'=check there is no html content and no " and no ../ 321 * 'aZ'=check it's a-z only 322 * 'aZ09'=check it's simple alpha string (recommended for keys) 323 * 'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string) 324 * 'nohtml'=check there is no html content and no " and no ../ 325 * 'restricthtml'=check html content is restricted to some tags only 326 * 'custom'= custom filter specify $filter and $options) 327 * @param int $method Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get) 328 * @param int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) 329 * @param mixed $options Options to pass to filter_var when $check is set to 'custom' 330 * @param string $noreplace Force disable of replacement of __xxx__ strings. 331 * @return string|array Value found (string or array), or '' if check fails 332 */ 333function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0) 334{ 335 global $mysoc, $user, $conf; 336 337 if (empty($paramname)) return 'BadFirstParameterForGETPOST'; 338 if (empty($check)) 339 { 340 dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING); 341 // Enable this line to know who call the GETPOST with '' $check parameter. 342 //var_dump(debug_backtrace()[0]); 343 } 344 345 if (empty($method)) $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : ''); 346 elseif ($method == 1) $out = isset($_GET[$paramname]) ? $_GET[$paramname] : ''; 347 elseif ($method == 2) $out = isset($_POST[$paramname]) ? $_POST[$paramname] : ''; 348 elseif ($method == 3) $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : ''); 349 else return 'BadThirdParameterForGETPOST'; 350 351 if (empty($method) || $method == 3 || $method == 4) 352 { 353 $relativepathstring = $_SERVER["PHP_SELF"]; 354 // Clean $relativepathstring 355 if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring); 356 $relativepathstring = preg_replace('/^\//', '', $relativepathstring); 357 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring); 358 //var_dump($relativepathstring); 359 //var_dump($user->default_values); 360 361 // Code for search criteria persistence. 362 // Retrieve values if restore_lastsearch_values 363 if (!empty($_GET['restore_lastsearch_values'])) // Use $_GET here and not GETPOST 364 { 365 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) // If there is saved values 366 { 367 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true); 368 if (is_array($tmp)) 369 { 370 foreach ($tmp as $key => $val) 371 { 372 if ($key == $paramname) // We are on the requested parameter 373 { 374 $out = $val; 375 break; 376 } 377 } 378 } 379 } 380 // If there is saved contextpage, page or limit 381 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) 382 { 383 $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring]; 384 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) 385 { 386 $out = $_SESSION['lastsearch_page_'.$relativepathstring]; 387 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) 388 { 389 $out = $_SESSION['lastsearch_limit_'.$relativepathstring]; 390 } 391 } // Else, retrieve default values if we are not doing a sort 392 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 393 { 394 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) 395 { 396 // Search default value from $object->field 397 global $object; 398 if (is_object($object) && isset($object->fields[$paramname]['default'])) 399 { 400 $out = $object->fields[$paramname]['default']; 401 } 402 } 403 if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) 404 { 405 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) 406 { 407 // Now search in setup to overwrite default values 408 if (!empty($user->default_values)) // $user->default_values defined from menu 'Setup - Default values' 409 { 410 if (isset($user->default_values[$relativepathstring]['createform'])) 411 { 412 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) 413 { 414 $qualified = 0; 415 if ($defkey != '_noquery_') 416 { 417 $tmpqueryarraytohave = explode('&', $defkey); 418 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); 419 $foundintru = 0; 420 foreach ($tmpqueryarraytohave as $tmpquerytohave) 421 { 422 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru = 1; 423 } 424 if (!$foundintru) $qualified = 1; 425 //var_dump($defkey.'-'.$qualified); 426 } else $qualified = 1; 427 428 if ($qualified) 429 { 430 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) 431 { 432 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname]; 433 break; 434 } 435 } 436 } 437 } 438 } 439 } // Management of default search_filters and sort order 440 elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) 441 { 442 if (!empty($user->default_values)) // $user->default_values defined from menu 'Setup - Default values' 443 { 444 //var_dump($user->default_values[$relativepathstring]); 445 if ($paramname == 'sortfield' || $paramname == 'sortorder') // Sorted on which fields ? ASC or DESC ? 446 { 447 if (isset($user->default_values[$relativepathstring]['sortorder'])) // Even if paramname is sortfield, data are stored into ['sortorder...'] 448 { 449 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) 450 { 451 $qualified = 0; 452 if ($defkey != '_noquery_') 453 { 454 $tmpqueryarraytohave = explode('&', $defkey); 455 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); 456 $foundintru = 0; 457 foreach ($tmpqueryarraytohave as $tmpquerytohave) 458 { 459 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru = 1; 460 } 461 if (!$foundintru) $qualified = 1; 462 //var_dump($defkey.'-'.$qualified); 463 } else $qualified = 1; 464 465 if ($qualified) 466 { 467 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , 468 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) 469 { 470 if ($out) $out .= ', '; 471 if ($paramname == 'sortfield') 472 { 473 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace); 474 } 475 if ($paramname == 'sortorder') 476 { 477 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace); 478 } 479 } 480 //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?) 481 } 482 } 483 } 484 } elseif (isset($user->default_values[$relativepathstring]['filters'])) 485 { 486 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) // $defkey is a querystring like 'a=b&c=d', $defval is key of user 487 { 488 $qualified = 0; 489 if ($defkey != '_noquery_') 490 { 491 $tmpqueryarraytohave = explode('&', $defkey); 492 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); 493 $foundintru = 0; 494 foreach ($tmpqueryarraytohave as $tmpquerytohave) 495 { 496 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru = 1; 497 } 498 if (!$foundintru) $qualified = 1; 499 //var_dump($defkey.'-'.$qualified); 500 } else $qualified = 1; 501 502 if ($qualified) 503 { 504 // We must keep $_POST and $_GET here 505 if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) 506 { 507 // We made a search from quick search menu, do we still use default filter ? 508 if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) 509 { 510 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , 511 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace); 512 } 513 } else { 514 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , 515 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace); 516 } 517 break; 518 } 519 } 520 } 521 } 522 } 523 } 524 } 525 } 526 527 // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters) 528 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ... 529 // 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. 530 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) 531 { 532 $reg = array(); 533 $maxloop = 20; $loopnb = 0; // Protection against infinite loop 534 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. 535 { 536 $loopnb++; $newout = ''; 537 538 if ($reg[1] == 'DAY') { 539 $tmp = dol_getdate(dol_now(), true); 540 $newout = $tmp['mday']; 541 } elseif ($reg[1] == 'MONTH') { 542 $tmp = dol_getdate(dol_now(), true); 543 $newout = $tmp['mon']; 544 } elseif ($reg[1] == 'YEAR') { 545 $tmp = dol_getdate(dol_now(), true); 546 $newout = $tmp['year']; 547 } elseif ($reg[1] == 'PREVIOUS_DAY') { 548 $tmp = dol_getdate(dol_now(), true); 549 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']); 550 $newout = $tmp2['day']; 551 } elseif ($reg[1] == 'PREVIOUS_MONTH') { 552 $tmp = dol_getdate(dol_now(), true); 553 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']); 554 $newout = $tmp2['month']; 555 } elseif ($reg[1] == 'PREVIOUS_YEAR') { 556 $tmp = dol_getdate(dol_now(), true); 557 $newout = ($tmp['year'] - 1); 558 } elseif ($reg[1] == 'NEXT_DAY') { 559 $tmp = dol_getdate(dol_now(), true); 560 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']); 561 $newout = $tmp2['day']; 562 } elseif ($reg[1] == 'NEXT_MONTH') { 563 $tmp = dol_getdate(dol_now(), true); 564 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']); 565 $newout = $tmp2['month']; 566 } elseif ($reg[1] == 'NEXT_YEAR') { 567 $tmp = dol_getdate(dol_now(), true); 568 $newout = ($tmp['year'] + 1); 569 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') { 570 $newout = $mysoc->country_id; 571 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') { 572 $newout = $user->id; 573 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') { 574 $newout = $user->fk_user; 575 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') { 576 $newout = $conf->entity; 577 } else { 578 $newout = ''; // Key not found, we replace with empty string 579 } 580 //var_dump('__'.$reg[1].'__ -> '.$newout); 581 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out); 582 } 583 } 584 585 // Check rule 586 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' 587 if (!is_array($out) || empty($out)) { 588 $out = array(); 589 } else { 590 $tmparray = explode(':', $check); 591 if (!empty($tmparray[1])) { 592 $tmpcheck = $tmparray[1]; 593 } else { 594 $tmpcheck = 'alphanohtml'; 595 } 596 foreach ($out as $outkey => $outval) { 597 $out[$outkey] = checkVal($outval, $tmpcheck, $filter, $options); 598 } 599 } 600 } 601 else { 602 $out = checkVal($out, $check, $filter, $options); 603 } 604 605 // Sanitizing for special parameters. There is no reason to allow the backtopage parameter to contains an external URL. 606 if ($paramname == 'backtopage') { 607 $out = str_replace('\\', '/', $out); 608 $out = preg_replace(array('/^\/\/+/', '/^[a-z]*:/i'), '', $out); 609 } 610 611 // Code for search criteria persistence. 612 // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year' 613 if (empty($method) || $method == 3 || $method == 4) 614 { 615 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) 616 { 617 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]); 618 619 // We save search key only if $out not empty that means: 620 // - posted value not empty, or 621 // - 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). 622 623 if ($out != '') // $out = '0' or 'abc', it is a search criteria to keep 624 { 625 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out; 626 } 627 } 628 } 629 630 return $out; 631} 632 633/** 634 * Return value of a param into GET or POST supervariable. 635 * Use the property $user->default_values[path]['creatform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder'] 636 * Note: The property $user->default_values is loaded by main.php when loading the user. 637 * 638 * @param string $paramname Name of parameter to found 639 * @param int $method Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get) 640 * @param int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) 641 * @param mixed $options Options to pass to filter_var when $check is set to 'custom' 642 * @param string $noreplace Force disable of replacement of __xxx__ strings. 643 * @return int Value found (int) 644 */ 645function GETPOSTINT($paramname, $method = 0, $filter = null, $options = null, $noreplace = 0) 646{ 647 return (int) GETPOST($paramname, 'int', $method, $filter, $options, $noreplace); 648} 649 650/** 651 * Return a value after checking on a rule. 652 * 653 * @param string $out Value to get/check 654 * @param string $check Type of check 655 * @param int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) 656 * @param mixed $options Options to pass to filter_var when $check is set to 'custom' 657 * @return string|array Value found (string or array), or '' if check fails 658 */ 659function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null) 660{ 661 // Check is done after replacement 662 switch ($check) 663 { 664 case 'none': 665 break; 666 case 'int': // Check param is a numeric value (integer but also float or hexadecimal) 667 if (!is_numeric($out)) { $out = ''; } 668 break; 669 case 'intcomma': 670 if (preg_match('/[^0-9,-]+/i', $out)) $out = ''; 671 break; 672 case 'san_alpha': 673 $out = filter_var($out, FILTER_SANITIZE_STRING); 674 break; 675 case 'email': 676 $out = filter_var($out, FILTER_SANITIZE_EMAIL); 677 break; 678 case 'aZ': 679 if (!is_array($out)) 680 { 681 $out = trim($out); 682 if (preg_match('/[^a-z]+/i', $out)) $out = ''; 683 } 684 break; 685 case 'aZ09': 686 if (!is_array($out)) 687 { 688 $out = trim($out); 689 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) $out = ''; 690 } 691 break; 692 case 'aZ09comma': // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh 693 if (!is_array($out)) 694 { 695 $out = trim($out); 696 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) $out = ''; 697 } 698 break; 699 case 'nohtml': // No html 700 $out = dol_string_nohtmltag($out, 0); 701 break; 702 case 'alpha': // No html and no ../ and " 703 case 'alphanohtml': // Recommended for most scalar parameters and search parameters 704 if (!is_array($out)) { 705 // '"' is dangerous because param in url can close the href= or src= and add javascript functions. 706 // '../' is dangerous because it allows dir transversals 707 $out = str_replace(array('"', '"'), '', trim($out)); 708 $out = str_replace(array('../'), '', $out); 709 // keep lines feed 710 $out = dol_string_nohtmltag($out, 0); 711 } 712 break; 713 case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name <email>" 714 if (!is_array($out)) { 715 // '"' is dangerous because param in url can close the href= or src= and add javascript functions. 716 // '../' is dangerous because it allows dir transversals 717 $out = str_replace(array('"', '"'), '', trim($out)); 718 $out = str_replace(array('../'), '', $out); 719 } 720 break; 721 case 'restricthtml': // Recommended for most html textarea 722 $out = dol_string_onlythesehtmltags($out, 0, 1, 1); 723 break; 724 case 'custom': 725 if (empty($filter)) return 'BadFourthParameterForGETPOST'; 726 $out = filter_var($out, $filter, $options); 727 break; 728 } 729 730 return $out; 731} 732 733 734 735if (!function_exists('dol_getprefix')) 736{ 737 /** 738 * Return a prefix to use for this Dolibarr instance, for session/cookie names or email id. 739 * The prefix is unique for instance and avoid conflict between multi-instances, even when having two instances with same root dir 740 * or two instances in same virtual servers. 741 * 742 * @param string $mode '' (prefix for session name) or 'email' (prefix for email id) 743 * @return string A calculated prefix 744 */ 745 function dol_getprefix($mode = '') 746 { 747 // If prefix is for email (we need to have $conf alreayd loaded for this case) 748 if ($mode == 'email') 749 { 750 global $conf; 751 752 if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) // If MAIL_PREFIX_FOR_EMAIL_ID is set (a value initialized with a random value is recommended) 753 { 754 if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID; 755 elseif (isset($_SERVER["SERVER_NAME"])) return $_SERVER["SERVER_NAME"]; 756 } 757 758 // The recommended value (may be not defined for old versions) 759 if (!empty($conf->file->instance_unique_id)) return $conf->file->instance_unique_id; 760 761 // For backward compatibility 762 return dol_hash(DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3'); 763 } 764 765 // If prefix is for session (no need to have $conf loaded) 766 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php 767 $tmp_instance_unique_id = empty($dolibarr_main_instance_unique_id) ? (empty($dolibarr_main_cookie_cryptkey) ? '' : $dolibarr_main_cookie_cryptkey) : $dolibarr_main_instance_unique_id; // Unique id of instance 768 769 // The recommended value (may be not defined for old versions) 770 if (!empty($tmp_instance_unique_id)) { 771 return $tmp_instance_unique_id; 772 } 773 774 // For backward compatibility 775 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) { 776 return dol_hash($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3'); 777 } 778 779 return dol_hash(DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3'); 780 } 781} 782 783/** 784 * Make an include_once using default root and alternate root if it fails. 785 * To link to a core file, use include(DOL_DOCUMENT_ROOT.'/pathtofile') 786 * To link to a module file from a module file, use include './mymodulefile'; 787 * To link to a module file from a core file, then this function can be used (call by hook / trigger / speciales pages) 788 * 789 * @param string $relpath Relative path to file (Ie: mydir/myfile, ../myfile, ...) 790 * @param string $classname Class name (deprecated) 791 * @return bool True if load is a success, False if it fails 792 */ 793function dol_include_once($relpath, $classname = '') 794{ 795 global $conf, $langs, $user, $mysoc; // Do not remove this. They must be defined for files we include. Other globals var must be retrieved with $GLOBALS['var'] 796 797 $fullpath = dol_buildpath($relpath); 798 799 if (!file_exists($fullpath)) { 800 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING); 801 return false; 802 } 803 804 if (!empty($classname) && !class_exists($classname)) { 805 return include $fullpath; 806 } else { 807 return include_once $fullpath; 808 } 809} 810 811 812/** 813 * Return path of url or filesystem. Can check into alternate dir or alternate dir + main dir depending on value of $returnemptyifnotfound. 814 * 815 * @param string $path Relative path to file (if mode=0) or relative url (if mode=1). Ie: mydir/myfile, ../myfile 816 * @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) 817 * @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) 818 * 1:If $type==0 and if file was not found into alternate dir, return empty string 819 * 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 820 * @return string Full filesystem path (if path=0) or '' if file not found, Full url path (if mode=1) 821 */ 822function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0) 823{ 824 global $conf; 825 826 $path = preg_replace('/^\//', '', $path); 827 828 if (empty($type)) // For a filesystem path 829 { 830 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path 831 if (is_array($conf->file->dol_document_root)) 832 { 833 foreach ($conf->file->dol_document_root as $key => $dirroot) // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...) 834 { 835 if ($key == 'main') 836 { 837 continue; 838 } 839 if (file_exists($dirroot.'/'.$path)) 840 { 841 $res = $dirroot.'/'.$path; 842 return $res; 843 } 844 } 845 } 846 if ($returnemptyifnotfound) // Not found into alternate dir 847 { 848 if ($returnemptyifnotfound == 1 || !file_exists($res)) return ''; 849 } 850 } else // For an url path 851 { 852 // We try to get local path of file on filesystem from url 853 // Note that trying to know if a file on disk exist by forging path on disk from url 854 // works only for some web server and some setup. This is bugged when 855 // using proxy, rewriting, virtual path, etc... 856 $res = ''; 857 if ($type == 1) $res = DOL_URL_ROOT.'/'.$path; // Standard value 858 if ($type == 2) $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value 859 if ($type == 3) $res = DOL_URL_ROOT.'/'.$path; 860 861 foreach ($conf->file->dol_document_root as $key => $dirroot) // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...) 862 { 863 if ($key == 'main') 864 { 865 if ($type == 3) 866 { 867 global $dolibarr_main_url_root; 868 869 // Define $urlwithroot 870 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); 871 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file 872 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current 873 874 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax 875 } 876 continue; 877 } 878 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?' 879 if (!empty($regs[1])) 880 { 881 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n"; 882 if (file_exists($dirroot.'/'.$regs[1])) 883 { 884 if ($type == 1) 885 { 886 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path; 887 } 888 if ($type == 2) 889 { 890 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path; 891 } 892 if ($type == 3) 893 { 894 global $dolibarr_main_url_root; 895 896 // Define $urlwithroot 897 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); 898 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file 899 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current 900 901 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).$conf->file->dol_url_root[$key].'/'.$path; // Test on start with http is for old conf syntax 902 } 903 break; 904 } 905 } 906 } 907 } 908 909 return $res; 910} 911 912/** 913 * Create a clone of instance of object (new instance with same value for properties) 914 * With native = 0: Property that are reference are also new object (full isolation clone). This means $this->db of new object is not valid. 915 * With native = 1: Use PHP clone. Property that are reference are same pointer. This means $this->db of new object is still valid but point to same this->db than original object. 916 * 917 * @param object $object Object to clone 918 * @param int $native 0=Full isolation method, 1=Native PHP method 919 * @return object Clone object 920 * @see https://php.net/manual/language.oop5.cloning.php 921 */ 922function dol_clone($object, $native = 0) 923{ 924 if (empty($native)) 925 { 926 $myclone = unserialize(serialize($object)); 927 } else { 928 $myclone = clone $object; // PHP clone is a shallow copy only, not a real clone, so properties of references will keep the reference (refering to the same target/variable) 929 } 930 931 return $myclone; 932} 933 934/** 935 * Optimize a size for some browsers (phone, smarphone, ...) 936 * 937 * @param int $size Size we want 938 * @param string $type Type of optimizing: 939 * '' = function used to define a size for truncation 940 * 'width' = function is used to define a width 941 * @return int New size after optimizing 942 */ 943function dol_size($size, $type = '') 944{ 945 global $conf; 946 if (empty($conf->dol_optimize_smallscreen)) return $size; 947 if ($type == 'width' && $size > 250) return 250; 948 else return 10; 949} 950 951 952/** 953 * Clean a string to use it as a file name 954 * 955 * @param string $str String to clean 956 * @param string $newstr String to replace bad chars with 957 * @param int $unaccent 1=Remove also accent (default), 0 do not remove them 958 * @return string String cleaned (a-zA-Z_) 959 * 960 * @see dol_string_nospecial(), dol_string_unaccent(), dol_sanitizePathName() 961 */ 962function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1) 963{ 964 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file 965 // Char '>' '<' '|' '$' and ';' are special chars for shells. 966 // Char '/' and '\' are file delimiters. 967 // -- car can be used into filename to inject special paramaters like --use-compress-program to make command with file as parameter making remote execution of command 968 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '--'); 969 return dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars); 970} 971 972/** 973 * Clean a string to use it as a path name 974 * 975 * @param string $str String to clean 976 * @param string $newstr String to replace bad chars with 977 * @param int $unaccent 1=Remove also accent (default), 0 do not remove them 978 * @return string String cleaned (a-zA-Z_) 979 * 980 * @see dol_string_nospecial(), dol_string_unaccent(), dol_sanitizeFileName() 981 */ 982function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1) 983{ 984 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°'); 985 return dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars); 986} 987 988/** 989 * Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName 990 * 991 * @param string $str String to clean 992 * @return string Cleaned string 993 * 994 * @see dol_sanitizeFilename(), dol_string_nospecial() 995 */ 996function dol_string_unaccent($str) 997{ 998 if (utf8_check($str)) 999 { 1000 // See http://www.utf8-chartable.de/ 1001 $string = rawurlencode($str); 1002 $replacements = array( 1003 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A', 1004 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E', 1005 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I', 1006 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O', 1007 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U', 1008 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a', 1009 '%C3%A7' => 'c', 1010 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e', 1011 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i', 1012 '%C3%B1' => 'n', 1013 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o', 1014 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u', 1015 '%C3%BF' => 'y' 1016 ); 1017 $string = strtr($string, $replacements); 1018 return rawurldecode($string); 1019 } else { 1020 // See http://www.ascii-code.com/ 1021 $string = strtr( 1022 $str, 1023 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7 1024 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1 1025 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD 1026 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB 1027 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8 1028 \xF9\xFA\xFB\xFC\xFD\xFF", 1029 "AAAAAAC 1030 EEEEIIIIDN 1031 OOOOOUUUY 1032 aaaaaaceeee 1033 iiiidnooooo 1034 uuuuyy" 1035 ); 1036 $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")); 1037 return $string; 1038 } 1039} 1040 1041/** 1042 * Clean a string from all punctuation characters to use it as a ref or login. 1043 * This is a more complete function than dol_sanitizeFileName. 1044 * 1045 * @param string $str String to clean 1046 * @param string $newstr String to replace forbidden chars with 1047 * @param array $badcharstoreplace List of forbidden characters 1048 * @return string Cleaned string 1049 * 1050 * @see dol_sanitizeFilename(), dol_string_unaccent(), dol_string_nounprintableascii() 1051 */ 1052function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '') 1053{ 1054 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°'); // more complete than dol_sanitizeFileName 1055 $forbidden_chars_to_remove = array(); 1056 if (is_array($badcharstoreplace)) $forbidden_chars_to_replace = $badcharstoreplace; 1057 //$forbidden_chars_to_remove=array("(",")"); 1058 1059 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str)); 1060} 1061 1062 1063/** 1064 * Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F). It can also removes also Tab-CR-LF. UTF8 chars remains. 1065 * This can be used to sanitize a string and view its real content. Some hacks try to obfuscate attacks by inserting non printable chars. 1066 * Note, for information: UTF8 on 1 byte are: \x00-\7F 1067 * 2 bytes are: byte 1 \xc0-\xdf, byte 2 = \x80-\xbf 1068 * 3 bytes are: byte 1 \xe0-\xef, byte 2 = \x80-\xbf, byte 3 = \x80-\xbf 1069 * 4 bytes are: byte 1 \xf0-\xf7, byte 2 = \x80-\xbf, byte 3 = \x80-\xbf, byte 4 = \x80-\xbf 1070 * @param string $str String to clean 1071 * @param int $removetabcrlf Remove also CR-LF 1072 * @return string Cleaned string 1073 * 1074 * @see dol_sanitizeFilename(), dol_string_unaccent(), dol_string_nospecial() 1075 */ 1076function dol_string_nounprintableascii($str, $removetabcrlf = 1) 1077{ 1078 if ($removetabcrlf) { 1079 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace 1080 } else { 1081 return preg_replace('/[\x00-\x08\x11-\x12\x14-\x1F\x7F]/u', '', $str); // /u operator should make UTF8 valid characters being ignored so are not included into the replace 1082 } 1083} 1084 1085 1086/** 1087 * Returns text escaped for inclusion into javascript code 1088 * 1089 * @param string $stringtoescape String to escape 1090 * @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 \ 1091 * @param int $noescapebackslashn 0=Escape also \n. 1=Do not escape \n. 1092 * @return string Escaped string. Both ' and " are escaped into ' if they are escaped. 1093 */ 1094function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0) 1095{ 1096 // escape quotes and backslashes, newlines, etc. 1097 $substitjs = array("'"=>"\\'", "\r"=>'\\r'); 1098 //$substitjs['</']='<\/'; // We removed this. Should be useless. 1099 if (empty($noescapebackslashn)) { $substitjs["\n"] = '\\n'; $substitjs['\\'] = '\\\\'; } 1100 if (empty($mode)) { $substitjs["'"] = "\\'"; $substitjs['"'] = "\\'"; } elseif ($mode == 1) $substitjs["'"] = "\\'"; 1101 elseif ($mode == 2) { $substitjs['"'] = '\\"'; } elseif ($mode == 3) { $substitjs["'"] = "\\'"; $substitjs['"'] = "\\\""; } 1102 return strtr($stringtoescape, $substitjs); 1103} 1104 1105/** 1106 * Returns text escaped for inclusion into javascript code 1107 * 1108 * @param string $stringtoescape String to escape 1109 * @return string Escaped string for json content. 1110 */ 1111function dol_escape_json($stringtoescape) 1112{ 1113 return str_replace('"', '\"', $stringtoescape); 1114} 1115 1116/** 1117 * Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields. 1118 * 1119 * @param string $stringtoescape String to escape 1120 * @param int $keepb 1=Keep b tags and escape them, 0=remove them 1121 * @param int $keepn 1=Preserve \r\n strings (otherwise, replace them with escaped value). Set to 1 when escaping for a <textarea>. 1122 * @param string $keepmoretags '' or 'common' or list of tags 1123 * @param int $escapeonlyhtmltags 1=Escape only html tags, not the special chars like accents. 1124 * @return string Escaped string 1125 * @see dol_string_nohtmltag(), dol_string_nospecial(), dol_string_unaccent() 1126 */ 1127function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $keepmoretags = '', $escapeonlyhtmltags = 0) 1128{ 1129 if ($keepmoretags == 'common') $keepmoretags = 'html,body,a,b,em,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody'; 1130 // TODO Implement $keepmoretags 1131 1132 // escape quotes and backslashes, newlines, etc. 1133 if ($escapeonlyhtmltags) { 1134 $tmp = htmlspecialchars_decode($stringtoescape, ENT_COMPAT); 1135 } else { 1136 $tmp = html_entity_decode($stringtoescape, ENT_COMPAT, 'UTF-8'); 1137 } 1138 if (!$keepb) $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>'')); 1139 if (!$keepn) $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n')); 1140 if ($escapeonlyhtmltags) { 1141 return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8'); 1142 } else { 1143 return htmlentities($tmp, ENT_COMPAT, 'UTF-8'); 1144 } 1145} 1146 1147/** 1148 * Convert a string to lower. Never use strtolower because it does not works with UTF8 strings. 1149 * 1150 * @param string $string String to encode 1151 * @param string $encoding Character set encoding 1152 * @return string String converted 1153 */ 1154function dol_strtolower($string, $encoding = "UTF-8") 1155{ 1156 if (function_exists('mb_strtolower')) { 1157 return mb_strtolower($string, $encoding); 1158 } else { 1159 return strtolower($string); 1160 } 1161} 1162 1163/** 1164 * Convert a string to upper. Never use strtolower because it does not works with UTF8 strings. 1165 * 1166 * @param string $string String to encode 1167 * @param string $encoding Character set encoding 1168 * @return string String converted 1169 */ 1170function dol_strtoupper($string, $encoding = "UTF-8") 1171{ 1172 if (function_exists('mb_strtoupper')) { 1173 return mb_strtoupper($string, $encoding); 1174 } else { 1175 return strtoupper($string); 1176 } 1177} 1178 1179/** 1180 * Convert first character of the first word of a string to upper. Never use ucfirst because it does not works with UTF8 strings. 1181 * 1182 * @param string $string String to encode 1183 * @param string $encoding Character set encodign 1184 * @return string String converted 1185 */ 1186function dol_ucfirst($string, $encoding = "UTF-8") 1187{ 1188 if (function_exists('mb_substr')) { 1189 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding); 1190 } else { 1191 return ucfirst($string); 1192 } 1193} 1194 1195/** 1196 * Convert first character of all the words of a string to upper. Never use ucfirst because it does not works with UTF8 strings. 1197 * 1198 * @param string $string String to encode 1199 * @param string $encoding Character set encodign 1200 * @return string String converted 1201 */ 1202function dol_ucwords($string, $encoding = "UTF-8") 1203{ 1204 if (function_exists('mb_convert_case')) { 1205 return mb_convert_case($string, MB_CASE_TITLE, $encoding); 1206 } else { 1207 return ucwords($string); 1208 } 1209} 1210 1211/** 1212 * Write log message into outputs. Possible outputs can be: 1213 * SYSLOG_HANDLERS = ["mod_syslog_file"] file name is then defined by SYSLOG_FILE 1214 * SYSLOG_HANDLERS = ["mod_syslog_syslog"] facility is then defined by SYSLOG_FACILITY 1215 * Warning, syslog functions are bugged on Windows, generating memory protection faults. To solve 1216 * this, use logging to files instead of syslog (see setup of module). 1217 * Note: If constant 'SYSLOG_FILE_NO_ERROR' defined, we never output any error message when writing to log fails. 1218 * Note: You can get log message into html sources by adding parameter &logtohtml=1 (constant MAIN_LOGTOHTML must be set) 1219 * This function works only if syslog module is enabled. 1220 * This must not use any call to other function calling dol_syslog (avoid infinite loop). 1221 * 1222 * @param string $message Line to log. ''=Show nothing 1223 * @param int $level Log level 1224 * 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 1225 * On Linux LOG_ERR=3, LOG_WARNING=4, LOG_INFO=6, LOG_DEBUG=7 1226 * @param int $ident 1=Increase ident of 1, -1=Decrease ident of 1 1227 * @param string $suffixinfilename When output is a file, append this suffix into default log filename. 1228 * @param string $restricttologhandler Force output of log only to this log handler 1229 * @param array|null $logcontext If defined, an array with extra informations (can be used by some log handlers) 1230 * @return void 1231 */ 1232function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null) 1233{ 1234 global $conf, $user, $debugbar; 1235 1236 // If syslog module enabled 1237 if (empty($conf->syslog->enabled)) return; 1238 1239 // Check if we are into execution of code of a website 1240 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) { 1241 global $website, $websitekey; 1242 if (is_object($website) && !empty($website->ref)) $suffixinfilename .= '_website_'.$website->ref; 1243 elseif (!empty($websitekey)) $suffixinfilename .= '_website_'.$websitekey; 1244 } 1245 1246 if ($ident < 0) 1247 { 1248 foreach ($conf->loghandlers as $loghandlerinstance) 1249 { 1250 $loghandlerinstance->setIdent($ident); 1251 } 1252 } 1253 1254 if (!empty($message)) 1255 { 1256 // Test log level 1257 $logLevels = array(LOG_EMERG=>'EMERG', LOG_ALERT=>'ALERT', LOG_CRIT=>'CRITICAL', LOG_ERR=>'ERR', LOG_WARNING=>'WARN', LOG_NOTICE=>'NOTICE', LOG_INFO=>'INFO', LOG_DEBUG=>'DEBUG'); 1258 if (!array_key_exists($level, $logLevels)) 1259 { 1260 throw new Exception('Incorrect log level'); 1261 } 1262 if ($level > $conf->global->SYSLOG_LEVEL) return; 1263 1264 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log 1265 1266 // If adding log inside HTML page is required 1267 if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML)) 1268 || (!empty($user->rights->debugbar->read) && is_object($debugbar))) 1269 { 1270 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message; 1271 } 1272 1273 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output 1274 // If html log tag enabled and url parameter log defined, we show output log on HTML comments 1275 if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) 1276 { 1277 print "\n\n<!-- Log start\n"; 1278 print $message."\n"; 1279 print "Log end -->\n"; 1280 } 1281 1282 $data = array( 1283 'message' => $message, 1284 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false), 1285 'level' => $level, 1286 'user' => ((is_object($user) && $user->id) ? $user->login : false), 1287 'ip' => false 1288 ); 1289 1290 $remoteip = getUserRemoteIP(); // Get ip when page run on a web server 1291 if (!empty($remoteip)) { 1292 $data['ip'] = $remoteip; 1293 // This is when server run behind a reverse proxy 1294 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip']; 1295 elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip']; 1296 } // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache) 1297 elseif (!empty($_SERVER['SERVER_ADDR'])) $data['ip'] = $_SERVER['SERVER_ADDR']; 1298 // 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). 1299 elseif (!empty($_SERVER['COMPUTERNAME'])) $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']); 1300 // 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). 1301 elseif (!empty($_SERVER['LOGNAME'])) $data['ip'] = '???@'.$_SERVER['LOGNAME']; 1302 // Loop on each log handler and send output 1303 foreach ($conf->loghandlers as $loghandlerinstance) 1304 { 1305 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) continue; 1306 $loghandlerinstance->export($data, $suffixinfilename); 1307 } 1308 unset($data); 1309 } 1310 1311 if ($ident > 0) 1312 { 1313 foreach ($conf->loghandlers as $loghandlerinstance) 1314 { 1315 $loghandlerinstance->setIdent($ident); 1316 } 1317 } 1318} 1319 1320/** 1321 * Return HTML code to output a button to open a dialog popup box. 1322 * Such buttons must be included inside a HTML form. 1323 * 1324 * @param string $name A name for the html component 1325 * @param string $label Label of button 1326 * @param string $buttonstring button string 1327 * @param string $url Url to open 1328 * @param string $disabled Disabled text 1329 * @return string HTML component with button 1330 */ 1331function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '') 1332{ 1333 if (strpos($url, '?') > 0) { 1334 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=1'; 1335 } else { 1336 $url .= '?dol_hide_menuinpopup=1&dol_hide_leftmenu=1&dol_openinpopup=1'; 1337 } 1338 1339 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">'; 1340 $out = '<a class="button bordertransp button_'.$name.'"'.$disabled.' title="'.dol_escape_htmltag($label).'">'.$buttonstring.'</a>'; 1341 $out .= '<script language="javascript"> 1342 jQuery(document).ready(function () { 1343 jQuery(".button_'.$name.'").click(function () { 1344 var $dialog = $(\'<div></div>\').html(\'<iframe class="iframedialog" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\') 1345 .dialog({ 1346 autoOpen: false, 1347 modal: true, 1348 height: (window.innerHeight - 150), 1349 width: \'80%\', 1350 title: "'.dol_escape_js($label).'" 1351 }); 1352 $dialog.dialog(\'open\'); 1353 }); 1354 }); 1355 </script>'; 1356 return $out; 1357} 1358 1359/** 1360 * Show tab header of a card 1361 * 1362 * @param array $links Array of tabs. Currently initialized by calling a function xxx_admin_prepare_head 1363 * @param string $active Active tab name (document', 'info', 'ldap', ....) 1364 * @param string $title Title 1365 * @param int $notab -1 or 0=Add tab header, 1=no tab header (if you set this to 1, using print dol_get_fiche_end() to close tab is not required), -2=Add tab header with no seaparation under tab (to start a tab just after) 1366 * @param string $picto Add a picto on tab title 1367 * @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. 1368 * @param string $morehtmlright Add more html content on right of tabs title 1369 * @param string $morecss More Css 1370 * @param int $limittoshow Limit number of tabs to show. Use 0 to use automatic default value. 1371 * @param string $moretabssuffix A suffix to use when you have several dol_get_fiche_head() in same page 1372 * @return void 1373 * @deprecated Use print dol_get_fiche_head() instead 1374 */ 1375function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '') 1376{ 1377 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix); 1378} 1379 1380/** 1381 * Show tabs of a record 1382 * 1383 * @param array $links Array of tabs 1384 * @param string $active Active tab name 1385 * @param string $title Title 1386 * @param int $notab -1 or 0=Add tab header, 1=no tab header (if you set this to 1, using print dol_get_fiche_end() to close tab is not required), -2=Add tab header with no seaparation under tab (to start a tab just after) 1387 * @param string $picto Add a picto on tab title 1388 * @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. 1389 * @param string $morehtmlright Add more html content on right of tabs title 1390 * @param string $morecss More Css 1391 * @param int $limittoshow Limit number of tabs to show. Use 0 to use automatic default value. 1392 * @param string $moretabssuffix A suffix to use when you have several dol_get_fiche_head() in same page 1393 * @return string 1394 */ 1395function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '') 1396{ 1397 global $conf, $langs, $hookmanager; 1398 1399 // Show title 1400 $showtitle = 1; 1401 if (!empty($conf->dol_optimize_smallscreen)) $showtitle = 0; 1402 1403 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->'; 1404 1405 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) { 1406 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n"; 1407 } 1408 1409 // Show right part 1410 if ($morehtmlright) $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. 1411 1412 // Show title 1413 if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) 1414 { 1415 $limittitle = 30; 1416 $out .= '<a class="tabTitle">'; 1417 if ($picto) $out .= img_picto($title, ($pictoisfullpath ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' '; 1418 $out .= '<span class="tabTitleText">'.dol_trunc($title, $limittitle).'</span>'; 1419 $out .= '</a>'; 1420 } 1421 1422 // Show tabs 1423 1424 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs). 1425 $maxkey = -1; 1426 if (is_array($links) && !empty($links)) 1427 { 1428 $keys = array_keys($links); 1429 if (count($keys)) $maxkey = max($keys); 1430 } 1431 1432 // Show tabs 1433 // if =0 we don't use the feature 1434 if (empty($limittoshow)) { 1435 $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD); 1436 } 1437 if (!empty($conf->dol_optimize_smallscreen)) $limittoshow = 2; 1438 1439 $displaytab = 0; 1440 $nbintab = 0; 1441 $popuptab = 0; 1442 $outmore = ''; 1443 for ($i = 0; $i <= $maxkey; $i++) 1444 { 1445 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) { 1446 // If active tab is already present 1447 if ($i >= $limittoshow) $limittoshow--; 1448 } 1449 } 1450 1451 for ($i = 0; $i <= $maxkey; $i++) 1452 { 1453 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) { 1454 $isactive = true; 1455 } else { 1456 $isactive = false; 1457 } 1458 1459 if ($i < $limittoshow || $isactive) 1460 { 1461 $out .= '<div class="inline-block tabsElem'.($isactive ? ' tabsElemActive' : '').((!$isactive && !empty($conf->global->MAIN_HIDE_INACTIVETAB_ON_PRINT)) ? ' hideonprint' : '').'"><!-- id tab = '.(empty($links[$i][2]) ? '' : $links[$i][2]).' -->'; 1462 if (isset($links[$i][2]) && $links[$i][2] == 'image') 1463 { 1464 if (!empty($links[$i][0])) 1465 { 1466 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n"; 1467 } else { 1468 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n"; 1469 } 1470 } elseif (!empty($links[$i][1])) 1471 { 1472 //print "x $i $active ".$links[$i][2]." z"; 1473 if ($isactive) 1474 { 1475 $out .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="tabactive tab inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'; 1476 $out .= $links[$i][1]; 1477 $out .= '</a>'."\n"; 1478 } else { 1479 $out .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="tabunactive tab inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'; 1480 $out .= $links[$i][1]; 1481 $out .= '</a>'."\n"; 1482 } 1483 } 1484 $out .= '</div>'; 1485 } else { 1486 // The popup with the other tabs 1487 if (!$popuptab) 1488 { 1489 $popuptab = 1; 1490 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup 1491 } 1492 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">'; 1493 if (isset($links[$i][2]) && $links[$i][2] == 'image') 1494 { 1495 if (!empty($links[$i][0])) 1496 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n"; 1497 else $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n"; 1498 } elseif (!empty($links[$i][1])) 1499 { 1500 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'; 1501 $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. 1502 $outmore .= '</a>'."\n"; 1503 } 1504 $outmore .= '</div>'; 1505 1506 $nbintab++; 1507 } 1508 $displaytab = $i; 1509 } 1510 if ($popuptab) $outmore .= '</div>'; 1511 1512 if ($popuptab) // If there is some tabs not shown 1513 { 1514 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left'); 1515 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right'); 1516 $widthofpopup = 200; 1517 1518 $tabsname = $moretabssuffix; 1519 if (empty($tabsname)) { $tabsname = str_replace("@", "", $picto); } 1520 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem">'; 1521 $out .= '<a href="#" class="tab moretab inline-block tabunactive">'.$langs->trans("More").'... ('.$nbintab.')</a>'; // Do not use "reposition" class in the "More". 1522 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">'; 1523 $out .= $outmore; 1524 $out .= '</div>'; 1525 $out .= '<div></div>'; 1526 $out .= "</div>\n"; 1527 1528 $out .= "<script>"; 1529 $out .= "$('#moretabs".$tabsname."').mouseenter( function() { 1530 var x = this.offsetLeft, y = this.offsetTop; 1531 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth); 1532 if ((window.innerWidth - x) < ".($widthofpopup + 10).") { 1533 $('#moretabsList".$tabsname."').css('".$right."','8px'); 1534 } 1535 $('#moretabsList".$tabsname."').css('".$left."','auto'); 1536 }); 1537 "; 1538 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});"; 1539 $out .= "</script>"; 1540 } 1541 1542 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) { 1543 $out .= "</div>\n"; 1544 } 1545 1546 if (!$notab || $notab == -1 || $notab == -2) $out .= "\n".'<div class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ' tabBarWithBottom')).'">'."\n"; 1547 1548 $parameters = array('tabname' => $active, 'out' => $out); 1549 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead" 1550 if ($reshook > 0) 1551 { 1552 $out = $hookmanager->resPrint; 1553 } 1554 1555 return $out; 1556} 1557 1558/** 1559 * Show tab footer of a card 1560 * 1561 * @param int $notab -1 or 0=Add tab footer, 1=no tab footer 1562 * @return void 1563 * @deprecated Use print dol_get_fiche_end() instead 1564 */ 1565function dol_fiche_end($notab = 0) 1566{ 1567 print dol_get_fiche_end($notab); 1568} 1569 1570/** 1571 * Return tab footer of a card 1572 * 1573 * @param int $notab -1 or 0=Add tab footer, 1=no tab footer 1574 * @return string 1575 */ 1576function dol_get_fiche_end($notab = 0) 1577{ 1578 if (!$notab || $notab == -1) return "\n</div>\n"; 1579 else return ''; 1580} 1581 1582/** 1583 * Show tab footer of a card. 1584 * Note: $object->next_prev_filter can be set to restrict select to find next or previous record by $form->showrefnav. 1585 * 1586 * @param Object $object Object to show 1587 * @param string $paramid Name of parameter to use to name the id into the URL next/previous link 1588 * @param string $morehtml More html content to output just before the nav bar 1589 * @param int $shownav Show Condition (navigation is shown if value is 1) 1590 * @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. 1591 * @param string $fieldref Nom du champ objet ref (object->ref) a utiliser pour select next et previous 1592 * @param string $morehtmlref More html to show after ref 1593 * @param string $moreparam More param to add in nav link url. 1594 * @param int $nodbprefix Do not include DB prefix to forge table name 1595 * @param string $morehtmlleft More html code to show before ref 1596 * @param string $morehtmlstatus More html code to show under navigation arrows 1597 * @param int $onlybanner Put this to 1, if the card will contains only a banner (this add css 'arearefnobottom' on div) 1598 * @param string $morehtmlright More html code to show before navigation arrows 1599 * @return void 1600 */ 1601function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '') 1602{ 1603 global $conf, $form, $user, $langs; 1604 1605 $error = 0; 1606 1607 $maxvisiblephotos = 1; 1608 $showimage = 1; 1609 $entity = (empty($object->entity) ? $conf->entity : $object->entity); 1610 $showbarcode = empty($conf->barcode->enabled) ? 0 : ($object->barcode ? 1 : 0); 1611 if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) $showbarcode = 0; 1612 $modulepart = 'unknown'; 1613 1614 if ($object->element == 'societe') $modulepart = 'societe'; 1615 if ($object->element == 'contact') $modulepart = 'contact'; 1616 if ($object->element == 'member') $modulepart = 'memberphoto'; 1617 if ($object->element == 'user') $modulepart = 'userphoto'; 1618 if ($object->element == 'product') $modulepart = 'product'; 1619 if ($object->element == 'ticket') $modulepart = 'ticket'; 1620 1621 if (class_exists("Imagick")) 1622 { 1623 if ($object->element == 'propal') $modulepart = 'propal'; 1624 if ($object->element == 'commande') $modulepart = 'commande'; 1625 if ($object->element == 'facture') $modulepart = 'facture'; 1626 if ($object->element == 'fichinter') $modulepart = 'ficheinter'; 1627 if ($object->element == 'contrat') $modulepart = 'contract'; 1628 if ($object->element == 'supplier_proposal') $modulepart = 'supplier_proposal'; 1629 if ($object->element == 'order_supplier') $modulepart = 'supplier_order'; 1630 if ($object->element == 'invoice_supplier') $modulepart = 'supplier_invoice'; 1631 if ($object->element == 'expensereport') $modulepart = 'expensereport'; 1632 } 1633 1634 if ($object->element == 'product') 1635 { 1636 $width = 80; $cssclass = 'photoref'; 1637 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]); 1638 $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5); 1639 if ($conf->browser->layout == 'phone') $maxvisiblephotos = 1; 1640 if ($showimage) { 1641 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('product', $conf->product->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0).'</div>'; 1642 } else { 1643 if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) { 1644 $nophoto = ''; 1645 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>'; 1646 } else { // Show no photo link 1647 $nophoto = '/public/theme/common/nophoto.png'; 1648 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>'; 1649 } 1650 } 1651 } elseif ($object->element == 'ticket') { 1652 $width = 80; $cssclass = 'photoref'; 1653 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref); 1654 $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2); 1655 if ($conf->browser->layout == 'phone') $maxvisiblephotos = 1; 1656 1657 if ($showimage) 1658 { 1659 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0); 1660 if ($object->nbphoto > 0) 1661 { 1662 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>'; 1663 } else { 1664 $showimage = 0; 1665 } 1666 } 1667 if (!$showimage) 1668 { 1669 if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) { 1670 $nophoto = ''; 1671 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>'; 1672 } else { // Show no photo link 1673 $nophoto = img_picto('No photo', 'object_ticket'); 1674 $morehtmlleft .= '<!-- No photo to show -->'; 1675 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">'; 1676 $morehtmlleft .= $nophoto; 1677 $morehtmlleft .= '</div></div>'; 1678 } 1679 } 1680 } else { 1681 if ($showimage) 1682 { 1683 if ($modulepart != 'unknown') 1684 { 1685 $phototoshow = ''; 1686 // Check if a preview file is available 1687 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) 1688 { 1689 $objectref = dol_sanitizeFileName($object->ref); 1690 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/"; 1691 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) 1692 { 1693 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart); 1694 $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 1695 } else { 1696 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart); 1697 } 1698 if (empty($subdir)) $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path 1699 1700 $filepath = $dir_output.$subdir."/"; 1701 1702 $filepdf = $filepath.$objectref.".pdf"; 1703 $relativepath = $subdir.'/'.$objectref.'.pdf'; 1704 1705 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png") 1706 $fileimage = $filepdf.'_preview.png'; 1707 $relativepathimage = $relativepath.'_preview.png'; 1708 1709 $pdfexists = file_exists($filepdf); 1710 1711 // If PDF file exists 1712 if ($pdfexists) 1713 { 1714 // Conversion du PDF en image png si fichier png non existant 1715 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) 1716 { 1717 if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) // If you experience trouble with pdf thumb generation and imagick, you can disable here. 1718 { 1719 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 1720 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png 1721 if ($ret < 0) $error++; 1722 } 1723 } 1724 } 1725 1726 if ($pdfexists && !$error) 1727 { 1728 $heightforphotref = 80; 1729 if (!empty($conf->dol_optimize_smallscreen)) $heightforphotref = 60; 1730 // If the preview file is found 1731 if (file_exists($fileimage)) 1732 { 1733 $phototoshow = '<div class="photoref">'; 1734 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&file='.urlencode($relativepathimage).'">'; 1735 $phototoshow .= '</div>'; 1736 } 1737 } 1738 } elseif (!$phototoshow) { // example if modulepart = 'photo' 1739 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photoref', 'small', 1, 0, $maxvisiblephotos); 1740 } 1741 1742 if ($phototoshow) { 1743 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'; 1744 $morehtmlleft .= $phototoshow; 1745 $morehtmlleft .= '</div>'; 1746 } 1747 } 1748 1749 if (!$phototoshow) // Show No photo link (picto of object) 1750 { 1751 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'; 1752 if ($object->element == 'action') 1753 { 1754 $width = 80; 1755 $cssclass = 'photorefcenter'; 1756 $nophoto = img_picto('No photo', 'title_agenda'); 1757 } else { 1758 $width = 14; $cssclass = 'photorefcenter'; 1759 $picto = $object->picto; 1760 if ($object->element == 'project' && !$object->public) $picto = 'project'; // instead of projectpub 1761 $nophoto = img_picto('No photo', 'object_'.$picto); 1762 } 1763 $morehtmlleft .= '<!-- No photo to show -->'; 1764 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">'; 1765 $morehtmlleft .= $nophoto; 1766 $morehtmlleft .= '</div></div>'; 1767 1768 $morehtmlleft .= '</div>'; 1769 } 1770 } 1771 } 1772 1773 if ($showbarcode) $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object).'</div>'; 1774 1775 if ($object->element == 'societe') 1776 { 1777 if (!empty($conf->use_javascript_ajax) && $user->rights->societe->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) 1778 { 1779 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased'); 1780 } else { 1781 $morehtmlstatus .= $object->getLibStatut(6); 1782 } 1783 } elseif ($object->element == 'product') 1784 { 1785 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') '; 1786 if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) { 1787 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell'); 1788 } else { 1789 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>'; 1790 } 1791 $morehtmlstatus .= ' '; 1792 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') '; 1793 if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) { 1794 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy'); 1795 } else { 1796 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>'; 1797 } 1798 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan'))) { 1799 $tmptxt = $object->getLibStatut(6, $object->totalpaye); 1800 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) $tmptxt = $object->getLibStatut(5, $object->totalpaye); 1801 $morehtmlstatus .= $tmptxt; 1802 } elseif ($object->element == 'contrat' || $object->element == 'contract') { 1803 if ($object->statut == 0) $morehtmlstatus .= $object->getLibStatut(5); 1804 else $morehtmlstatus .= $object->getLibStatut(4); 1805 } elseif ($object->element == 'facturerec') { 1806 if ($object->frequency == 0) $morehtmlstatus .= $object->getLibStatut(2); 1807 else $morehtmlstatus .= $object->getLibStatut(5); 1808 } elseif ($object->element == 'project_task') { 1809 $object->fk_statut = 1; 1810 if ($object->progress > 0) $object->fk_statut = 2; 1811 if ($object->progress >= 100) $object->fk_statut = 3; 1812 $tmptxt = $object->getLibStatut(5); 1813 $morehtmlstatus .= $tmptxt; // No status on task 1814 } else { // Generic case 1815 $tmptxt = $object->getLibStatut(6); 1816 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) $tmptxt = $object->getLibStatut(5); 1817 $morehtmlstatus .= $tmptxt; 1818 } 1819 1820 // Add if object was dispatched "into accountancy" 1821 if (!empty($conf->accounting->enabled) && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) 1822 { 1823 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank') 1824 if (method_exists($object, 'getVentilExportCompta')) 1825 { 1826 $accounted = $object->getVentilExportCompta(); 1827 $langs->load("accountancy"); 1828 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>'; 1829 } 1830 } 1831 1832 // Add alias for thirdparty 1833 if (!empty($object->name_alias)) $morehtmlref .= '<div class="refidno">'.$object->name_alias.'</div>'; 1834 1835 // Add label 1836 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) 1837 { 1838 if (!empty($object->label)) $morehtmlref .= '<div class="refidno">'.$object->label.'</div>'; 1839 } 1840 1841 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) 1842 { 1843 $moreaddress = $object->getBannerAddress('refaddress', $object); 1844 if ($moreaddress) { 1845 $morehtmlref .= '<div class="refidno">'; 1846 $morehtmlref .= $moreaddress; 1847 $morehtmlref .= '</div>'; 1848 } 1849 } 1850 if (!empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && ($conf->global->MAIN_SHOW_TECHNICAL_ID == '1' || preg_match('/'.preg_quote($object->element, '/').'/i', $conf->global->MAIN_SHOW_TECHNICAL_ID)) && !empty($object->id)) 1851 { 1852 $morehtmlref .= '<div style="clear: both;"></div>'; 1853 $morehtmlref .= '<div class="refidno">'; 1854 $morehtmlref .= $langs->trans("TechnicalID").': '.$object->id; 1855 $morehtmlref .= '</div>'; 1856 } 1857 1858 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">'; 1859 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright); 1860 print '</div>'; 1861 print '<div class="underrefbanner clearboth"></div>'; 1862} 1863 1864/** 1865 * Show a string with the label tag dedicated to the HTML edit field. 1866 * 1867 * @param string $langkey Translation key 1868 * @param string $fieldkey Key of the html select field the text refers to 1869 * @param int $fieldrequired 1=Field is mandatory 1870 * @return string 1871 * @deprecated Form::editfieldkey 1872 */ 1873function fieldLabel($langkey, $fieldkey, $fieldrequired = 0) 1874{ 1875 global $langs; 1876 $ret = ''; 1877 if ($fieldrequired) $ret .= '<span class="fieldrequired">'; 1878 $ret .= '<label for="'.$fieldkey.'">'; 1879 $ret .= $langs->trans($langkey); 1880 $ret .= '</label>'; 1881 if ($fieldrequired) $ret .= '</span>'; 1882 return $ret; 1883} 1884 1885/** 1886 * Return string to add class property on html element with pair/impair. 1887 * 1888 * @param string $var 0 or 1 1889 * @param string $moreclass More class to add 1890 * @return string String to add class onto HTML element 1891 */ 1892function dol_bc($var, $moreclass = '') 1893{ 1894 global $bc; 1895 $ret = ' '.$bc[$var]; 1896 if ($moreclass) $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret); 1897 return $ret; 1898} 1899 1900/** 1901 * Return a formated address (part address/zip/town/state) according to country rules. 1902 * See https://en.wikipedia.org/wiki/Address 1903 * 1904 * @param Object $object A company or contact object 1905 * @param int $withcountry 1=Add country into address string 1906 * @param string $sep Separator to use to build string 1907 * @param Translate $outputlangs Object lang that contains language for text translation. 1908 * @param int $mode 0=Standard output, 1=Remove address 1909 * @param string $extralangcode User extralanguage $langcode as values for address, town 1910 * @return string Formated string 1911 * @see dol_print_address() 1912 */ 1913function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '') 1914{ 1915 global $conf, $langs; 1916 1917 $ret = ''; 1918 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR'); // See also MAIN_FORCE_STATE_INTO_ADDRESS 1919 1920 // See format of addresses on https://en.wikipedia.org/wiki/Address 1921 // Address 1922 if (empty($mode)) { 1923 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : $object->address); 1924 } 1925 // Zip/Town/State 1926 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) { // US: title firstname name \n address lines \n town, state, zip \n country 1927 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : $object->town); 1928 $ret .= ($ret ? $sep : '').$town; 1929 if (!empty($object->state)) { 1930 $ret .= ($ret ? ", " : '').$object->state; 1931 } 1932 if ($object->zip) $ret .= ($ret ? ", " : '').$object->zip; 1933 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) { // UK: title firstname name \n address lines \n town state \n zip \n country 1934 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : $object->town); 1935 $ret .= ($ret ? $sep : '').$town; 1936 if (!empty($object->state)) { 1937 $ret .= ($ret ? ", " : '').$object->state; 1938 } 1939 if ($object->zip) $ret .= ($ret ? $sep : '').$object->zip; 1940 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) { // ES: title firstname name \n address lines \n zip town \n state \n country 1941 $ret .= ($ret ? $sep : '').$object->zip; 1942 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : $object->town); 1943 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : ''); 1944 if (!empty($object->state)) { 1945 $ret .= "\n".$object->state; 1946 } 1947 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) { // IT: tile firstname name\n address lines \n zip (Code Departement) \n country 1948 $ret .= ($ret ? $sep : '').$object->zip; 1949 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : $object->town); 1950 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : ''); 1951 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code)); 1952 } else { // Other: title firstname name \n address lines \n zip town \n country 1953 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : $object->town); 1954 $ret .= $object->zip ? (($ret ? $sep : '').$object->zip) : ''; 1955 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : ''); 1956 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) { 1957 $ret .= ($ret ? ", " : '').$object->state; 1958 } 1959 } 1960 if (!is_object($outputlangs)) $outputlangs = $langs; 1961 if ($withcountry) { 1962 $langs->load("dict"); 1963 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code))); 1964 } 1965 1966 return $ret; 1967} 1968 1969 1970 1971/** 1972 * Format a string. 1973 * 1974 * @param string $fmt Format of strftime function (http://php.net/manual/fr/function.strftime.php) 1975 * @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) 1976 * @param int $is_gmt See comment of timestamp parameter 1977 * @return string A formatted string 1978 */ 1979function dol_strftime($fmt, $ts = false, $is_gmt = false) 1980{ 1981 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range 1982 return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts); 1983 } else return 'Error date into a not supported range'; 1984} 1985 1986/** 1987 * Output date in a string format according to outputlangs (or langs if not defined). 1988 * Return charset is always UTF-8, except if encodetoouput is defined. In this case charset is output charset 1989 * 1990 * @param int $time GM Timestamps date 1991 * @param string $format Output date format (tag of strftime function) 1992 * "%d %b %Y", 1993 * "%d/%m/%Y %H:%M", 1994 * "%d/%m/%Y %H:%M:%S", 1995 * "%B"=Long text of month, "%A"=Long text of day, "%b"=Short text of month, "%a"=Short text of day 1996 * "day", "daytext", "dayhour", "dayhourldap", "dayhourtext", "dayrfc", "dayhourrfc", "...inputnoreduce", "...reduceformat" 1997 * @param string $tzoutput true or 'gmt' => string is for Greenwich location 1998 * false or 'tzserver' => output string is for local PHP server TZ usage 1999 * 'tzuser' => output string is for user TZ (current browser TZ with current dst) => In a future, we should have same behaviour than 'tzuserrel' 2000 * 'tzuserrel' => output string is for user TZ (current browser TZ with dst or not, depending on date position) (TODO not implemented yet) 2001 * @param Translate $outputlangs Object lang that contains language for text translation. 2002 * @param boolean $encodetooutput false=no convert into output pagecode 2003 * @return string Formated date or '' if time is null 2004 * 2005 * @see dol_mktime(), dol_stringtotime(), dol_getdate() 2006 */ 2007function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false) 2008{ 2009 global $conf, $langs; 2010 2011 if ($tzoutput === 'auto') { 2012 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver')); 2013 } 2014 2015 // Clean parameters 2016 $to_gmt = false; 2017 $offsettz = $offsetdst = 0; 2018 if ($tzoutput) { 2019 $to_gmt = true; // For backward compatibility 2020 if (is_string($tzoutput)) { 2021 if ($tzoutput == 'tzserver') { 2022 $to_gmt = false; 2023 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion' 2024 $offsettz = 0; // Timezone offset with server timezone, so 0 2025 $offsetdst = 0; // Dst offset with server timezone, so 0 2026 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') { 2027 $to_gmt = true; 2028 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion' 2029 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore 2030 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore 2031 } 2032 } 2033 } 2034 if (!is_object($outputlangs)) { 2035 $outputlangs = $langs; 2036 } 2037 if (!$format) { 2038 $format = 'daytextshort'; 2039 } 2040 2041 // Do we have to reduce the length of date (year on 2 chars) to save space. 2042 // Note: dayinputnoreduce is same than day but no reduction of year length will be done 2043 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param. 2044 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day 2045 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format); 2046 if ($formatwithoutreduce != $format) { 2047 $format = $formatwithoutreduce; 2048 $reduceformat = 1; 2049 } // so format 'dayreduceformat' is processed like day 2050 2051 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default. 2052 // TODO Add format daysmallyear and dayhoursmallyear 2053 if ($format == 'day') { 2054 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short); 2055 } elseif ($format == 'hour') { 2056 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short); 2057 } elseif ($format == 'hourduration') { 2058 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration); 2059 } elseif ($format == 'daytext') { 2060 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text); 2061 } elseif ($format == 'daytextshort') { 2062 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short); 2063 } elseif ($format == 'dayhour') { 2064 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short); 2065 } elseif ($format == 'dayhoursec') { 2066 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short); 2067 } elseif ($format == 'dayhourtext') { 2068 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text); 2069 } elseif ($format == 'dayhourtextshort') { 2070 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short); 2071 } elseif ($format == 'dayhourlog') { 2072 // Format not sensitive to language 2073 $format = '%Y%m%d%H%M%S'; 2074 } elseif ($format == 'dayhourldap') { 2075 $format = '%Y%m%d%H%M%SZ'; 2076 } elseif ($format == 'dayhourxcard') { 2077 $format = '%Y%m%dT%H%M%SZ'; 2078 } elseif ($format == 'dayxcard') { 2079 $format = '%Y%m%d'; 2080 } elseif ($format == 'dayrfc') { 2081 $format = '%Y-%m-%d'; // DATE_RFC3339 2082 } elseif ($format == 'dayhourrfc') { 2083 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339 2084 } elseif ($format == 'standard') { 2085 $format = '%Y-%m-%d %H:%M:%S'; 2086 } 2087 2088 if ($reduceformat) { 2089 $format = str_replace('%Y', '%y', $format); 2090 $format = str_replace('yyyy', 'yy', $format); 2091 } 2092 2093 // If date undefined or "", we return "" 2094 if (dol_strlen($time) == 0) { 2095 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00) 2096 } 2097 2098 // Clean format 2099 if (preg_match('/%b/i', $format)) { // There is some text to translate 2100 // We inhibate translation to text made by strftime functions. We will use trans instead later. 2101 $format = str_replace('%b', '__b__', $format); 2102 $format = str_replace('%B', '__B__', $format); 2103 } 2104 if (preg_match('/%a/i', $format)) { // There is some text to translate 2105 // We inhibate translation to text made by strftime functions. We will use trans instead later. 2106 $format = str_replace('%a', '__a__', $format); 2107 $format = str_replace('%A', '__A__', $format); 2108 } 2109 2110 2111 // Analyze date 2112 $reg = array(); 2113 if (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 2114 dol_print_error("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]); 2115 return ''; 2116 } elseif (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i', $time, $reg)) { // Still available to solve problems in extrafields of type date 2117 // This part of code should not be used anymore. 2118 dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING); 2119 //if (function_exists('debug_print_backtrace')) debug_print_backtrace(); 2120 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' 2121 $syear = (!empty($reg[1]) ? $reg[1] : ''); 2122 $smonth = (!empty($reg[2]) ? $reg[2] : ''); 2123 $sday = (!empty($reg[3]) ? $reg[3] : ''); 2124 $shour = (!empty($reg[4]) ? $reg[4] : ''); 2125 $smin = (!empty($reg[5]) ? $reg[5] : ''); 2126 $ssec = (!empty($reg[6]) ? $reg[6] : ''); 2127 2128 $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true); 2129 $ret = adodb_strftime($format, $time + $offsettz + $offsetdst, $to_gmt); 2130 } else { 2131 // Date is a timestamps 2132 if ($time < 100000000000) { // Protection against bad date values 2133 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring. 2134 2135 $ret = adodb_strftime($format, $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server 2136 } else { 2137 $ret = 'Bad value '.$time.' for date'; 2138 } 2139 } 2140 2141 if (preg_match('/__b__/i', $format)) { 2142 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring. 2143 2144 // Here ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs. 2145 $month = adodb_strftime('%m', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server 2146 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'. 2147 if ($encodetooutput) { 2148 $monthtext = $outputlangs->transnoentities('Month'.$month); 2149 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month); 2150 } else { 2151 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month); 2152 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month); 2153 } 2154 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort; 2155 $ret = str_replace('__b__', $monthtextshort, $ret); 2156 $ret = str_replace('__B__', $monthtext, $ret); 2157 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x'; 2158 //return $ret; 2159 } 2160 if (preg_match('/__a__/i', $format)) { 2161 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring"; 2162 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring. 2163 2164 $w = adodb_strftime('%w', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server 2165 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w); 2166 $ret = str_replace('__A__', $dayweek, $ret); 2167 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret); 2168 } 2169 2170 return $ret; 2171} 2172 2173 2174/** 2175 * Return an array with locale date info. 2176 * WARNING: This function use PHP server timezone by default to return locale informations. 2177 * Be aware to add the third parameter to "UTC" if you need to work on UTC. 2178 * 2179 * @param int $timestamp Timestamp 2180 * @param boolean $fast Fast mode. deprecated. 2181 * @param string $forcetimezone '' to use the PHP server timezone. Or use a form like 'Europe/Paris' or '+0200' to force timezone. 2182 * @return array Array of informations 2183 * 'seconds' => $secs, 2184 * 'minutes' => $min, 2185 * 'hours' => $hour, 2186 * 'mday' => $day, 2187 * 'wday' => $dow, 0=sunday, 6=saturday 2188 * 'mon' => $month, 2189 * 'year' => $year, 2190 * 'yday' => floor($secsInYear/$_day_power) 2191 * '0' => original timestamp 2192 * @see dol_print_date(), dol_stringtotime(), dol_mktime() 2193 */ 2194function dol_getdate($timestamp, $fast = false, $forcetimezone = '') 2195{ 2196 global $conf; 2197 2198 if (empty($conf->global->MAIN_USE_OLD_FUNCTIONS_FOR_GETDATE)) { 2199 //$datetimeobj = new DateTime('@'.$timestamp); 2200 $datetimeobj = new DateTime(); 2201 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone 2202 if ($forcetimezone) $datetimeobj->setTimezone(new DateTimeZone($forcetimezone)); // (add timezone relative to the date entered) 2203 $arrayinfo = array( 2204 'year'=>((int) date_format($datetimeobj, 'Y')), 2205 'mon'=>((int) date_format($datetimeobj, 'm')), 2206 'mday'=>((int) date_format($datetimeobj, 'd')), 2207 'wday'=>((int) date_format($datetimeobj, 'w')), 2208 'yday'=>((int) date_format($datetimeobj, 'z')), 2209 'hours'=>((int) date_format($datetimeobj, 'H')), 2210 'minutes'=>((int) date_format($datetimeobj, 'i')), 2211 'seconds'=>((int) date_format($datetimeobj, 's')), 2212 '0'=>$timestamp 2213 ); 2214 } else { 2215 // PHP getdate is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows 2216 $usealternatemethod = false; 2217 if ($timestamp <= 0) $usealternatemethod = true; // <= 1970 2218 if ($timestamp >= 2145913200) $usealternatemethod = true; // >= 2038 2219 2220 if ($usealternatemethod) 2221 { 2222 $arrayinfo = adodb_getdate($timestamp, $fast); 2223 } else { 2224 $arrayinfo = getdate($timestamp); 2225 } 2226 } 2227 2228 return $arrayinfo; 2229} 2230 2231/** 2232 * Return a timestamp date built from detailed informations (by default a local PHP server timestamp) 2233 * Replace function mktime not available under Windows if year < 1970 2234 * PHP mktime is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows 2235 * 2236 * @param int $hour Hour (can be -1 for undefined) 2237 * @param int $minute Minute (can be -1 for undefined) 2238 * @param int $second Second (can be -1 for undefined) 2239 * @param int $month Month (1 to 12) 2240 * @param int $day Day (1 to 31) 2241 * @param int $year Year 2242 * @param mixed $gm True or 1 or 'gmt'=Input informations are GMT values 2243 * False or 0 or 'tzserver' = local to server TZ 2244 * 'auto' 2245 * 'tzuser' = local to user TZ taking dst into account at the current date. Not yet implemented. 2246 * 'tzuserrel' = local to user TZ taking dst into account at the given date. Use this one to convert date input from user. 2247 * 'tz,TimeZone' = use specified timezone 2248 * @param int $check 0=No check on parameters (Can use day 32, etc...) 2249 * @return int|string Date as a timestamp, '' or false if error 2250 * @see dol_print_date(), dol_stringtotime(), dol_getdate() 2251 */ 2252function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1) 2253{ 2254 global $conf; 2255 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -"; 2256 //print 'gm:'.$gm.' gm==auto:'.($gm == 'auto').'<br>'; 2257 2258 if ($gm === 'auto') { 2259 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey); 2260 } 2261 2262 // Clean parameters 2263 if ($hour == -1 || empty($hour)) $hour = 0; 2264 if ($minute == -1 || empty($minute)) $minute = 0; 2265 if ($second == -1 || empty($second)) $second = 0; 2266 2267 // Check parameters 2268 if ($check) 2269 { 2270 if (!$month || !$day) return ''; 2271 if ($day > 31) return ''; 2272 if ($month > 12) return ''; 2273 if ($hour < 0 || $hour > 24) return ''; 2274 if ($minute < 0 || $minute > 60) return ''; 2275 if ($second < 0 || $second > 60) return ''; 2276 } 2277 2278 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) 2279 { 2280 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin' 2281 $localtz = new DateTimeZone($default_timezone); 2282 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') 2283 { 2284 // We use dol_tz_string first because it is more reliable. 2285 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin' 2286 try { 2287 $localtz = new DateTimeZone($default_timezone); 2288 } catch (Exception $e) 2289 { 2290 dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING); 2291 $default_timezone = @date_default_timezone_get(); 2292 } 2293 } elseif (strrpos($gm, "tz,") !== false) 2294 { 2295 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin' 2296 try { 2297 $localtz = new DateTimeZone($timezone); 2298 } catch (Exception $e) 2299 { 2300 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING); 2301 } 2302 } 2303 2304 if (empty($localtz)) { 2305 $localtz = new DateTimeZone('UTC'); 2306 } 2307 //var_dump($localtz); 2308 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute); 2309 $dt = new DateTime(null, $localtz); 2310 $dt->setDate((int) $year, (int) $month, (int) $day); 2311 $dt->setTime((int) $hour, (int) $minute, (int) $second); 2312 $date = $dt->getTimestamp(); // should include daylight saving time 2313 //var_dump($date); 2314 return $date; 2315} 2316 2317 2318/** 2319 * Return date for now. In most cases, we use this function without parameters (that means GMT time). 2320 * 2321 * @param string $mode 'auto' => for backward compatibility (avoid this), 2322 * 'gmt' => we return GMT timestamp, 2323 * 'tzserver' => we add the PHP server timezone 2324 * 'tzref' => we add the company timezone. Not implemented. 2325 * 'tzuser' or 'tzuserrel' => we add the user timezone 2326 * @return int $date Timestamp 2327 */ 2328function dol_now($mode = 'auto') 2329{ 2330 $ret = 0; 2331 2332 if ($mode === 'auto') { 2333 $mode = 'gmt'; 2334 } 2335 2336 if ($mode == 'gmt') $ret = time(); // Time for now at greenwich. 2337 elseif ($mode == 'tzserver') // Time for now with PHP server timezone added 2338 { 2339 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; 2340 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time 2341 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600)); 2342 } /*elseif ($mode == 'tzref') // Time for now with parent company timezone is added 2343 { 2344 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; 2345 $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time 2346 $ret=dol_now('gmt')+($tzsecond*3600); 2347 }*/ 2348 elseif ($mode == 'tzuser' || $mode == 'tzuserrel') // Time for now with user timezone added 2349 { 2350 //print 'time: '.time(); 2351 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; 2352 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; 2353 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst)); 2354 } 2355 2356 return $ret; 2357} 2358 2359 2360/** 2361 * Return string with formated size 2362 * 2363 * @param int $size Size to print 2364 * @param int $shortvalue Tell if we want long value to use another unit (Ex: 1.5Kb instead of 1500b) 2365 * @param int $shortunit Use short label of size unit (for example 'b' instead of 'bytes') 2366 * @return string Link 2367 */ 2368function dol_print_size($size, $shortvalue = 0, $shortunit = 0) 2369{ 2370 global $conf, $langs; 2371 $level = 1024; 2372 2373 if (!empty($conf->dol_optimize_smallscreen)) $shortunit = 1; 2374 2375 // Set value text 2376 if (empty($shortvalue) || $size < ($level * 10)) 2377 { 2378 $ret = $size; 2379 $textunitshort = $langs->trans("b"); 2380 $textunitlong = $langs->trans("Bytes"); 2381 } else { 2382 $ret = round($size / $level, 0); 2383 $textunitshort = $langs->trans("Kb"); 2384 $textunitlong = $langs->trans("KiloBytes"); 2385 } 2386 // Use long or short text unit 2387 if (empty($shortunit)) { $ret .= ' '.$textunitlong; } else { $ret .= ' '.$textunitshort; } 2388 2389 return $ret; 2390} 2391 2392/** 2393 * Show Url link 2394 * 2395 * @param string $url Url to show 2396 * @param string $target Target for link 2397 * @param int $max Max number of characters to show 2398 * @param int $withpicto With picto 2399 * @return string HTML Link 2400 */ 2401function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0) 2402{ 2403 global $langs; 2404 2405 if (empty($url)) return ''; 2406 2407 $link = '<a href="'; 2408 if (!preg_match('/^http/i', $url)) $link .= 'http://'; 2409 $link .= $url; 2410 $link .= '"'; 2411 if ($target) $link .= ' target="'.$target.'"'; 2412 $link .= '>'; 2413 if (!preg_match('/^http/i', $url)) $link .= 'http://'; 2414 $link .= dol_trunc($url, $max); 2415 $link .= '</a>'; 2416 return '<div class="nospan float" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>'; 2417} 2418 2419/** 2420 * Show EMail link formatted for HTML output. 2421 * 2422 * @param string $email EMail to show (only email, without 'Name of recipient' before) 2423 * @param int $cid Id of contact if known 2424 * @param int $socid Id of third party if known 2425 * @param int $addlink 0=no link, 1=email has a html email link (+ link to create action if constant AGENDA_ADDACTIONFOREMAIL is on) 2426 * @param int $max Max number of characters to show 2427 * @param int $showinvalid 1=Show warning if syntax email is wrong 2428 * @param int $withpicto Show picto 2429 * @return string HTML Link 2430 */ 2431function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0) 2432{ 2433 global $conf, $user, $langs, $hookmanager; 2434 2435 $newemail = dol_escape_htmltag($email); 2436 2437 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) $withpicto = 0; 2438 2439 if (empty($email)) return ' '; 2440 2441 if (!empty($addlink)) 2442 { 2443 $newemail = '<a style="text-overflow: ellipsis;" href="'; 2444 if (!preg_match('/^mailto:/i', $email)) $newemail .= 'mailto:'; 2445 $newemail .= $email; 2446 $newemail .= '">'; 2447 $newemail .= dol_trunc($email, $max); 2448 $newemail .= '</a>'; 2449 if ($showinvalid && !isValidEmail($email)) 2450 { 2451 $langs->load("errors"); 2452 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email)); 2453 } 2454 2455 if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) 2456 { 2457 $type = 'AC_EMAIL'; $link = ''; 2458 if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) $link = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&backtopage=1&actioncode='.$type.'&contactid='.$cid.'&socid='.$socid.'">'.img_object($langs->trans("AddAction"), "calendar").'</a>'; 2459 if ($link) $newemail = '<div>'.$newemail.' '.$link.'</div>'; 2460 } 2461 } else { 2462 if ($showinvalid && !isValidEmail($email)) 2463 { 2464 $langs->load("errors"); 2465 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email)); 2466 } 2467 } 2468 2469 //$rep = '<div class="nospan" style="margin-right: 10px">'; 2470 $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, 'object_email.png').' ' : '').$newemail; 2471 //$rep .= '</div>'; 2472 if ($hookmanager) { 2473 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto); 2474 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email); 2475 if ($reshook > 0) { 2476 $rep = ''; 2477 } 2478 $rep .= $hookmanager->resPrint; 2479 } 2480 2481 return $rep; 2482} 2483 2484/** 2485 * Get array of social network dictionary 2486 * 2487 * @return array Array of Social Networks Dictionary 2488 */ 2489function getArrayOfSocialNetworks() 2490{ 2491 global $conf, $db; 2492 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks"; 2493 $sql .= " WHERE entity=".$conf->entity; 2494 $socialnetworks = array(); 2495 $resql = $db->query($sql); 2496 if ($resql) { 2497 while ($obj = $db->fetch_object($resql)) { 2498 $socialnetworks[$obj->code] = array( 2499 'rowid' => $obj->rowid, 2500 'label' => $obj->label, 2501 'url' => $obj->url, 2502 'icon' => $obj->icon, 2503 'active' => $obj->active, 2504 ); 2505 } 2506 } 2507 return $socialnetworks; 2508} 2509 2510/** 2511 * Show social network link 2512 * 2513 * @param string $value Skype to show (only skype, without 'Name of recipient' before) 2514 * @param int $cid Id of contact if known 2515 * @param int $socid Id of third party if known 2516 * @param string $type 'skype','facebook',... 2517 * @param array $dictsocialnetworks socialnetworks availables 2518 * @return string HTML Link 2519 */ 2520function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array()) 2521{ 2522 global $conf, $user, $langs; 2523 2524 $htmllink = $value; 2525 2526 if (empty($value)) return ' '; 2527 2528 if (!empty($type)) { 2529 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">'; 2530 // TODO use dictionary definition for picto $dictsocialnetworks[$type]['icon'] 2531 $htmllink .= img_picto($langs->trans(dol_ucfirst($type)), $type.'.png', '', false, 0, 0, '', 'paddingright', 0); 2532 if ($type == 'skype') { 2533 $htmllink .= $value; 2534 $htmllink .= ' '; 2535 $htmllink .= '<a href="skype:'; 2536 $htmllink .= $value; 2537 $htmllink .= '?call" alt="'.$langs->trans("Call").' '.$value.'" title="'.$langs->trans("Call").' '.$value.'">'; 2538 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">'; 2539 $htmllink .= '</a><a href="skype:'; 2540 $htmllink .= $value; 2541 $htmllink .= '?chat" alt="'.$langs->trans("Chat").' '.$value.'" title="'.$langs->trans("Chat").' '.$value.'">'; 2542 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">'; 2543 $htmllink .= '</a>'; 2544 if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) { 2545 $addlink = 'AC_SKYPE'; 2546 $link = ''; 2547 if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) $link = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&backtopage=1&actioncode='.$addlink.'&contactid='.$cid.'&socid='.$socid.'">'.img_object($langs->trans("AddAction"), "calendar").'</a>'; 2548 $htmllink .= ($link ? ' '.$link : ''); 2549 } 2550 } else { 2551 if (!empty($dictsocialnetworks[$type]['url'])) { 2552 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']); 2553 $htmllink .= ' <a href="'.$link.'" target="_blank">'.$value.'</a>'; 2554 } else { 2555 $htmllink .= $value; 2556 } 2557 } 2558 $htmllink .= '</div>'; 2559 } else { 2560 $langs->load("errors"); 2561 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value)); 2562 } 2563 return $htmllink; 2564} 2565 2566/** 2567 * Format phone numbers according to country 2568 * 2569 * @param string $phone Phone number to format 2570 * @param string $countrycode Country code to use for formatting 2571 * @param int $cid Id of contact if known 2572 * @param int $socid Id of third party if known 2573 * @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) 2574 * @param string $separ Separation between numbers for a better visibility example : xx.xx.xx.xx.xx 2575 * @param string $withpicto Show picto 2576 * @param string $titlealt Text to show on alt 2577 * @param int $adddivfloat Add div float around phone. 2578 * @return string Formated phone number 2579 */ 2580function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = " ", $withpicto = '', $titlealt = '', $adddivfloat = 0) 2581{ 2582 global $conf, $user, $langs, $mysoc, $hookmanager; 2583 2584 // Clean phone parameter 2585 $phone = preg_replace("/[\s.-]/", "", trim($phone)); 2586 if (empty($phone)) { return ''; } 2587 if (!empty($conf->global->MAIN_PHONE_SEPAR)) $separ = $conf->global->MAIN_PHONE_SEPAR; 2588 if (empty($countrycode)) $countrycode = $mysoc->country_code; 2589 2590 // Short format for small screens 2591 if ($conf->dol_optimize_smallscreen) $separ = ''; 2592 2593 $newphone = $phone; 2594 if (strtoupper($countrycode) == "FR") 2595 { 2596 // France 2597 if (dol_strlen($phone) == 10) { 2598 $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); 2599 } elseif (dol_strlen($phone) == 7) 2600 { 2601 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2); 2602 } elseif (dol_strlen($phone) == 9) 2603 { 2604 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2); 2605 } elseif (dol_strlen($phone) == 11) 2606 { 2607 $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); 2608 } elseif (dol_strlen($phone) == 12) 2609 { 2610 $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); 2611 } 2612 } elseif (strtoupper($countrycode) == "CA") 2613 { 2614 if (dol_strlen($phone) == 10) { 2615 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4); 2616 } 2617 } elseif (strtoupper($countrycode) == "PT") 2618 {//Portugal 2619 if (dol_strlen($phone) == 13) 2620 {//ex: +351_ABC_DEF_GHI 2621 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); 2622 } 2623 } elseif (strtoupper($countrycode) == "SR") 2624 {//Suriname 2625 if (dol_strlen($phone) == 10) 2626 {//ex: +597_ABC_DEF 2627 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3); 2628 } elseif (dol_strlen($phone) == 11) 2629 {//ex: +597_ABC_DEFG 2630 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4); 2631 } 2632 } elseif (strtoupper($countrycode) == "DE") 2633 {//Allemagne 2634 if (dol_strlen($phone) == 14) 2635 {//ex: +49_ABCD_EFGH_IJK 2636 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3); 2637 } elseif (dol_strlen($phone) == 13) 2638 {//ex: +49_ABC_DEFG_HIJ 2639 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3); 2640 } 2641 } elseif (strtoupper($countrycode) == "ES") 2642 {//Espagne 2643 if (dol_strlen($phone) == 12) 2644 {//ex: +34_ABC_DEF_GHI 2645 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); 2646 } 2647 } elseif (strtoupper($countrycode) == "BF") 2648 {// Burkina Faso 2649 if (dol_strlen($phone) == 12) 2650 {//ex : +22 A BC_DE_FG_HI 2651 $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); 2652 } 2653 } elseif (strtoupper($countrycode) == "RO") 2654 {// Roumanie 2655 if (dol_strlen($phone) == 12) 2656 {//ex : +40 AB_CDE_FG_HI 2657 $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); 2658 } 2659 } elseif (strtoupper($countrycode) == "TR") 2660 {//Turquie 2661 if (dol_strlen($phone) == 13) 2662 {//ex : +90 ABC_DEF_GHIJ 2663 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4); 2664 } 2665 } elseif (strtoupper($countrycode) == "US") 2666 {//Etat-Unis 2667 if (dol_strlen($phone) == 12) 2668 {//ex: +1 ABC_DEF_GHIJ 2669 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4); 2670 } 2671 } elseif (strtoupper($countrycode) == "MX") 2672 {//Mexique 2673 if (dol_strlen($phone) == 12) 2674 {//ex: +52 ABCD_EFG_HI 2675 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2); 2676 } elseif (dol_strlen($phone) == 11) 2677 {//ex: +52 AB_CD_EF_GH 2678 $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); 2679 } elseif (dol_strlen($phone) == 13) 2680 {//ex: +52 ABC_DEF_GHIJ 2681 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4); 2682 } 2683 } elseif (strtoupper($countrycode) == "ML") 2684 {//Mali 2685 if (dol_strlen($phone) == 12) 2686 {//ex: +223 AB_CD_EF_GH 2687 $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); 2688 } 2689 } elseif (strtoupper($countrycode) == "TH") 2690 {//Thaïlande 2691 if (dol_strlen($phone) == 11) 2692 {//ex: +66_ABC_DE_FGH 2693 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3); 2694 } elseif (dol_strlen($phone) == 12) 2695 {//ex: +66_A_BCD_EF_GHI 2696 $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); 2697 } 2698 } elseif (strtoupper($countrycode) == "MU") 2699 { 2700 //Maurice 2701 if (dol_strlen($phone) == 11) 2702 {//ex: +230_ABC_DE_FG 2703 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2); 2704 } elseif (dol_strlen($phone) == 12) 2705 {//ex: +230_ABCD_EF_GH 2706 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2); 2707 } 2708 } elseif (strtoupper($countrycode) == "ZA") 2709 {//Afrique du sud 2710 if (dol_strlen($phone) == 12) 2711 {//ex: +27_AB_CDE_FG_HI 2712 $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); 2713 } 2714 } elseif (strtoupper($countrycode) == "SY") 2715 {//Syrie 2716 if (dol_strlen($phone) == 12) 2717 {//ex: +963_AB_CD_EF_GH 2718 $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); 2719 } elseif (dol_strlen($phone) == 13) 2720 {//ex: +963_AB_CD_EF_GHI 2721 $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); 2722 } 2723 } elseif (strtoupper($countrycode) == "AE") 2724 {//Emirats Arabes Unis 2725 if (dol_strlen($phone) == 12) 2726 {//ex: +971_ABC_DEF_GH 2727 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2); 2728 } elseif (dol_strlen($phone) == 13) 2729 {//ex: +971_ABC_DEF_GHI 2730 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); 2731 } elseif (dol_strlen($phone) == 14) 2732 {//ex: +971_ABC_DEF_GHIK 2733 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4); 2734 } 2735 } elseif (strtoupper($countrycode) == "DZ") 2736 {//Algérie 2737 if (dol_strlen($phone) == 13) 2738 {//ex: +213_ABC_DEF_GHI 2739 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); 2740 } 2741 } elseif (strtoupper($countrycode) == "BE") 2742 {//Belgique 2743 if (dol_strlen($phone) == 11) 2744 {//ex: +32_ABC_DE_FGH 2745 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3); 2746 } elseif (dol_strlen($phone) == 12) 2747 {//ex: +32_ABC_DEF_GHI 2748 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); 2749 } 2750 } elseif (strtoupper($countrycode) == "PF") 2751 {//Polynésie française 2752 if (dol_strlen($phone) == 12) 2753 {//ex: +689_AB_CD_EF_GH 2754 $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); 2755 } 2756 } elseif (strtoupper($countrycode) == "CO") 2757 {//Colombie 2758 if (dol_strlen($phone) == 13) 2759 {//ex: +57_ABC_DEF_GH_IJ 2760 $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); 2761 } 2762 } elseif (strtoupper($countrycode) == "JO") 2763 {//Jordanie 2764 if (dol_strlen($phone) == 12) 2765 {//ex: +962_A_BCD_EF_GH 2766 $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); 2767 } 2768 } elseif (strtoupper($countrycode) == "JM") 2769 {//Jamaïque 2770 if (dol_strlen($newphone) == 12) 2771 {//ex: +1867_ABC_DEFG 2772 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4); 2773 } 2774 } elseif (strtoupper($countrycode) == "MG") 2775 {//Madagascar 2776 if (dol_strlen($phone) == 13) 2777 {//ex: +261_AB_CD_EF_GHI 2778 $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); 2779 } 2780 } elseif (strtoupper($countrycode) == "GB") 2781 {//Royaume uni 2782 if (dol_strlen($phone) == 13) 2783 {//ex: +44_ABCD_EFG_HIJ 2784 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); 2785 } 2786 } elseif (strtoupper($countrycode) == "CH") 2787 {//Suisse 2788 if (dol_strlen($phone) == 12) 2789 {//ex: +41_AB_CDE_FG_HI 2790 $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); 2791 } elseif (dol_strlen($phone) == 15) 2792 {// +41_AB_CDE_FGH_IJKL 2793 $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); 2794 } 2795 } elseif (strtoupper($countrycode) == "TN") 2796 {//Tunisie 2797 if (dol_strlen($phone) == 12) 2798 {//ex: +216_AB_CDE_FGH 2799 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); 2800 } 2801 } elseif (strtoupper($countrycode) == "GF") 2802 {//Guyane francaise 2803 if (dol_strlen($phone) == 13) 2804 {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau) 2805 $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); 2806 } 2807 } elseif (strtoupper($countrycode) == "GP") 2808 {//Guadeloupe 2809 if (dol_strlen($phone) == 13) 2810 {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau) 2811 $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); 2812 } 2813 } elseif (strtoupper($countrycode) == "MQ") 2814 {//Martinique 2815 if (dol_strlen($phone) == 13) 2816 {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau) 2817 $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); 2818 } 2819 } elseif (strtoupper($countrycode) == "IT") 2820 {//Italie 2821 if (dol_strlen($phone) == 12) 2822 {//ex: +39_ABC_DEF_GHI 2823 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); 2824 } elseif (dol_strlen($phone) == 13) 2825 {//ex: +39_ABC_DEF_GH_IJ 2826 $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); 2827 } 2828 } elseif (strtoupper($countrycode) == "AU") 2829 { 2830 //Australie 2831 if (dol_strlen($phone) == 12) 2832 { 2833 //ex: +61_A_BCDE_FGHI 2834 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4); 2835 } 2836 } 2837 if (!empty($addlink)) // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set) 2838 { 2839 if ($conf->browser->layout == 'phone' || (!empty($conf->clicktodial->enabled) && !empty($conf->global->CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS))) // If phone or option for, we use link of phone 2840 { 2841 $newphoneform = $newphone; 2842 $newphone = '<a href="tel:'.$phone.'"'; 2843 $newphone .= '>'.$newphoneform.'</a>'; 2844 } elseif (!empty($conf->clicktodial->enabled) && $addlink == 'AC_TEL') // If click to dial, we use click to dial url 2845 { 2846 if (empty($user->clicktodial_loaded)) $user->fetch_clicktodial(); 2847 2848 // Define urlmask 2849 $urlmask = 'ErrorClickToDialModuleNotConfigured'; 2850 if (!empty($conf->global->CLICKTODIAL_URL)) $urlmask = $conf->global->CLICKTODIAL_URL; 2851 if (!empty($user->clicktodial_url)) $urlmask = $user->clicktodial_url; 2852 2853 $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : ''); 2854 $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : ''); 2855 $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : ''); 2856 // This line is for backward compatibility 2857 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password); 2858 // Thoose lines are for substitution 2859 $substitarray = array('__PHONEFROM__'=>$clicktodial_poste, 2860 '__PHONETO__'=>urlencode($phone), 2861 '__LOGIN__'=>$clicktodial_login, 2862 '__PASS__'=>$clicktodial_password); 2863 $url = make_substitutions($url, $substitarray); 2864 $newphonesav = $newphone; 2865 $newphone = '<a href="'.$url.'"'; 2866 if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) $newphone .= ' target="_blank"'; 2867 $newphone .= '>'.$newphonesav.'</a>'; 2868 } 2869 2870 //if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) 2871 if (!empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) 2872 { 2873 $type = 'AC_TEL'; $link = ''; 2874 if ($addlink == 'AC_FAX') $type = 'AC_FAX'; 2875 if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) $link = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&backtopage=1&actioncode='.$type.($cid ? '&contactid='.$cid : '').($socid ? '&socid='.$socid : '').'">'.img_object($langs->trans("AddAction"), "calendar").'</a>'; 2876 if ($link) $newphone = '<div>'.$newphone.' '.$link.'</div>'; 2877 } 2878 } 2879 2880 if (empty($titlealt)) 2881 { 2882 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone")); 2883 } 2884 $rep = ''; 2885 2886 if ($hookmanager) { 2887 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto); 2888 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone); 2889 $rep .= $hookmanager->resPrint; 2890 } 2891 if (empty($reshook)) 2892 { 2893 $picto = ''; 2894 if ($withpicto) { 2895 if ($withpicto == 'fax') { 2896 $picto = 'phoning_fax'; 2897 } elseif ($withpicto == 'phone') { 2898 $picto = 'phoning'; 2899 } elseif ($withpicto == 'mobile') { 2900 $picto = 'phoning_mobile'; 2901 } else { 2902 $picto = ''; 2903 } 2904 } 2905 if ($adddivfloat) $rep .= '<div class="nospan float" style="margin-right: 10px">'; 2906 else $rep .= '<span style="margin-right: 10px;">'; 2907 $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone; 2908 if ($adddivfloat) $rep .= '</div>'; 2909 else $rep .= '</span>'; 2910 } 2911 2912 return $rep; 2913} 2914 2915/** 2916 * Return an IP formated to be shown on screen 2917 * 2918 * @param string $ip IP 2919 * @param int $mode 0=return IP + country/flag, 1=return only country/flag, 2=return only IP 2920 * @return string Formated IP, with country if GeoIP module is enabled 2921 */ 2922function dol_print_ip($ip, $mode = 0) 2923{ 2924 global $conf, $langs; 2925 2926 $ret = ''; 2927 2928 if (empty($mode)) $ret .= $ip; 2929 2930 if ($mode != 2) 2931 { 2932 $countrycode = dolGetCountryCodeFromIp($ip); 2933 if ($countrycode) // If success, countrycode is us, fr, ... 2934 { 2935 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) 2936 { 2937 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1); 2938 } else $ret .= ' ('.$countrycode.')'; 2939 } else { 2940 // Nothing 2941 } 2942 } 2943 2944 return $ret; 2945} 2946 2947/** 2948 * Return the IP of remote user. 2949 * Take HTTP_X_FORWARDED_FOR (defined when using proxy) 2950 * Then HTTP_CLIENT_IP if defined (rare) 2951 * Then REMOTE_ADDR (no way to be modified by user but may be wrong if user is using a proxy) 2952 * 2953 * @return string Ip of remote user. 2954 */ 2955function getUserRemoteIP() 2956{ 2957 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) { 2958 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) { 2959 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) { 2960 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been forged by client 2961 } else { 2962 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client 2963 } 2964 } else { 2965 $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy 2966 } 2967 } else { 2968 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy 2969 } 2970 return $ip; 2971} 2972 2973/** 2974 * Return if we are using a HTTPS connexion 2975 * Check HTTPS (no way to be modified by user but may be empty or wrong if user is using a proxy) 2976 * Take HTTP_X_FORWARDED_PROTO (defined when using proxy) 2977 * Then HTTP_X_FORWARDED_SSL 2978 * 2979 * @return boolean True if user is using HTTPS 2980 */ 2981function isHTTPS() 2982{ 2983 $isSecure = false; 2984 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { 2985 $isSecure = true; 2986 } 2987 elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') { 2988 $isSecure = true; 2989 } 2990 return $isSecure; 2991} 2992 2993/** 2994 * Return a country code from IP. Empty string if not found. 2995 * 2996 * @param string $ip IP 2997 * @return string Country code ('us', 'fr', ...) 2998 */ 2999function dolGetCountryCodeFromIp($ip) 3000{ 3001 global $conf; 3002 3003 $countrycode = ''; 3004 3005 if (!empty($conf->geoipmaxmind->enabled)) 3006 { 3007 $datafile = $conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE; 3008 //$ip='24.24.24.24'; 3009 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages) 3010 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php'; 3011 $geoip = new DolGeoIP('country', $datafile); 3012 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n"; 3013 $countrycode = $geoip->getCountryCodeFromIP($ip); 3014 } 3015 3016 return $countrycode; 3017} 3018 3019 3020/** 3021 * Return country code for current user. 3022 * If software is used inside a local network, detection may fails (we need a public ip) 3023 * 3024 * @return string Country code (fr, es, it, us, ...) 3025 */ 3026function dol_user_country() 3027{ 3028 global $conf, $langs, $user; 3029 3030 //$ret=$user->xxx; 3031 $ret = ''; 3032 if (!empty($conf->geoipmaxmind->enabled)) 3033 { 3034 $ip = getUserRemoteIP(); 3035 $datafile = $conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE; 3036 //$ip='24.24.24.24'; 3037 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat'; 3038 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php'; 3039 $geoip = new DolGeoIP('country', $datafile); 3040 $countrycode = $geoip->getCountryCodeFromIP($ip); 3041 $ret = $countrycode; 3042 } 3043 return $ret; 3044} 3045 3046/** 3047 * Format address string 3048 * 3049 * @param string $address Address string, already formatted with dol_format_address() 3050 * @param int $htmlid Html ID (for example 'gmap') 3051 * @param int $element 'thirdparty'|'contact'|'member'|'other' 3052 * @param int $id Id of object 3053 * @param int $noprint No output. Result is the function return 3054 * @param string $charfornl Char to use instead of nl2br. '' means we use a standad nl2br. 3055 * @return string|void Nothing if noprint is 0, formatted address if noprint is 1 3056 * @see dol_format_address() 3057 */ 3058function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '') 3059{ 3060 global $conf, $user, $langs, $hookmanager; 3061 3062 $out = ''; 3063 3064 if ($address) 3065 { 3066 if ($hookmanager) { 3067 $parameters = array('element' => $element, 'id' => $id); 3068 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address); 3069 $out .= $hookmanager->resPrint; 3070 } 3071 if (empty($reshook)) 3072 { 3073 if (empty($charfornl)) $out .= nl2br($address); 3074 else $out .= preg_replace('/[\r\n]+/', $charfornl, $address); 3075 3076 // TODO Remove this block, we can add this using the hook now 3077 $showgmap = $showomap = 0; 3078 if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) $showgmap = 1; 3079 if ($element == 'contact' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) $showgmap = 1; 3080 if ($element == 'member' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) $showgmap = 1; 3081 if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) $showomap = 1; 3082 if ($element == 'contact' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) $showomap = 1; 3083 if ($element == 'member' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) $showomap = 1; 3084 if ($showgmap) 3085 { 3086 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1); 3087 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>'; 3088 } 3089 if ($showomap) 3090 { 3091 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1); 3092 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>'; 3093 } 3094 } 3095 } 3096 if ($noprint) return $out; 3097 else print $out; 3098} 3099 3100 3101/** 3102 * Return true if email syntax is ok. 3103 * 3104 * @param string $address email (Ex: "toto@examle.com". Long form "John Do <johndo@example.com>" will be false) 3105 * @param int $acceptsupervisorkey If 1, the special string '__SUPERVISOREMAIL__' is also accepted as valid 3106 * @return boolean true if email syntax is OK, false if KO or empty string 3107 * @see isValidMXRecord() 3108 */ 3109function isValidEmail($address, $acceptsupervisorkey = 0) 3110{ 3111 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') return true; 3112 if (filter_var($address, FILTER_VALIDATE_EMAIL)) return true; 3113 3114 return false; 3115} 3116 3117/** 3118 * Return if the domain name has a valid MX record. 3119 * WARNING: This need function idn_to_ascii, checkdnsrr and getmxrr 3120 * 3121 * @param string $domain Domain name (Ex: "yahoo.com", "yhaoo.com", "dolibarr.fr") 3122 * @return int -1 if error (function not available), 0=Not valid, 1=Valid 3123 * @see isValidEmail() 3124 */ 3125function isValidMXRecord($domain) 3126{ 3127 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) 3128 { 3129 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) 3130 { 3131 return 0; 3132 } 3133 if (function_exists('getmxrr')) 3134 { 3135 $mxhosts = array(); 3136 $weight = array(); 3137 getmxrr(idn_to_ascii($domain), $mxhosts, $weight); 3138 if (count($mxhosts) > 1) return 1; 3139 if (count($mxhosts) == 1 && !empty($mxhosts[0])) return 1; 3140 3141 return 0; 3142 } 3143 } 3144 return -1; 3145} 3146 3147/** 3148 * Return true if phone number syntax is ok 3149 * TODO Decide what to do with this 3150 * 3151 * @param string $phone phone (Ex: "0601010101") 3152 * @return boolean true if phone syntax is OK, false if KO or empty string 3153 */ 3154function isValidPhone($phone) 3155{ 3156 return true; 3157} 3158 3159 3160/** 3161 * Make a strlen call. Works even if mbstring module not enabled 3162 * 3163 * @param string $string String to calculate length 3164 * @param string $stringencoding Encoding of string 3165 * @return int Length of string 3166 */ 3167function dol_strlen($string, $stringencoding = 'UTF-8') 3168{ 3169 if (function_exists('mb_strlen')) return mb_strlen($string, $stringencoding); 3170 else return strlen($string); 3171} 3172 3173/** 3174 * Make a substring. Works even if mbstring module is not enabled for better compatibility. 3175 * 3176 * @param string $string String to scan 3177 * @param string $start Start position 3178 * @param int $length Length (in nb of characters or nb of bytes depending on trunconbytes param) 3179 * @param string $stringencoding Page code used for input string encoding 3180 * @param int $trunconbytes 1=Length is max of bytes instead of max of characters 3181 * @return string substring 3182 */ 3183function dol_substr($string, $start, $length, $stringencoding = '', $trunconbytes = 0) 3184{ 3185 global $langs; 3186 3187 if (empty($stringencoding)) $stringencoding = $langs->charset_output; 3188 3189 $ret = ''; 3190 if (empty($trunconbytes)) 3191 { 3192 if (function_exists('mb_substr')) 3193 { 3194 $ret = mb_substr($string, $start, $length, $stringencoding); 3195 } else { 3196 $ret = substr($string, $start, $length); 3197 } 3198 } else { 3199 if (function_exists('mb_strcut')) 3200 { 3201 $ret = mb_strcut($string, $start, $length, $stringencoding); 3202 } else { 3203 $ret = substr($string, $start, $length); 3204 } 3205 } 3206 return $ret; 3207} 3208 3209 3210/** 3211 * Truncate a string to a particular length adding '...' if string larger than length. 3212 * If length = max length+1, we do no truncate to avoid having just 1 char replaced with '...'. 3213 * MAIN_DISABLE_TRUNC=1 can disable all truncings 3214 * 3215 * @param string $string String to truncate 3216 * @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 ...) 3217 * @param string $trunc Where to trunc: 'right', 'left', 'middle' (size must be a 2 power), 'wrap' 3218 * @param string $stringencoding Tell what is source string encoding 3219 * @param int $nodot Truncation do not add ... after truncation. So it's an exact truncation. 3220 * @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) 3221 * @return string Truncated string. WARNING: length is never higher than $size if $nodot is set, but can be 3 chars higher otherwise. 3222 */ 3223function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0) 3224{ 3225 global $conf; 3226 3227 if ($size == 0 || !empty($conf->global->MAIN_DISABLE_TRUNC)) return $string; 3228 3229 if (empty($stringencoding)) $stringencoding = 'UTF-8'; 3230 // reduce for small screen 3231 if ($conf->dol_optimize_smallscreen == 1 && $display == 1) $size = round($size / 3); 3232 3233 // We go always here 3234 if ($trunc == 'right') 3235 { 3236 $newstring = dol_textishtml($string) ?dol_string_nohtmltag($string, 1) : $string; 3237 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 3))) // If nodot is 0 and size is 1,2 or 3 chars more, we don't trunc and don't add ... 3238 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '...'); 3239 else //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string; 3240 return $string; 3241 } elseif ($trunc == 'middle') 3242 { 3243 $newstring = dol_textishtml($string) ?dol_string_nohtmltag($string, 1) : $string; 3244 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) 3245 { 3246 $size1 = round($size / 2); 3247 $size2 = round($size / 2); 3248 return dol_substr($newstring, 0, $size1, $stringencoding).'...'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding); 3249 } else return $string; 3250 } elseif ($trunc == 'left') 3251 { 3252 $newstring = dol_textishtml($string) ?dol_string_nohtmltag($string, 1) : $string; 3253 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 3))) // If nodot is 0 and size is 1,2 or 3 chars more, we don't trunc and don't add ... 3254 return '...'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding); 3255 else return $string; 3256 } elseif ($trunc == 'wrap') 3257 { 3258 $newstring = dol_textishtml($string) ?dol_string_nohtmltag($string, 1) : $string; 3259 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) 3260 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc); 3261 else return $string; 3262 } else return 'BadParam3CallingDolTrunc'; 3263} 3264 3265/** 3266 * Show picto whatever it's its name (generic function) 3267 * 3268 * @param string $titlealt Text on title tag for tooltip. Not used if param notitle is set to 1. 3269 * @param string $picto Name of image file to show ('filenew', ...) 3270 * If no extension provided, we use '.png'. Image must be stored into theme/xxx/img directory. 3271 * Example: picto.png if picto.png is stored into htdocs/theme/mytheme/img 3272 * Example: picto.png@mymodule if picto.png is stored into htdocs/mymodule/img 3273 * Example: /mydir/mysubdir/picto.png if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1) 3274 * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"') 3275 * @param boolean|int $pictoisfullpath If true or 1, image path is a full path 3276 * @param int $srconly Return only content of the src attribute of img. 3277 * @param int $notitle 1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip. 3278 * @param string $alt Force alt for bind people 3279 * @param string $morecss Add more class css on img tag (For example 'myclascss'). 3280 * @param string $marginleftonlyshort 1 = Add a short left margin on picto, 2 = Add a larger left margin on picto, 0 = No margin left. Works for fontawesome picto only. 3281 * @return string Return img tag 3282 * @see img_object(), img_picto_common() 3283 */ 3284function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2) 3285{ 3286 global $conf, $langs; 3287 3288 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto 3289 $url = DOL_URL_ROOT; 3290 $theme = isset($conf->theme) ? $conf->theme : null; 3291 $path = 'theme/'.$theme; 3292 // Define fullpathpicto to use into src 3293 if ($pictoisfullpath) { 3294 // Clean parameters 3295 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) { 3296 $picto .= '.png'; 3297 } 3298 $fullpathpicto = $picto; 3299 $reg = array(); 3300 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) { 3301 $morecss .= ($morecss ? ' ' : '').$reg[1]; 3302 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt); 3303 } 3304 } else { 3305 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto); 3306 if (empty($srconly) && in_array($pictowithouttext, array( 3307 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected', 3308 'accountancy', 'account', 'accountline', 'action', 'add', 'address', 'bank_account', 'barcode', 'bank', 'bill', 'billa', 'billr', 'billd', 'bookmark', 'bom', 'building', 3309 'cash-register', 'category', 'check', 'clock', 'close_title', 'company', 'contact', 'contract', 'cron', 'cubes', 3310 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'edit', 'ellipsis-h', 'email', 'eraser', 'external-link-alt', 'external-link-square-alt', 3311 'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'folder', 'folder-open', 'globe', 'globe-americas', 'grip', 'grip_title', 'group', 3312 'help', 'holiday', 3313 'intervention', 'label', 'language', 'link', 'list', 'listlight', 'lot', 3314 'map-marker-alt', 'member', 'money-bill-alt', 'mrp', 'note', 'next', 3315 'object_accounting', 'object_account', 'object_accountline', 'object_action', 'object_barcode', 'object_bill', 'object_billa', 'object_billr', 'object_billd', 'object_bom', 3316 'object_category', 'object_conversation', 'object_bookmark', 'object_bug', 'object_clock', 'object_dolly', 'object_dollyrevert', 'object_generic', 'object_folder', 3317 'object_list-alt', 'object_calendar', 'object_calendarweek', 'object_calendarmonth', 'object_calendarday', 'object_calendarperuser', 3318 'object_cash-register', 'object_company', 'object_contact', 'object_contract', 'object_donation', 'object_dynamicprice', 3319 'object_globe', 'object_holiday', 'object_hrm', 'object_invoice', 'object_intervention', 'object_label', 3320 'object_margin', 'object_money-bill-alt', 'object_multicurrency', 'object_order', 'object_payment', 3321 'object_lot', 'object_mrp', 'object_other', 3322 'object_payment', 'object_pdf', 'object_product', 'object_propal', 3323 'object_paragraph', 'object_poll', 'object_printer', 'object_project', 'object_projectpub', 'object_propal', 'object_resource', 'object_rss', 'object_projecttask', 3324 'object_recruitmentjobposition', 'object_recruitmentcandidature', 3325 'object_shipment', 'object_share-alt', 'object_supplier_invoice', 'object_supplier_invoicea', 'object_supplier_invoiced', 'object_supplier_order', 'object_supplier_proposal', 'object_service', 'object_stock', 3326 'object_technic', 'object_ticket', 'object_trip', 'object_user', 'object_group', 'object_member', 3327 'object_phoning', 'object_phoning_mobile', 'object_phoning_fax', 'object_email', 'object_website', 'object_movement', 3328 'off', 'on', 'order', 3329 'paiment', 'play', 'pdf', 'playdisabled', 'previous', 'poll', 'printer', 'product', 'propal', 'projecttask', 'stock', 'resize', 'service', 'stats', 'trip', 3330 'setup', 'share-alt', 'sign-out', 'split', 'stripe-s', 'switch_off', 'switch_on', 'tools', 'unlink', 'uparrow', 'user', 'vcard', 'wrench', 3331 'jabber', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp', 3332 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies', 3333 'generic', 'home', 'hrm', 'members', 'products', 'invoicing', 3334 'payment', 'pencil-ruler', 'preview', 'project', 'projectpub', 'refresh', 'supplier_invoice', 'ticket', 3335 'error', 'warning', 3336 'recruitmentcandidature', 'recruitmentjobposition', 'resource', 3337 'supplier_proposal', 'supplier_order', 'supplier_invoice', 3338 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda' 3339 ) 3340 )) { 3341 $pictowithouttext = str_replace('object_', '', $pictowithouttext); 3342 3343 $fakey = $pictowithouttext; 3344 $facolor = ''; $fasize = ''; 3345 $fa = 'fas'; 3346 if (in_array($pictowithouttext, array('clock', 'generic', 'minus-square', 'object_generic', 'pdf', 'plus-square', 'note', 'off', 'on', 'object_bookmark', 'bookmark', 'vcard'))) { 3347 $fa = 'far'; 3348 } 3349 if (in_array($pictowithouttext, array('black-tie', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) { 3350 $fa = 'fab'; 3351 } 3352 3353 $arrayconvpictotofa = array( 3354 'account'=>'university', 'accountline'=>'receipt', 'accountancy'=>'money-check-alt', 'action'=>'calendar-alt', 'add'=>'plus-circle', 'address'=> 'address-book', 3355 'bank_account'=>'university', 'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'billd'=>'file-medical', 'supplier_invoiced'=>'file-medical', 'bom'=>'cubes', 3356 'company'=>'building', 'contact'=>'address-book', 'contract'=>'suitcase', 'conversation'=>'comments', 'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd', 3357 'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins', 3358 'accounting'=>'chart-line', 'category'=>'tag', 'dollyrevert'=>'dolly', 3359 'hrm'=>'user-tie', 'margin'=>'calculator', 'members'=>'users', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode', 3360 'email'=>'at', 3361 'edit'=>'pencil-alt', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle', 3362 'generic'=>'file', 'holiday'=>'umbrella-beach', 'label'=>'layer-group', 3363 'member'=>'users', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right', 3364 'trip'=>'wallet', 'group'=>'users', 'movement'=>'people-carry', 3365 'sign-out'=>'sign-out-alt', 3366 'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star', 'bookmark'=>'star', 3367 'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'edit'=>'pencil-alt', 'filter'=>'filter', 3368 'list-alt'=>'list-alt', 'calendar'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarmonth'=>'calendar-alt', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table', 3369 'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice', 3370 'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle', 3371 'other'=>'square', 3372 'playdisabled'=>'play', 'pdf'=>'file-pdf', 'poll'=>'check-double', 'preview'=>'binoculars', 'project'=>'sitemap', 'projectpub'=>'sitemap', 'projecttask'=>'tasks', 'propal'=>'file-signature', 3373 'payment'=>'money-check-alt', 'phoning'=>'phone', 'phoning_mobile'=>'mobile-alt', 'phoning_fax'=>'fax', 'previous'=>'arrow-alt-circle-left', 'printer'=>'print', 'product'=>'cube', 'service'=>'concierge-bell', 3374 'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge', 3375 'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature', 3376 'refresh'=>'redo', 'resource'=>'laptop-house', 3377 'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'supplier_invoice'=>'file-invoice-dollar', 'technic'=>'cogs', 'ticket'=>'ticket-alt', 3378 'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach', 3379 'title_agenda'=>'calendar-alt', 3380 'uparrow'=>'share', 'vcard'=>'address-card', 3381 'jabber'=>'comment-o', 3382 'website'=>'globe-americas' 3383 ); 3384 if ($pictowithouttext == 'off') { 3385 $fakey = 'fa-square'; 3386 $fasize = '1.3em'; 3387 } elseif ($pictowithouttext == 'on') { 3388 $fakey = 'fa-check-square'; 3389 $fasize = '1.3em'; 3390 } elseif ($pictowithouttext == 'listlight') { 3391 $fakey = 'fa-download'; 3392 $marginleftonlyshort = 1; 3393 } elseif ($pictowithouttext == 'printer') { 3394 $fakey = 'fa-print'; 3395 $fasize = '1.2em'; 3396 } elseif ($pictowithouttext == 'note') { 3397 $fakey = 'fa-sticky-note'; 3398 $marginleftonlyshort = 1; 3399 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) { 3400 $convertarray = array('1uparrow'=>'caret-up', '1downarrow'=>'caret-down', '1leftarrow'=>'caret-left', '1rightarrow'=>'caret-right', '1uparrow_selected'=>'caret-up', '1downarrow_selected'=>'caret-down', '1leftarrow_selected'=>'caret-left', '1rightarrow_selected'=>'caret-right'); 3401 $fakey = 'fa-'.$convertarray[$pictowithouttext]; 3402 if (preg_match('/selected/', $pictowithouttext)) $facolor = '#888'; 3403 $marginleftonlyshort = 1; 3404 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) { 3405 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext]; 3406 } else { 3407 $fakey = 'fa-'.$pictowithouttext; 3408 } 3409 3410 // Define $marginleftonlyshort 3411 $arrayconvpictotomarginleftonly = array( 3412 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber', 3413 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_off', 3414 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected' 3415 ); 3416 if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) { 3417 $marginleftonlyshort = 0; 3418 } 3419 3420 // Add CSS 3421 $arrayconvpictotomorcess = array( 3422 'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account', 3423 'bank_account'=>'bg-infobox-bank_account', 3424 'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billr'=>'infobox-commande', 'billd'=>'infobox-commande', 3425 'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'conversation'=>'infobox-contrat', 3426 'donation'=>'infobox-commande', 'dollyrevert'=>'flip', 'ecm'=>'infobox-action', 3427 'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat', 3428 'multicurrency'=>'infobox-bank_account', 3429 'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account', 3430 'order'=>'infobox-commande', 3431 'user'=>'infobox-adherent', 'users'=>'infobox-adherent', 3432 'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 3433 'holiday'=>'infobox-holiday', 'invoice'=>'infobox-commande', 3434 'payment'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'project'=>'infobox-project', 'projecttask'=>'infobox-project', 'propal'=>'infobox-propal', 3435 'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent', 3436 'resource'=>'infobox-action', 3437 'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier', 3438 'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal', 3439 'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action', 3440 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action', 3441 'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode' 3442 ); 3443 if (!empty($arrayconvpictotomorcess[$pictowithouttext])) { 3444 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext]; 3445 } 3446 3447 // Define $color 3448 $arrayconvpictotocolor = array( 3449 'address'=>'#6c6aa8', 'building'=>'#6c6aa8', 'bom'=>'#a69944', 3450 'companies'=>'#6c6aa8', 'company'=>'#6c6aa8', 'contact'=>'#6c6aa8', 'dynamicprice'=>'#a69944', 3451 'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999', 3452 'dolly'=>'#a69944', 'dollyrevert'=>'#a69944', 'lot'=>'#a69944', 3453 'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'stock'=>'#a69944', 'movement'=>'#a69944', 3454 'other'=>'#ddd', 3455 'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'resize'=>'#444', 'rss'=>'#cba', 3456 'shipment'=>'#a69944', 'stats'=>'#444', 'switch_off'=>'#999', 'uparrow'=>'#555', 'globe-americas'=>'#aaa', 3457 'website'=>'#304' 3458 ); 3459 if (isset($arrayconvpictotocolor[$pictowithouttext])) { 3460 $facolor = $arrayconvpictotocolor[$pictowithouttext]; 3461 } 3462 3463 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only. 3464 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes. 3465 $morestyle = ''; 3466 $reg = array(); 3467 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) { 3468 $morecss .= ($morecss ? ' ' : '').$reg[1]; 3469 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt); 3470 } 3471 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) { 3472 $morestyle = $reg[1]; 3473 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt); 3474 } 3475 $moreatt = trim($moreatt); 3476 3477 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : ''); 3478 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>'; 3479 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { 3480 $enabledisablehtml .= $titlealt; 3481 }*/ 3482 $enabledisablehtml .= '</span>'; 3483 3484 return $enabledisablehtml; 3485 } 3486 3487 if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) { 3488 $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module 3489 } elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) { 3490 $path = $conf->global->MAIN_OVERWRITE_THEME_RES.'/theme/'.$conf->global->MAIN_OVERWRITE_THEME_RES; // To allow an external module to overwrite image resources whatever is activated theme 3491 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) { 3492 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module 3493 } 3494 3495 // If we ask an image into $url/$mymodule/img (instead of default path) 3496 $regs = array(); 3497 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) { 3498 $picto = $regs[1]; 3499 $path = $regs[2]; // $path is $mymodule 3500 } 3501 3502 // Clean parameters 3503 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) { 3504 $picto .= '.png'; 3505 } 3506 // If alt path are defined, define url where img file is, according to physical path 3507 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...) 3508 foreach ($conf->file->dol_document_root as $type => $dirroot) { 3509 if ($type == 'main') { 3510 continue; 3511 } 3512 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded 3513 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) { 3514 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type]; 3515 break; 3516 } 3517 } 3518 3519 // $url is '' or '/custom', $path is current theme or 3520 $fullpathpicto = $url.'/'.$path.'/img/'.$picto; 3521 } 3522 3523 if ($srconly) { 3524 return $fullpathpicto; 3525 } 3526 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people 3527 return '<img src="'.$fullpathpicto.'" alt="'.dol_escape_htmltag($alt).'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt.($morecss ? ' class="'.$morecss.'"' : '') : ' class="inline-block'.($morecss ? ' '.$morecss : '').'"').'>'; // Alt is used for accessibility, title for popup 3528} 3529 3530/** 3531 * Show a picto called object_picto (generic function) 3532 * 3533 * @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. 3534 * @param string $picto Name of image to show object_picto (example: user, group, action, bill, contract, propal, product, ...) 3535 * For external modules use imagename@mymodule to search into directory "img" of module. 3536 * @param string $moreatt Add more attribute on img tag (ie: class="datecallink") 3537 * @param int $pictoisfullpath If 1, image path is a full path 3538 * @param int $srconly Return only content of the src attribute of img. 3539 * @param int $notitle 1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip. 3540 * @return string Return img tag 3541 * @see img_picto(), img_picto_common() 3542 */ 3543function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0) 3544{ 3545 if (strpos($picto, '^') === 0) return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle); 3546 else return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle); 3547} 3548 3549/** 3550 * Show weather picto 3551 * 3552 * @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. 3553 * @param string|int $picto Name of image file to show (If no extension provided, we use '.png'). Image must be stored into htdocs/theme/common directory. Or level of meteo image (0-4). 3554 * @param string $moreatt Add more attribute on img tag 3555 * @param int $pictoisfullpath If 1, image path is a full path 3556 * @param string $morecss More CSS 3557 * @return string Return img tag 3558 * @see img_object(), img_picto() 3559 */ 3560function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '') 3561{ 3562 global $conf; 3563 3564 if (is_numeric($picto)) { 3565 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png'); 3566 //$picto = $leveltopicto[$picto]; 3567 return '<i class="fa fa-weather-level'.$picto.'"></i>'; 3568 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) { 3569 $picto .= '.png'; 3570 } 3571 3572 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto; 3573 3574 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss); 3575} 3576 3577/** 3578 * Show picto (generic function) 3579 * 3580 * @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. 3581 * @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. 3582 * @param string $moreatt Add more attribute on img tag 3583 * @param int $pictoisfullpath If 1, image path is a full path 3584 * @return string Return img tag 3585 * @see img_object(), img_picto() 3586 */ 3587function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0) 3588{ 3589 global $conf; 3590 3591 if (!preg_match('/(\.png|\.gif)$/i', $picto)) $picto .= '.png'; 3592 3593 if ($pictoisfullpath) $path = $picto; 3594 else { 3595 $path = DOL_URL_ROOT.'/theme/common/'.$picto; 3596 3597 if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) 3598 { 3599 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto; 3600 3601 if (file_exists($themepath)) $path = $themepath; 3602 } 3603 } 3604 3605 return img_picto($titlealt, $path, $moreatt, 1); 3606} 3607 3608/** 3609 * Show logo action 3610 * 3611 * @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. 3612 * @param string $numaction Action id or code to show 3613 * @param string $picto Name of image file to show ('filenew', ...) 3614 * If no extension provided, we use '.png'. Image must be stored into theme/xxx/img directory. 3615 * Example: picto.png if picto.png is stored into htdocs/theme/mytheme/img 3616 * Example: picto.png@mymodule if picto.png is stored into htdocs/mymodule/img 3617 * Example: /mydir/mysubdir/picto.png if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1) 3618 * @return string Return an img tag 3619 */ 3620function img_action($titlealt, $numaction, $picto = '') 3621{ 3622 global $langs; 3623 3624 if (empty($titlealt) || $titlealt == 'default') 3625 { 3626 if ($numaction == '-1' || $numaction == 'ST_NO') { 3627 $numaction = -1; 3628 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact'); 3629 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') { 3630 $numaction = 0; 3631 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted'); 3632 } elseif ($numaction == '1' || $numaction == 'ST_TODO') { 3633 $numaction = 1; 3634 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact'); 3635 } elseif ($numaction == '2' || $numaction == 'ST_PEND') { 3636 $numaction = 2; 3637 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess'); 3638 } elseif ($numaction == '3' || $numaction == 'ST_DONE') { 3639 $numaction = 3; 3640 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone'); 3641 } else { 3642 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction); 3643 $numaction = 0; 3644 } 3645 } 3646 if (!is_numeric($numaction)) $numaction = 0; 3647 3648 return img_picto($titlealt, !empty($picto) ? $picto : 'stcomm'.$numaction.'.png'); 3649} 3650 3651/** 3652 * Show pdf logo 3653 * 3654 * @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. 3655 * @param int $size Taille de l'icone : 3 = 16x16px , 2 = 14x14px 3656 * @return string Retourne tag img 3657 */ 3658function img_pdf($titlealt = 'default', $size = 3) 3659{ 3660 global $langs; 3661 3662 if ($titlealt == 'default') $titlealt = $langs->trans('Show'); 3663 3664 return img_picto($titlealt, 'pdf'.$size.'.png'); 3665} 3666 3667/** 3668 * Show logo + 3669 * 3670 * @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. 3671 * @param string $other Add more attributes on img 3672 * @return string Return tag img 3673 */ 3674function img_edit_add($titlealt = 'default', $other = '') 3675{ 3676 global $langs; 3677 3678 if ($titlealt == 'default') $titlealt = $langs->trans('Add'); 3679 3680 return img_picto($titlealt, 'edit_add.png', $other); 3681} 3682/** 3683 * Show logo - 3684 * 3685 * @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. 3686 * @param string $other Add more attributes on img 3687 * @return string Return tag img 3688 */ 3689function img_edit_remove($titlealt = 'default', $other = '') 3690{ 3691 global $langs; 3692 3693 if ($titlealt == 'default') $titlealt = $langs->trans('Remove'); 3694 3695 return img_picto($titlealt, 'edit_remove.png', $other); 3696} 3697 3698/** 3699 * Show logo editer/modifier fiche 3700 * 3701 * @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. 3702 * @param integer $float If you have to put the style "float: right" 3703 * @param string $other Add more attributes on img 3704 * @return string Return tag img 3705 */ 3706function img_edit($titlealt = 'default', $float = 0, $other = '') 3707{ 3708 global $langs; 3709 3710 if ($titlealt == 'default') $titlealt = $langs->trans('Modify'); 3711 3712 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : '')); 3713} 3714 3715/** 3716 * Show logo view card 3717 * 3718 * @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. 3719 * @param integer $float If you have to put the style "float: right" 3720 * @param string $other Add more attributes on img 3721 * @return string Return tag img 3722 */ 3723function img_view($titlealt = 'default', $float = 0, $other = '') 3724{ 3725 global $langs; 3726 3727 if ($titlealt == 'default') $titlealt = $langs->trans('View'); 3728 3729 $moreatt = ($float ? 'style="float: right" ' : '').$other; 3730 3731 return img_picto($titlealt, 'view.png', $moreatt); 3732} 3733 3734/** 3735 * Show delete logo 3736 * 3737 * @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. 3738 * @param string $other Add more attributes on img 3739 * @param string $morecss More CSS 3740 * @return string Retourne tag img 3741 */ 3742function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '') 3743{ 3744 global $langs; 3745 3746 if ($titlealt == 'default') $titlealt = $langs->trans('Delete'); 3747 3748 return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss); 3749} 3750 3751/** 3752 * Show printer logo 3753 * 3754 * @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. 3755 * @param string $other Add more attributes on img 3756 * @return string Retourne tag img 3757 */ 3758function img_printer($titlealt = "default", $other = '') 3759{ 3760 global $langs; 3761 if ($titlealt == "default") $titlealt = $langs->trans("Print"); 3762 return img_picto($titlealt, 'printer.png', $other); 3763} 3764 3765/** 3766 * Show split logo 3767 * 3768 * @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. 3769 * @param string $other Add more attributes on img 3770 * @return string Retourne tag img 3771 */ 3772function img_split($titlealt = 'default', $other = 'class="pictosplit"') 3773{ 3774 global $langs; 3775 3776 if ($titlealt == 'default') $titlealt = $langs->trans('Split'); 3777 3778 return img_picto($titlealt, 'split.png', $other); 3779} 3780 3781/** 3782 * Show help logo with cursor "?" 3783 * 3784 * @param int $usehelpcursor 1=Use help cursor, 2=Use click pointer cursor, 0=No specific cursor 3785 * @param int|string $usealttitle Text to use as alt title 3786 * @return string Return tag img 3787 */ 3788function img_help($usehelpcursor = 1, $usealttitle = 1) 3789{ 3790 global $langs; 3791 3792 if ($usealttitle) 3793 { 3794 if (is_string($usealttitle)) $usealttitle = dol_escape_htmltag($usealttitle); 3795 else $usealttitle = $langs->trans('Info'); 3796 } 3797 3798 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"'); 3799} 3800 3801/** 3802 * Show info logo 3803 * 3804 * @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. 3805 * @return string Return img tag 3806 */ 3807function img_info($titlealt = 'default') 3808{ 3809 global $langs; 3810 3811 if ($titlealt == 'default') $titlealt = $langs->trans('Informations'); 3812 3813 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"'); 3814} 3815 3816/** 3817 * Show warning logo 3818 * 3819 * @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. 3820 * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"'). If 1, add float: right. Can't be "class" attribute. 3821 * @param string $morecss Add more CSS 3822 * @return string Return img tag 3823 */ 3824function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning') 3825{ 3826 global $langs; 3827 3828 if ($titlealt == 'default') $titlealt = $langs->trans('Warning'); 3829 3830 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>'; 3831 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : '')); 3832} 3833 3834/** 3835 * Show error logo 3836 * 3837 * @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. 3838 * @return string Return img tag 3839 */ 3840function img_error($titlealt = 'default') 3841{ 3842 global $langs; 3843 3844 if ($titlealt == 'default') $titlealt = $langs->trans('Error'); 3845 3846 return img_picto($titlealt, 'error.png'); 3847} 3848 3849/** 3850 * Show next logo 3851 * 3852 * @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. 3853* @param string $moreatt Add more attribute on img tag (For example 'style="float: right"') 3854 * @return string Return img tag 3855 */ 3856function img_next($titlealt = 'default', $moreatt = '') 3857{ 3858 global $langs; 3859 3860 if ($titlealt == 'default') $titlealt = $langs->trans('Next'); 3861 3862 //return img_picto($titlealt, 'next.png', $moreatt); 3863 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>'; 3864} 3865 3866/** 3867 * Show previous logo 3868 * 3869 * @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. 3870 * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"') 3871 * @return string Return img tag 3872 */ 3873function img_previous($titlealt = 'default', $moreatt = '') 3874{ 3875 global $langs; 3876 3877 if ($titlealt == 'default') $titlealt = $langs->trans('Previous'); 3878 3879 //return img_picto($titlealt, 'previous.png', $moreatt); 3880 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>'; 3881} 3882 3883/** 3884 * Show down arrow logo 3885 * 3886 * @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. 3887 * @param int $selected Selected 3888 * @param string $moreclass Add more CSS classes 3889 * @return string Return img tag 3890 */ 3891function img_down($titlealt = 'default', $selected = 0, $moreclass = '') 3892{ 3893 global $langs; 3894 3895 if ($titlealt == 'default') $titlealt = $langs->trans('Down'); 3896 3897 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"'); 3898} 3899 3900/** 3901 * Show top arrow logo 3902 * 3903 * @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. 3904 * @param int $selected Selected 3905 * @param string $moreclass Add more CSS classes 3906 * @return string Return img tag 3907 */ 3908function img_up($titlealt = 'default', $selected = 0, $moreclass = '') 3909{ 3910 global $langs; 3911 3912 if ($titlealt == 'default') $titlealt = $langs->trans('Up'); 3913 3914 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"'); 3915} 3916 3917/** 3918 * Show left arrow logo 3919 * 3920 * @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. 3921 * @param int $selected Selected 3922 * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"') 3923 * @return string Return img tag 3924 */ 3925function img_left($titlealt = 'default', $selected = 0, $moreatt = '') 3926{ 3927 global $langs; 3928 3929 if ($titlealt == 'default') { 3930 $titlealt = $langs->trans('Left'); 3931 } 3932 3933 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt); 3934} 3935 3936/** 3937 * Show right arrow logo 3938 * 3939 * @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. 3940 * @param int $selected Selected 3941 * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"') 3942 * @return string Return img tag 3943 */ 3944function img_right($titlealt = 'default', $selected = 0, $moreatt = '') 3945{ 3946 global $langs; 3947 3948 if ($titlealt == 'default') $titlealt = $langs->trans('Right'); 3949 3950 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt); 3951} 3952 3953/** 3954 * Show tick logo if allowed 3955 * 3956 * @param string $allow Allow 3957 * @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. 3958 * @return string Return img tag 3959 */ 3960function img_allow($allow, $titlealt = 'default') 3961{ 3962 global $langs; 3963 3964 if ($titlealt == 'default') $titlealt = $langs->trans('Active'); 3965 3966 if ($allow == 1) return img_picto($titlealt, 'tick.png'); 3967 3968 return '-'; 3969} 3970 3971/** 3972 * Return image of a credit card according to its brand name 3973 * 3974 * @param string $brand Brand name of credit card 3975 * @param string $morecss More CSS 3976 * @return string Return img tag 3977 */ 3978function img_credit_card($brand, $morecss = null) 3979{ 3980 if (is_null($morecss)) $morecss = 'fa-2x'; 3981 3982 if ($brand == 'visa' || $brand == 'Visa') { 3983 $brand = 'cc-visa'; 3984 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') { 3985 $brand = 'cc-mastercard'; 3986 } elseif ($brand == 'amex' || $brand == 'American Express') { 3987 $brand = 'cc-amex'; 3988 } elseif ($brand == 'discover' || $brand == 'Discover') { 3989 $brand = 'cc-discover'; 3990 } elseif ($brand == 'jcb' || $brand == 'JCB') { 3991 $brand = 'cc-jcb'; 3992 } elseif ($brand == 'diners' || $brand == 'Diners club') { 3993 $brand = 'cc-diners-club'; 3994 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) { 3995 $brand = 'credit-card'; 3996 } 3997 3998 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>'; 3999} 4000 4001/** 4002 * Show MIME img of a file 4003 * 4004 * @param string $file Filename 4005 * @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. 4006 * @param string $morecss More css 4007 * @return string Return img tag 4008 */ 4009function img_mime($file, $titlealt = '', $morecss = '') 4010{ 4011 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 4012 4013 $mimetype = dol_mimetype($file, '', 1); 4014 $mimeimg = dol_mimetype($file, '', 2); 4015 $mimefa = dol_mimetype($file, '', 4); 4016 4017 if (empty($titlealt)) $titlealt = 'Mime type: '.$mimetype; 4018 4019 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"'); 4020 return '<i class="fa fa-'.$mimefa.' paddingright"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>'; 4021} 4022 4023 4024/** 4025 * Show search logo 4026 * 4027 * @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. 4028 * @param string $other Add more attributes on img 4029 * @return string Retourne tag img 4030 */ 4031function img_search($titlealt = 'default', $other = '') 4032{ 4033 global $conf, $langs; 4034 4035 if ($titlealt == 'default') $titlealt = $langs->trans('Search'); 4036 4037 $img = img_picto($titlealt, 'search.png', $other, false, 1); 4038 4039 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" '; 4040 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >'; 4041 4042 return $input; 4043} 4044 4045/** 4046 * Show search logo 4047 * 4048 * @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. 4049 * @param string $other Add more attributes on img 4050 * @return string Retourne tag img 4051 */ 4052function img_searchclear($titlealt = 'default', $other = '') 4053{ 4054 global $conf, $langs; 4055 4056 if ($titlealt == 'default') $titlealt = $langs->trans('Search'); 4057 4058 $img = img_picto($titlealt, 'searchclear.png', $other, false, 1); 4059 4060 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" '; 4061 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >'; 4062 4063 return $input; 4064} 4065 4066/** 4067 * Show information for admin users or standard users 4068 * 4069 * @param string $text Text info 4070 * @param integer $infoonimgalt Info is shown only on alt of star picto, otherwise it is show on output after the star picto 4071 * @param int $nodiv No div 4072 * @param string $admin '1'=Info for admin users. '0'=Info for standard users (change only the look), 'error', 'warning', 'xxx'=Other 4073 * @param string $morecss More CSS ('', 'warning', 'error') 4074 * @param string $textfordropdown Show a text to click to dropdown the info box. 4075 * @return string String with info text 4076 */ 4077function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = '', $textfordropdown = '') 4078{ 4079 global $conf, $langs; 4080 4081 if ($infoonimgalt) 4082 { 4083 $result = img_picto($text, 'info', 'class="hideonsmartphone'.($morecss ? ' '.$morecss : '').'"'); 4084 } else { 4085 if (empty($conf->use_javascript_ajax)) $textfordropdown = ''; 4086 4087 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin)); 4088 $result = ($nodiv ? '' : '<div class="'.$class.' hideonsmartphone'.($morecss ? ' '.$morecss : '').($textfordropdown ? ' hidden' : '').'">').'<span class="fa fa-info-circle" title="'.dol_escape_htmltag($admin ? $langs->trans('InfoAdmin') : $langs->trans('Note')).'"></span> '.$text.($nodiv ? '' : '</div>'); 4089 4090 if ($textfordropdown) { 4091 $tmpresult .= '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>'; 4092 $tmpresult .= '<script type="text/javascript" language="javascript"> 4093 jQuery(document).ready(function() { 4094 jQuery(".'.$class.'text").click(function() { 4095 console.log("toggle text"); 4096 jQuery(".'.$class.'").toggle(); 4097 }); 4098 }); 4099 </script>'; 4100 4101 $result = $tmpresult.$result; 4102 } 4103 } 4104 4105 return $result; 4106} 4107 4108 4109/** 4110 * Displays error message system with all the information to facilitate the diagnosis and the escalation of the bugs. 4111 * This function must be called when a blocking technical error is encountered. 4112 * However, one must try to call it only within php pages, classes must return their error through their property "error". 4113 * 4114 * @param DoliDB $db Database handler 4115 * @param string|string[] $error String or array of errors strings to show 4116 * @param array $errors Array of errors 4117 * @return void 4118 * @see dol_htmloutput_errors() 4119 */ 4120function dol_print_error($db = '', $error = '', $errors = null) 4121{ 4122 global $conf, $langs, $argv; 4123 global $dolibarr_main_prod; 4124 4125 $out = ''; 4126 $syslog = ''; 4127 4128 // If error occurs before the $lang object was loaded 4129 if (!$langs) 4130 { 4131 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php'; 4132 $langs = new Translate('', $conf); 4133 $langs->load("main"); 4134 } 4135 4136 // Load translation files required by the error messages 4137 $langs->loadLangs(array('main', 'errors')); 4138 4139 if ($_SERVER['DOCUMENT_ROOT']) // Mode web 4140 { 4141 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n"; 4142 if (!empty($conf->global->MAIN_FEATURES_LEVEL)) $out .= "You use an experimental or develop level of features, so please do NOT report any bugs or vulnerability, except if problem is confirmed after moving option MAIN_FEATURES_LEVEL back to 0.<br>\n"; 4143 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n"; 4144 4145 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n"; 4146 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n"; 4147 if (isset($conf->global->MAIN_FEATURES_LEVEL)) $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".$conf->global->MAIN_FEATURES_LEVEL."<br>\n"; 4148 if (function_exists("phpversion")) 4149 { 4150 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n"; 4151 } 4152 $out .= "<b>".$langs->trans("Server").":</b> ".dol_htmlentities($_SERVER["SERVER_SOFTWARE"])."<br>\n"; 4153 if (function_exists("php_uname")) 4154 { 4155 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n"; 4156 } 4157 $out .= "<b>".$langs->trans("UserAgent").":</b> ".dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT, 'UTF-8')."<br>\n"; 4158 $out .= "<br>\n"; 4159 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT, 'UTF-8')."<br>\n"; 4160 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT, 'UTF-8') : '')."<br>\n"; 4161 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu) : '')."<br>\n"; 4162 $out .= "<br>\n"; 4163 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]); 4164 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]); 4165 } else // Mode CLI 4166 { 4167 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n"; 4168 $syslog .= "pid=".dol_getmypid(); 4169 } 4170 4171 if (!empty($conf->modules)) 4172 { 4173 $out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n"; 4174 } 4175 4176 if (is_object($db)) 4177 { 4178 if ($_SERVER['DOCUMENT_ROOT']) // Mode web 4179 { 4180 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n"; 4181 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror() ? dol_escape_htmltag($db->lastqueryerror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n"; 4182 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n"; 4183 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n"; 4184 $out .= "<br>\n"; 4185 } else // Mode CLI 4186 { 4187 // No dol_escape_htmltag for output, we are in CLI mode 4188 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n"; 4189 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n"; 4190 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n"; 4191 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n"; 4192 } 4193 $syslog .= ", sql=".$db->lastquery(); 4194 $syslog .= ", db_error=".$db->lasterror(); 4195 } 4196 4197 if ($error || $errors) 4198 { 4199 $langs->load("errors"); 4200 4201 // Merge all into $errors array 4202 if (is_array($error) && is_array($errors)) $errors = array_merge($error, $errors); 4203 elseif (is_array($error)) $errors = $error; 4204 elseif (is_array($errors)) $errors = array_merge(array($error), $errors); 4205 else $errors = array_merge(array($error)); 4206 4207 foreach ($errors as $msg) 4208 { 4209 if (empty($msg)) continue; 4210 if ($_SERVER['DOCUMENT_ROOT']) // Mode web 4211 { 4212 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n"; 4213 } else // Mode CLI 4214 { 4215 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n"; 4216 } 4217 $syslog .= ", msg=".$msg; 4218 } 4219 } 4220 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) 4221 { 4222 xdebug_print_function_stack(); 4223 $out .= '<b>XDebug informations:</b>'."<br>\n"; 4224 $out .= 'File: '.xdebug_call_file()."<br>\n"; 4225 $out .= 'Line: '.xdebug_call_line()."<br>\n"; 4226 $out .= 'Function: '.xdebug_call_function()."<br>\n"; 4227 $out .= "<br>\n"; 4228 } 4229 4230 // Return a http error code if possible 4231 if (!headers_sent()) { 4232 http_response_code(500); 4233 } 4234 4235 if (empty($dolibarr_main_prod)) { 4236 print $out; 4237 } else { 4238 if (empty($langs->defaultlang)) $langs->setDefaultLang(); 4239 $langs->loadLangs(array("main", "errors")); // Reload main because language may have been set only on previous line so we have to reload files we need. 4240 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case. 4241 print 'This website or feature is currently temporarly not available or failed after a technical error.<br><br>This may be due to a maintenance operation. Current status of operation are on next line...<br><br>'."\n"; 4242 print $langs->trans("DolibarrHasDetectedError").'. '; 4243 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero"); 4244 define("MAIN_CORE_ERROR", 1); 4245 } 4246 4247 dol_syslog("Error ".$syslog, LOG_ERR); 4248} 4249 4250/** 4251 * Show a public email and error code to contact if technical error 4252 * 4253 * @param string $prefixcode Prefix of public error code 4254 * @param string $errormessage Complete error message 4255 * @param array $errormessages Array of error messages 4256 * @param string $morecss More css 4257 * @param string $email Email 4258 * @return void 4259 */ 4260function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '') 4261{ 4262 global $langs, $conf; 4263 4264 if (empty($email)) $email = $conf->global->MAIN_INFO_SOCIETE_MAIL; 4265 4266 $langs->load("errors"); 4267 $now = dol_now(); 4268 4269 print '<br><div class="center login_main_message"><div class="'.$morecss.'">'; 4270 print $langs->trans("ErrorContactEMail", $email, $prefixcode.dol_print_date($now, '%Y%m%d%H%M%S')); 4271 if ($errormessage) print '<br><br>'.$errormessage; 4272 if (is_array($errormessages) && count($errormessages)) 4273 { 4274 foreach ($errormessages as $mesgtoshow) 4275 { 4276 print '<br><br>'.$mesgtoshow; 4277 } 4278 } 4279 print '</div></div>'; 4280} 4281 4282/** 4283 * Show title line of an array 4284 * 4285 * @param string $name Label of field 4286 * @param string $file Url used when we click on sort picto 4287 * @param string $field Field to use for new sorting 4288 * @param string $begin ("" by defaut) 4289 * @param string $moreparam Add more parameters on sort url links ("" by default) 4290 * @param string $moreattrib Options of attribute td ("" by defaut, example: 'align="center"') 4291 * @param string $sortfield Current field used to sort 4292 * @param string $sortorder Current sort order 4293 * @param string $prefix Prefix for css. Use space after prefix to add your own CSS tag. 4294 * @param string $tooltip Tooltip 4295 * @param string $forcenowrapcolumntitle No need for use 'wrapcolumntitle' css style 4296 * @return void 4297 */ 4298function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0) 4299{ 4300 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle); 4301} 4302 4303/** 4304 * Get title line of an array 4305 * 4306 * @param string $name Translation key of field to show or complete HTML string to show 4307 * @param int $thead 0=To use with standard table format, 1=To use inside <thead><tr>, 2=To use with <div> 4308 * @param string $file Url used when we click on sort picto 4309 * @param string $field Field to use for new sorting. Empty if this field is not sortable. Example "t.abc" or "t.abc,t.def" 4310 * @param string $begin ("" by defaut) 4311 * @param string $moreparam Add more parameters on sort url links ("" by default) 4312 * @param string $moreattrib Add more attributes on th ("" by defaut, example: 'align="center"'). To add more css class, use param $prefix. 4313 * @param string $sortfield Current field used to sort (Ex: 'd.datep,d.id') 4314 * @param string $sortorder Current sort order (Ex: 'asc,desc') 4315 * @param string $prefix Prefix for css. Use space after prefix to add your own CSS tag, for example 'mycss '. 4316 * @param string $disablesortlink 1=Disable sort link 4317 * @param string $tooltip Tooltip 4318 * @param string $forcenowrapcolumntitle No need for use 'wrapcolumntitle' css style 4319 * @return string 4320 */ 4321function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0) 4322{ 4323 global $conf, $langs, $form; 4324 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n"; 4325 4326 if ($moreattrib == 'class="right"') $prefix .= 'right '; // For backward compatibility 4327 4328 $sortorder = strtoupper($sortorder); 4329 $out = ''; 4330 $sortimg = ''; 4331 4332 $tag = 'th'; 4333 if ($thead == 2) $tag = 'div'; 4334 4335 $tmpsortfield = explode(',', $sortfield); 4336 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep' 4337 $tmpfield = explode(',', $field); 4338 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep' 4339 4340 if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle)) { 4341 $prefix = 'wrapcolumntitle '.$prefix; 4342 } 4343 4344 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1); 4345 // If field is used as sort criteria we use a specific css class liste_titre_sel 4346 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom") 4347 $liste_titre = 'liste_titre'; 4348 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) { 4349 $liste_titre = 'liste_titre_sel'; 4350 } 4351 $out .= '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib; 4352 //$out .= (($field && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && preg_match('/^[a-zA-Z_0-9\s\.\-:&;]*$/', $name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : ''); 4353 $out .= ($name && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : ''; 4354 $out .= '>'; 4355 4356 if (empty($thead) && $field && empty($disablesortlink)) // If this is a sort field 4357 { 4358 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : '')); 4359 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options); 4360 $options = preg_replace('/&+/i', '&', $options); 4361 if (!preg_match('/^&/', $options)) $options = '&'.$options; 4362 4363 $sortordertouseinlink = ''; 4364 if ($field1 != $sortfield1) // We are on another field than current sorted field 4365 { 4366 if (preg_match('/^DESC/i', $sortorder)) 4367 { 4368 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field))); 4369 } else // We reverse the var $sortordertouseinlink 4370 { 4371 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field))); 4372 } 4373 } else // We are on field that is the first current sorting criteria 4374 { 4375 if (preg_match('/^ASC/i', $sortorder)) // We reverse the var $sortordertouseinlink 4376 { 4377 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field))); 4378 } else { 4379 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field))); 4380 } 4381 } 4382 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink); 4383 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"'; 4384 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : ''); 4385 $out .= '>'; 4386 } 4387 4388 if ($tooltip) { 4389 // You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click. 4390 $tmptooltip = explode(':', $tooltip); 4391 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1])); 4392 } 4393 else $out .= $langs->trans($name); 4394 4395 if (empty($thead) && $field && empty($disablesortlink)) // If this is a sort field 4396 { 4397 $out .= '</a>'; 4398 } 4399 4400 if (empty($thead) && $field) // If this is a sort field 4401 { 4402 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : '')); 4403 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options); 4404 $options = preg_replace('/&+/i', '&', $options); 4405 if (!preg_match('/^&/', $options)) $options = '&'.$options; 4406 4407 if (!$sortorder || $field1 != $sortfield1) 4408 { 4409 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>'; 4410 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>'; 4411 } else { 4412 if (preg_match('/^DESC/', $sortorder)) { 4413 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>'; 4414 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>'; 4415 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingleft').'</span>'; 4416 } 4417 if (preg_match('/^ASC/', $sortorder)) { 4418 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>'; 4419 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>'; 4420 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingleft').'</span>'; 4421 } 4422 } 4423 } 4424 4425 $out .= $sortimg; 4426 4427 $out .= '</'.$tag.'>'; 4428 4429 return $out; 4430} 4431 4432/** 4433 * Show a title. 4434 * 4435 * @param string $title Title to show 4436 * @return string Title to show 4437 * @deprecated Use load_fiche_titre instead 4438 * @see load_fiche_titre() 4439 */ 4440function print_titre($title) 4441{ 4442 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING); 4443 4444 print '<div class="titre">'.$title.'</div>'; 4445} 4446 4447/** 4448 * Show a title with picto 4449 * 4450 * @param string $title Title to show 4451 * @param string $mesg Added message to show on right 4452 * @param string $picto Icon to use before title (should be a 32x32 transparent png file) 4453 * @param int $pictoisfullpath 1=Icon name is a full absolute url of image 4454 * @param int $id To force an id on html objects 4455 * @return void 4456 * @deprecated Use print load_fiche_titre instead 4457 */ 4458function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '') 4459{ 4460 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id); 4461} 4462 4463/** 4464 * Load a title with picto 4465 * 4466 * @param string $titre Title to show 4467 * @param string $morehtmlright Added message to show on right 4468 * @param string $picto Icon to use before title (should be a 32x32 transparent png file) 4469 * @param int $pictoisfullpath 1=Icon name is a full absolute url of image 4470 * @param string $id To force an id on html objects 4471 * @param string $morecssontable More css on table 4472 * @param string $morehtmlcenter Added message to show on center 4473 * @return string 4474 * @see print_barre_liste() 4475 */ 4476function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '') 4477{ 4478 global $conf; 4479 4480 $return = ''; 4481 4482 if ($picto == 'setup') $picto = 'generic'; 4483 4484 $return .= "\n"; 4485 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list 4486 $return .= '<tr class="titre">'; 4487 if ($picto) $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>'; 4488 $return .= '<td class="nobordernopadding valignmiddle col-title">'; 4489 $return .= '<div class="titre inline-block">'.$titre.'</div>'; 4490 $return .= '</td>'; 4491 if (dol_strlen($morehtmlcenter)) 4492 { 4493 $return .= '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>'; 4494 } 4495 if (dol_strlen($morehtmlright)) 4496 { 4497 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle">'.$morehtmlright.'</td>'; 4498 } 4499 $return .= '</tr></table>'."\n"; 4500 4501 return $return; 4502} 4503 4504/** 4505 * Print a title with navigation controls for pagination 4506 * 4507 * @param string $titre Title to show (required) 4508 * @param int $page Numero of page to show in navigation links (required) 4509 * @param string $file Url of page (required) 4510 * @param string $options More parameters for links ('' by default, does not include sortfield neither sortorder). Value must be 'urlencoded' before calling function. 4511 * @param string $sortfield Field to sort on ('' by default) 4512 * @param string $sortorder Order to sort ('' by default) 4513 * @param string $morehtmlcenter String in the middle ('' by default). We often find here string $massaction comming from $form->selectMassAction() 4514 * @param int $num Number of records found by select with limit+1 4515 * @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. 4516 * @param string $picto Icon to use before title (should be a 32x32 transparent png file) 4517 * @param int $pictoisfullpath 1=Icon name is a full absolute url of image 4518 * @param string $morehtmlright More html to show (after arrows) 4519 * @param string $morecss More css to the table 4520 * @param int $limit Max number of lines (-1 = use default, 0 = no limit, > 0 = limit). 4521 * @param int $hideselectlimit Force to hide select limit 4522 * @param int $hidenavigation Force to hide all navigation tools 4523 * @param int $pagenavastextinput 1=Do not suggest list of pages to navigate but suggest the page number into an input field. 4524 * @param string $morehtmlrightbeforearrow More html to show (before arrows) 4525 * @return void 4526 */ 4527function print_barre_liste($titre, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'generic', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $hideselectlimit = 0, $hidenavigation = 0, $pagenavastextinput = 0, $morehtmlrightbeforearrow = '') 4528{ 4529 global $conf, $langs; 4530 4531 $savlimit = $limit; 4532 $savtotalnboflines = $totalnboflines; 4533 $totalnboflines = abs((int) $totalnboflines); 4534 4535 if ($picto == 'setup') $picto = 'title_setup.png'; 4536 if (($conf->browser->name == 'ie') && $picto == 'generic') $picto = 'title.gif'; 4537 if ($limit < 0) $limit = $conf->liste_limit; 4538 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) 4539 { 4540 $nextpage = 1; 4541 } else { 4542 $nextpage = 0; 4543 } 4544 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage; 4545 4546 print "\n"; 4547 print "<!-- Begin title -->\n"; 4548 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire 4549 4550 // Left 4551 4552 if ($picto && $titre) print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>'; 4553 print '<td class="nobordernopadding valignmiddle col-title">'; 4554 print '<div class="titre inline-block">'.$titre; 4555 if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>'; 4556 print '</div></td>'; 4557 4558 // Center 4559 if ($morehtmlcenter) 4560 { 4561 print '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>'; 4562 } 4563 4564 // Right 4565 print '<td class="nobordernopadding valignmiddle right">'; 4566 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">'; 4567 if ($sortfield) $options .= "&sortfield=".urlencode($sortfield); 4568 if ($sortorder) $options .= "&sortorder=".urlencode($sortorder); 4569 // Show navigation bar 4570 $pagelist = ''; 4571 if ($savlimit != 0 && ($page > 0 || $num > $limit)) 4572 { 4573 if ($totalnboflines) // If we know total nb of lines 4574 { 4575 // Define nb of extra page links before and after selected page + ... + first or last 4576 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0); 4577 4578 if ($limit > 0) $nbpages = ceil($totalnboflines / $limit); 4579 else $nbpages = 1; 4580 $cpt = ($page - $maxnbofpage); 4581 if ($cpt < 0) { $cpt = 0; } 4582 4583 if ($cpt >= 1) 4584 { 4585 if (empty($pagenavastextinput)) { 4586 $pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>'; 4587 if ($cpt > 2) $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>'; 4588 elseif ($cpt == 2) $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>'; 4589 } 4590 } 4591 4592 do { 4593 if ($pagenavastextinput) { 4594 if ($cpt == $page) 4595 { 4596 $pagelist .= '<li class="pagination"><input type="text" class="width25 center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>'; 4597 $pagelist .= '/'; 4598 //if (($cpt + 1) < $nbpages) $pagelist .= '/'; 4599 } 4600 } else { 4601 if ($cpt == $page) 4602 { 4603 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>'; 4604 } else { 4605 $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>'; 4606 } 4607 } 4608 $cpt++; 4609 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage)); 4610 4611 if (empty($pagenavastextinput)) { 4612 if ($cpt < $nbpages) 4613 { 4614 if ($cpt < $nbpages - 2) $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>'; 4615 elseif ($cpt == $nbpages - 2) $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>'; 4616 $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>'; 4617 } 4618 } else { 4619 //var_dump($page.' '.$cpt.' '.$nbpages); 4620 //if (($page + 1) < $nbpages) { 4621 $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>'; 4622 //} 4623 } 4624 } else { 4625 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>"; 4626 } 4627 } 4628 4629 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) { 4630 print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $hideselectlimit, $morehtmlrightbeforearrow); // output the div and ul for previous/last completed with page numbers into $pagelist 4631 } 4632 4633 // js to autoselect page field on focus 4634 if ($pagenavastextinput) { 4635 print ajax_autoselect('.pageplusone'); 4636 } 4637 4638 print '</td>'; 4639 4640 print '</tr></table>'."\n"; 4641 print "<!-- End title -->\n\n"; 4642} 4643 4644/** 4645 * Function to show navigation arrows into lists 4646 * 4647 * @param int $page Number of page 4648 * @param string $file Page URL (in most cases provided with $_SERVER["PHP_SELF"]) 4649 * @param string $options Other url parameters to propagate ("" by default, may include sortfield and sortorder) 4650 * @param integer $nextpage Do we show a next page button 4651 * @param string $betweenarrows HTML content to show between arrows. MUST contains '<li> </li>' tags or '<li><span> </span></li>'. 4652 * @param string $afterarrows HTML content to show after arrows. Must NOT contains '<li> </li>' tags. 4653 * @param int $limit Max nb of record to show (-1 = no combo with limit, 0 = no limit, > 0 = limit) 4654 * @param int $totalnboflines Total number of records/lines for all pages (if known) 4655 * @param int $hideselectlimit Force to hide select limit 4656 * @param string $beforearrows HTML content to show before arrows. Must NOT contains '<li> </li>' tags. 4657 * @return void 4658 */ 4659function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '') 4660{ 4661 global $conf, $langs; 4662 4663 print '<div class="pagination"><ul>'; 4664 if ($beforearrows) 4665 { 4666 print '<li class="paginationbeforearrows">'; 4667 print $beforearrows; 4668 print '</li>'; 4669 } 4670 if ((int) $limit > 0 && empty($hideselectlimit)) 4671 { 4672 $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000,5000:5000,25000:25000'; 4673 //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported 4674 //$pagesizechoices.=',2:2'; 4675 if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES; 4676 4677 print '<li class="pagination">'; 4678 print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">'; 4679 $tmpchoice = explode(',', $pagesizechoices); 4680 $tmpkey = $limit.':'.$limit; 4681 if (!in_array($tmpkey, $tmpchoice)) $tmpchoice[] = $tmpkey; 4682 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit; 4683 if (!in_array($tmpkey, $tmpchoice)) $tmpchoice[] = $tmpkey; 4684 asort($tmpchoice, SORT_NUMERIC); 4685 foreach ($tmpchoice as $val) 4686 { 4687 $selected = ''; 4688 $tmp = explode(':', $val); 4689 $key = $tmp[0]; 4690 $val = $tmp[1]; 4691 if ($key != '' && $val != '') 4692 { 4693 if ((int) $key == (int) $limit) 4694 { 4695 $selected = ' selected="selected"'; 4696 } 4697 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n"; 4698 } 4699 } 4700 print '</select>'; 4701 if ($conf->use_javascript_ajax) 4702 { 4703 print '<!-- JS CODE TO ENABLE select limit to launch submit of page --> 4704 <script> 4705 jQuery(document).ready(function () { 4706 jQuery(".selectlimit").change(function() { 4707 console.log("Change limit. Send submit"); 4708 $(this).parents(\'form:first\').submit(); 4709 }); 4710 }); 4711 </script> 4712 '; 4713 } 4714 print '</li>'; 4715 } 4716 if ($page > 0) 4717 { 4718 print '<li class="pagination paginationpage paginationpageleft"><a class="paginationprevious" href="'.$file.'?page='.($page - 1).$options.'"><i class="fa fa-chevron-left" title="'.dol_escape_htmltag($langs->trans("Previous")).'"></i></a></li>'; 4719 } 4720 if ($betweenarrows) 4721 { 4722 print '<!--<div class="betweenarrows nowraponall inline-block">-->'; 4723 print $betweenarrows; 4724 print '<!--</div>-->'; 4725 } 4726 if ($nextpage > 0) 4727 { 4728 print '<li class="pagination paginationpage paginationpageright"><a class="paginationnext" href="'.$file.'?page='.($page + 1).$options.'"><i class="fa fa-chevron-right" title="'.dol_escape_htmltag($langs->trans("Next")).'"></i></a></li>'; 4729 } 4730 if ($afterarrows) 4731 { 4732 print '<li class="paginationafterarrows">'; 4733 print $afterarrows; 4734 print '</li>'; 4735 } 4736 print '</ul></div>'."\n"; 4737} 4738 4739 4740/** 4741 * Return a string with VAT rate label formated for view output 4742 * Used into pdf and HTML pages 4743 * 4744 * @param string $rate Rate value to format ('19.6', '19,6', '19.6%', '19,6%', '19.6 (CODEX)', ...) 4745 * @param boolean $addpercent Add a percent % sign in output 4746 * @param int $info_bits Miscellaneous information on vat (0=Default, 1=French NPR vat) 4747 * @param int $usestarfornpr -1=Never show, 0 or 1=Use '*' for NPR vat rates 4748 * @return string String with formated amounts ('19,6' or '19,6%' or '8.5% (NPR)' or '8.5% *' or '19,6 (CODEX)') 4749 */ 4750function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0) 4751{ 4752 $morelabel = ''; 4753 4754 if (preg_match('/%/', $rate)) 4755 { 4756 $rate = str_replace('%', '', $rate); 4757 $addpercent = true; 4758 } 4759 if (preg_match('/\((.*)\)/', $rate, $reg)) 4760 { 4761 $morelabel = ' ('.$reg[1].')'; 4762 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate); 4763 } 4764 if (preg_match('/\*/', $rate)) 4765 { 4766 $rate = str_replace('*', '', $rate); 4767 $info_bits |= 1; 4768 } 4769 4770 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price() 4771 if (!preg_match('/\//', $rate)) $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : ''); 4772 else { 4773 // TODO Split on / and output with a price2num to have clean numbers without ton of 000. 4774 $ret = $rate.($addpercent ? '%' : ''); 4775 } 4776 if (($info_bits & 1) && $usestarfornpr >= 0) $ret .= ' *'; 4777 $ret .= $morelabel; 4778 return $ret; 4779} 4780 4781 4782/** 4783 * Function to format a value into an amount for visual output 4784 * Function used into PDF and HTML pages 4785 * 4786 * @param float $amount Amount to format 4787 * @param integer $form Type of format, HTML or not (not by default) 4788 * @param Translate $outlangs Object langs for output 4789 * @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. 4790 * @param int $rounding Minimum number of decimal to show. If 0, no change, if -1, we use min($conf->global->MAIN_MAX_DECIMALS_UNIT,$conf->global->MAIN_MAX_DECIMALS_TOT) 4791 * @param int $forcerounding Force the number of decimal to forcerounding decimal (-1=do not force) 4792 * @param string $currency_code To add currency symbol (''=add nothing, 'auto'=Use default currency, 'XXX'=add currency symbols for XXX currency) 4793 * @return string Chaine avec montant formate 4794 * 4795 * @see price2num() Revert function of price 4796 */ 4797function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '') 4798{ 4799 global $langs, $conf; 4800 4801 // Clean parameters 4802 if (empty($amount)) $amount = 0; // To have a numeric value if amount not defined or = '' 4803 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number) 4804 if ($rounding < 0) $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT); 4805 $nbdecimal = $rounding; 4806 4807 // Output separators by default (french) 4808 $dec = ','; $thousand = ' '; 4809 4810 // If $outlangs not forced, we use use language 4811 if (!is_object($outlangs)) $outlangs = $langs; 4812 4813 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal"); 4814 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand"); 4815 if ($thousand == 'None') $thousand = ''; 4816 elseif ($thousand == 'Space') $thousand = ' '; 4817 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>"; 4818 4819 //print "amount=".$amount."-"; 4820 $amount = str_replace(',', '.', $amount); // should be useless 4821 //print $amount."-"; 4822 $datas = explode('.', $amount); 4823 $decpart = isset($datas[1]) ? $datas[1] : ''; 4824 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale 4825 //print "decpart=".$decpart."<br>"; 4826 $end = ''; 4827 4828 // We increase nbdecimal if there is more decimal than asked (to not loose information) 4829 if (dol_strlen($decpart) > $nbdecimal) $nbdecimal = dol_strlen($decpart); 4830 // Si on depasse max 4831 if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN) 4832 { 4833 $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN; 4834 if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN)) 4835 { 4836 // Si un affichage est tronque, on montre des ... 4837 $end = '...'; 4838 } 4839 } 4840 4841 // If force rounding 4842 if ($forcerounding >= 0) $nbdecimal = $forcerounding; 4843 4844 // Format number 4845 $output = number_format($amount, $nbdecimal, $dec, $thousand); 4846 if ($form) 4847 { 4848 $output = preg_replace('/\s/', ' ', $output); 4849 $output = preg_replace('/\'/', ''', $output); 4850 } 4851 // Add symbol of currency if requested 4852 $cursymbolbefore = $cursymbolafter = ''; 4853 if ($currency_code) 4854 { 4855 if ($currency_code == 'auto') $currency_code = $conf->currency; 4856 4857 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD'); 4858 $listoflanguagesbefore = array('nl_NL'); 4859 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) 4860 { 4861 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code); 4862 } else { 4863 $tmpcur = $outlangs->getCurrencySymbol($currency_code); 4864 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur); 4865 } 4866 } 4867 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter; 4868 4869 return $output; 4870} 4871 4872/** 4873 * Function that return a number with universal decimal format (decimal separator is '.') from an amount typed by a user. 4874 * Function to use on each input amount before any numeric test or database insert. A better name for this function 4875 * should be roundtext2num(). 4876 * 4877 * @param string|float $amount Amount to convert/clean or round 4878 * @param string $rounding ''=No rounding 4879 * 'MU'=Round to Max unit price (MAIN_MAX_DECIMALS_UNIT) 4880 * 'MT'=Round to Max for totals with Tax (MAIN_MAX_DECIMALS_TOT) 4881 * 'MS'=Round to Max for stock quantity (MAIN_MAX_DECIMALS_STOCK) 4882 * 'CU'=Round to Max unit price of foreign currency accuracy 4883 * 'CT'=Round to Max for totals with Tax of foreign currency accuracy 4884 * Numeric = Nb of digits for rounding 4885 * @param int $option Put 1 if you know that content is already universal format number (so no correction on decimal will be done) 4886 * Put 2 if you know that number is a user input (so we know we don't have to fix decimal separator). 4887 * @return string Amount with universal numeric format (Example: '99.99999'). 4888 * If conversion fails, it return text unchanged if $rounding = '' or '0' if $rounding is defined. 4889 * If amount is null or '', it returns '' if $rounding = '' or '0' if $rounding is defined.. 4890 * 4891 * @see price() Opposite function of price2num 4892 */ 4893function price2num($amount, $rounding = '', $option = 0) 4894{ 4895 global $langs, $conf; 4896 4897 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56' 4898 // Numbers must be '1234.56' 4899 // Decimal delimiter for PHP and database SQL requests must be '.' 4900 $dec = ','; $thousand = ' '; 4901 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") $dec = $langs->transnoentitiesnoconv("SeparatorDecimal"); 4902 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") $thousand = $langs->transnoentitiesnoconv("SeparatorThousand"); 4903 if ($thousand == 'None') $thousand = ''; 4904 elseif ($thousand == 'Space') $thousand = ' '; 4905 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>"; 4906 4907 // Convert value to universal number format (no thousand separator, '.' as decimal separator) 4908 if ($option != 1) { // If not a PHP number or unknown, we change or clean format 4909 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>'; 4910 if (!is_numeric($amount)) { 4911 $amount = preg_replace('/[a-zA-Z\/\\\*\(\)\<\>\_]/', '', $amount); 4912 } 4913 4914 if ($option == 2 && $thousand == '.' && preg_match('/\.(\d\d\d)$/', (string) $amount)) { // It means the . is used as a thousand separator and string come from input data, so 1.123 is 1123 4915 $amount = str_replace($thousand, '', $amount); 4916 } 4917 4918 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number 4919 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup. 4920 // So if number was already a good number, it is converted into local Dolibarr setup. 4921 if (is_numeric($amount)) 4922 { 4923 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10 4924 $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000 4925 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1 4926 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0." 4927 $amount = number_format($amount, $nbofdec, $dec, $thousand); 4928 } 4929 //print "QQ".$amount."<br>\n"; 4930 4931 // Now make replace (the main goal of function) 4932 if ($thousand != ',' && $thousand != '.') { 4933 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users 4934 } 4935 $amount = str_replace(' ', '', $amount); // To avoid spaces 4936 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is . 4937 $amount = str_replace($dec, '.', $amount); 4938 } 4939 //print ' XX'.$amount.' '.$rounding; 4940 4941 // Now, make a rounding if required 4942 if ($rounding) 4943 { 4944 $nbofdectoround = ''; 4945 if ($rounding == 'MU') { 4946 $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT; 4947 } 4948 elseif ($rounding == 'MT') { 4949 $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT; 4950 } 4951 elseif ($rounding == 'MS') { 4952 $nbofdectoround = empty($conf->global->MAIN_MAX_DECIMALS_STOCK) ? 5 : $conf->global->MAIN_MAX_DECIMALS_STOCK; 4953 } 4954 elseif ($rounding == 'CU') { 4955 $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_UNIT, 8); // TODO Use param of currency 4956 } 4957 elseif ($rounding == 'CT') { 4958 $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_TOT, 8); // TODO Use param of currency 4959 } 4960 elseif (is_numeric($rounding)) $nbofdectoround = $rounding; 4961 //print " RR".$amount.' - '.$nbofdectoround.'<br>'; 4962 if (dol_strlen($nbofdectoround)) $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0. 4963 else return 'ErrorBadParameterProvidedToFunction'; 4964 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>'; 4965 4966 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number 4967 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup. 4968 if (is_numeric($amount)) 4969 { 4970 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10 4971 $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000 4972 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1 4973 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0." 4974 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand 4975 } 4976 //print "TT".$amount.'<br>'; 4977 4978 // Always make replace because each math function (like round) replace 4979 // with local values and we want a number that has a SQL string format x.y 4980 if ($thousand != ',' && $thousand != '.') $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users 4981 $amount = str_replace(' ', '', $amount); // To avoid spaces 4982 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is . 4983 $amount = str_replace($dec, '.', $amount); 4984 } 4985 4986 return $amount; 4987} 4988 4989/** 4990 * Output a dimension with best unit 4991 * 4992 * @param float $dimension Dimension 4993 * @param int $unit Unit scale of dimension (Example: 0=kg, -3=g, -6=mg, 98=ounce, 99=pound, ...) 4994 * @param string $type 'weight', 'volume', ... 4995 * @param Translate $outputlangs Translate language object 4996 * @param int $round -1 = non rounding, x = number of decimal 4997 * @param string $forceunitoutput 'no' or numeric (-3, -6, ...) compared to $unit (In most case, this value is value defined into $conf->global->MAIN_WEIGHT_DEFAULT_UNIT) 4998 * @return string String to show dimensions 4999 */ 5000function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no') 5001{ 5002 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; 5003 5004 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) 5005 { 5006 $dimension = $dimension * 1000000; 5007 $unit = $unit - 6; 5008 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) 5009 { 5010 $dimension = $dimension * 1000; 5011 $unit = $unit - 3; 5012 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) 5013 { 5014 $dimension = $dimension / 1000000; 5015 $unit = $unit + 6; 5016 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) 5017 { 5018 $dimension = $dimension / 1000; 5019 $unit = $unit + 3; 5020 } 5021 // Special case when we want output unit into pound or ounce 5022 /* TODO 5023 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99)) 5024 { 5025 $dimension = // convert dimension from standard unit into ounce or pound 5026 $unit = $forceunitoutput; 5027 } 5028 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90) 5029 { 5030 $dimension = // convert dimension from standard unit into ounce or pound 5031 $unit = $forceunitoutput; 5032 }*/ 5033 5034 $ret = price($dimension, 0, $outputlangs, 0, 0, $round).' '.measuringUnitString(0, $type, $unit); 5035 5036 return $ret; 5037} 5038 5039 5040/** 5041 * Return localtax rate for a particular vat, when selling a product with vat $vatrate, from a $thirdparty_buyer to a $thirdparty_seller 5042 * Note: This function applies same rules than get_default_tva 5043 * 5044 * @param float $vatrate Vat rate. Can be '8.5' or '8.5 (VATCODEX)' for example 5045 * @param int $local Local tax to search and return (1 or 2 return only tax rate 1 or tax rate 2) 5046 * @param Societe $thirdparty_buyer Object of buying third party 5047 * @param Societe $thirdparty_seller Object of selling third party ($mysoc if not defined) 5048 * @param int $vatnpr If vat rate is NPR or not 5049 * @return mixed 0 if not found, localtax rate if found 5050 * @see get_default_tva() 5051 */ 5052function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0) 5053{ 5054 global $db, $conf, $mysoc; 5055 5056 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) $thirdparty_seller = $mysoc; 5057 5058 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); 5059 5060 $vatratecleaned = $vatrate; 5061 $reg = array(); 5062 if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "xx (yy)" 5063 $vatratecleaned = trim($reg[1]); 5064 $vatratecode = $reg[2]; 5065 } 5066 5067 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code) 5068 { 5069 return 0; 5070 }*/ 5071 5072 // Some test to guess with no need to make database access 5073 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax 5074 if ($local == 1) { 5075 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") return 0; 5076 if ($thirdparty_seller->id == $mysoc->id) { 5077 if (!$thirdparty_buyer->localtax1_assuj) return 0; 5078 } else { 5079 if (!$thirdparty_seller->localtax1_assuj) return 0; 5080 } 5081 } 5082 5083 if ($local == 2) { 5084 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0; 5085 if (!$mysoc->localtax2_assuj) return 0; // If main vat is 0, IRPF may be different than 0. 5086 if ($thirdparty_seller->id == $mysoc->id) { 5087 if (!$thirdparty_buyer->localtax2_assuj) return 0; 5088 } else { 5089 if (!$thirdparty_seller->localtax2_assuj) return 0; 5090 } 5091 } 5092 } else { 5093 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) return 0; 5094 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) return 0; 5095 } 5096 5097 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on. 5098 if (in_array($mysoc->country_code, array('ES'))) { 5099 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1; 5100 } 5101 5102 // Search local taxes 5103 if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) 5104 { 5105 if ($local == 1) { 5106 if ($thirdparty_seller != $mysoc) { 5107 if (!isOnlyOneLocalTax($local)) // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate 5108 { 5109 return $thirdparty_seller->localtax1_value; 5110 } 5111 } else { // i am the seller 5112 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat. 5113 return $conf->global->MAIN_INFO_VALUE_LOCALTAX1; 5114 } 5115 } 5116 } 5117 if ($local == 2) { 5118 if ($thirdparty_seller != $mysoc) { 5119 if (!isOnlyOneLocalTax($local)) // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate 5120 // TODO We should also return value defined on thirdparty only if defined 5121 { 5122 return $thirdparty_seller->localtax2_value; 5123 } 5124 } else { // i am the seller 5125 if (in_array($mysoc->country_code, array('ES'))) { 5126 return $thirdparty_buyer->localtax2_value; 5127 } else { 5128 return $conf->global->MAIN_INFO_VALUE_LOCALTAX2; 5129 } 5130 } 5131 } 5132 } 5133 5134 // By default, search value of local tax on line of common tax 5135 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type"; 5136 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; 5137 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'"; 5138 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1"; 5139 if (!empty($vatratecode)) $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority 5140 else $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'"; 5141 dol_syslog("get_localtax", LOG_DEBUG); 5142 $resql = $db->query($sql); 5143 5144 if ($resql) 5145 { 5146 $obj = $db->fetch_object($resql); 5147 if ($obj) { 5148 if ($local == 1) return $obj->localtax1; 5149 elseif ($local == 2) return $obj->localtax2; 5150 } 5151 } 5152 5153 return 0; 5154} 5155 5156 5157/** 5158 * Return true if LocalTax (1 or 2) is unique. 5159 * Example: If localtax1 is 5 on line with highest common vat rate, return true 5160 * Example: If localtax1 is 5:8:15 on line with highest common vat rate, return false 5161 * 5162 * @param int $local Local tax to test (1 or 2) 5163 * @return boolean True if LocalTax have multiple values, False if not 5164 */ 5165function isOnlyOneLocalTax($local) 5166{ 5167 $tax = get_localtax_by_third($local); 5168 5169 $valors = explode(":", $tax); 5170 5171 if (count($valors) > 1) { 5172 return false; 5173 } else { 5174 return true; 5175 } 5176} 5177 5178/** 5179 * Get values of localtaxes (1 or 2) for company country for the common vat with the highest value 5180 * 5181 * @param int $local LocalTax to get 5182 * @return number Values of localtax 5183 */ 5184function get_localtax_by_third($local) 5185{ 5186 global $db, $mysoc; 5187 $sql = "SELECT t.localtax1, t.localtax2 "; 5188 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=t.fk_pays"; 5189 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux=("; 5190 $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"; 5191 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1"; 5192 $sql .= " )"; 5193 5194 $resql = $db->query($sql); 5195 if ($resql) 5196 { 5197 $obj = $db->fetch_object($resql); 5198 if ($local == 1) return $obj->localtax1; 5199 elseif ($local == 2) return $obj->localtax2; 5200 } 5201 5202 return 0; 5203} 5204 5205 5206/** 5207 * Get tax (VAT) main information from Id. 5208 * You can also call getLocalTaxesFromRate() after to get only localtax fields. 5209 * 5210 * @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. 5211 * @param Societe $buyer Company object 5212 * @param Societe $seller Company object 5213 * @param int $firstparamisid 1 if first param is id into table (use this if you can) 5214 * @return array array('rowid'=> , 'code'=> ...) 5215 * @see getLocalTaxesFromRate() 5216 */ 5217function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1) 5218{ 5219 global $db, $mysoc; 5220 5221 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate); 5222 5223 // Search local taxes 5224 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,"; 5225 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type"; 5226 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t"; 5227 if ($firstparamisid) { 5228 $sql .= " WHERE t.rowid = ".(int) $vatrate; 5229 } else { 5230 $vatratecleaned = $vatrate; 5231 $vatratecode = ''; 5232 $reg = array(); 5233 if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) // If vat is "xx (yy)" 5234 { 5235 $vatratecleaned = $reg[1]; 5236 $vatratecode = $reg[2]; 5237 } 5238 5239 $sql .= ", ".MAIN_DB_PREFIX."c_country as c"; 5240 /*if ($mysoc->country_code == 'ES') $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // vat in spain use the buyer country ?? 5241 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/ 5242 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'"; 5243 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1"; 5244 if ($vatratecode) $sql .= " AND t.code = '".$db->escape($vatratecode)."'"; 5245 } 5246 5247 $resql = $db->query($sql); 5248 if ($resql) { 5249 $obj = $db->fetch_object($resql); 5250 if ($obj) return array( 5251 'rowid'=>$obj->rowid, 5252 'code'=>$obj->code, 5253 'rate'=>$obj->rate, 5254 'localtax1'=>$obj->localtax1, 5255 'localtax1_type'=>$obj->localtax1_type, 5256 'localtax2'=>$obj->localtax2, 5257 'localtax2_type'=>$obj->localtax2_type, 5258 'npr'=>$obj->npr, 5259 'accountancy_code_sell'=>$obj->accountancy_code_sell, 5260 'accountancy_code_buy'=>$obj->accountancy_code_buy 5261 ); 5262 else return array(); 5263 } else dol_print_error($db); 5264 5265 return array(); 5266} 5267 5268/** 5269 * Get type and rate of localtaxes for a particular vat rate/country of a thirdparty. 5270 * This does not take into account the seller setup if subject to vat or not, only country. 5271 * 5272 * TODO This function is ALSO called to retrieve type for building PDF. Such call of function must be removed. 5273 * Instead this function must be called when adding a line to get the array of possible values for localtax and type, and then 5274 * provide the selected value to the function calcul_price_total. 5275 * 5276 * @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. 5277 * @param int $local Number of localtax (1 or 2, or 0 to return 1 & 2) 5278 * @param Societe $buyer Company object 5279 * @param Societe $seller Company object 5280 * @param int $firstparamisid 1 if first param is ID into table instead of Rate+code (use this if you can) 5281 * @return array array(localtax_type1(1-6 or 0 if not found), rate localtax1, localtax_type2, rate localtax2, accountancycodecust, accountancycodesupp) 5282 * @see getTaxesFromId() 5283 */ 5284function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0) 5285{ 5286 global $db, $mysoc; 5287 5288 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local); 5289 5290 // Search local taxes 5291 $sql = "SELECT t.taux as rate, t.code, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy"; 5292 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t"; 5293 if ($firstparamisid) { 5294 $sql .= " WHERE t.rowid = ".(int) $vatrate; 5295 } else { 5296 $vatratecleaned = $vatrate; 5297 $vatratecode = ''; 5298 $reg = array(); 5299 if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "x.x (yy)" 5300 $vatratecleaned = $reg[1]; 5301 $vatratecode = $reg[2]; 5302 } 5303 5304 $sql .= ", ".MAIN_DB_PREFIX."c_country as c"; 5305 if ($mysoc->country_code == 'ES') $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // local tax in spain use the buyer country ?? 5306 else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape(empty($seller->country_code) ? $mysoc->country_code : $seller->country_code)."'"; 5307 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1"; 5308 if ($vatratecode) $sql .= " AND t.code = '".$db->escape($vatratecode)."'"; 5309 } 5310 5311 $resql = $db->query($sql); 5312 if ($resql) { 5313 $obj = $db->fetch_object($resql); 5314 5315 if ($obj) { 5316 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : ''); 5317 5318 if ($local == 1) { 5319 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy); 5320 } elseif ($local == 2) { 5321 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy); 5322 } else { 5323 return array($obj->localtax1_type, get_localtax($vateratestring, 1, $buyer, $seller), $obj->localtax2_type, get_localtax($vateratestring, 2, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy); 5324 } 5325 } 5326 } 5327 5328 return array(); 5329} 5330 5331/** 5332 * Return vat rate of a product in a particular selling country or default country vat if product is unknown 5333 * Function called by get_default_tva 5334 * 5335 * @param int $idprod Id of product or 0 if not a predefined product 5336 * @param Societe $thirdparty_seller Thirdparty with a ->country_code defined (FR, US, IT, ...) 5337 * @param int $idprodfournprice Id product_fournisseur_price (for "supplier" proposal/order/invoice) 5338 * @return float|string Vat rate to use with format 5.0 or '5.0 (XXX)' 5339 * @see get_product_localtax_for_country() 5340 */ 5341function get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice = 0) 5342{ 5343 global $db, $conf, $mysoc; 5344 5345 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; 5346 5347 $ret = 0; 5348 $found = 0; 5349 5350 if ($idprod > 0) 5351 { 5352 // Load product 5353 $product = new Product($db); 5354 $result = $product->fetch($idprod); 5355 5356 if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours 5357 { 5358 if ($idprodfournprice > 0) // We want vat for product for a "supplier" object 5359 { 5360 $product->get_buyprice($idprodfournprice, 0, 0, 0); 5361 $ret = $product->vatrate_supplier; 5362 if ($product->default_vat_code) $ret .= ' ('.$product->default_vat_code.')'; 5363 } else { 5364 $ret = $product->tva_tx; // Default vat of product we defined 5365 if ($product->default_vat_code) $ret .= ' ('.$product->default_vat_code.')'; 5366 } 5367 $found = 1; 5368 } else { 5369 // TODO Read default product vat according to countrycode and product. Vat for couple countrycode/product is a feature not implemeted yet. 5370 // May be usefull/required if hidden option SERVICE_ARE_ECOMMERCE_200238EC is on 5371 } 5372 } 5373 5374 if (!$found) 5375 { 5376 if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) 5377 { 5378 // If vat of product for the country not found or not defined, we return the first higher vat of country. 5379 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code"; 5380 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; 5381 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdparty_seller->country_code)."'"; 5382 $sql .= " ORDER BY t.taux DESC, t.code ASC, t.recuperableonly ASC"; 5383 $sql .= $db->plimit(1); 5384 5385 $resql = $db->query($sql); 5386 if ($resql) 5387 { 5388 $obj = $db->fetch_object($resql); 5389 if ($obj) 5390 { 5391 $ret = $obj->vat_rate; 5392 if ($obj->default_vat_code) $ret .= ' ('.$obj->default_vat_code.')'; 5393 } 5394 $db->free($sql); 5395 } else dol_print_error($db); 5396 } else $ret = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS; // Forced value if autodetect fails 5397 } 5398 5399 dol_syslog("get_product_vat_for_country: ret=".$ret); 5400 return $ret; 5401} 5402 5403/** 5404 * Return localtax vat rate of a product in a particular selling country or default country vat if product is unknown 5405 * 5406 * @param int $idprod Id of product 5407 * @param int $local 1 for localtax1, 2 for localtax 2 5408 * @param Societe $thirdparty_seller Thirdparty with a ->country_code defined (FR, US, IT, ...) 5409 * @return int <0 if KO, Vat rate if OK 5410 * @see get_product_vat_for_country() 5411 */ 5412function get_product_localtax_for_country($idprod, $local, $thirdparty_seller) 5413{ 5414 global $db, $mysoc; 5415 5416 if (!class_exists('Product')) { 5417 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; 5418 } 5419 5420 $ret = 0; 5421 $found = 0; 5422 5423 if ($idprod > 0) 5424 { 5425 // Load product 5426 $product = new Product($db); 5427 $result = $product->fetch($idprod); 5428 5429 if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours 5430 { 5431 /* Not defined yet, so we don't use this 5432 if ($local==1) $ret=$product->localtax1_tx; 5433 elseif ($local==2) $ret=$product->localtax2_tx; 5434 $found=1; 5435 */ 5436 } else { 5437 // TODO Read default product vat according to countrycode and product 5438 } 5439 } 5440 5441 if (!$found) 5442 { 5443 // If vat of product for the country not found or not defined, we return higher vat of country. 5444 $sql = "SELECT taux as vat_rate, localtax1, localtax2"; 5445 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; 5446 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdparty_seller->country_code)."'"; 5447 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC"; 5448 $sql .= $db->plimit(1); 5449 5450 $resql = $db->query($sql); 5451 if ($resql) 5452 { 5453 $obj = $db->fetch_object($resql); 5454 if ($obj) 5455 { 5456 if ($local == 1) $ret = $obj->localtax1; 5457 elseif ($local == 2) $ret = $obj->localtax2; 5458 } 5459 } else dol_print_error($db); 5460 } 5461 5462 dol_syslog("get_product_localtax_for_country: ret=".$ret); 5463 return $ret; 5464} 5465 5466/** 5467 * Function that return vat rate of a product line (according to seller, buyer and product vat rate) 5468 * Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle. 5469 * Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle. 5470 * 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. 5471 * 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 5472 * Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise avec num TVA) intra alors TVA par defaut=0. Fin de regle 5473 * Sinon TVA proposee par defaut=0. Fin de regle. 5474 * 5475 * @param Societe $thirdparty_seller Objet societe vendeuse 5476 * @param Societe $thirdparty_buyer Objet societe acheteuse 5477 * @param int $idprod Id product 5478 * @param int $idprodfournprice Id product_fournisseur_price (for supplier order/invoice) 5479 * @return float|string Vat rate to use with format 5.0 or '5.0 (XXX)', -1 if we can't guess it 5480 * @see get_default_npr(), get_default_localtax() 5481 */ 5482function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0) 5483{ 5484 global $conf; 5485 5486 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; 5487 5488 // Note: possible values for tva_assuj are 0/1 or franchise/reel 5489 $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; 5490 5491 $seller_country_code = $thirdparty_seller->country_code; 5492 $seller_in_cee = isInEEC($thirdparty_seller); 5493 5494 $buyer_country_code = $thirdparty_buyer->country_code; 5495 $buyer_in_cee = isInEEC($thirdparty_buyer); 5496 5497 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($conf->global->SERVICES_ARE_ECOMMERCE_200238EC) ? $conf->global->SERVICES_ARE_ECOMMERCE_200238EC : '')); 5498 5499 // 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) 5500 // we use the buyer VAT. 5501 if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) 5502 { 5503 if ($seller_in_cee && $buyer_in_cee && !$thirdparty_buyer->isACompany()) 5504 { 5505 //print 'VATRULE 0'; 5506 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice); 5507 } 5508 } 5509 5510 // If seller does not use VAT 5511 if (!$seller_use_vat) 5512 { 5513 //print 'VATRULE 1'; 5514 return 0; 5515 } 5516 5517 // 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. 5518 5519 // Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle. 5520 if (($seller_country_code == $buyer_country_code) 5521 || (in_array($seller_country_code, array('FR,MC')) && in_array($buyer_country_code, array('FR', 'MC')))) // Warning ->country_code not always defined 5522 { 5523 //print 'VATRULE 2'; 5524 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice); 5525 } 5526 5527 // 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. 5528 // Not supported 5529 5530 // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle 5531 // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle 5532 if (($seller_in_cee && $buyer_in_cee)) 5533 { 5534 $isacompany = $thirdparty_buyer->isACompany(); 5535 if ($isacompany) 5536 { 5537 if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID) && !isValidVATID($thirdparty_buyer)) { 5538 //print 'VATRULE 6'; 5539 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice); 5540 } 5541 //print 'VATRULE 3'; 5542 return 0; 5543 } else { 5544 //print 'VATRULE 4'; 5545 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice); 5546 } 5547 } 5548 5549 // Si (vendeur en France et acheteur hors Communaute europeenne et acheteur particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle 5550 if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee) && !$thirdparty_buyer->isACompany()) { 5551 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice); 5552 } 5553 5554 // Sinon la TVA proposee par defaut=0. Fin de regle. 5555 // Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe 5556 //print 'VATRULE 5'; 5557 return 0; 5558} 5559 5560 5561/** 5562 * Fonction qui renvoie si tva doit etre tva percue recuperable 5563 * 5564 * @param Societe $thirdparty_seller Thirdparty seller 5565 * @param Societe $thirdparty_buyer Thirdparty buyer 5566 * @param int $idprod Id product 5567 * @param int $idprodfournprice Id supplier price for product 5568 * @return float 0 or 1 5569 * @see get_default_tva(), get_default_localtax() 5570 */ 5571function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0) 5572{ 5573 global $db; 5574 5575 if ($idprodfournprice > 0) 5576 { 5577 if (!class_exists('ProductFournisseur')) { 5578 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php'; 5579 } 5580 $prodprice = new ProductFournisseur($db); 5581 $prodprice->fetch_product_fournisseur_price($idprodfournprice); 5582 return $prodprice->fourn_tva_npr; 5583 } elseif ($idprod > 0) 5584 { 5585 if (!class_exists('Product')) { 5586 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; 5587 } 5588 $prod = new Product($db); 5589 $prod->fetch($idprod); 5590 return $prod->tva_npr; 5591 } 5592 5593 return 0; 5594} 5595 5596/** 5597 * Function that return localtax of a product line (according to seller, buyer and product vat rate) 5598 * Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle. 5599 * Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle. 5600 * Sinon TVA proposee par defaut=0. Fin de regle. 5601 * 5602 * @param Societe $thirdparty_seller Thirdparty seller 5603 * @param Societe $thirdparty_buyer Thirdparty buyer 5604 * @param int $local Localtax to process (1 or 2) 5605 * @param int $idprod Id product 5606 * @return integer localtax, -1 si ne peut etre determine 5607 * @see get_default_tva(), get_default_npr() 5608 */ 5609function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0) 5610{ 5611 global $mysoc; 5612 5613 if (!is_object($thirdparty_seller)) return -1; 5614 if (!is_object($thirdparty_buyer)) return -1; 5615 5616 if ($local == 1) // Localtax 1 5617 { 5618 if ($mysoc->country_code == 'ES') 5619 { 5620 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) return 0; 5621 } else { 5622 // Si vendeur non assujeti a Localtax1, localtax1 par default=0 5623 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) return 0; 5624 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') return 0; 5625 } 5626 } elseif ($local == 2) //I Localtax 2 5627 { 5628 // Si vendeur non assujeti a Localtax2, localtax2 par default=0 5629 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) return 0; 5630 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') return 0; 5631 } 5632 5633 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) 5634 { 5635 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller); 5636 } 5637 5638 return 0; 5639} 5640 5641/** 5642 * Return yes or no in current language 5643 * 5644 * @param string|int $yesno Value to test (1, 'yes', 'true' or 0, 'no', 'false') 5645 * @param integer $case 1=Yes/No, 0=yes/no, 2=Disabled checkbox, 3=Disabled checkbox + Yes/No 5646 * @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. 5647 * @return string HTML string 5648 */ 5649function yn($yesno, $case = 1, $color = 0) 5650{ 5651 global $langs; 5652 $result = 'unknown'; $classname = ''; 5653 if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') // A mettre avant test sur no a cause du == 0 5654 { 5655 $result = $langs->trans('yes'); 5656 if ($case == 1 || $case == 3) $result = $langs->trans("Yes"); 5657 if ($case == 2) $result = '<input type="checkbox" value="1" checked disabled>'; 5658 if ($case == 3) $result = '<input type="checkbox" value="1" checked disabled> '.$result; 5659 5660 $classname = 'ok'; 5661 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') 5662 { 5663 $result = $langs->trans("no"); 5664 if ($case == 1 || $case == 3) $result = $langs->trans("No"); 5665 if ($case == 2) $result = '<input type="checkbox" value="0" disabled>'; 5666 if ($case == 3) $result = '<input type="checkbox" value="0" disabled> '.$result; 5667 5668 if ($color == 2) $classname = 'ok'; 5669 else $classname = 'error'; 5670 } 5671 if ($color) return '<font class="'.$classname.'">'.$result.'</font>'; 5672 return $result; 5673} 5674 5675/** 5676 * Return a path to have a the directory according to object where files are stored. 5677 * New usage: $conf->module->multidir_output[$object->entity].'/'.get_exdir(0, 0, 0, 1, $object, '').'/' 5678 * or: $conf->module->dir_output.'/'.get_exdir(0, 0, 0, 0, $object, '') if multidir_output not defined. 5679 * Example out with new usage: $object is invoice -> 'INYYMM-ABCD' 5680 * Example out with old usage: '015' with level 3->"0/1/5/", '015' with level 1->"5/", 'ABC-1' with level 3 ->"0/0/1/" 5681 * 5682 * @param string|int $num Id of object (deprecated, $object will be used in future) 5683 * @param int $level Level of subdirs to return (1, 2 or 3 levels). (deprecated, global option will be used in future) 5684 * @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) 5685 * @param int $withoutslash 0=With slash at end (except if '/', we return ''), 1=without slash at end 5686 * @param Object $object Object to use to get ref to forge the path. 5687 * @param string $modulepart Type of object ('invoice_supplier, 'donation', 'invoice', ...'). Use '' for autodetect from $object. 5688 * @return string Dir to use ending. Example '' or '1/' or '1/2/' 5689 */ 5690function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '') 5691{ 5692 global $conf; 5693 5694 if (empty($modulepart) && !empty($object->module)) $modulepart = $object->module; 5695 5696 $path = ''; 5697 5698 $arrayforoldpath = array('cheque', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment'); 5699 if (!empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) $arrayforoldpath[] = 'product'; 5700 if (!empty($level) && in_array($modulepart, $arrayforoldpath)) { 5701 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided. 5702 if (empty($alpha)) $num = preg_replace('/([^0-9])/i', '', $num); 5703 else $num = preg_replace('/^.*\-/i', '', $num); 5704 $num = substr("000".$num, -$level); 5705 if ($level == 1) $path = substr($num, 0, 1); 5706 if ($level == 2) $path = substr($num, 1, 1).'/'.substr($num, 0, 1); 5707 if ($level == 3) $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1); 5708 } else { 5709 // We will enhance here a common way of forging path for document storage. 5710 // In a future, we may distribute directories on several levels depending on setup and object. 5711 // Here, $object->id, $object->ref and $modulepart are required. 5712 //var_dump($modulepart); 5713 $path = dol_sanitizeFileName(empty($object->ref) ? (string) $object->id : $object->ref); 5714 } 5715 5716 if (empty($withoutslash) && !empty($path)) $path .= '/'; 5717 5718 return $path; 5719} 5720 5721/** 5722 * Creation of a directory (this can create recursive subdir) 5723 * 5724 * @param string $dir Directory to create (Separator must be '/'. Example: '/mydir/mysubdir') 5725 * @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) 5726 * @param string|null $newmask Mask for new file (Defaults to $conf->global->MAIN_UMASK or 0755 if unavailable). Example: '0444' 5727 * @return int < 0 if KO, 0 = already exists, > 0 if OK 5728 */ 5729function dol_mkdir($dir, $dataroot = '', $newmask = null) 5730{ 5731 global $conf; 5732 5733 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO); 5734 5735 $dir_osencoded = dol_osencode($dir); 5736 if (@is_dir($dir_osencoded)) return 0; 5737 5738 $nberr = 0; 5739 $nbcreated = 0; 5740 5741 $ccdir = ''; 5742 if (!empty($dataroot)) { 5743 // Remove data root from loop 5744 $dir = str_replace($dataroot.'/', '', $dir); 5745 $ccdir = $dataroot.'/'; 5746 } 5747 5748 $cdir = explode("/", $dir); 5749 $num = count($cdir); 5750 for ($i = 0; $i < $num; $i++) 5751 { 5752 if ($i > 0) $ccdir .= '/'.$cdir[$i]; 5753 else $ccdir .= $cdir[$i]; 5754 if (preg_match("/^.:$/", $ccdir, $regs)) continue; // Si chemin Windows incomplet, on poursuit par rep suivant 5755 5756 // Attention, le is_dir() peut echouer bien que le rep existe. 5757 // (ex selon config de open_basedir) 5758 if ($ccdir) 5759 { 5760 $ccdir_osencoded = dol_osencode($ccdir); 5761 if (!@is_dir($ccdir_osencoded)) 5762 { 5763 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG); 5764 5765 umask(0); 5766 $dirmaskdec = octdec($newmask); 5767 if (empty($newmask)) { 5768 $dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK); 5769 } 5770 $dirmaskdec |= octdec('0111'); // Set x bit required for directories 5771 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) 5772 { 5773 // Si le is_dir a renvoye une fausse info, alors on passe ici. 5774 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING); 5775 $nberr++; 5776 } else { 5777 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG); 5778 $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore 5779 $nbcreated++; 5780 } 5781 } else { 5782 $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores 5783 } 5784 } 5785 } 5786 return ($nberr ? -$nberr : $nbcreated); 5787} 5788 5789 5790/** 5791 * Return picto saying a field is required 5792 * 5793 * @return string Chaine avec picto obligatoire 5794 */ 5795function picto_required() 5796{ 5797 return '<span class="fieldrequired">*</span>'; 5798} 5799 5800 5801/** 5802 * Clean a string from all HTML tags and entities. 5803 * This function differs from strip_tags because: 5804 * - <br> are replaced with \n if removelinefeed=0 or 1 5805 * - if entities are found, they are decoded BEFORE the strip 5806 * - you can decide to convert line feed into a space 5807 * 5808 * @param string $stringtoclean String to clean 5809 * @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..." 5810 * @param string $pagecodeto Encoding of input/output string 5811 * @param integer $strip_tags 0=Use internal strip, 1=Use strip_tags() php function (bugged when text contains a < char that is not for a html tag or when tags is not closed like '<img onload=aaa') 5812 * @param integer $removedoublespaces Replace double space into one space 5813 * @return string String cleaned 5814 * 5815 * @see dol_escape_htmltag() strip_tags() dol_string_onlythesehtmltags() dol_string_neverthesehtmltags(), dolStripPhpCode() 5816 */ 5817function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1) 5818{ 5819 if ($removelinefeed == 2) $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean); 5820 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean); 5821 5822 // We remove entities BEFORE stripping (in case of an open separator char that is entity encoded and not the closing other, the strip will fails) 5823 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto); 5824 5825 $temp = str_replace('< ', '__ltspace__', $temp); 5826 5827 if ($strip_tags) { 5828 $temp = strip_tags($temp); 5829 } else { 5830 $pattern = "/<[^<>]+>/"; 5831 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a> 5832 $temp = preg_replace($pattern, "", $temp); // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021 5833 $temp = preg_replace($pattern, "", $temp); // pass 2 - $temp after pass 2: 0000-021 5834 // Remove '<' into remainging, so non closing html tags like '<abc'. Note: '<123abc' is not a html tag (can be kept), but '<abc123' is (must be removed). 5835 $temp = preg_replace('/<([a-z]+)/i', '\1', $temp); 5836 } 5837 5838 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto); 5839 5840 // Remove also carriage returns 5841 if ($removelinefeed == 1) $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp); 5842 5843 // And double quotes 5844 if ($removedoublespaces) { 5845 while (strpos($temp, " ")) { 5846 $temp = str_replace(" ", " ", $temp); 5847 } 5848 } 5849 5850 $temp = str_replace('__ltspace__', '< ', $temp); 5851 5852 return trim($temp); 5853} 5854 5855/** 5856 * Clean a string to keep only desirable HTML tags. 5857 * WARNING: This also clean HTML comments (used to obfuscate tag name). 5858 * 5859 * @param string $stringtoclean String to clean 5860 * @param int $cleanalsosomestyles Remove absolute/fixed positioning from inline styles 5861 * @param int $removeclassattribute Remove the class attribute from tags 5862 * @param int $cleanalsojavascript Remove also occurence of 'javascript:'. 5863 * @return string String cleaned 5864 * 5865 * @see dol_escape_htmltag() strip_tags() dol_string_nohtmltag() dol_string_neverthesehtmltags() 5866 */ 5867function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0) 5868{ 5869 $allowed_tags = array( 5870 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li", "link", 5871 "ol", "p", "q", "s", "section", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6" 5872 ); 5873 5874 $allowed_tags_string = join("><", $allowed_tags); 5875 $allowed_tags_string = '<'.$allowed_tags_string.'>'; 5876 5877 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags 5878 5879 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0); 5880 $stringtoclean = preg_replace('/:/i', ':', $stringtoclean); 5881 5882 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean); 5883 $stringtoclean = preg_replace('/:|:|:/i', '', $stringtoclean); // refused string ':' encoded (no reason to have it encoded) to lock 'javascript:...' 5884 $stringtoclean = preg_replace('/javascript\s*:/i', '', $stringtoclean); 5885 5886 $temp = strip_tags($stringtoclean, $allowed_tags_string); 5887 5888 if ($cleanalsosomestyles) { // Clean for remaining html tags 5889 $temp = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/i', '', $temp); // Note: If hacker try to introduce css comment into string to bypass this regex, the string must also be encoded by the dol_htmlentitiesbr during output so it become harmless 5890 } 5891 if ($removeclassattribute) { // Clean for remaining html tags 5892 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp); 5893 } 5894 5895 // Remove 'javascript:' that we should not find into a text with 5896 // Warning: This is not reliable to fight against obfuscated javascript, there is a lot of other solution to include js into a common html tag (only filtered by the GETPOST). 5897 if ($cleanalsojavascript) { 5898 $temp = preg_replace('/javascript\s*:/i', '', $temp); 5899 } 5900 5901 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE 5902 5903 return $temp; 5904} 5905 5906/** 5907 * Clean a string from some undesirable HTML tags. 5908 * Note. Not as secured as dol_string_onlythesehtmltags(). 5909 * 5910 * @param string $stringtoclean String to clean 5911 * @param array $disallowed_tags Array of tags not allowed 5912 * @param string $cleanalsosomestyles Clean also some tags 5913 * @return string String cleaned 5914 * 5915 * @see dol_escape_htmltag() strip_tags() dol_string_nohtmltag() dol_string_onlythesehtmltags() 5916 */ 5917function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0) 5918{ 5919 $temp = $stringtoclean; 5920 foreach ($disallowed_tags as $tagtoremove) 5921 { 5922 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp); 5923 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp); 5924 } 5925 5926 if ($cleanalsosomestyles) { 5927 $temp = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/', '', $temp); // Note: If hacker try to introduce css comment into string to avoid this, string should be encoded by the dol_htmlentitiesbr so be harmless 5928 } 5929 5930 return $temp; 5931} 5932 5933 5934/** 5935 * Return first line of text. Cut will depends if content is HTML or not. 5936 * 5937 * @param string $text Input text 5938 * @param int $nboflines Nb of lines to get (default is 1 = first line only) 5939 * @param string $charset Charset of $text string (UTF-8 by default) 5940 * @return string Output text 5941 * @see dol_nboflines_bis(), dol_string_nohtmltag(), dol_escape_htmltag() 5942 */ 5943function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8') 5944{ 5945 if ($nboflines == 1) 5946 { 5947 if (dol_textishtml($text)) 5948 { 5949 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters 5950 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters 5951 } else { 5952 $firstline = preg_replace('/[\n\r].*/', '', $text); 5953 } 5954 return $firstline.((strlen($firstline) != strlen($text)) ? '...' : ''); 5955 } else { 5956 $ishtml = 0; 5957 if (dol_textishtml($text)) 5958 { 5959 $text = preg_replace('/\n/', '', $text); 5960 $ishtml = 1; 5961 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " "); 5962 } else { 5963 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " "); 5964 } 5965 5966 $text = strtr($text, $repTable); 5967 if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; } // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support 5968 else $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag. 5969 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 5970 5971 $firstline = ''; 5972 $i = 0; 5973 $nba = count($a); // 2x nb of lines in $a because $a contains also a line for each new line separator 5974 while (($i < $nba) && ($i < ($nboflines * 2))) 5975 { 5976 if ($i % 2 == 0) $firstline .= $a[$i]; 5977 elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) $firstline .= ($ishtml ? "<br>\n" : "\n"); 5978 $i++; 5979 } 5980 unset($a); 5981 return $firstline.(($i < $nba) ? '...' : ''); 5982 } 5983} 5984 5985 5986/** 5987 * Replace CRLF in string with a HTML BR tag. 5988 * WARNING: The content after operation contains some HTML tags (the <br>) so be sure to also have encode the special chars of stringtoencode into HTML before. 5989 * 5990 * @param string $stringtoencode String to encode 5991 * @param int $nl2brmode 0=Adding br before \n, 1=Replacing \n by br 5992 * @param bool $forxml false=Use <br>, true=Use <br /> 5993 * @return string String encoded 5994 * @see dol_nboflines(), dolGetFirstLineOfText() 5995 */ 5996function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false) 5997{ 5998 if (!$nl2brmode) { 5999 return nl2br($stringtoencode, $forxml); 6000 } else { 6001 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode); 6002 return $ret; 6003 } 6004} 6005 6006 6007/** 6008 * This function is called to encode a string into a HTML string but differs from htmlentities because 6009 * a detection is done before to see if text is already HTML or not. Also, all entities but &,<,>," are converted. 6010 * This permits to encode special chars to entities with no double encoding for already encoded HTML strings. 6011 * This function also remove last EOL or BR if $removelasteolbr=1 (default). 6012 * For PDF usage, you can show text by 2 ways: 6013 * - writeHTMLCell -> param must be encoded into HTML. 6014 * - MultiCell -> param must not be encoded into HTML. 6015 * Because writeHTMLCell convert also \n into <br>, if function 6016 * is used to build PDF, nl2brmode must be 1. 6017 * 6018 * @param string $stringtoencode String to encode 6019 * @param int $nl2brmode 0=Adding br before \n, 1=Replacing \n by br (for use with FPDF writeHTMLCell function for example) 6020 * @param string $pagecodefrom Pagecode stringtoencode is encoded 6021 * @param int $removelasteolbr 1=Remove last br or lasts \n (default), 0=Do nothing 6022 * @return string String encoded 6023 */ 6024function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1) 6025{ 6026 $newstring = $stringtoencode; 6027 if (dol_textishtml($stringtoencode)) // Check if text is already HTML or not 6028 { 6029 $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. 6030 if ($removelasteolbr) $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one) 6031 $newstring = strtr($newstring, array('&'=>'__and__', '<'=>'__lt__', '>'=>'__gt__', '"'=>'__dquot__')); 6032 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding 6033 $newstring = strtr($newstring, array('__and__'=>'&', '__lt__'=>'<', '__gt__'=>'>', '__dquot__'=>'"')); 6034 } else { 6035 if ($removelasteolbr) $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several) 6036 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode); 6037 } 6038 // Other substitutions that htmlentities does not do 6039 //$newstring=str_replace(chr(128),'€',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages 6040 return $newstring; 6041} 6042 6043/** 6044 * This function is called to decode a HTML string (it decodes entities and br tags) 6045 * 6046 * @param string $stringtodecode String to decode 6047 * @param string $pagecodeto Page code for result 6048 * @return string String decoded 6049 */ 6050function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8') 6051{ 6052 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto); 6053 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret); 6054 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret); 6055 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret); 6056 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret); 6057 return $ret; 6058} 6059 6060/** 6061 * This function remove all ending \n and br at end 6062 * 6063 * @param string $stringtodecode String to decode 6064 * @return string String decoded 6065 */ 6066function dol_htmlcleanlastbr($stringtodecode) 6067{ 6068 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $stringtodecode); 6069 return $ret; 6070} 6071 6072/** 6073 * Replace html_entity_decode functions to manage errors 6074 * 6075 * @param string $a Operand a 6076 * @param string $b Operand b (ENT_QUOTES|ENT_HTML5=convert simple, double quotes, colon, e accent, ...) 6077 * @param string $c Operand c 6078 * @param string $keepsomeentities Entities but &, <, >, " are not converted. 6079 * @return string String decoded 6080 */ 6081function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0) 6082{ 6083 $newstring = $a; 6084 if ($keepsomeentities) $newstring = strtr($newstring, array('&'=>'__andamp__', '<'=>'__andlt__', '>'=>'__andgt__', '"'=>'__dquot__')); 6085 $newstring = html_entity_decode($newstring, $b, $c); 6086 if ($keepsomeentities) $newstring = strtr($newstring, array('__andamp__'=>'&', '__andlt__'=>'<', '__andgt__'=>'>', '__dquot__'=>'"')); 6087 return $newstring; 6088} 6089 6090/** 6091 * Replace htmlentities functions. 6092 * Goal of this function is to be sure to have default values of htmlentities that match what we need. 6093 * 6094 * @param string $string The input string to encode 6095 * @param int $flags Flags (see PHP doc above) 6096 * @param string $encoding Encoding page code 6097 * @param bool $double_encode When double_encode is turned off, PHP will not encode existing html entities 6098 * @return string $ret Encoded string 6099 */ 6100function dol_htmlentities($string, $flags = null, $encoding = 'UTF-8', $double_encode = false) 6101{ 6102 return htmlentities($string, $flags, $encoding, $double_encode); 6103} 6104 6105/** 6106 * Check if a string is a correct iso string 6107 * If not, it will we considered not HTML encoded even if it is by FPDF. 6108 * Example, if string contains euro symbol that has ascii code 128 6109 * 6110 * @param string $s String to check 6111 * @param string $clean Clean if it is not an ISO. Warning, if file is utf8, you will get a bad formated file. 6112 * @return int|string 0 if bad iso, 1 if good iso, Or the clean string if $clean is 1 6113 */ 6114function dol_string_is_good_iso($s, $clean = 0) 6115{ 6116 $len = dol_strlen($s); 6117 $out = ''; 6118 $ok = 1; 6119 for ($scursor = 0; $scursor < $len; $scursor++) 6120 { 6121 $ordchar = ord($s[$scursor]); 6122 //print $scursor.'-'.$ordchar.'<br>'; 6123 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) { $ok = 0; break; } elseif ($ordchar > 126 && $ordchar < 160) { $ok = 0; break; } elseif ($clean) { 6124 $out .= $s[$scursor]; 6125 } 6126 } 6127 if ($clean) return $out; 6128 return $ok; 6129} 6130 6131/** 6132 * Return nb of lines of a clear text 6133 * 6134 * @param string $s String to check 6135 * @param int $maxchar Not yet used 6136 * @return int Number of lines 6137 * @see dol_nboflines_bis(), dolGetFirstLineOfText() 6138 */ 6139function dol_nboflines($s, $maxchar = 0) 6140{ 6141 if ($s == '') return 0; 6142 $arraystring = explode("\n", $s); 6143 $nb = count($arraystring); 6144 6145 return $nb; 6146} 6147 6148 6149/** 6150 * Return nb of lines of a formated text with \n and <br> (WARNING: string must not have mixed \n and br separators) 6151 * 6152 * @param string $text Text 6153 * @param int $maxlinesize Largeur de ligne en caracteres (ou 0 si pas de limite - defaut) 6154 * @param string $charset Give the charset used to encode the $text variable in memory. 6155 * @return int Number of lines 6156 * @see dol_nboflines(), dolGetFirstLineOfText() 6157 */ 6158function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8') 6159{ 6160 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " "); 6161 if (dol_textishtml($text)) $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " "); 6162 6163 $text = strtr($text, $repTable); 6164 if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; } // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support 6165 else $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag. 6166 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 6167 6168 $nblines = (int) floor((count($a) + 1) / 2); 6169 // count possible auto line breaks 6170 if ($maxlinesize) 6171 { 6172 foreach ($a as $line) 6173 { 6174 if (dol_strlen($line) > $maxlinesize) 6175 { 6176 //$line_dec = html_entity_decode(strip_tags($line)); 6177 $line_dec = html_entity_decode($line); 6178 if (dol_strlen($line_dec) > $maxlinesize) 6179 { 6180 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true); 6181 $nblines += substr_count($line_dec, '\n'); 6182 } 6183 } 6184 } 6185 } 6186 6187 unset($a); 6188 return $nblines; 6189} 6190 6191/** 6192 * Return if a text is a html content 6193 * 6194 * @param string $msg Content to check 6195 * @param int $option 0=Full detection, 1=Fast check 6196 * @return boolean true/false 6197 * @see dol_concatdesc() 6198 */ 6199function dol_textishtml($msg, $option = 0) 6200{ 6201 if ($option == 1) 6202 { 6203 if (preg_match('/<html/i', $msg)) return true; 6204 elseif (preg_match('/<body/i', $msg)) return true; 6205 elseif (preg_match('/<\/textarea/i', $msg)) return true; 6206 elseif (preg_match('/<br/i', $msg)) return true; 6207 return false; 6208 } else { 6209 if (preg_match('/<html/i', $msg)) return true; 6210 elseif (preg_match('/<body/i', $msg)) return true; 6211 elseif (preg_match('/<\/textarea/i', $msg)) return true; 6212 elseif (preg_match('/<(b|em|i|u)>/i', $msg)) return true; 6213 elseif (preg_match('/<br\/>/i', $msg)) return true; 6214 elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i', $msg)) return true; 6215 elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*>/i', $msg)) return true; 6216 elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/>/i', $msg)) return true; 6217 elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) return true; // must accept <img src="http://example.com/aaa.png" /> 6218 elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) return true; // must accept <a href="http://example.com/aaa.png" /> 6219 elseif (preg_match('/<h[0-9]>/i', $msg)) return true; 6220 elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp) 6221 elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp) 6222 6223 return false; 6224 } 6225} 6226 6227/** 6228 * Concat 2 descriptions with a new line between them (second operand after first one with appropriate new line separator) 6229 * text1 html + text2 html => text1 + '<br>' + text2 6230 * text1 html + text2 txt => text1 + '<br>' + dol_nl2br(text2) 6231 * text1 txt + text2 html => dol_nl2br(text1) + '<br>' + text2 6232 * text1 txt + text2 txt => text1 + '\n' + text2 6233 * 6234 * @param string $text1 Text 1 6235 * @param string $text2 Text 2 6236 * @param bool $forxml true=Use <br /> instead of <br> if we have to add a br tag 6237 * @param bool $invert invert order of description lines (we often use config MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION in this parameter) 6238 * @return string Text 1 + new line + Text2 6239 * @see dol_textishtml() 6240 */ 6241function dol_concatdesc($text1, $text2, $forxml = false, $invert = false) 6242{ 6243 if (!empty($invert)) 6244 { 6245 $tmp = $text1; 6246 $text1 = $text2; 6247 $text2 = $tmp; 6248 } 6249 6250 $ret = ''; 6251 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1; 6252 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br \>\n" : "<br>\n") : "\n") : ""; 6253 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2; 6254 return $ret; 6255} 6256 6257 6258 6259/** 6260 * Return array of possible common substitutions. This includes several families like: 'system', 'mycompany', 'object', 'objectamount', 'date', 'user' 6261 * 6262 * @param Translate $outputlangs Output language 6263 * @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) 6264 * @param array $exclude Array of family keys we want to exclude. For example array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...) 6265 * @param Object $object Object for keys on object 6266 * @return array Array of substitutions 6267 * @see setSubstitFromObject() 6268 */ 6269function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null) 6270{ 6271 global $db, $conf, $mysoc, $user, $extrafields; 6272 6273 $substitutionarray = array(); 6274 6275 if (empty($exclude) || !in_array('user', $exclude)) 6276 { 6277 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution, 6278 // this will include signature content first and then replace var found into content of signature 6279 $signature = $user->signature; 6280 $substitutionarray = array_merge($substitutionarray, array( 6281 '__USER_SIGNATURE__' => (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '') 6282 ) 6283 ); 6284 6285 $substitutionarray = array_merge($substitutionarray, array( 6286 '__USER_ID__' => (string) $user->id, 6287 '__USER_LOGIN__' => (string) $user->login, 6288 '__USER_EMAIL__' => (string) $user->email, 6289 '__USER_LASTNAME__' => (string) $user->lastname, 6290 '__USER_FIRSTNAME__' => (string) $user->firstname, 6291 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs), 6292 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'), 6293 '__USER_REMOTE_IP__' => (string) getUserRemoteIP() 6294 ) 6295 ); 6296 } 6297 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc)) 6298 { 6299 $substitutionarray = array_merge($substitutionarray, array( 6300 '__MYCOMPANY_NAME__' => $mysoc->name, 6301 '__MYCOMPANY_EMAIL__' => $mysoc->email, 6302 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1, 6303 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2, 6304 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3, 6305 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4, 6306 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5, 6307 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6, 6308 '__MYCOMPANY_CAPITAL__' => $mysoc->capital, 6309 '__MYCOMPANY_FULLADDRESS__' => $mysoc->getFullAddress(1, ', '), 6310 '__MYCOMPANY_ADDRESS__' => $mysoc->address, 6311 '__MYCOMPANY_ZIP__' => $mysoc->zip, 6312 '__MYCOMPANY_TOWN__' => $mysoc->town, 6313 '__MYCOMPANY_COUNTRY__' => $mysoc->country, 6314 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id, 6315 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code, 6316 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency 6317 )); 6318 } 6319 6320 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude))) 6321 { 6322 if ($onlykey) 6323 { 6324 $substitutionarray['__ID__'] = '__ID__'; 6325 $substitutionarray['__REF__'] = '__REF__'; 6326 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__'; 6327 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__'; 6328 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__'; 6329 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__'; 6330 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__'; 6331 6332 if (!empty($conf->societe->enabled)) // Most objects are concerned 6333 { 6334 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__'; 6335 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__'; 6336 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__'; 6337 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__'; 6338 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__'; 6339 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__'; 6340 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__'; 6341 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__'; 6342 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__'; 6343 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__'; 6344 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__'; 6345 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__'; 6346 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__'; 6347 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__'; 6348 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__'; 6349 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__'; 6350 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__'; 6351 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__'; 6352 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__'; 6353 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__'; 6354 } 6355 if (!empty($conf->adherent->enabled) && (!is_object($object) || $object->element == 'adherent')) 6356 { 6357 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__'; 6358 $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__'; 6359 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__'; 6360 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__'; 6361 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__'; 6362 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/ 6363 } 6364 if (!empty($conf->recruitment->enabled) && (!is_object($object) || $object->element == 'candidature')) 6365 { 6366 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__'; 6367 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__'; 6368 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__'; 6369 } 6370 if (!empty($conf->projet->enabled)) // Most objects 6371 { 6372 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__'; 6373 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__'; 6374 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__'; 6375 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__'; 6376 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/ 6377 } 6378 if (!empty($conf->contrat->enabled) && (!is_object($object) || $object->element == 'contract')) 6379 { 6380 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start'; 6381 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start'; 6382 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service'; 6383 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service'; 6384 } 6385 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable'; 6386 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable'; 6387 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)'; 6388 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)'; 6389 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order'; 6390 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice'; 6391 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a a service'; 6392 6393 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal'; 6394 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order'; 6395 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice'; 6396 6397 if (!empty($conf->expedition->enabled) && (!is_object($object) || $object->element == 'shipping')) 6398 { 6399 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number'; 6400 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url'; 6401 } 6402 if (!empty($conf->reception->enabled) && (!is_object($object) || $object->element == 'reception')) 6403 { 6404 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shippin tracking number of shipment'; 6405 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url'; 6406 } 6407 } else { 6408 $substitutionarray['__ID__'] = $object->id; 6409 $substitutionarray['__REF__'] = $object->ref; 6410 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null)); 6411 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null); 6412 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null); 6413 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null); 6414 6415 $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : ''); 6416 6417 // For backward compatibility 6418 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null)); 6419 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null); 6420 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null)); 6421 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null); 6422 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : ''); 6423 $substitutionarray['__SUPPLIER_ORDER_DELAY_DELIVERY__'] = (isset($object->availability_code) ? ($outputlangs->transnoentities("AvailabilityType".$object->availability_code) != ('AvailabilityType'.$object->availability_code) ? $outputlangs->transnoentities("AvailabilityType".$object->availability_code) : $outputlangs->convToOutputCharset(isset($object->availability) ? $object->availability : '')) : ''); 6424 6425 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) 6426 { 6427 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day')); 6428 6429 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : ''); 6430 if (method_exists($object, 'getCivilityLabel')) $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel(); 6431 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : ''); 6432 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : ''); 6433 if (method_exists($object, 'getFullName')) $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs); 6434 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : ''); 6435 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : ''); 6436 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : ''); 6437 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : ''); 6438 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : ''); 6439 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : ''); 6440 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : ''); 6441 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : ''); 6442 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : ''); 6443 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : ''); 6444 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? $object->phone : ''); 6445 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? $object->phone_perso : ''); 6446 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? $object->phone_mobile : ''); 6447 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : ''); 6448 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'dayrfc'); 6449 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->first_subscription_date_start, 'dayrfc'); 6450 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->first_subscription_date_end, 'dayrfc'); 6451 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'dayrfc'); 6452 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc'); 6453 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc'); 6454 } 6455 6456 if (is_object($object) && $object->element == 'societe') { 6457 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : ''); 6458 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : ''); 6459 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : ''); 6460 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : ''); 6461 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : ''); 6462 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : ''); 6463 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? $object->phone : ''); 6464 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? $object->fax : ''); 6465 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : ''); 6466 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : ''); 6467 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : ''); 6468 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : ''); 6469 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : ''); 6470 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : ''); 6471 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : ''); 6472 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : ''); 6473 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : ''); 6474 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : ''); 6475 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : ''); 6476 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : ''); 6477 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : ''); 6478 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : ''); 6479 } elseif (is_object($object->thirdparty)) { 6480 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : ''); 6481 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : ''); 6482 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : ''); 6483 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : ''); 6484 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : ''); 6485 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : ''); 6486 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? $object->thirdparty->phone : ''); 6487 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? $object->thirdparty->fax : ''); 6488 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : ''); 6489 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : ''); 6490 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : ''); 6491 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : ''); 6492 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : ''); 6493 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : ''); 6494 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : ''); 6495 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : ''); 6496 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : ''); 6497 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : ''); 6498 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : ''); 6499 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : ''); 6500 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : ''); 6501 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : ''); 6502 } 6503 6504 if (is_object($object) && $object->element == 'recruitmentcandidature') { 6505 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs); 6506 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = $object->firstname; 6507 $substitutionarray['__CANDIDATE_LASTNAME__'] = $object->lastname; 6508 } 6509 6510 if (is_object($object->project)) 6511 { 6512 $substitutionarray['__PROJECT_ID__'] = (is_object($object->project) ? $object->project->id : ''); 6513 $substitutionarray['__PROJECT_REF__'] = (is_object($object->project) ? $object->project->ref : ''); 6514 $substitutionarray['__PROJECT_NAME__'] = (is_object($object->project) ? $object->project->title : ''); 6515 } 6516 if (is_object($object->projet)) // Deprecated, for backward compatibility 6517 { 6518 $substitutionarray['__PROJECT_ID__'] = (is_object($object->projet) ? $object->projet->id : ''); 6519 $substitutionarray['__PROJECT_REF__'] = (is_object($object->projet) ? $object->projet->ref : ''); 6520 $substitutionarray['__PROJECT_NAME__'] = (is_object($object->projet) ? $object->projet->title : ''); 6521 } 6522 6523 if (is_object($object) && $object->element == 'shipping') 6524 { 6525 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number; 6526 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url; 6527 } 6528 if (is_object($object) && $object->element == 'reception') 6529 { 6530 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number; 6531 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url; 6532 } 6533 6534 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) 6535 { 6536 $dateplannedstart = ''; 6537 $datenextexpiration = ''; 6538 foreach ($object->lines as $line) 6539 { 6540 if ($line->date_ouverture_prevue > $dateplannedstart) $dateplannedstart = $line->date_ouverture_prevue; 6541 if ($line->statut == 4 && $line->date_fin_prevue && (!$datenextexpiration || $line->date_fin_prevue < $datenextexpiration)) $datenextexpiration = $line->date_fin_prevue; 6542 } 6543 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'dayrfc'); 6544 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard'); 6545 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'dayrfc'); 6546 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard'); 6547 } 6548 6549 // Create dynamic tags for __EXTRAFIELD_FIELD__ 6550 if ($object->table_element && $object->id > 0) 6551 { 6552 if (!is_object($extrafields)) $extrafields = new ExtraFields($db); 6553 $extrafields->fetch_name_optionals_label($object->table_element, true); 6554 6555 if ($object->fetch_optionals() > 0) 6556 { 6557 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) 6558 { 6559 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) { 6560 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key]; 6561 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') { 6562 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day'); 6563 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs); 6564 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc'); 6565 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') { 6566 $datetime = $object->array_options['options_'.$key]; 6567 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : ''); 6568 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : ''); 6569 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : ''); 6570 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : ''); 6571 } 6572 } 6573 } 6574 } 6575 } 6576 6577 // Complete substitution array with the url to make online payment 6578 $paymenturl = ''; 6579 if (empty($substitutionarray['__REF__'])) 6580 { 6581 $paymenturl = ''; 6582 } else { 6583 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key 6584 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; 6585 $outputlangs->loadLangs(array('paypal', 'other')); 6586 $typeforonlinepayment = 'free'; 6587 if (is_object($object) && $object->element == 'commande') $typeforonlinepayment = 'order'; 6588 if (is_object($object) && $object->element == 'facture') $typeforonlinepayment = 'invoice'; 6589 if (is_object($object) && $object->element == 'member') $typeforonlinepayment = 'member'; 6590 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__']); 6591 $paymenturl = $url; 6592 } 6593 6594 if ($object->id > 0) 6595 { 6596 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ?str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : ''); 6597 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl; 6598 6599 if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal') 6600 { 6601 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element); 6602 } else $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = ''; 6603 if (!empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'commande') 6604 { 6605 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element); 6606 } else $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = ''; 6607 if (!empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'facture') 6608 { 6609 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element); 6610 } else $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = ''; 6611 6612 if (is_object($object) && $object->element == 'propal') $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id; 6613 if (is_object($object) && $object->element == 'commande') $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id; 6614 if (is_object($object) && $object->element == 'facture') $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id; 6615 } 6616 6617 if (is_object($object) && $object->element == 'action') 6618 { 6619 $substitutionarray['__EVENT_LABEL__'] = $object->label; 6620 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, '%A %d %b %Y'); 6621 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, '%H:%M:%S'); 6622 } 6623 } 6624 } 6625 if (empty($exclude) || !in_array('objectamount', $exclude)) 6626 { 6627 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php'; 6628 6629 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : ''; 6630 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : ''; 6631 6632 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : ''; 6633 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : ''; 6634 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : ''; 6635 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : ''; 6636 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : ''; 6637 $substitutionarray['__AMOUNT_VAT_TEXT__'] = is_object($object) ? (isset($object->total_vat) ? dol_convertToWord($object->total_vat, $outputlangs, '', true) : dol_convertToWord($object->total_tva, $outputlangs, '', true)) : ''; 6638 $substitutionarray['__AMOUNT_VAT_TEXTCURRENCY__'] = is_object($object) ? (isset($object->total_vat) ? dol_convertToWord($object->total_vat, $outputlangs, $conf->currency, true) : dol_convertToWord($object->total_tva, $outputlangs, $conf->currency, true)) : ''; 6639 if ($onlykey != 2 || $mysoc->useLocalTax(1)) $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : ''; 6640 if ($onlykey != 2 || $mysoc->useLocalTax(2)) $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : ''; 6641 6642 $substitutionarray['__AMOUNT_FORMATED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : ''; 6643 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : ''; 6644 $substitutionarray['__AMOUNT_VAT_FORMATED__'] = is_object($object) ? (isset($object->total_vat) ? price($object->total_vat, 0, $outputlangs, 0, -1, -1, $conf->currency) : ($object->total_tva ? price($object->total_tva, 0, $outputlangs, 0, -1, -1, $conf->currency) : null)) : ''; 6645 if ($onlykey != 2 || $mysoc->useLocalTax(1)) $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : ''; 6646 if ($onlykey != 2 || $mysoc->useLocalTax(2)) $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : ''; 6647 6648 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : ''; 6649 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : ''; 6650 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : ''; 6651 // TODO Add other keys for foreign multicurrency 6652 6653 // For backward compatibility 6654 if ($onlykey != 2) 6655 { 6656 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : ''; 6657 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : ''; 6658 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : ''; 6659 } 6660 } 6661 6662 //var_dump($substitutionarray['__AMOUNT_FORMATED__']); 6663 if (empty($exclude) || !in_array('date', $exclude)) 6664 { 6665 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; 6666 6667 $tmp = dol_getdate(dol_now(), true); 6668 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']); 6669 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']); 6670 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']); 6671 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']); 6672 6673 $daytext = $outputlangs->trans('Day'.$tmp['wday']); 6674 6675 $substitutionarray = array_merge($substitutionarray, array( 6676 '__DAY__' => (string) $tmp['mday'], 6677 '__DAY_TEXT__' => $daytext, // Monday 6678 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon 6679 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M 6680 '__MONTH__' => (string) $tmp['mon'], 6681 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])), 6682 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])), 6683 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])), 6684 '__YEAR__' => (string) $tmp['year'], 6685 '__PREVIOUS_DAY__' => (string) $tmp2['day'], 6686 '__PREVIOUS_MONTH__' => (string) $tmp3['month'], 6687 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1), 6688 '__NEXT_DAY__' => (string) $tmp4['day'], 6689 '__NEXT_MONTH__' => (string) $tmp5['month'], 6690 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1), 6691 )); 6692 } 6693 6694 if (!empty($conf->multicompany->enabled)) 6695 { 6696 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity)); 6697 } 6698 if (empty($exclude) || !in_array('system', $exclude)) 6699 { 6700 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT; 6701 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey'); 6702 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)'; 6703 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey'); 6704 } 6705 6706 return $substitutionarray; 6707} 6708 6709/** 6710 * Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newval), 6711 * and texts like __(TranslationKey|langfile)__ and __[ConstantKey]__ are also replaced. 6712 * Example of usage: 6713 * $substitutionarray = getCommonSubstitutionArray($langs, 0, null, $thirdparty); 6714 * complete_substitutions_array($substitutionarray, $langs, $thirdparty); 6715 * $mesg = make_substitutions($mesg, $substitutionarray, $langs); 6716 * 6717 * @param string $text Source string in which we must do substitution 6718 * @param array $substitutionarray Array with key->val to substitute. Example: array('__MYKEY__' => 'MyVal', ...) 6719 * @param Translate $outputlangs Output language 6720 * @return string Output string after substitutions 6721 * @see complete_substitutions_array(), getCommonSubstitutionArray() 6722 */ 6723function make_substitutions($text, $substitutionarray, $outputlangs = null) 6724{ 6725 global $conf, $langs; 6726 6727 if (!is_array($substitutionarray)) return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions'; 6728 6729 if (empty($outputlangs)) $outputlangs = $langs; 6730 6731 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__ 6732 if (is_object($outputlangs)) 6733 { 6734 $reg = array(); 6735 while (preg_match('/__\(([^\)]+)\)__/', $text, $reg)) 6736 { 6737 $msgishtml = 0; 6738 if (dol_textishtml($text, 1)) $msgishtml = 1; 6739 6740 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang 6741 $tmp = explode('|', $reg[1]); 6742 if (!empty($tmp[1])) $outputlangs->load($tmp[1]); 6743 6744 $text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $msgishtml ?dol_htmlentitiesbr($outputlangs->transnoentitiesnoconv($reg[1])) : $outputlangs->transnoentitiesnoconv($reg[1]), $text); 6745 } 6746 } 6747 6748 // Make substitution for constant keys. 6749 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted. 6750 $reg = array(); 6751 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) 6752 { 6753 $msgishtml = 0; 6754 if (dol_textishtml($text, 1)) $msgishtml = 1; 6755 6756 $keyfound = $reg[1]; 6757 if (isASecretKey($keyfound)) $newval = '*****forbidden*****'; 6758 else $newval = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound; 6759 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ?dol_htmlentitiesbr($newval) : $newval, $text); 6760 } 6761 6762 // Make substitition for array $substitutionarray 6763 foreach ($substitutionarray as $key => $value) 6764 { 6765 if (!isset($value)) continue; // If value is null, it same than not having substitution key at all into array, we do not replace. 6766 6767 if ($key == '__USER_SIGNATURE__' && (!empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) $value = ''; // Protection 6768 6769 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example 6770 } 6771 6772 return $text; 6773} 6774 6775/** 6776 * Complete the $substitutionarray with more entries coming from external module that had set the "substitutions=1" into module_part array. 6777 * In this case, method completesubstitutionarray provided by module is called. 6778 * 6779 * @param array $substitutionarray Array substitution old value => new value value 6780 * @param Translate $outputlangs Output language 6781 * @param Object $object Source object 6782 * @param mixed $parameters Add more parameters (useful to pass product lines) 6783 * @param string $callfunc What is the name of the custom function that will be called? (default: completesubstitutionarray) 6784 * @return void 6785 * @see make_substitutions() 6786 */ 6787function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray") 6788{ 6789 global $conf, $user; 6790 6791 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 6792 6793 // Add a substitution key for each extrafields, using key __EXTRA_XXX__ 6794 // TODO Remove this. Already available into the getCommonSubstitutionArray used to build the substitution array. 6795 /*if (is_object($object) && is_array($object->array_options)) 6796 { 6797 foreach($object->array_options as $key => $val) 6798 { 6799 $keyshort=preg_replace('/^(options|extra)_/','',$key); 6800 $substitutionarray['__EXTRAFIELD_'.$keyshort.'__']=$val; 6801 // For backward compatibiliy 6802 $substitutionarray['%EXTRA_'.$keyshort.'%']=$val; 6803 } 6804 }*/ 6805 6806 // Check if there is external substitution to do, requested by plugins 6807 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']); 6808 6809 foreach ($dirsubstitutions as $reldir) 6810 { 6811 $dir = dol_buildpath($reldir, 0); 6812 6813 // Check if directory exists 6814 if (!dol_is_dir($dir)) continue; 6815 6816 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_'); 6817 foreach ($substitfiles as $substitfile) 6818 { 6819 $reg = array(); 6820 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) 6821 { 6822 $module = $reg[1]; 6823 6824 dol_syslog("Library ".$substitfile['name']." found into ".$dir); 6825 // Include the user's functions file 6826 require_once $dir.$substitfile['name']; 6827 // Call the user's function, and only if it is defined 6828 $function_name = $module."_".$callfunc; 6829 if (function_exists($function_name)) { 6830 $function_name($substitutionarray, $outputlangs, $object, $parameters); 6831 } 6832 } 6833 } 6834 } 6835 if (!empty($conf->global->ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS)) { 6836 // to list all tags in odt template 6837 $tags = ''; 6838 foreach ($substitutionarray as $key => $value) { 6839 $tags .= '{'.$key.'} => '.$value."\n"; 6840 } 6841 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags)); 6842 } 6843} 6844 6845/** 6846 * Format output for start and end date 6847 * 6848 * @param int $date_start Start date 6849 * @param int $date_end End date 6850 * @param string $format Output format 6851 * @param Translate $outputlangs Output language 6852 * @return void 6853 */ 6854function print_date_range($date_start, $date_end, $format = '', $outputlangs = '') 6855{ 6856 print get_date_range($date_start, $date_end, $format, $outputlangs); 6857} 6858 6859/** 6860 * Format output for start and end date 6861 * 6862 * @param int $date_start Start date 6863 * @param int $date_end End date 6864 * @param string $format Output format 6865 * @param Translate $outputlangs Output language 6866 * @param integer $withparenthesis 1=Add parenthesis, 0=non parenthesis 6867 * @return string String 6868 */ 6869function get_date_range($date_start, $date_end, $format = '', $outputlangs = '', $withparenthesis = 1) 6870{ 6871 global $langs; 6872 6873 $out = ''; 6874 6875 if (!is_object($outputlangs)) $outputlangs = $langs; 6876 6877 if ($date_start && $date_end) 6878 { 6879 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : ''); 6880 } 6881 if ($date_start && !$date_end) 6882 { 6883 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : ''); 6884 } 6885 if (!$date_start && $date_end) 6886 { 6887 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : ''); 6888 } 6889 6890 return $out; 6891} 6892 6893/** 6894 * Return firstname and lastname in correct order 6895 * 6896 * @param string $firstname Firstname 6897 * @param string $lastname Lastname 6898 * @param int $nameorder -1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname, 3=Firstname if defined else lastname, 4=Lastname, 5=Lastname if defined else firstname 6899 * @return string Firstname + lastname or Lastname + firstname 6900 */ 6901function dolGetFirstLastname($firstname, $lastname, $nameorder = -1) 6902{ 6903 global $conf; 6904 6905 $ret = ''; 6906 // If order not defined, we use the setup 6907 if ($nameorder < 0) $nameorder = (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION) ? 1 : 0); 6908 if ($nameorder == 1) { 6909 $ret .= $firstname; 6910 if ($firstname && $lastname) $ret .= ' '; 6911 $ret .= $lastname; 6912 } elseif ($nameorder == 2 || $nameorder == 3) { 6913 $ret .= $firstname; 6914 if (empty($ret) && $nameorder == 3) { 6915 $ret .= $lastname; 6916 } 6917 } else { // 0, 4 or 5 6918 $ret .= $lastname; 6919 if (empty($ret) && $nameorder == 5) { 6920 $ret .= $firstname; 6921 } 6922 if ($nameorder == 0) { 6923 if ($firstname && $lastname) $ret .= ' '; 6924 $ret .= $firstname; 6925 } 6926 } 6927 return $ret; 6928} 6929 6930 6931/** 6932 * Set event message in dol_events session object. Will be output by calling dol_htmloutput_events. 6933 * Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function. 6934 * Note: Prefer to use setEventMessages instead. 6935 * 6936 * @param string|string[] $mesgs Message string or array 6937 * @param string $style Which style to use ('mesgs' by default, 'warnings', 'errors') 6938 * @return void 6939 * @see dol_htmloutput_events() 6940 */ 6941function setEventMessage($mesgs, $style = 'mesgs') 6942{ 6943 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function 6944 if (!is_array($mesgs)) { 6945 // If mesgs is a string 6946 if ($mesgs) $_SESSION['dol_events'][$style][] = $mesgs; 6947 } else { 6948 // If mesgs is an array 6949 foreach ($mesgs as $mesg) 6950 { 6951 if ($mesg) $_SESSION['dol_events'][$style][] = $mesg; 6952 } 6953 } 6954} 6955 6956/** 6957 * Set event messages in dol_events session object. Will be output by calling dol_htmloutput_events. 6958 * Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function. 6959 * 6960 * @param string $mesg Message string 6961 * @param array $mesgs Message array 6962 * @param string $style Which style to use ('mesgs' by default, 'warnings', 'errors') 6963 * @param string $messagekey A key to be used to allow the feature "Never show this message again" 6964 * @return void 6965 * @see dol_htmloutput_events() 6966 */ 6967function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '') 6968{ 6969 if (empty($mesg) && empty($mesgs)) 6970 { 6971 dol_syslog("Try to add a message in stack with empty message", LOG_WARNING); 6972 } else { 6973 if ($messagekey) 6974 { 6975 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey; 6976 // TODO 6977 $mesg .= ''; 6978 } 6979 if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) 6980 { 6981 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) dol_print_error('', 'Bad parameter style='.$style.' for setEventMessages'); 6982 if (empty($mesgs)) setEventMessage($mesg, $style); 6983 else { 6984 if (!empty($mesg) && !in_array($mesg, $mesgs)) setEventMessage($mesg, $style); // Add message string if not already into array 6985 setEventMessage($mesgs, $style); 6986 } 6987 } 6988 } 6989} 6990 6991/** 6992 * Print formated messages to output (Used to show messages on html output). 6993 * Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function, so there is 6994 * no need to call it explicitely. 6995 * 6996 * @param int $disabledoutputofmessages Clear all messages stored into session without diplaying them 6997 * @return void 6998 * @see dol_htmloutput_mesg() 6999 */ 7000function dol_htmloutput_events($disabledoutputofmessages = 0) 7001{ 7002 // Show mesgs 7003 if (isset($_SESSION['dol_events']['mesgs'])) { 7004 if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']); 7005 unset($_SESSION['dol_events']['mesgs']); 7006 } 7007 7008 // Show errors 7009 if (isset($_SESSION['dol_events']['errors'])) { 7010 if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error'); 7011 unset($_SESSION['dol_events']['errors']); 7012 } 7013 7014 // Show warnings 7015 if (isset($_SESSION['dol_events']['warnings'])) { 7016 if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning'); 7017 unset($_SESSION['dol_events']['warnings']); 7018 } 7019} 7020 7021/** 7022 * Get formated messages to output (Used to show messages on html output). 7023 * This include also the translation of the message key. 7024 * 7025 * @param string $mesgstring Message string or message key 7026 * @param string[] $mesgarray Array of message strings or message keys 7027 * @param string $style Style of message output ('ok' or 'error') 7028 * @param int $keepembedded Set to 1 in error message must be kept embedded into its html place (this disable jnotify) 7029 * @return string Return html output 7030 * 7031 * @see dol_print_error() 7032 * @see dol_htmloutput_errors() 7033 * @see setEventMessages() 7034 */ 7035function get_htmloutput_mesg($mesgstring = '', $mesgarray = '', $style = 'ok', $keepembedded = 0) 7036{ 7037 global $conf, $langs; 7038 7039 $ret = 0; $return = ''; 7040 $out = ''; 7041 $divstart = $divend = ''; 7042 7043 // If inline message with no format, we add it. 7044 if ((empty($conf->use_javascript_ajax) || !empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) 7045 { 7046 $divstart = '<div class="'.$style.' clearboth">'; 7047 $divend = '</div>'; 7048 } 7049 7050 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) 7051 { 7052 $langs->load("errors"); 7053 $out .= $divstart; 7054 if (is_array($mesgarray) && count($mesgarray)) 7055 { 7056 foreach ($mesgarray as $message) 7057 { 7058 $ret++; 7059 $out .= $langs->trans($message); 7060 if ($ret < count($mesgarray)) $out .= "<br>\n"; 7061 } 7062 } 7063 if ($mesgstring) 7064 { 7065 $langs->load("errors"); 7066 $ret++; 7067 $out .= $langs->trans($mesgstring); 7068 } 7069 $out .= $divend; 7070 } 7071 7072 if ($out) 7073 { 7074 if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded)) 7075 { 7076 $return = '<script> 7077 $(document).ready(function() { 7078 var block = '.(!empty($conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false").' 7079 if (block) { 7080 $.dolEventValid("","'.dol_escape_js($out).'"); 7081 } else { 7082 /* jnotify(message, preset of message type, keepmessage) */ 7083 $.jnotify("'.dol_escape_js($out).'", 7084 "'.($style == "ok" ? 3000 : $style).'", 7085 '.($style == "ok" ? "false" : "true").', 7086 { remove: function (){} } ); 7087 } 7088 }); 7089 </script>'; 7090 } else { 7091 $return = $out; 7092 } 7093 } 7094 7095 return $return; 7096} 7097 7098/** 7099 * Get formated error messages to output (Used to show messages on html output). 7100 * 7101 * @param string $mesgstring Error message 7102 * @param array $mesgarray Error messages array 7103 * @param int $keepembedded Set to 1 in error message must be kept embedded into its html place (this disable jnotify) 7104 * @return string Return html output 7105 * 7106 * @see dol_print_error() 7107 * @see dol_htmloutput_mesg() 7108 */ 7109function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0) 7110{ 7111 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded); 7112} 7113 7114/** 7115 * Print formated messages to output (Used to show messages on html output). 7116 * 7117 * @param string $mesgstring Message string or message key 7118 * @param string[] $mesgarray Array of message strings or message keys 7119 * @param string $style Which style to use ('ok', 'warning', 'error') 7120 * @param int $keepembedded Set to 1 if message must be kept embedded into its html place (this disable jnotify) 7121 * @return void 7122 * 7123 * @see dol_print_error() 7124 * @see dol_htmloutput_errors() 7125 * @see setEventMessages() 7126 */ 7127function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0) 7128{ 7129 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) return; 7130 7131 $iserror = 0; 7132 $iswarning = 0; 7133 if (is_array($mesgarray)) 7134 { 7135 foreach ($mesgarray as $val) 7136 { 7137 if ($val && preg_match('/class="error"/i', $val)) { $iserror++; break; } 7138 if ($val && preg_match('/class="warning"/i', $val)) { $iswarning++; break; } 7139 } 7140 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) $iserror++; 7141 elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) $iswarning++; 7142 if ($style == 'error') $iserror++; 7143 if ($style == 'warning') $iswarning++; 7144 7145 if ($iserror || $iswarning) 7146 { 7147 // Remove div from texts 7148 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring); 7149 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring); 7150 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring); 7151 // Remove div from texts array 7152 if (is_array($mesgarray)) 7153 { 7154 $newmesgarray = array(); 7155 foreach ($mesgarray as $val) 7156 { 7157 if (is_string($val)) 7158 { 7159 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val); 7160 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring); 7161 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring); 7162 $newmesgarray[] = $tmpmesgstring; 7163 } else { 7164 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING); 7165 } 7166 } 7167 $mesgarray = $newmesgarray; 7168 } 7169 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded); 7170 } else print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded); 7171} 7172 7173/** 7174 * Print formated error messages to output (Used to show messages on html output). 7175 * 7176 * @param string $mesgstring Error message 7177 * @param array $mesgarray Error messages array 7178 * @param int $keepembedded Set to 1 in error message must be kept embedded into its html place (this disable jnotify) 7179 * @return void 7180 * 7181 * @see dol_print_error() 7182 * @see dol_htmloutput_mesg() 7183 */ 7184function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0) 7185{ 7186 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded); 7187} 7188 7189/** 7190 * Advanced sort array by second index function, which produces ascending (default) 7191 * or descending output and uses optionally natural case insensitive sorting (which 7192 * can be optionally case sensitive as well). 7193 * 7194 * @param array $array Array to sort (array of array('key1'=>val1,'key2'=>val2,'key3'...) or array of objects) 7195 * @param string $index Key in array to use for sorting criteria 7196 * @param int $order Sort order ('asc' or 'desc') 7197 * @param int $natsort 1=use "natural" sort (natsort), 0=use "standard" sort (asort) 7198 * @param int $case_sensitive 1=sort is case sensitive, 0=not case sensitive 7199 * @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. 7200 * @return array Sorted array 7201 */ 7202function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0) 7203{ 7204 // Clean parameters 7205 $order = strtolower($order); 7206 7207 if (is_array($array)) 7208 { 7209 $sizearray = count($array); 7210 if ($sizearray > 0) 7211 { 7212 $temp = array(); 7213 foreach (array_keys($array) as $key) 7214 { 7215 if (is_object($array[$key])) { 7216 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index; 7217 } else { 7218 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index]; 7219 } 7220 } 7221 7222 if (!$natsort) { 7223 ($order == 'asc') ? asort($temp) : arsort($temp); 7224 } else { 7225 ($case_sensitive) ? natsort($temp) : natcasesort($temp); 7226 if ($order != 'asc') $temp = array_reverse($temp, true); 7227 } 7228 7229 $sorted = array(); 7230 7231 foreach (array_keys($temp) as $key) 7232 { 7233 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key]; 7234 } 7235 7236 return $sorted; 7237 } 7238 } 7239 return $array; 7240} 7241 7242 7243/** 7244 * Check if a string is in UTF8 7245 * 7246 * @param string $str String to check 7247 * @return boolean True if string is UTF8 or ISO compatible with UTF8, False if not (ISO with special char or Binary) 7248 */ 7249function utf8_check($str) 7250{ 7251 $str = (string) $str; // Sometimes string is an int. 7252 7253 // We must use here a binary strlen function (so not dol_strlen) 7254 $strLength = dol_strlen($str); 7255 for ($i = 0; $i < $strLength; $i++) { 7256 if (ord($str[$i]) < 0x80) continue; // 0bbbbbbb 7257 elseif ((ord($str[$i]) & 0xE0) == 0xC0) $n = 1; // 110bbbbb 7258 elseif ((ord($str[$i]) & 0xF0) == 0xE0) $n = 2; // 1110bbbb 7259 elseif ((ord($str[$i]) & 0xF8) == 0xF0) $n = 3; // 11110bbb 7260 elseif ((ord($str[$i]) & 0xFC) == 0xF8) $n = 4; // 111110bb 7261 elseif ((ord($str[$i]) & 0xFE) == 0xFC) $n = 5; // 1111110b 7262 else return false; // Does not match any model 7263 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ? 7264 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) 7265 return false; 7266 } 7267 } 7268 return true; 7269} 7270 7271/** 7272 * Check if a string is in ASCII 7273 * 7274 * @param string $str String to check 7275 * @return boolean True if string is ASCII, False if not (byte value > 0x7F) 7276 */ 7277function ascii_check($str) 7278{ 7279 if (function_exists('mb_check_encoding')) { 7280 //if (mb_detect_encoding($str, 'ASCII', true) return false; 7281 if (!mb_check_encoding($str, 'ASCII')) return false; 7282 } else { 7283 if (preg_match('/[^\x00-\x7f]/', $str)) return false; // Contains a byte > 7f 7284 } 7285 7286 return true; 7287} 7288 7289 7290/** 7291 * Return a string encoded into OS filesystem encoding. This function is used to define 7292 * value to pass to filesystem PHP functions. 7293 * 7294 * @param string $str String to encode (UTF-8) 7295 * @return string Encoded string (UTF-8, ISO-8859-1) 7296 */ 7297function dol_osencode($str) 7298{ 7299 global $conf; 7300 7301 $tmp = ini_get("unicode.filesystem_encoding"); // Disponible avec PHP 6.0 7302 if (empty($tmp) && !empty($_SERVER["WINDIR"])) $tmp = 'iso-8859-1'; // By default for windows 7303 if (empty($tmp)) $tmp = 'utf-8'; // By default for other 7304 if (!empty($conf->global->MAIN_FILESYSTEM_ENCODING)) $tmp = $conf->global->MAIN_FILESYSTEM_ENCODING; 7305 7306 if ($tmp == 'iso-8859-1') return utf8_decode($str); 7307 return $str; 7308} 7309 7310 7311/** 7312 * Return an id or code from a code or id. 7313 * Store also Code-Id into a cache to speed up next request on same key. 7314 * 7315 * @param DoliDB $db Database handler 7316 * @param string $key Code or Id to get Id or Code 7317 * @param string $tablename Table name without prefix 7318 * @param string $fieldkey Field to search the key into 7319 * @param string $fieldid Field to get 7320 * @param int $entityfilter Filter by entity 7321 * @return int <0 if KO, Id of code if OK 7322 * @see $langs->getLabelFromKey 7323 */ 7324function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0) 7325{ 7326 global $cache_codes; 7327 7328 // If key empty 7329 if ($key == '') return ''; 7330 7331 // Check in cache 7332 if (isset($cache_codes[$tablename][$key][$fieldid])) // Can be defined to 0 or '' 7333 { 7334 return $cache_codes[$tablename][$key][$fieldid]; // Found in cache 7335 } 7336 7337 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG); 7338 7339 $sql = "SELECT ".$fieldid." as valuetoget"; 7340 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename; 7341 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'"; 7342 if (!empty($entityfilter)) 7343 $sql .= " AND entity IN (".getEntity($tablename).")"; 7344 7345 $resql = $db->query($sql); 7346 if ($resql) 7347 { 7348 $obj = $db->fetch_object($resql); 7349 if ($obj) $cache_codes[$tablename][$key][$fieldid] = $obj->valuetoget; 7350 else $cache_codes[$tablename][$key][$fieldid] = ''; 7351 $db->free($resql); 7352 return $cache_codes[$tablename][$key][$fieldid]; 7353 } else { 7354 return -1; 7355 } 7356} 7357 7358/** 7359 * Verify if condition in string is ok or not 7360 * 7361 * @param string $strRights String with condition to check 7362 * @return boolean True or False. Return True if strRights is '' 7363 */ 7364function verifCond($strRights) 7365{ 7366 global $user, $conf, $langs; 7367 global $leftmenu; 7368 global $rights; // To export to dol_eval function 7369 7370 //print $strRights."<br>\n"; 7371 $rights = true; 7372 if ($strRights != '') 7373 { 7374 $str = 'if(!('.$strRights.')) { $rights = false; }'; 7375 dol_eval($str); // The dol_eval must contains all the global $xxx used into a condition 7376 } 7377 return $rights; 7378} 7379 7380/** 7381 * Replace eval function to add more security. 7382 * This function is called by verifCond() or trans() and transnoentitiesnoconv(). 7383 * 7384 * @param string $s String to evaluate 7385 * @param int $returnvalue 0=No return (used to execute eval($a=something)). 1=Value of eval is returned (used to eval($something)). 7386 * @param int $hideerrors 1=Hide errors 7387 * @return mixed Nothing or return result of eval 7388 */ 7389function dol_eval($s, $returnvalue = 0, $hideerrors = 1) 7390{ 7391 // Only global variables can be changed by eval function and returned to caller 7392 global $db, $langs, $user, $conf, $website, $websitepage; 7393 global $action, $mainmenu, $leftmenu; 7394 global $rights; 7395 global $object; 7396 global $mysoc; 7397 7398 global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object 7399 global $soc; // For backward compatibility 7400 7401 //print $s."<br>\n"; 7402 if ($returnvalue) { 7403 if ($hideerrors) return @eval('return '.$s.';'); 7404 else return eval('return '.$s.';'); 7405 } else { 7406 if ($hideerrors) @eval($s); 7407 else eval($s); 7408 } 7409} 7410 7411/** 7412 * Return if var element is ok 7413 * 7414 * @param string $element Variable to check 7415 * @return boolean Return true of variable is not empty 7416 */ 7417function dol_validElement($element) 7418{ 7419 return (trim($element) != ''); 7420} 7421 7422/** 7423 * Return img flag of country for a language code or country code 7424 * 7425 * @param string $codelang Language code ('en_IN', 'fr_CA', ...) or ISO Country code on 2 characters in uppercase ('IN', 'FR') 7426 * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"' or 'class="saturatemedium"') 7427 * @return string HTML img string with flag. 7428 */ 7429function picto_from_langcode($codelang, $moreatt = '') 7430{ 7431 if (empty($codelang)) return ''; 7432 7433 if ($codelang == 'auto') 7434 { 7435 return '<span class="fa fa-globe"></span>'; 7436 } 7437 7438 $langtocountryflag = array( 7439 'ar_AR' => '', 7440 'ca_ES' => 'catalonia', 7441 'da_DA' => 'dk', 7442 'fr_CA' => 'mq', 7443 'sv_SV' => 'se', 7444 'sw_SW' => 'unknown', 7445 'AQ' => 'unknown', 7446 'CW' => 'unknown', 7447 'IM' => 'unknown', 7448 'JE' => 'unknown', 7449 'MF' => 'unknown', 7450 'BL' => 'unknown', 7451 'SX' => 'unknown' 7452 ); 7453 7454 if (isset($langtocountryflag[$codelang])) $flagImage = $langtocountryflag[$codelang]; 7455 else { 7456 $tmparray = explode('_', $codelang); 7457 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1]; 7458 } 7459 7460 return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt); 7461} 7462 7463/** 7464 * Return default language from country code. 7465 * Return null if not found. 7466 * 7467 * @param string $countrycode Country code like 'US', 'FR', 'CA', ... 7468 * @return string Value of locale like 'en_US', 'fr_FR', ... 7469 */ 7470function getLanguageCodeFromCountryCode($countrycode) 7471{ 7472 global $mysoc; 7473 7474 if (empty($countrycode)) return null; 7475 7476 if (strtoupper($countrycode) == 'MQ') return 'fr_CA'; 7477 if (strtoupper($countrycode) == 'SE') return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country 7478 if (strtoupper($countrycode) == 'CH') 7479 { 7480 if ($mysoc->country_code == 'FR') return 'fr_CH'; 7481 if ($mysoc->country_code == 'DE') return 'de_CH'; 7482 } 7483 7484 // Locale list taken from: 7485 // http://stackoverflow.com/questions/3191664/ 7486 // list-of-all-locales-and-their-short-codes 7487 $locales = array( 7488 'af-ZA', 7489 'am-ET', 7490 'ar-AE', 7491 'ar-BH', 7492 'ar-DZ', 7493 'ar-EG', 7494 'ar-IQ', 7495 'ar-JO', 7496 'ar-KW', 7497 'ar-LB', 7498 'ar-LY', 7499 'ar-MA', 7500 'ar-OM', 7501 'ar-QA', 7502 'ar-SA', 7503 'ar-SY', 7504 'ar-TN', 7505 'ar-YE', 7506 'as-IN', 7507 'ba-RU', 7508 'be-BY', 7509 'bg-BG', 7510 'bn-BD', 7511 'bn-IN', 7512 'bo-CN', 7513 'br-FR', 7514 'ca-ES', 7515 'co-FR', 7516 'cs-CZ', 7517 'cy-GB', 7518 'da-DK', 7519 'de-AT', 7520 'de-CH', 7521 'de-DE', 7522 'de-LI', 7523 'de-LU', 7524 'dv-MV', 7525 'el-GR', 7526 'en-AU', 7527 'en-BZ', 7528 'en-CA', 7529 'en-GB', 7530 'en-IE', 7531 'en-IN', 7532 'en-JM', 7533 'en-MY', 7534 'en-NZ', 7535 'en-PH', 7536 'en-SG', 7537 'en-TT', 7538 'en-US', 7539 'en-ZA', 7540 'en-ZW', 7541 'es-AR', 7542 'es-BO', 7543 'es-CL', 7544 'es-CO', 7545 'es-CR', 7546 'es-DO', 7547 'es-EC', 7548 'es-ES', 7549 'es-GT', 7550 'es-HN', 7551 'es-MX', 7552 'es-NI', 7553 'es-PA', 7554 'es-PE', 7555 'es-PR', 7556 'es-PY', 7557 'es-SV', 7558 'es-US', 7559 'es-UY', 7560 'es-VE', 7561 'et-EE', 7562 'eu-ES', 7563 'fa-IR', 7564 'fi-FI', 7565 'fo-FO', 7566 'fr-BE', 7567 'fr-CA', 7568 'fr-CH', 7569 'fr-FR', 7570 'fr-LU', 7571 'fr-MC', 7572 'fy-NL', 7573 'ga-IE', 7574 'gd-GB', 7575 'gl-ES', 7576 'gu-IN', 7577 'he-IL', 7578 'hi-IN', 7579 'hr-BA', 7580 'hr-HR', 7581 'hu-HU', 7582 'hy-AM', 7583 'id-ID', 7584 'ig-NG', 7585 'ii-CN', 7586 'is-IS', 7587 'it-CH', 7588 'it-IT', 7589 'ja-JP', 7590 'ka-GE', 7591 'kk-KZ', 7592 'kl-GL', 7593 'km-KH', 7594 'kn-IN', 7595 'ko-KR', 7596 'ky-KG', 7597 'lb-LU', 7598 'lo-LA', 7599 'lt-LT', 7600 'lv-LV', 7601 'mi-NZ', 7602 'mk-MK', 7603 'ml-IN', 7604 'mn-MN', 7605 'mr-IN', 7606 'ms-BN', 7607 'ms-MY', 7608 'mt-MT', 7609 'nb-NO', 7610 'ne-NP', 7611 'nl-BE', 7612 'nl-NL', 7613 'nn-NO', 7614 'oc-FR', 7615 'or-IN', 7616 'pa-IN', 7617 'pl-PL', 7618 'ps-AF', 7619 'pt-BR', 7620 'pt-PT', 7621 'rm-CH', 7622 'ro-RO', 7623 'ru-RU', 7624 'rw-RW', 7625 'sa-IN', 7626 'se-FI', 7627 'se-NO', 7628 'se-SE', 7629 'si-LK', 7630 'sk-SK', 7631 'sl-SI', 7632 'sq-AL', 7633 'sv-FI', 7634 'sv-SE', 7635 'sw-KE', 7636 'ta-IN', 7637 'te-IN', 7638 'th-TH', 7639 'tk-TM', 7640 'tn-ZA', 7641 'tr-TR', 7642 'tt-RU', 7643 'ug-CN', 7644 'uk-UA', 7645 'ur-PK', 7646 'vi-VN', 7647 'wo-SN', 7648 'xh-ZA', 7649 'yo-NG', 7650 'zh-CN', 7651 'zh-HK', 7652 'zh-MO', 7653 'zh-SG', 7654 'zh-TW', 7655 'zu-ZA', 7656 ); 7657 7658 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode); 7659 if (in_array($buildprimarykeytotest, $locales)) return strtolower($countrycode).'_'.strtoupper($countrycode); 7660 7661 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) // Need extension php-intl 7662 { 7663 foreach ($locales as $locale) 7664 { 7665 $locale_language = locale_get_primary_language($locale); 7666 $locale_region = locale_get_region($locale); 7667 if (strtoupper($countrycode) == $locale_region) 7668 { 7669 //var_dump($locale.'-'.$locale_language.'-'.$locale_region); 7670 return strtolower($locale_language).'_'.strtoupper($locale_region); 7671 } 7672 } 7673 } else { 7674 dol_syslog("Warning Exention php-intl is not available", LOG_WARNING); 7675 } 7676 7677 return null; 7678} 7679 7680/** 7681 * Complete or removed entries into a head array (used to build tabs). 7682 * For example, with value added by external modules. Such values are declared into $conf->modules_parts['tab']. 7683 * Or by change using hook completeTabsHead 7684 * 7685 * @param Conf $conf Object conf 7686 * @param Translate $langs Object langs 7687 * @param object|null $object Object object 7688 * @param array $head Object head 7689 * @param int $h New position to fill 7690 * @param string $type Value for object where objectvalue can be 7691 * 'thirdparty' to add a tab in third party view 7692 * 'intervention' to add a tab in intervention view 7693 * 'supplier_order' to add a tab in supplier order view 7694 * 'supplier_invoice' to add a tab in supplier invoice view 7695 * 'invoice' to add a tab in customer invoice view 7696 * 'order' to add a tab in customer order view 7697 * 'contract' to add a tabl in contract view 7698 * 'product' to add a tab in product view 7699 * 'propal' to add a tab in propal view 7700 * 'user' to add a tab in user view 7701 * 'group' to add a tab in group view 7702 * 'member' to add a tab in fundation member view 7703 * 'categories_x' to add a tab in category view ('x': type of category (0=product, 1=supplier, 2=customer, 3=member) 7704 * 'ecm' to add a tab for another ecm view 7705 * 'stock' to add a tab for warehouse view 7706 * @param string $mode 'add' to complete head, 'remove' to remove entries 7707 * @return void 7708 */ 7709function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add') 7710{ 7711 global $hookmanager; 7712 7713 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) 7714 { 7715 foreach ($conf->modules_parts['tabs'][$type] as $value) 7716 { 7717 $values = explode(':', $value); 7718 7719 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) 7720 { 7721 if (count($values) == 6) // new declaration with permissions: $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__' 7722 { 7723 if ($values[0] != $type) continue; 7724 7725 if (verifCond($values[4])) 7726 { 7727 if ($values[3]) $langs->load($values[3]); 7728 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) 7729 { 7730 $substitutionarray = array(); 7731 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2])); 7732 $label = make_substitutions($reg[1], $substitutionarray); 7733 } else $label = $langs->trans($values[2]); 7734 7735 $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1); 7736 $head[$h][1] = $label; 7737 $head[$h][2] = str_replace('+', '', $values[1]); 7738 $h++; 7739 } 7740 } elseif (count($values) == 5) // deprecated 7741 { 7742 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING); 7743 7744 if ($values[0] != $type) continue; 7745 if ($values[3]) $langs->load($values[3]); 7746 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) 7747 { 7748 $substitutionarray = array(); 7749 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2])); 7750 $label = make_substitutions($reg[1], $substitutionarray); 7751 } else $label = $langs->trans($values[2]); 7752 7753 $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1); 7754 $head[$h][1] = $label; 7755 $head[$h][2] = str_replace('+', '', $values[1]); 7756 $h++; 7757 } 7758 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) 7759 { 7760 if ($values[0] != $type) continue; 7761 $tabname = str_replace('-', '', $values[1]); 7762 foreach ($head as $key => $val) 7763 { 7764 $condition = (!empty($values[3]) ? verifCond($values[3]) : 1); 7765 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition); 7766 if ($head[$key][2] == $tabname && $condition) 7767 { 7768 unset($head[$key]); 7769 break; 7770 } 7771 } 7772 } 7773 } 7774 } 7775 7776 // No need to make a return $head. Var is modified as a reference 7777 if (!empty($hookmanager)) 7778 { 7779 $parameters = array('object' => $object, 'mode' => $mode, 'head' => $head); 7780 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters); 7781 if ($reshook > 0) { // Hook ask to replace completely the array 7782 $head = $hookmanager->resArray; 7783 } else { // Hook 7784 $head = array_merge($head, $hookmanager->resArray); 7785 } 7786 $h = count($head); 7787 } 7788} 7789 7790/** 7791 * Print common footer : 7792 * conf->global->MAIN_HTML_FOOTER 7793 * js for switch of menu hider 7794 * js for conf->global->MAIN_GOOGLE_AN_ID 7795 * js for conf->global->MAIN_SHOW_TUNING_INFO or $_SERVER["MAIN_SHOW_TUNING_INFO"] 7796 * js for conf->logbuffer 7797 * 7798 * @param string $zone 'private' (for private pages) or 'public' (for public pages) 7799 * @return void 7800 */ 7801function printCommonFooter($zone = 'private') 7802{ 7803 global $conf, $hookmanager, $user, $debugbar; 7804 global $action; 7805 global $micro_start_time; 7806 7807 if ($zone == 'private') print "\n".'<!-- Common footer for private page -->'."\n"; 7808 else print "\n".'<!-- Common footer for public page -->'."\n"; 7809 7810 // A div to store page_y POST parameter so we can read it using javascript 7811 print "\n<!-- A div to store page_y POST parameter -->\n"; 7812 print '<div id="page_y" style="display: none;">'.(empty($_POST['page_y']) ? '' : $_POST['page_y']).'</div>'."\n"; 7813 7814 $parameters = array(); 7815 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks 7816 if (empty($reshook)) 7817 { 7818 if (!empty($conf->global->MAIN_HTML_FOOTER)) print $conf->global->MAIN_HTML_FOOTER."\n"; 7819 7820 print "\n"; 7821 if (!empty($conf->use_javascript_ajax)) 7822 { 7823 print '<script>'."\n"; 7824 print 'jQuery(document).ready(function() {'."\n"; 7825 7826 if ($zone == 'private' && empty($conf->dol_use_jmobile)) 7827 { 7828 print "\n"; 7829 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n"; 7830 print 'jQuery("li.menuhider").click(function(event) {'; 7831 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n"; 7832 print ' console.log("We click on .menuhider");'."\n"; 7833 print ' $("body").toggleClass("sidebar-collapse")'."\n"; 7834 print '});'."\n"; 7835 } 7836 7837 // Management of focus and mandatory for fields 7838 if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"])))) 7839 { 7840 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n"; 7841 $relativepathstring = $_SERVER["PHP_SELF"]; 7842 // Clean $relativepathstring 7843 if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring); 7844 $relativepathstring = preg_replace('/^\//', '', $relativepathstring); 7845 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring); 7846 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); 7847 if (!empty($user->default_values[$relativepathstring]['focus'])) 7848 { 7849 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) 7850 { 7851 $qualified = 0; 7852 if ($defkey != '_noquery_') 7853 { 7854 $tmpqueryarraytohave = explode('&', $defkey); 7855 $foundintru = 0; 7856 foreach ($tmpqueryarraytohave as $tmpquerytohave) 7857 { 7858 $tmpquerytohaveparam = explode('=', $tmpquerytohave); 7859 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');"; 7860 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) $foundintru = 1; 7861 } 7862 if (!$foundintru) $qualified = 1; 7863 //var_dump($defkey.'-'.$qualified); 7864 } else $qualified = 1; 7865 7866 if ($qualified) 7867 { 7868 foreach ($defval as $paramkey => $paramval) 7869 { 7870 // Set focus on field 7871 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n"; 7872 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; 7873 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really usefull, but we keep it in case of. 7874 } 7875 } 7876 } 7877 } 7878 if (!empty($user->default_values[$relativepathstring]['mandatory'])) 7879 { 7880 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) 7881 { 7882 $qualified = 0; 7883 if ($defkey != '_noquery_') 7884 { 7885 $tmpqueryarraytohave = explode('&', $defkey); 7886 $foundintru = 0; 7887 foreach ($tmpqueryarraytohave as $tmpquerytohave) 7888 { 7889 $tmpquerytohaveparam = explode('=', $tmpquerytohave); 7890 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');"; 7891 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) $foundintru = 1; 7892 } 7893 if (!$foundintru) $qualified = 1; 7894 //var_dump($defkey.'-'.$qualified); 7895 } else $qualified = 1; 7896 7897 if ($qualified) 7898 { 7899 foreach ($defval as $paramkey => $paramval) 7900 { 7901 // Add property 'required' on input 7902 print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; 7903 print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; 7904 print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; // required on a select works only if key is "", this does not happen in Dolibarr 7905 } 7906 } 7907 } 7908 } 7909 } 7910 7911 print '});'."\n"; 7912 7913 // End of tuning 7914 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || !empty($conf->global->MAIN_SHOW_TUNING_INFO)) 7915 { 7916 print "\n"; 7917 print "/* JS CODE TO ENABLE to add memory info */\n"; 7918 print 'window.console && console.log("'; 7919 if (!empty($conf->global->MEMCACHED_SERVER)) print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - '; 7920 print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED) ? $conf->global->MAIN_OPTIMIZE_SPEED : 'off'); 7921 if (!empty($micro_start_time)) // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable. 7922 { 7923 $micro_end_time = microtime(true); 7924 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms'; 7925 } 7926 7927 if (function_exists("memory_get_usage")) { 7928 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount 7929 } 7930 if (function_exists("memory_get_peak_usage")) { 7931 print ' - Real mem peak: '.memory_get_peak_usage(true); 7932 } 7933 if (function_exists("zend_loader_file_encoded")) 7934 { 7935 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no'); 7936 } 7937 print '");'."\n"; 7938 } 7939 7940 print "\n".'</script>'."\n"; 7941 7942 // Google Analytics 7943 // TODO Add a hook here 7944 if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AN_ID)) 7945 { 7946 $tmptagarray = explode(',', $conf->global->MAIN_GOOGLE_AN_ID); 7947 foreach ($tmptagarray as $tmptag) { 7948 print "\n"; 7949 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n"; 7950 print " 7951 <!-- Global site tag (gtag.js) - Google Analytics --> 7952 <script async src=\"https://www.googletagmanager.com/gtag/js?id=".trim($tmptag)."\"></script> 7953 <script> 7954 window.dataLayer = window.dataLayer || []; 7955 function gtag(){dataLayer.push(arguments);} 7956 gtag('js', new Date()); 7957 7958 gtag('config', '".trim($tmptag)."'); 7959 </script>"; 7960 print "\n"; 7961 } 7962 } 7963 } 7964 7965 // Add Xdebug coverage of code 7966 if (defined('XDEBUGCOVERAGE')) 7967 { 7968 print_r(xdebug_get_code_coverage()); 7969 } 7970 7971 // Add DebugBar data 7972 if (!empty($user->rights->debugbar->read) && is_object($debugbar)) 7973 { 7974 $debugbar['time']->stopMeasure('pageaftermaster'); 7975 print '<!-- Output debugbar data -->'."\n"; 7976 $renderer = $debugbar->getRenderer(); 7977 print $debugbar->getRenderer()->render(); 7978 } elseif (count($conf->logbuffer)) // If there is some logs in buffer to show 7979 { 7980 print "\n"; 7981 print "<!-- Start of log output\n"; 7982 //print '<div class="hidden">'."\n"; 7983 foreach ($conf->logbuffer as $logline) 7984 { 7985 print $logline."<br>\n"; 7986 } 7987 //print '</div>'."\n"; 7988 print "End of log output -->\n"; 7989 } 7990 } 7991} 7992 7993/** 7994 * Split a string with 2 keys into key array. 7995 * For example: "A=1;B=2;C=2" is exploded into array('A'=>1,'B'=>2,'C'=>3) 7996 * 7997 * @param string $string String to explode 7998 * @param string $delimiter Delimiter between each couple of data 7999 * @param string $kv Delimiter between key and value 8000 * @return array Array of data exploded 8001 */ 8002function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=') 8003{ 8004 if ($a = explode($delimiter, $string)) 8005 { 8006 $ka = array(); 8007 foreach ($a as $s) { // each part 8008 if ($s) { 8009 if ($pos = strpos($s, $kv)) { // key/value delimiter 8010 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv))); 8011 } else { // key delimiter not found 8012 $ka[] = trim($s); 8013 } 8014 } 8015 } 8016 return $ka; 8017 } 8018 return array(); 8019} 8020 8021 8022/** 8023 * Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag) 8024 * 8025 * @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. 8026 * @return string HTML code to set focus 8027 */ 8028function dol_set_focus($selector) 8029{ 8030 print "\n".'<!-- Set focus onto a specific field -->'."\n"; 8031 print '<script>jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n"; 8032} 8033 8034 8035/** 8036 * Return getmypid() or random PID when function is disabled 8037 * Some web hosts disable this php function for security reasons 8038 * and sometimes we can't redeclare function 8039 * 8040 * @return int 8041 */ 8042function dol_getmypid() 8043{ 8044 if (!function_exists('getmypid')) { 8045 return mt_rand(1, 32768); 8046 } else { 8047 return getmypid(); 8048 } 8049} 8050 8051 8052/** 8053 * Generate natural SQL search string for a criteria (this criteria can be tested on one or several fields) 8054 * 8055 * @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") 8056 * @param string $value The value to look for. 8057 * If param $mode is 0, can contains several keywords separated with a space or | 8058 * like "keyword1 keyword2" = We want record field like keyword1 AND field like keyword2 8059 * or like "keyword1|keyword2" = We want record field like keyword1 OR field like keyword2 8060 * If param $mode is 1, can contains an operator <, > or = like "<10" or ">=100.5 < 1000" 8061 * If param $mode is 2, can contains a list of int id separated by comma like "1,3,4" 8062 * If param $mode is 3, can contains a list of string separated by comma like "a,b,c" 8063 * @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') 8064 * 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 '2,7') to be used to search into a multiselect string '1,2,3,4' 8065 * @param integer $nofirstand 1=Do not output the first 'AND' 8066 * @return string $res The statement to append to the SQL query 8067 */ 8068function natural_search($fields, $value, $mode = 0, $nofirstand = 0) 8069{ 8070 global $db, $langs; 8071 8072 $value = trim($value); 8073 8074 if ($mode == 0) 8075 { 8076 $value = preg_replace('/\*/', '%', $value); // Replace * with % 8077 } 8078 if ($mode == 1) 8079 { 8080 $value = preg_replace('/([<>=]+)\s+([0-9'.preg_quote($langs->trans("DecimalSeparator"), '/').'\-])/', '\1\2', $value); // Clean string '< 10' into '<10' so we can the explode on space to get all tests to do 8081 } 8082 8083 $value = preg_replace('/\s*\|\s*/', '|', $value); 8084 8085 $crits = explode(' ', $value); 8086 $res = ''; 8087 if (!is_array($fields)) $fields = array($fields); 8088 8089 $j = 0; 8090 foreach ($crits as $crit) 8091 { 8092 $crit = trim($crit); 8093 $i = 0; $i2 = 0; 8094 $newres = ''; 8095 foreach ($fields as $field) 8096 { 8097 if ($mode == 1) 8098 { 8099 $operator = '='; 8100 $newcrit = preg_replace('/([<>=]+)/', '', $crit); 8101 8102 $reg = array(); 8103 preg_match('/([<>=]+)/', $crit, $reg); 8104 if ($reg[1]) 8105 { 8106 $operator = $reg[1]; 8107 } 8108 if ($newcrit != '') 8109 { 8110 $numnewcrit = price2num($newcrit); 8111 if (is_numeric($numnewcrit)) 8112 { 8113 $newres .= ($i2 > 0 ? ' OR ' : '').$field.' '.$operator.' '.$db->sanitize($numnewcrit); // should be a numeric 8114 } else { 8115 $newres .= ($i2 > 0 ? ' OR ' : '').'1 = 2'; // force false 8116 } 8117 $i2++; // a criteria was added to string 8118 } 8119 } elseif ($mode == 2 || $mode == -2) 8120 { 8121 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer 8122 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : ''); 8123 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)"; 8124 if ($mode == -2) $newres .= ' OR '.$field.' IS NULL'; 8125 $i2++; // a criteria was added to string 8126 } elseif ($mode == 3 || $mode == -3) 8127 { 8128 $tmparray = explode(',', $crit); 8129 if (count($tmparray)) 8130 { 8131 $listofcodes = ''; 8132 foreach ($tmparray as $val) 8133 { 8134 $val = trim($val); 8135 if ($val) 8136 { 8137 $listofcodes .= ($listofcodes ? ',' : ''); 8138 $listofcodes .= "'".$db->escape($val)."'"; 8139 } 8140 } 8141 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")"; 8142 $i2++; // a criteria was added to string 8143 } 8144 if ($mode == -3) $newres .= ' OR '.$field.' IS NULL'; 8145 } elseif ($mode == 4) 8146 { 8147 $tmparray = explode(',', $crit); 8148 if (count($tmparray)) 8149 { 8150 $listofcodes = ''; 8151 foreach ($tmparray as $val) 8152 { 8153 $val = trim($val); 8154 if ($val) 8155 { 8156 $newres .= ($i2 > 0 ? ' OR (' : '(').$field.' LIKE \''.$db->escape($val).',%\''; 8157 $newres .= ' OR '.$field.' = \''.$db->escape($val).'\''; 8158 $newres .= ' OR '.$field.' LIKE \'%,'.$db->escape($val).'\''; 8159 $newres .= ' OR '.$field.' LIKE \'%,'.$db->escape($val).',%\''; 8160 $newres .= ')'; 8161 $i2++; 8162 } 8163 } 8164 } 8165 } else // $mode=0 8166 { 8167 $tmpcrits = explode('|', $crit); 8168 $i3 = 0; 8169 foreach ($tmpcrits as $tmpcrit) 8170 { 8171 if ($tmpcrit !== '0' && empty($tmpcrit)) continue; 8172 8173 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : ''); 8174 8175 if (preg_match('/\.(id|rowid)$/', $field)) // Special case for rowid that is sometimes a ref so used as a search field 8176 { 8177 $newres .= $field." = ".(is_numeric(trim($tmpcrit)) ?trim($tmpcrit) : '0'); 8178 } else { 8179 $newres .= $field." LIKE '"; 8180 8181 $tmpcrit = trim($tmpcrit); 8182 $tmpcrit2 = $tmpcrit; 8183 $tmpbefore = '%'; $tmpafter = '%'; 8184 if (preg_match('/^[\^\$]/', $tmpcrit)) 8185 { 8186 $tmpbefore = ''; 8187 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2); 8188 } 8189 if (preg_match('/[\^\$]$/', $tmpcrit)) 8190 { 8191 $tmpafter = ''; 8192 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2); 8193 } 8194 $newres .= $tmpbefore; 8195 $newres .= $db->escape($tmpcrit2); 8196 $newres .= $tmpafter; 8197 $newres .= "'"; 8198 if ($tmpcrit2 == '') 8199 { 8200 $newres .= ' OR '.$field." IS NULL"; 8201 } 8202 } 8203 8204 $i3++; 8205 } 8206 $i2++; // a criteria was added to string 8207 } 8208 $i++; 8209 } 8210 if ($newres) $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : ''); 8211 $j++; 8212 } 8213 $res = ($nofirstand ? "" : " AND ")."(".$res.")"; 8214 //print 'xx'.$res.'yy'; 8215 return $res; 8216} 8217 8218/** 8219 * Return string with full Url. The file qualified is the one defined by relative path in $object->last_main_doc 8220 * 8221 * @param Object $object Object 8222 * @return string Url string 8223 */ 8224function showDirectDownloadLink($object) 8225{ 8226 global $conf, $langs; 8227 8228 $out = ''; 8229 $url = $object->getLastMainDocLink($object->element); 8230 8231 if ($url) 8232 { 8233 $out .= img_picto('', 'globe').' '.$langs->trans("DirectDownloadLink").'<br>'; 8234 $out .= '<input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'">'; 8235 $out .= ajax_autoselect("directdownloadlink", 0); 8236 } 8237 return $out; 8238} 8239 8240/** 8241 * Return the filename of file to get the thumbs 8242 * 8243 * @param string $file Original filename (full or relative path) 8244 * @param string $extName Extension to differenciate thumb file name ('', '_small', '_mini') 8245 * @param string $extImgTarget Force image extension for thumbs. Use '' to keep same extension than original image (default). 8246 * @return string New file name (full or relative path, including the thumbs/) 8247 */ 8248function getImageFileNameForSize($file, $extName, $extImgTarget = '') 8249{ 8250 $dirName = dirname($file); 8251 if ($dirName == '.') $dirName = ''; 8252 8253 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case 8254 $fileName = basename($fileName); 8255 8256 if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : ''); 8257 if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : ''); 8258 if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : ''); 8259 if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : ''); 8260 if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : ''); 8261 if (empty($extImgTarget)) $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : ''); 8262 8263 if (!$extImgTarget) return $file; 8264 8265 $subdir = ''; 8266 if ($extName) $subdir = 'thumbs/'; 8267 8268 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb 8269} 8270 8271 8272/** 8273 * Return URL we can use for advanced preview links 8274 * 8275 * @param string $modulepart propal, facture, facture_fourn, ... 8276 * @param string $relativepath Relative path of docs. 8277 * @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) 8278 * @param string $param More param on http links 8279 * @return string|array Output string with href link or array with all components of link 8280 */ 8281function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '') 8282{ 8283 global $conf, $langs; 8284 8285 if (empty($conf->use_javascript_ajax)) return ''; 8286 8287 $isAllowedForPreview = dolIsAllowedForPreview($relativepath); 8288 8289 if ($alldata == 1) 8290 { 8291 if ($isAllowedForPreview) return array('target'=>'_blank', 'css'=>'documentpreview', 'url'=>DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : ''), 'mime'=>dol_mimetype($relativepath)); 8292 else return array(); 8293 } 8294 8295 // old behavior, return a string 8296 if ($isAllowedForPreview) return 'javascript:document_preview(\''.dol_escape_js(DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '')).'\', \''.dol_mimetype($relativepath).'\', \''.dol_escape_js($langs->trans('Preview')).'\')'; 8297 else return ''; 8298} 8299 8300 8301/** 8302 * Make content of an input box selected when we click into input field. 8303 * 8304 * @param string $htmlname Id of html object ('#idvalue' or '.classvalue') 8305 * @param string $addlink Add a 'link to' after 8306 * @return string 8307 */ 8308function ajax_autoselect($htmlname, $addlink = '') 8309{ 8310 global $langs; 8311 $out = '<script> 8312 jQuery(document).ready(function () { 8313 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } ); 8314 }); 8315 </script>'; 8316 if ($addlink) $out .= ' <a href="'.$addlink.'" target="_blank">'.$langs->trans("Link").'</a>'; 8317 return $out; 8318} 8319 8320/** 8321 * Return if a file is qualified for preview 8322 * 8323 * @param string $file Filename we looking for information 8324 * @return int 1 If allowed, 0 otherwise 8325 * @see dol_mimetype(), image_format_supported() from images.lib.php 8326 */ 8327function dolIsAllowedForPreview($file) 8328{ 8329 global $conf; 8330 8331 // Check .noexe extension in filename 8332 if (preg_match('/\.noexe$/i', $file)) return 0; 8333 8334 // Check mime types 8335 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp'); 8336 if (!empty($conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES)) $mime_preview[] = 'svg+xml'; 8337 //$mime_preview[]='vnd.oasis.opendocument.presentation'; 8338 //$mime_preview[]='archive'; 8339 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview); 8340 if ($num_mime !== false) return 1; 8341 8342 // By default, not allowed for preview 8343 return 0; 8344} 8345 8346 8347/** 8348 * Return mime type of a file 8349 * 8350 * @param string $file Filename we looking for MIME type 8351 * @param string $default Default mime type if extension not found in known list 8352 * @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 8353 * @return string Return a mime type family (text/xxx, application/xxx, image/xxx, audio, video, archive) 8354 * @see dolIsAllowedForPreview(), image_format_supported() from images.lib.php 8355 */ 8356function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0) 8357{ 8358 $mime = $default; 8359 $imgmime = 'other.png'; 8360 $famime = 'file-o'; 8361 $srclang = ''; 8362 8363 $tmpfile = preg_replace('/\.noexe$/', '', $file); 8364 8365 // Plain text files 8366 if (preg_match('/\.txt$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $famime = 'file-text-o'; } 8367 if (preg_match('/\.rtx$/i', $tmpfile)) { $mime = 'text/richtext'; $imgmime = 'text.png'; $famime = 'file-text-o'; } 8368 if (preg_match('/\.csv$/i', $tmpfile)) { $mime = 'text/csv'; $imgmime = 'text.png'; $famime = 'file-text-o'; } 8369 if (preg_match('/\.tsv$/i', $tmpfile)) { $mime = 'text/tab-separated-values'; $imgmime = 'text.png'; $famime = 'file-text-o'; } 8370 if (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $famime = 'file-text-o'; } 8371 if (preg_match('/\.ini$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'ini'; $famime = 'file-text-o'; } 8372 if (preg_match('/\.md$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'md'; $famime = 'file-text-o'; } 8373 if (preg_match('/\.css$/i', $tmpfile)) { $mime = 'text/css'; $imgmime = 'css.png'; $srclang = 'css'; $famime = 'file-text-o'; } 8374 if (preg_match('/\.lang$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'lang'; $famime = 'file-text-o'; } 8375 // Certificate files 8376 if (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $famime = 'file-text-o'; } 8377 // XML based (HTML/XML/XAML) 8378 if (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { $mime = 'text/html'; $imgmime = 'html.png'; $srclang = 'html'; $famime = 'file-text-o'; } 8379 if (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) { $mime = 'text/xml'; $imgmime = 'other.png'; $srclang = 'xml'; $famime = 'file-text-o'; } 8380 if (preg_match('/\.xaml$/i', $tmpfile)) { $mime = 'text/xml'; $imgmime = 'other.png'; $srclang = 'xaml'; $famime = 'file-text-o'; } 8381 // Languages 8382 if (preg_match('/\.bas$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'bas'; $famime = 'file-code-o'; } 8383 if (preg_match('/\.(c)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'c'; $famime = 'file-code-o'; } 8384 if (preg_match('/\.(cpp)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'cpp'; $famime = 'file-code-o'; } 8385 if (preg_match('/\.cs$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'cs'; $famime = 'file-code-o'; } 8386 if (preg_match('/\.(h)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'h'; $famime = 'file-code-o'; } 8387 if (preg_match('/\.(java|jsp)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'java'; $famime = 'file-code-o'; } 8388 if (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'php.png'; $srclang = 'php'; $famime = 'file-code-o'; } 8389 if (preg_match('/\.phtml$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'php.png'; $srclang = 'php'; $famime = 'file-code-o'; } 8390 if (preg_match('/\.(pl|pm)$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'pl.png'; $srclang = 'perl'; $famime = 'file-code-o'; } 8391 if (preg_match('/\.sql$/i', $tmpfile)) { $mime = 'text/plain'; $imgmime = 'text.png'; $srclang = 'sql'; $famime = 'file-code-o'; } 8392 if (preg_match('/\.js$/i', $tmpfile)) { $mime = 'text/x-javascript'; $imgmime = 'jscript.png'; $srclang = 'js'; $famime = 'file-code-o'; } 8393 // Open office 8394 if (preg_match('/\.odp$/i', $tmpfile)) { $mime = 'application/vnd.oasis.opendocument.presentation'; $imgmime = 'ooffice.png'; $famime = 'file-powerpoint-o'; } 8395 if (preg_match('/\.ods$/i', $tmpfile)) { $mime = 'application/vnd.oasis.opendocument.spreadsheet'; $imgmime = 'ooffice.png'; $famime = 'file-excel-o'; } 8396 if (preg_match('/\.odt$/i', $tmpfile)) { $mime = 'application/vnd.oasis.opendocument.text'; $imgmime = 'ooffice.png'; $famime = 'file-word-o'; } 8397 // MS Office 8398 if (preg_match('/\.mdb$/i', $tmpfile)) { $mime = 'application/msaccess'; $imgmime = 'mdb.png'; $famime = 'file-o'; } 8399 if (preg_match('/\.doc(x|m)?$/i', $tmpfile)) { $mime = 'application/msword'; $imgmime = 'doc.png'; $famime = 'file-word-o'; } 8400 if (preg_match('/\.dot(x|m)?$/i', $tmpfile)) { $mime = 'application/msword'; $imgmime = 'doc.png'; $famime = 'file-word-o'; } 8401 if (preg_match('/\.xlt(x)?$/i', $tmpfile)) { $mime = 'application/vnd.ms-excel'; $imgmime = 'xls.png'; $famime = 'file-excel-o'; } 8402 if (preg_match('/\.xla(m)?$/i', $tmpfile)) { $mime = 'application/vnd.ms-excel'; $imgmime = 'xls.png'; $famime = 'file-excel-o'; } 8403 if (preg_match('/\.xls$/i', $tmpfile)) { $mime = 'application/vnd.ms-excel'; $imgmime = 'xls.png'; $famime = 'file-excel-o'; } 8404 if (preg_match('/\.xls(b|m|x)$/i', $tmpfile)) { $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; $imgmime = 'xls.png'; $famime = 'file-excel-o'; } 8405 if (preg_match('/\.pps(m|x)?$/i', $tmpfile)) { $mime = 'application/vnd.ms-powerpoint'; $imgmime = 'ppt.png'; $famime = 'file-powerpoint-o'; } 8406 if (preg_match('/\.ppt(m|x)?$/i', $tmpfile)) { $mime = 'application/x-mspowerpoint'; $imgmime = 'ppt.png'; $famime = 'file-powerpoint-o'; } 8407 // Other 8408 if (preg_match('/\.pdf$/i', $tmpfile)) { $mime = 'application/pdf'; $imgmime = 'pdf.png'; $famime = 'file-pdf-o'; } 8409 // Scripts 8410 if (preg_match('/\.bat$/i', $tmpfile)) { $mime = 'text/x-bat'; $imgmime = 'script.png'; $srclang = 'dos'; $famime = 'file-code-o'; } 8411 if (preg_match('/\.sh$/i', $tmpfile)) { $mime = 'text/x-sh'; $imgmime = 'script.png'; $srclang = 'bash'; $famime = 'file-code-o'; } 8412 if (preg_match('/\.ksh$/i', $tmpfile)) { $mime = 'text/x-ksh'; $imgmime = 'script.png'; $srclang = 'bash'; $famime = 'file-code-o'; } 8413 if (preg_match('/\.bash$/i', $tmpfile)) { $mime = 'text/x-bash'; $imgmime = 'script.png'; $srclang = 'bash'; $famime = 'file-code-o'; } 8414 // Images 8415 if (preg_match('/\.ico$/i', $tmpfile)) { $mime = 'image/x-icon'; $imgmime = 'image.png'; $famime = 'file-image-o'; } 8416 if (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) { $mime = 'image/jpeg'; $imgmime = 'image.png'; $famime = 'file-image-o'; } 8417 if (preg_match('/\.png$/i', $tmpfile)) { $mime = 'image/png'; $imgmime = 'image.png'; $famime = 'file-image-o'; } 8418 if (preg_match('/\.gif$/i', $tmpfile)) { $mime = 'image/gif'; $imgmime = 'image.png'; $famime = 'file-image-o'; } 8419 if (preg_match('/\.bmp$/i', $tmpfile)) { $mime = 'image/bmp'; $imgmime = 'image.png'; $famime = 'file-image-o'; } 8420 if (preg_match('/\.(tif|tiff)$/i', $tmpfile)) { $mime = 'image/tiff'; $imgmime = 'image.png'; $famime = 'file-image-o'; } 8421 if (preg_match('/\.svg$/i', $tmpfile)) { $mime = 'image/svg+xml'; $imgmime = 'image.png'; $famime = 'file-image-o'; } 8422 if (preg_match('/\.webp$/i', $tmpfile)) { $mime = 'image/webp'; $imgmime = 'image.png'; $famime = 'file-image-o'; } 8423 // Calendar 8424 if (preg_match('/\.vcs$/i', $tmpfile)) { $mime = 'text/calendar'; $imgmime = 'other.png'; $famime = 'file-text-o'; } 8425 if (preg_match('/\.ics$/i', $tmpfile)) { $mime = 'text/calendar'; $imgmime = 'other.png'; $famime = 'file-text-o'; } 8426 // Other 8427 if (preg_match('/\.torrent$/i', $tmpfile)) { $mime = 'application/x-bittorrent'; $imgmime = 'other.png'; $famime = 'file-o'; } 8428 // Audio 8429 if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { $mime = 'audio'; $imgmime = 'audio.png'; $famime = 'file-audio-o'; } 8430 // Video 8431 if (preg_match('/\.mp4$/i', $tmpfile)) { $mime = 'video/mp4'; $imgmime = 'video.png'; $famime = 'file-video-o'; } 8432 if (preg_match('/\.ogv$/i', $tmpfile)) { $mime = 'video/ogg'; $imgmime = 'video.png'; $famime = 'file-video-o'; } 8433 if (preg_match('/\.webm$/i', $tmpfile)) { $mime = 'video/webm'; $imgmime = 'video.png'; $famime = 'file-video-o'; } 8434 if (preg_match('/\.avi$/i', $tmpfile)) { $mime = 'video/x-msvideo'; $imgmime = 'video.png'; $famime = 'file-video-o'; } 8435 if (preg_match('/\.divx$/i', $tmpfile)) { $mime = 'video/divx'; $imgmime = 'video.png'; $famime = 'file-video-o'; } 8436 if (preg_match('/\.xvid$/i', $tmpfile)) { $mime = 'video/xvid'; $imgmime = 'video.png'; $famime = 'file-video-o'; } 8437 if (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) { $mime = 'video'; $imgmime = 'video.png'; $famime = 'file-video-o'; } 8438 // Archive 8439 if (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh)$/i', $tmpfile)) { $mime = 'archive'; $imgmime = 'archive.png'; $famime = 'file-archive-o'; } // application/xxx where zzz is zip, ... 8440 // Exe 8441 if (preg_match('/\.(exe|com)$/i', $tmpfile)) { $mime = 'application/octet-stream'; $imgmime = 'other.png'; $famime = 'file-o'; } 8442 // Lib 8443 if (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { $mime = 'library'; $imgmime = 'library.png'; $famime = 'file-o'; } 8444 // Err 8445 if (preg_match('/\.err$/i', $tmpfile)) { $mime = 'error'; $imgmime = 'error.png'; $famime = 'file-text-o'; } 8446 8447 // Return string 8448 if ($mode == 1) 8449 { 8450 $tmp = explode('/', $mime); 8451 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]); 8452 } 8453 if ($mode == 2) 8454 { 8455 return $imgmime; 8456 } 8457 if ($mode == 3) 8458 { 8459 return $srclang; 8460 } 8461 if ($mode == 4) 8462 { 8463 return $famime; 8464 } 8465 return $mime; 8466} 8467 8468/** 8469 * Return value from dictionary 8470 * 8471 * @param string $tablename name of dictionary 8472 * @param string $field the value to return 8473 * @param int $id id of line 8474 * @param bool $checkentity add filter on entity 8475 * @param string $rowidfield name of the column rowid 8476 * @return string 8477 */ 8478function getDictvalue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid') 8479{ 8480 global $dictvalues, $db, $langs; 8481 8482 if (!isset($dictvalues[$tablename])) 8483 { 8484 $dictvalues[$tablename] = array(); 8485 $sql = 'SELECT * FROM '.$tablename.' WHERE 1 = 1'; // Here select * is allowed as it is generic code and we don't have list of fields 8486 if ($checkentity) $sql .= ' AND entity IN (0,'.getEntity($tablename).')'; 8487 8488 $resql = $db->query($sql); 8489 if ($resql) 8490 { 8491 while ($obj = $db->fetch_object($resql)) 8492 { 8493 $dictvalues[$tablename][$obj->{$rowidfield}] = $obj; 8494 } 8495 } else { 8496 dol_print_error($db); 8497 } 8498 } 8499 8500 if (!empty($dictvalues[$tablename][$id])) return $dictvalues[$tablename][$id]->{$field}; // Found 8501 else // Not found 8502 { 8503 if ($id > 0) return $id; 8504 return ''; 8505 } 8506} 8507 8508/** 8509 * Return true if the color is light 8510 * 8511 * @param string $stringcolor String with hex (FFFFFF) or comma RGB ('255,255,255') 8512 * @return int -1 : Error with argument passed |0 : color is dark | 1 : color is light 8513 */ 8514function colorIsLight($stringcolor) 8515{ 8516 $stringcolor = str_replace('#', '', $stringcolor); 8517 $res = -1; 8518 if (!empty($stringcolor)) 8519 { 8520 $res = 0; 8521 $tmp = explode(',', $stringcolor); 8522 if (count($tmp) > 1) // This is a comma RGB ('255','255','255') 8523 { 8524 $r = $tmp[0]; 8525 $g = $tmp[1]; 8526 $b = $tmp[2]; 8527 } else { 8528 $hexr = $stringcolor[0].$stringcolor[1]; 8529 $hexg = $stringcolor[2].$stringcolor[3]; 8530 $hexb = $stringcolor[4].$stringcolor[5]; 8531 $r = hexdec($hexr); 8532 $g = hexdec($hexg); 8533 $b = hexdec($hexb); 8534 } 8535 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm 8536 if ($bright > 0.6) $res = 1; 8537 } 8538 return $res; 8539} 8540 8541/** 8542 * Function to test if an entry is enabled or not 8543 * 8544 * @param string $type_user 0=We test for internal user, 1=We test for external user 8545 * @param array $menuentry Array for feature entry to test 8546 * @param array $listofmodulesforexternal Array with list of modules allowed to external users 8547 * @return int 0=Hide, 1=Show, 2=Show gray 8548 */ 8549function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal) 8550{ 8551 global $conf; 8552 8553 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms']; 8554 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal); 8555 if (empty($menuentry['enabled'])) return 0; // Entry disabled by condition 8556 if ($type_user && $menuentry['module']) 8557 { 8558 $tmploops = explode('|', $menuentry['module']); 8559 $found = 0; 8560 foreach ($tmploops as $tmploop) 8561 { 8562 if (in_array($tmploop, $listofmodulesforexternal)) { 8563 $found++; break; 8564 } 8565 } 8566 if (!$found) return 0; // Entry is for menus all excluded to external users 8567 } 8568 if (!$menuentry['perms'] && $type_user) return 0; // No permissions and user is external 8569 if (!$menuentry['perms'] && !empty($conf->global->MAIN_MENU_HIDE_UNAUTHORIZED)) return 0; // No permissions and option to hide when not allowed, even for internal user, is on 8570 if (!$menuentry['perms']) return 2; // No permissions and user is external 8571 return 1; 8572} 8573 8574/** 8575 * Round to next multiple. 8576 * 8577 * @param double $n Number to round up 8578 * @param integer $x Multiple. For example 60 to round up to nearest exact minute for a date with seconds. 8579 * @return integer Value rounded. 8580 */ 8581function roundUpToNextMultiple($n, $x = 5) 8582{ 8583 return (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x; 8584} 8585 8586/** 8587 * Function dolGetBadge 8588 * 8589 * @param string $label label of badge no html : use in alt attribute for accessibility 8590 * @param string $html optional : label of badge with html 8591 * @param string $type type of badge : Primary Secondary Success Danger Warning Info Light Dark status0 status1 status2 status3 status4 status5 status6 status7 status8 status9 8592 * @param string $mode default '' , 'pill', 'dot' 8593 * @param string $url the url for link 8594 * @param array $params various params for future : recommended rather than adding more fuction arguments. array('attr'=>array('title'=>'abc')) 8595 * @return string Html badge 8596 */ 8597function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array()) 8598{ 8599 $attr = array( 8600 'class'=>'badge '.(!empty($mode) ? ' badge-'.$mode : '').(!empty($type) ? ' badge-'.$type : '').(empty($params['css']) ? '' : ' '.$params['css']) 8601 ); 8602 8603 if (empty($html)) { 8604 $html = $label; 8605 } 8606 8607 if (!empty($url)) { 8608 $attr['href'] = $url; 8609 } 8610 8611 if ($mode === 'dot') { 8612 $attr['class'] .= ' classfortooltip'; 8613 $attr['title'] = $html; 8614 $attr['aria-label'] = $label; 8615 $html = ''; 8616 } 8617 8618 // Override attr 8619 if (!empty($params['attr']) && is_array($params['attr'])) { 8620 foreach ($params['attr']as $key => $value) { 8621 if ($key == 'class') { 8622 $attr['class'] .= ' '.$value; 8623 } elseif ($key == 'classOverride') { 8624 $attr['class'] = $value; 8625 } else { 8626 $attr[$key] = $value; 8627 } 8628 } 8629 } 8630 8631 // TODO: add hook 8632 8633 // escape all attribute 8634 $attr = array_map('dol_escape_htmltag', $attr); 8635 8636 $TCompiledAttr = array(); 8637 foreach ($attr as $key => $value) { 8638 $TCompiledAttr[] = $key.'="'.$value.'"'; 8639 } 8640 8641 $compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : ''; 8642 8643 $tag = !empty($url) ? 'a' : 'span'; 8644 8645 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>'; 8646} 8647 8648 8649/** 8650 * Output the badge of a status. 8651 * 8652 * @param string $statusLabel Label of badge no html : use in alt attribute for accessibility 8653 * @param string $statusLabelShort Short label of badge no html 8654 * @param string $html Optional : label of badge with html 8655 * @param string $statusType status0 status1 status2 status3 status4 status5 status6 status7 status8 status9 : image name or badge name 8656 * @param int $displayMode 0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto, 6=Long label + Picto 8657 * @param string $url The url for link 8658 * @param array $params Various params. Example: array('tooltip'=>'no|...', 'badgeParams'=>...) 8659 * @return string Html status string 8660 */ 8661function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array()) 8662{ 8663 global $conf; 8664 8665 $return = ''; 8666 $dolGetBadgeParams = array(); 8667 8668 if (!empty($params['badgeParams'])) { 8669 $dolGetBadgeParams = $params['badgeParams']; 8670 } 8671 8672 // TODO : add a hook 8673 if ($displayMode == 0) { 8674 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)); 8675 } elseif ($displayMode == 1) { 8676 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort); 8677 } // Use status with images (for backward compatibility) 8678 elseif (!empty($conf->global->MAIN_STATUS_USES_IMAGES)) { 8679 $return = ''; 8680 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : ''); 8681 $htmlLabelShort = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : (!empty($statusLabelShort) ? $statusLabelShort : $statusLabel)).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : ''); 8682 8683 // For small screen, we always use the short label instead of long label. 8684 if (!empty($conf->dol_optimize_smallscreen)) 8685 { 8686 if ($displayMode == 0) $displayMode = 1; 8687 elseif ($displayMode == 4) $displayMode = 2; 8688 elseif ($displayMode == 6) $displayMode = 5; 8689 } 8690 8691 // For backward compatibility. Image's filename are still in French, so we use this array to convert 8692 $statusImg = array( 8693 'status0' => 'statut0', 8694 'status1' => 'statut1', 8695 'status2' => 'statut2', 8696 'status3' => 'statut3', 8697 'status4' => 'statut4', 8698 'status5' => 'statut5', 8699 'status6' => 'statut6', 8700 'status7' => 'statut7', 8701 'status8' => 'statut8', 8702 'status9' => 'statut9' 8703 ); 8704 8705 if (!empty($statusImg[$statusType])) { 8706 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]); 8707 } else { 8708 $htmlImg = img_picto($statusLabel, $statusType); 8709 } 8710 8711 if ($displayMode === 2) { 8712 $return = $htmlImg.' '.$htmlLabelShort; 8713 } elseif ($displayMode === 3) { 8714 $return = $htmlImg; 8715 } elseif ($displayMode === 4) { 8716 $return = $htmlImg.' '.$htmlLabel; 8717 } elseif ($displayMode === 5) { 8718 $return = $htmlLabelShort.' '.$htmlImg; 8719 } else { // $displayMode >= 6 8720 $return = $htmlLabel.' '.$htmlImg; 8721 } 8722 } // Use new badge 8723 elseif (empty($conf->global->MAIN_STATUS_USES_IMAGES) && !empty($displayMode)) { 8724 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort); 8725 8726 $dolGetBadgeParams['attr']['class'] = 'badge-status'; 8727 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : ''); 8728 8729 if ($displayMode == 3) { 8730 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams); 8731 } elseif ($displayMode === 5) { 8732 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams); 8733 } else { 8734 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams); 8735 } 8736 } 8737 8738 return $return; 8739} 8740 8741 8742/** 8743 * Function dolGetButtonAction 8744 * 8745 * @param string $label label of button no html : use in alt attribute for accessibility $html is not empty 8746 * @param string $html optional : content with html 8747 * @param string $actionType default, delete, danger 8748 * @param string $url the url for link 8749 * @param string $id attribute id of button 8750 * @param int $userRight user action right 8751 * @param array $params various params for future : recommended rather than adding more function arguments 8752 * @return string html button 8753 */ 8754function dolGetButtonAction($label, $html = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array()) 8755{ 8756 $class = 'butAction'; 8757 if ($actionType == 'danger' || $actionType == 'delete') { 8758 $class = 'butActionDelete'; 8759 if (strpos($url, 'token=') === false) $url .= '&token='.newToken(); 8760 } 8761 8762 $attr = array( 8763 'class' => $class 8764 ,'href' => empty($url) ? '' : $url 8765 ); 8766 8767 if (empty($html)) { 8768 $html = $label; 8769 } else { 8770 $attr['aria-label'] = $label; 8771 } 8772 8773 if (empty($userRight)) { 8774 $attr['class'] = 'butActionRefused'; 8775 $attr['href'] = ''; 8776 } 8777 8778 if (!empty($id)) { 8779 $attr['id'] = $id; 8780 } 8781 8782 // Override attr 8783 if (!empty($params['attr']) && is_array($params['attr'])) { 8784 foreach ($params['attr'] as $key => $value) { 8785 if ($key == 'class') { 8786 $attr['class'] .= ' '.$value; 8787 } elseif ($key == 'classOverride') { 8788 $attr['class'] = $value; 8789 } else { 8790 $attr[$key] = $value; 8791 } 8792 } 8793 } 8794 8795 if (isset($attr['href']) && empty($attr['href'])) { 8796 unset($attr['href']); 8797 } 8798 8799 // TODO : add a hook 8800 8801 // escape all attribute 8802 $attr = array_map('dol_escape_htmltag', $attr); 8803 8804 $TCompiledAttr = array(); 8805 foreach ($attr as $key => $value) { 8806 $TCompiledAttr[] = $key.'="'.$value.'"'; 8807 } 8808 8809 $compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : ''; 8810 8811 $tag = !empty($attr['href']) ? 'a' : 'span'; 8812 8813 return '<div class="inline-block divButAction"><'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'></div>'; 8814} 8815 8816/** 8817 * Function dolGetButtonTitle : this kind of buttons are used in title in list 8818 * 8819 * @param string $label label of button 8820 * @param string $helpText optional : content for help tooltip 8821 * @param string $iconClass class for icon element (Example: 'fa fa-file') 8822 * @param string $url the url for link 8823 * @param string $id attribute id of button 8824 * @param int $status 0 no user rights, 1 active, -1 Feature Disabled, -2 disable Other reason use helpText as tooltip 8825 * @param array $params various params for future : recommended rather than adding more function arguments 8826 * @return string html button 8827 */ 8828function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array()) 8829{ 8830 global $langs, $conf, $user; 8831 8832 // Actually this conf is used in css too for external module compatibility and smooth transition to this function 8833 if (!empty($conf->global->MAIN_BUTTON_HIDE_UNAUTHORIZED) && (!$user->admin) && $status <= 0) { 8834 return ''; 8835 } 8836 8837 $class = 'btnTitle'; 8838 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-comment-dots'))) $class .= ' btnTitlePlus'; 8839 $useclassfortooltip = 1; 8840 8841 if (!empty($params['morecss'])) $class .= ' '.$params['morecss']; 8842 8843 $attr = array( 8844 'class' => $class, 8845 'href' => empty($url) ? '' : $url 8846 ); 8847 8848 if (!empty($helpText)) { 8849 $attr['title'] = dol_escape_htmltag($helpText); 8850 } elseif (empty($attr['title']) && $label) { 8851 $attr['title'] = $label; 8852 $useclassfortooltip = 0; 8853 } 8854 8855 if ($status <= 0) { 8856 $attr['class'] .= ' refused'; 8857 8858 $attr['href'] = ''; 8859 8860 if ($status == -1) { // disable 8861 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled")); 8862 } elseif ($status == 0) { // Not enough permissions 8863 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions")); 8864 } 8865 } 8866 8867 if (!empty($attr['title']) && $useclassfortooltip) { 8868 $attr['class'] .= ' classfortooltip'; 8869 } 8870 8871 if (!empty($id)) { 8872 $attr['id'] = $id; 8873 } 8874 8875 // Override attr 8876 if (!empty($params['attr']) && is_array($params['attr'])) { 8877 foreach ($params['attr'] as $key => $value) { 8878 if ($key == 'class') { 8879 $attr['class'] .= ' '.$value; 8880 } elseif ($key == 'classOverride') { 8881 $attr['class'] = $value; 8882 } else { 8883 $attr[$key] = $value; 8884 } 8885 } 8886 } 8887 8888 if (isset($attr['href']) && empty($attr['href'])) { 8889 unset($attr['href']); 8890 } 8891 8892 // TODO : add a hook 8893 8894 // escape all attribute 8895 $attr = array_map('dol_escape_htmltag', $attr); 8896 8897 $TCompiledAttr = array(); 8898 foreach ($attr as $key => $value) { 8899 $TCompiledAttr[] = $key.'="'.$value.'"'; 8900 } 8901 8902 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr)); 8903 8904 $tag = (empty($attr['href']) ? 'span' : 'a'); 8905 8906 $button = '<'.$tag.' '.$compiledAttributes.'>'; 8907 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>'; 8908 if (!empty($params['forcenohideoftext'])) { 8909 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>'; 8910 } 8911 $button .= '</'.$tag.'>'; 8912 8913 return $button; 8914} 8915 8916/** 8917 * Get an array with properties of an element. 8918 * Called by fetchObjectByElement. 8919 * 8920 * @param string $element_type Element type (Value of $object->element). Example: 'action', 'facture', 'project_task' or 'object@mymodule'... 8921 * @return array (module, classpath, element, subelement, classfile, classname) 8922 */ 8923function getElementProperties($element_type) 8924{ 8925 $regs = array(); 8926 8927 $classfile = $classname = $classpath = ''; 8928 8929 // Parse element/subelement (ex: project_task) 8930 $module = $element_type; 8931 $element = $element_type; 8932 $subelement = $element_type; 8933 8934 // If we ask an resource form external module (instead of default path) 8935 if (preg_match('/^([^@]+)@([^@]+)$/i', $element_type, $regs)) { 8936 $element = $subelement = $regs[1]; 8937 $module = $regs[2]; 8938 } 8939 8940 //print '<br>1. element : '.$element.' - module : '.$module .'<br>'; 8941 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { 8942 $module = $element = $regs[1]; 8943 $subelement = $regs[2]; 8944 } 8945 8946 // For compat 8947 if ($element_type == "action") { 8948 $classpath = 'comm/action/class'; 8949 $subelement = 'Actioncomm'; 8950 $module = 'agenda'; 8951 } 8952 8953 // To work with non standard path 8954 if ($element_type == 'facture' || $element_type == 'invoice') { 8955 $classpath = 'compta/facture/class'; 8956 $module = 'facture'; 8957 $subelement = 'facture'; 8958 } 8959 if ($element_type == 'commande' || $element_type == 'order') { 8960 $classpath = 'commande/class'; 8961 $module = 'commande'; 8962 $subelement = 'commande'; 8963 } 8964 if ($element_type == 'propal') { 8965 $classpath = 'comm/propal/class'; 8966 } 8967 if ($element_type == 'supplier_proposal') { 8968 $classpath = 'supplier_proposal/class'; 8969 } 8970 if ($element_type == 'shipping') { 8971 $classpath = 'expedition/class'; 8972 $subelement = 'expedition'; 8973 $module = 'expedition_bon'; 8974 } 8975 if ($element_type == 'delivery') { 8976 $classpath = 'delivery/class'; 8977 $subelement = 'delivery'; 8978 $module = 'delivery_note'; 8979 } 8980 if ($element_type == 'contract') { 8981 $classpath = 'contrat/class'; 8982 $module = 'contrat'; 8983 $subelement = 'contrat'; 8984 } 8985 if ($element_type == 'member') { 8986 $classpath = 'adherents/class'; 8987 $module = 'adherent'; 8988 $subelement = 'adherent'; 8989 } 8990 if ($element_type == 'cabinetmed_cons') { 8991 $classpath = 'cabinetmed/class'; 8992 $module = 'cabinetmed'; 8993 $subelement = 'cabinetmedcons'; 8994 } 8995 if ($element_type == 'fichinter') { 8996 $classpath = 'fichinter/class'; 8997 $module = 'ficheinter'; 8998 $subelement = 'fichinter'; 8999 } 9000 if ($element_type == 'dolresource' || $element_type == 'resource') { 9001 $classpath = 'resource/class'; 9002 $module = 'resource'; 9003 $subelement = 'dolresource'; 9004 } 9005 if ($element_type == 'propaldet') { 9006 $classpath = 'comm/propal/class'; 9007 $module = 'propal'; 9008 $subelement = 'propaleligne'; 9009 } 9010 if ($element_type == 'order_supplier') { 9011 $classpath = 'fourn/class'; 9012 $module = 'fournisseur'; 9013 $subelement = 'commandefournisseur'; 9014 $classfile = 'fournisseur.commande'; 9015 } 9016 if ($element_type == 'invoice_supplier') { 9017 $classpath = 'fourn/class'; 9018 $module = 'fournisseur'; 9019 $subelement = 'facturefournisseur'; 9020 $classfile = 'fournisseur.facture'; 9021 } 9022 if ($element_type == "service") { 9023 $classpath = 'product/class'; 9024 $subelement = 'product'; 9025 } 9026 9027 if (empty($classfile)) $classfile = strtolower($subelement); 9028 if (empty($classname)) $classname = ucfirst($subelement); 9029 if (empty($classpath)) $classpath = $module.'/class'; 9030 9031 $element_properties = array( 9032 'module' => $module, 9033 'classpath' => $classpath, 9034 'element' => $element, 9035 'subelement' => $subelement, 9036 'classfile' => $classfile, 9037 'classname' => $classname 9038 ); 9039 return $element_properties; 9040} 9041 9042/** 9043 * Fetch an object from its id and element_type 9044 * Inclusion of classes is automatic 9045 * 9046 * @param int $element_id Element id 9047 * @param string $element_type Element type 9048 * @param string $element_ref Element ref (Use this or element_id but not both) 9049 * @return int|object object || 0 || -1 if error 9050 */ 9051function fetchObjectByElement($element_id, $element_type, $element_ref = '') 9052{ 9053 global $conf, $db; 9054 9055 $element_prop = getElementProperties($element_type); 9056 if (is_array($element_prop) && $conf->{$element_prop['module']}->enabled) 9057 { 9058 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php'); 9059 9060 $objecttmp = new $element_prop['classname']($db); 9061 $ret = $objecttmp->fetch($element_id, $element_ref); 9062 if ($ret >= 0) 9063 { 9064 return $objecttmp; 9065 } 9066 } 9067 return 0; 9068} 9069 9070/** 9071 * Return if a file can contains executable content 9072 * 9073 * @param string $filename File name to test 9074 * @return boolean True if yes, False if no 9075 */ 9076function isAFileWithExecutableContent($filename) 9077{ 9078 if (preg_match('/\.(htm|html|js|phar|php|php\d+|phtml|pht|pl|py|cgi|ksh|sh|shtml|bash|bat|cmd|wpk|exe|dmg)$/i', $filename)) 9079 { 9080 return true; 9081 } 9082 9083 return false; 9084} 9085 9086/** 9087 * Return the value of token currently saved into session with name 'newtoken'. 9088 * This token must be send by any POST as it will be used by next page for comparison with value in session. 9089 * 9090 * @return string 9091 */ 9092function newToken() 9093{ 9094 return $_SESSION['newtoken']; 9095} 9096 9097/** 9098 * Return the value of token currently saved into session with name 'token'. 9099 * 9100 * @return string 9101 */ 9102function currentToken() 9103{ 9104 return $_SESSION['token']; 9105} 9106 9107/** 9108 * Start a table with headers and a optinal clickable number (don't forget to use "finishSimpleTable()" after the last table row) 9109 * 9110 * @param string $header The first left header of the table (automatic translated) 9111 * @param string $link (optional) The link to a internal dolibarr page, when click on the number (without the first "/") 9112 * @param string $arguments (optional) Additional arguments for the link (e.g. "search_status=0") 9113 * @param integer $emptyRows (optional) The count of empty rows after the first header 9114 * @param integer $number (optional) The number that is shown right after the first header, when not set the link is shown on the right side of the header as "FullList" 9115 * @return void 9116 * 9117 * @see finishSimpleTable() 9118 */ 9119function startSimpleTable($header, $link = "", $arguments = "", $emptyRows = 0, $number = -1) 9120{ 9121 global $langs; 9122 9123 print '<div class="div-table-responsive-no-min">'; 9124 print '<table class="noborder centpercent">'; 9125 print '<tr class="liste_titre">'; 9126 9127 print $emptyRows < 1 ? '<th>' : '<th colspan="'.($emptyRows + 1).'">'; 9128 9129 print $langs->trans($header); 9130 9131 // extra space between the first header and the number 9132 if ($number > -1) { 9133 print ' '; 9134 } 9135 9136 if (!empty($link)) { 9137 if (!empty($arguments)) { 9138 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">'; 9139 } else { 9140 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">'; 9141 } 9142 } 9143 9144 if ($number > -1) { 9145 print '<span class="badge">'.$number.'</span>'; 9146 } 9147 9148 if (!empty($link)) { 9149 print '</a>'; 9150 } 9151 9152 print '</th>'; 9153 9154 if ($number < 0 && !empty($link)) { 9155 print '<th class="right">'; 9156 9157 if (!empty($arguments)) { 9158 print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">'; 9159 } else { 9160 print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'">'; 9161 } 9162 9163 print $langs->trans("FullList"); 9164 print '</a>'; 9165 print '</th>'; 9166 } 9167 9168 print '</tr>'; 9169} 9170 9171/** 9172 * Add the correct HTML close tags for "startSimpleTable(...)" (use after the last table line) 9173 * 9174 * @param bool $addLineBreak (optional) Add a extra line break after the complete table (\<br\>) 9175 * @return void 9176 * 9177 * @see startSimpleTable() 9178 */ 9179function finishSimpleTable($addLineBreak = false) 9180{ 9181 print '</table>'; 9182 print '</div>'; 9183 9184 if ($addLineBreak) { 9185 print '<br>'; 9186 } 9187} 9188 9189/** 9190 * Add a summary line to the current open table ("None", "XMoreLines" or "Total xxx") 9191 * 9192 * @param integer $tableColumnCount The complete count columns of the table 9193 * @param integer $num The count of the rows of the table, when it is zero (0) the "$noneWord" is shown instead 9194 * @param integer $nbofloop (optional) The maximum count of rows thaht the table show (when it is zero (0) no summary line will show, expect "$noneWord" when $num === 0) 9195 * @param integer $total (optional) The total value thaht is shown after when the table has minimum of one entire 9196 * @param string $noneWord (optional) The word that is shown when the table has no entires ($num === 0) 9197 * @param boolean $extraRightColumn (optional) Add a addtional column after the summary word and total number 9198 * @return void 9199 */ 9200function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false) 9201{ 9202 global $langs; 9203 9204 if ($num === 0) { 9205 print '<tr class="oddeven">'; 9206 print '<td colspan="'.$tableColumnCount.'" class="opacitymedium">'.$langs->trans($noneWord).'</td>'; 9207 print '</tr>'; 9208 return; 9209 } 9210 9211 if ($nbofloop === 0) 9212 { 9213 // don't show a summary line 9214 return; 9215 } 9216 9217 if ($num === 0) { 9218 $colspan = $tableColumnCount; 9219 } 9220 elseif ($num > $nbofloop) { 9221 $colspan = $tableColumnCount; 9222 } else { 9223 $colspan = $tableColumnCount - 1; 9224 } 9225 9226 if ($extraRightColumn) { 9227 $colspan--; 9228 } 9229 9230 print '<tr class="liste_total">'; 9231 9232 if ($nbofloop > 0 && $num > $nbofloop) { 9233 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>'; 9234 } else { 9235 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>'; 9236 print '<td class="right" width="100">'.price($total).'</td>'; 9237 } 9238 9239 if ($extraRightColumn) { 9240 print '<td></td>'; 9241 } 9242 9243 print '</tr>'; 9244} 9245 9246/** 9247 * Return a file on output using a low memory. It can return very large files with no need of memory. 9248 * WARNING: This close output buffers. 9249 * 9250 * @param string $fullpath_original_file_osencoded Full path of file to return. 9251 * @param int $method -1 automatic, 0=readfile, 1=fread, 2=stream_copy_to_stream 9252 * @return void 9253 */ 9254function readfileLowMemory($fullpath_original_file_osencoded, $method = -1) 9255{ 9256 global $conf; 9257 9258 if ($method == -1) { 9259 $method = 0; 9260 if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_FREAD)) $method = 1; 9261 if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_STREAM_COPY)) $method = 2; 9262 } 9263 9264 // Be sure we don't have output buffering enabled to have readfile working correctly 9265 while (ob_get_level()) ob_end_flush(); 9266 9267 // Solution 0 9268 if ($method == 0) { 9269 readfile($fullpath_original_file_osencoded); 9270 } 9271 // Solution 1 9272 elseif ($method == 1) { 9273 $handle = fopen($fullpath_original_file_osencoded, "rb"); 9274 while (!feof($handle)) { 9275 print fread($handle, 8192); 9276 } 9277 fclose($handle); 9278 } 9279 // Solution 2 9280 elseif ($method == 2) { 9281 $handle1 = fopen($fullpath_original_file_osencoded, "rb"); 9282 $handle2 = fopen("php://output", "wb"); 9283 stream_copy_to_stream($handle1, $handle2); 9284 fclose($handle1); 9285 fclose($handle2); 9286 } 9287} 9288