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-2021 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-2021 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 * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr> 20 * 21 * This program is free software; you can redistribute it and/or modify 22 * it under the terms of the GNU General Public License as published by 23 * the Free Software Foundation; either version 3 of the License, or 24 * (at your option) any later version. 25 * 26 * This program is distributed in the hope that it will be useful, 27 * but WITHOUT ANY WARRANTY; without even the implied warranty of 28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 * GNU General Public License for more details. 30 * 31 * You should have received a copy of the GNU General Public License 32 * along with this program. If not, see <https://www.gnu.org/licenses/>. 33 * or see https://www.gnu.org/ 34 */ 35 36/** 37 * \file htdocs/core/lib/functions.lib.php 38 * \brief A set of functions for Dolibarr 39 * This file contains all frequently used functions. 40 */ 41 42include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php'; 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': 112 $element = 'contract'; 113 break; // "/contrat/class/contrat.class.php" 114 case 'order_supplier': 115 $element = 'supplier_order'; 116 break; // "/fourn/class/fournisseur.commande.class.php" 117 } 118 119 if (is_object($mc)) { 120 return $mc->getEntity($element, $shared, $currentobject); 121 } else { 122 $out = ''; 123 $addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values'); 124 if (in_array($element, $addzero)) { 125 $out .= '0,'; 126 } 127 $out .= ((int) $conf->entity); 128 return $out; 129 } 130} 131 132/** 133 * Set entity id to use when to create an object 134 * 135 * @param object $currentobject Current object 136 * @return mixed Entity id to use ( eg. entity = '.setEntity($object) ) 137 */ 138function setEntity($currentobject) 139{ 140 global $conf, $mc; 141 142 if (is_object($mc) && method_exists($mc, 'setEntity')) { 143 return $mc->setEntity($currentobject); 144 } else { 145 return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity); 146 } 147} 148 149/** 150 * Return if string has a name dedicated to store a secret 151 * 152 * @param string $keyname Name of key to test 153 * @return boolean True if key is used to store a secret 154 */ 155function isASecretKey($keyname) 156{ 157 return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname); 158} 159 160/** 161 * Return information about user browser 162 * 163 * Returns array with the following format: 164 * array( 165 * 'browsername' => Browser name (firefox|chrome|iceweasel|epiphany|safari|opera|ie|unknown) 166 * 'browserversion' => Browser version. Empty if unknown 167 * 'browseros' => Set with mobile OS (android|blackberry|ios|palm|symbian|webos|maemo|windows|unknown) 168 * 'layout' => (tablet|phone|classic) 169 * 'phone' => empty if not mobile, (android|blackberry|ios|palm|unknown) if mobile 170 * 'tablet' => true/false 171 * ) 172 * 173 * @param string $user_agent Content of $_SERVER["HTTP_USER_AGENT"] variable 174 * @return array Check function documentation 175 */ 176function getBrowserInfo($user_agent) 177{ 178 include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php'; 179 180 $name = 'unknown'; 181 $version = ''; 182 $os = 'unknown'; 183 $phone = ''; 184 185 $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent 186 187 $detectmobile = new Mobile_Detect(null, $user_agent); 188 $tablet = $detectmobile->isTablet(); 189 190 if ($detectmobile->isMobile()) { 191 $phone = 'unknown'; 192 193 // If phone/smartphone, we set phone os name. 194 if ($detectmobile->is('AndroidOS')) { 195 $os = $phone = 'android'; 196 } elseif ($detectmobile->is('BlackBerryOS')) { 197 $os = $phone = 'blackberry'; 198 } elseif ($detectmobile->is('iOS')) { 199 $os = 'ios'; 200 $phone = 'iphone'; 201 } elseif ($detectmobile->is('PalmOS')) { 202 $os = $phone = 'palm'; 203 } elseif ($detectmobile->is('SymbianOS')) { 204 $os = 'symbian'; 205 } elseif ($detectmobile->is('webOS')) { 206 $os = 'webos'; 207 } elseif ($detectmobile->is('MaemoOS')) { 208 $os = 'maemo'; 209 } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) { 210 $os = 'windows'; 211 } 212 } 213 214 // OS 215 if (preg_match('/linux/i', $user_agent)) { 216 $os = 'linux'; 217 } elseif (preg_match('/macintosh/i', $user_agent)) { 218 $os = 'macintosh'; 219 } elseif (preg_match('/windows/i', $user_agent)) { 220 $os = 'windows'; 221 } 222 223 // Name 224 $reg = array(); 225 if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { 226 $name = 'firefox'; 227 $version = $reg[2]; 228 } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { 229 $name = 'edge'; 230 $version = $reg[2]; 231 } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) { 232 $name = 'chrome'; 233 $version = $reg[2]; 234 } elseif (preg_match('/chrome/i', $user_agent, $reg)) { 235 // we can have 'chrome (Mozilla...) chrome x.y' in one string 236 $name = 'chrome'; 237 } elseif (preg_match('/iceweasel/i', $user_agent)) { 238 $name = 'iceweasel'; 239 } elseif (preg_match('/epiphany/i', $user_agent)) { 240 $name = 'epiphany'; 241 } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { 242 $name = 'safari'; 243 $version = $reg[2]; 244 } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { 245 // Safari is often present in string for mobile but its not. 246 $name = 'opera'; 247 $version = $reg[2]; 248 } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) { 249 $name = 'ie'; 250 $version = end($reg); 251 } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) { 252 // MS products at end 253 $name = 'ie'; 254 $version = end($reg); 255 } elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) { 256 // MS products at end 257 $name = 'lynxlinks'; 258 $version = $reg[4]; 259 } 260 261 if ($tablet) { 262 $layout = 'tablet'; 263 } elseif ($phone) { 264 $layout = 'phone'; 265 } else { 266 $layout = 'classic'; 267 } 268 269 return array( 270 'browsername' => $name, 271 'browserversion' => $version, 272 'browseros' => $os, 273 'layout' => $layout, 274 'phone' => $phone, 275 'tablet' => $tablet 276 ); 277} 278 279/** 280 * Function called at end of web php process 281 * 282 * @return void 283 */ 284function dol_shutdown() 285{ 286 global $conf, $user, $langs, $db; 287 $disconnectdone = false; 288 $depth = 0; 289 if (is_object($db) && !empty($db->connected)) { 290 $depth = $db->transaction_opened; 291 $disconnectdone = $db->close(); 292 } 293 dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ?LOG_WARNING:LOG_INFO)); 294} 295 296/** 297 * Return true if we are in a context of submitting the parameter $paramname from a POST of a form. 298 * 299 * @param string $paramname Name or parameter to test 300 * @return boolean True if we have just submit a POST or GET request with the parameter provided (even if param is empty) 301 */ 302function GETPOSTISSET($paramname) 303{ 304 $isset = false; 305 306 $relativepathstring = $_SERVER["PHP_SELF"]; 307 // Clean $relativepathstring 308 if (constant('DOL_URL_ROOT')) { 309 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring); 310 } 311 $relativepathstring = preg_replace('/^\//', '', $relativepathstring); 312 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring); 313 //var_dump($relativepathstring); 314 //var_dump($user->default_values); 315 316 // Code for search criteria persistence. 317 // Retrieve values if restore_lastsearch_values 318 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST 319 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values 320 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true); 321 if (is_array($tmp)) { 322 foreach ($tmp as $key => $val) { 323 if ($key == $paramname) { // We are on the requested parameter 324 $isset = true; 325 break; 326 } 327 } 328 } 329 } 330 // If there is saved contextpage, page or limit 331 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) { 332 $isset = true; 333 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) { 334 $isset = true; 335 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) { 336 $isset = true; 337 } 338 } else { 339 $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here 340 } 341 342 return $isset; 343} 344 345/** 346 * Return value of a param into GET or POST supervariable. 347 * Use the property $user->default_values[path]['createform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder'] 348 * Note: The property $user->default_values is loaded by main.php when loading the user. 349 * 350 * @param string $paramname Name of parameter to found 351 * @param string $check Type of check 352 * ''=no check (deprecated) 353 * 'none'=no check (only for param that should have very rich content) 354 * 'array', 'array:restricthtml' or 'array:aZ09' to check it's an array 355 * 'int'=check it's numeric (integer or float) 356 * 'intcomma'=check it's integer+comma ('1,2,3,4...') 357 * 'alpha'=Same than alphanohtml since v13 358 * 'alphawithlgt'=alpha with lgt 359 * 'alphanohtml'=check there is no html content and no " and no ../ 360 * 'aZ'=check it's a-z only 361 * 'aZ09'=check it's simple alpha string (recommended for keys) 362 * 'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string) 363 * 'nohtml'=check there is no html content and no " and no ../ 364 * 'restricthtml'=check html content is restricted to some tags only 365 * 'custom'= custom filter specify $filter and $options) 366 * @param int $method Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get) 367 * @param int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) 368 * @param mixed $options Options to pass to filter_var when $check is set to 'custom' 369 * @param string $noreplace Force disable of replacement of __xxx__ strings. 370 * @return string|array Value found (string or array), or '' if check fails 371 */ 372function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0) 373{ 374 global $mysoc, $user, $conf; 375 376 if (empty($paramname)) { 377 return 'BadFirstParameterForGETPOST'; 378 } 379 if (empty($check)) { 380 dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING); 381 // Enable this line to know who call the GETPOST with '' $check parameter. 382 //var_dump(debug_backtrace()[0]); 383 } 384 385 if (empty($method)) { 386 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : ''); 387 } elseif ($method == 1) { 388 $out = isset($_GET[$paramname]) ? $_GET[$paramname] : ''; 389 } elseif ($method == 2) { 390 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : ''; 391 } elseif ($method == 3) { 392 $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : ''); 393 } else { 394 return 'BadThirdParameterForGETPOST'; 395 } 396 397 if (empty($method) || $method == 3 || $method == 4) { 398 $relativepathstring = $_SERVER["PHP_SELF"]; 399 // Clean $relativepathstring 400 if (constant('DOL_URL_ROOT')) { 401 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring); 402 } 403 $relativepathstring = preg_replace('/^\//', '', $relativepathstring); 404 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring); 405 //var_dump($relativepathstring); 406 //var_dump($user->default_values); 407 408 // Code for search criteria persistence. 409 // Retrieve values if restore_lastsearch_values 410 if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST 411 if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values 412 $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true); 413 if (is_array($tmp)) { 414 foreach ($tmp as $key => $val) { 415 if ($key == $paramname) { // We are on the requested parameter 416 $out = $val; 417 break; 418 } 419 } 420 } 421 } 422 // If there is saved contextpage, page or limit 423 if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) { 424 $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring]; 425 } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) { 426 $out = $_SESSION['lastsearch_page_'.$relativepathstring]; 427 } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) { 428 $out = $_SESSION['lastsearch_limit_'.$relativepathstring]; 429 } 430 } elseif (!isset($_GET['sortfield'])) { 431 // Else, retrieve default values if we are not doing a sort 432 // 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 433 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) { 434 // Search default value from $object->field 435 global $object; 436 if (is_object($object) && isset($object->fields[$paramname]['default'])) { 437 $out = $object->fields[$paramname]['default']; 438 } 439 } 440 if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) { 441 if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) { 442 // Now search in setup to overwrite default values 443 if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values' 444 if (isset($user->default_values[$relativepathstring]['createform'])) { 445 foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) { 446 $qualified = 0; 447 if ($defkey != '_noquery_') { 448 $tmpqueryarraytohave = explode('&', $defkey); 449 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); 450 $foundintru = 0; 451 foreach ($tmpqueryarraytohave as $tmpquerytohave) { 452 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) { 453 $foundintru = 1; 454 } 455 } 456 if (!$foundintru) { 457 $qualified = 1; 458 } 459 //var_dump($defkey.'-'.$qualified); 460 } else { 461 $qualified = 1; 462 } 463 464 if ($qualified) { 465 if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) { 466 $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname]; 467 break; 468 } 469 } 470 } 471 } 472 } 473 } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) { 474 // Management of default search_filters and sort order 475 if (!empty($user->default_values)) { 476 // $user->default_values defined from menu 'Setup - Default values' 477 //var_dump($user->default_values[$relativepathstring]); 478 if ($paramname == 'sortfield' || $paramname == 'sortorder') { 479 // Sorted on which fields ? ASC or DESC ? 480 if (isset($user->default_values[$relativepathstring]['sortorder'])) { 481 // Even if paramname is sortfield, data are stored into ['sortorder...'] 482 foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) { 483 $qualified = 0; 484 if ($defkey != '_noquery_') { 485 $tmpqueryarraytohave = explode('&', $defkey); 486 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); 487 $foundintru = 0; 488 foreach ($tmpqueryarraytohave as $tmpquerytohave) { 489 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) { 490 $foundintru = 1; 491 } 492 } 493 if (!$foundintru) { 494 $qualified = 1; 495 } 496 //var_dump($defkey.'-'.$qualified); 497 } else { 498 $qualified = 1; 499 } 500 501 if ($qualified) { 502 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , 503 foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) { 504 if ($out) { 505 $out .= ', '; 506 } 507 if ($paramname == 'sortfield') { 508 $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace); 509 } 510 if ($paramname == 'sortorder') { 511 $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace); 512 } 513 } 514 //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?) 515 } 516 } 517 } 518 } elseif (isset($user->default_values[$relativepathstring]['filters'])) { 519 foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user 520 $qualified = 0; 521 if ($defkey != '_noquery_') { 522 $tmpqueryarraytohave = explode('&', $defkey); 523 $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); 524 $foundintru = 0; 525 foreach ($tmpqueryarraytohave as $tmpquerytohave) { 526 if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) { 527 $foundintru = 1; 528 } 529 } 530 if (!$foundintru) { 531 $qualified = 1; 532 } 533 //var_dump($defkey.'-'.$qualified); 534 } else { 535 $qualified = 1; 536 } 537 538 if ($qualified) { 539 // We must keep $_POST and $_GET here 540 if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) { 541 // We made a search from quick search menu, do we still use default filter ? 542 if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) { 543 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , 544 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace); 545 } 546 } else { 547 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , 548 $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace); 549 } 550 break; 551 } 552 } 553 } 554 } 555 } 556 } 557 } 558 } 559 560 // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters) 561 // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ... 562 // 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. 563 if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) { 564 $reg = array(); 565 $maxloop = 20; 566 $loopnb = 0; // Protection against infinite loop 567 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. 568 $loopnb++; 569 $newout = ''; 570 571 if ($reg[1] == 'DAY') { 572 $tmp = dol_getdate(dol_now(), true); 573 $newout = $tmp['mday']; 574 } elseif ($reg[1] == 'MONTH') { 575 $tmp = dol_getdate(dol_now(), true); 576 $newout = $tmp['mon']; 577 } elseif ($reg[1] == 'YEAR') { 578 $tmp = dol_getdate(dol_now(), true); 579 $newout = $tmp['year']; 580 } elseif ($reg[1] == 'PREVIOUS_DAY') { 581 $tmp = dol_getdate(dol_now(), true); 582 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']); 583 $newout = $tmp2['day']; 584 } elseif ($reg[1] == 'PREVIOUS_MONTH') { 585 $tmp = dol_getdate(dol_now(), true); 586 $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']); 587 $newout = $tmp2['month']; 588 } elseif ($reg[1] == 'PREVIOUS_YEAR') { 589 $tmp = dol_getdate(dol_now(), true); 590 $newout = ($tmp['year'] - 1); 591 } elseif ($reg[1] == 'NEXT_DAY') { 592 $tmp = dol_getdate(dol_now(), true); 593 $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']); 594 $newout = $tmp2['day']; 595 } elseif ($reg[1] == 'NEXT_MONTH') { 596 $tmp = dol_getdate(dol_now(), true); 597 $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']); 598 $newout = $tmp2['month']; 599 } elseif ($reg[1] == 'NEXT_YEAR') { 600 $tmp = dol_getdate(dol_now(), true); 601 $newout = ($tmp['year'] + 1); 602 } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') { 603 $newout = $mysoc->country_id; 604 } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') { 605 $newout = $user->id; 606 } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') { 607 $newout = $user->fk_user; 608 } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') { 609 $newout = $conf->entity; 610 } else { 611 $newout = ''; // Key not found, we replace with empty string 612 } 613 //var_dump('__'.$reg[1].'__ -> '.$newout); 614 $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out); 615 } 616 } 617 618 // Check rule 619 if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' 620 if (!is_array($out) || empty($out)) { 621 $out = array(); 622 } else { 623 $tmparray = explode(':', $check); 624 if (!empty($tmparray[1])) { 625 $tmpcheck = $tmparray[1]; 626 } else { 627 $tmpcheck = 'alphanohtml'; 628 } 629 foreach ($out as $outkey => $outval) { 630 $out[$outkey] = checkVal($outval, $tmpcheck, $filter, $options); 631 } 632 } 633 } else { 634 $out = checkVal($out, $check, $filter, $options); 635 } 636 637 // Sanitizing for special parameters. 638 // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. 639 if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') { 640 $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements. 641 $out = str_replace(array(':', ';', '@'), '', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements. 642 do { 643 $oldstringtoclean = $out; 644 $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out); 645 } while ($oldstringtoclean != $out); 646 647 $out = preg_replace(array('/^[a-z]*\/\/+/i'), '', $out); // We remove schema*// to remove external URL 648 } 649 650 // Code for search criteria persistence. 651 // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year' 652 if (empty($method) || $method == 3 || $method == 4) { 653 if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) { 654 //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]); 655 656 // We save search key only if $out not empty that means: 657 // - posted value not empty, or 658 // - 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). 659 660 if ($out != '') { // $out = '0' or 'abc', it is a search criteria to keep 661 $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out; 662 } 663 } 664 } 665 666 return $out; 667} 668 669/** 670 * Return value of a param into GET or POST supervariable. 671 * Use the property $user->default_values[path]['creatform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder'] 672 * Note: The property $user->default_values is loaded by main.php when loading the user. 673 * 674 * @param string $paramname Name of parameter to found 675 * @param int $method Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get) 676 * @param int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) 677 * @param mixed $options Options to pass to filter_var when $check is set to 'custom' 678 * @param string $noreplace Force disable of replacement of __xxx__ strings. 679 * @return int Value found (int) 680 */ 681function GETPOSTINT($paramname, $method = 0, $filter = null, $options = null, $noreplace = 0) 682{ 683 return (int) GETPOST($paramname, 'int', $method, $filter, $options, $noreplace); 684} 685 686/** 687 * Return a value after checking on a rule. A sanitization may also have been done. 688 * 689 * @param string $out Value to check/clear. 690 * @param string $check Type of check/sanitizing 691 * @param int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) 692 * @param mixed $options Options to pass to filter_var when $check is set to 'custom' 693 * @return string|array Value sanitized (string or array). It may be '' if format check fails. 694 */ 695function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null) 696{ 697 global $conf; 698 699 // Check is done after replacement 700 switch ($check) { 701 case 'none': 702 break; 703 case 'int': // Check param is a numeric value (integer but also float or hexadecimal) 704 if (!is_numeric($out)) { 705 $out = ''; 706 } 707 break; 708 case 'intcomma': 709 if (preg_match('/[^0-9,-]+/i', $out)) { 710 $out = ''; 711 } 712 break; 713 case 'san_alpha': 714 $out = filter_var($out, FILTER_SANITIZE_STRING); 715 break; 716 case 'email': 717 $out = filter_var($out, FILTER_SANITIZE_EMAIL); 718 break; 719 case 'aZ': 720 if (!is_array($out)) { 721 $out = trim($out); 722 if (preg_match('/[^a-z]+/i', $out)) { 723 $out = ''; 724 } 725 } 726 break; 727 case 'aZ09': 728 if (!is_array($out)) { 729 $out = trim($out); 730 if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) { 731 $out = ''; 732 } 733 } 734 break; 735 case 'aZ09comma': // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh 736 if (!is_array($out)) { 737 $out = trim($out); 738 if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) { 739 $out = ''; 740 } 741 } 742 break; 743 case 'nohtml': // No html 744 $out = dol_string_nohtmltag($out, 0); 745 break; 746 case 'alpha': // No html and no ../ and " 747 case 'alphanohtml': // Recommended for most scalar parameters and search parameters 748 if (!is_array($out)) { 749 $out = trim($out); 750 do { 751 $oldstringtoclean = $out; 752 // Remove html tags 753 $out = dol_string_nohtmltag($out, 0); 754 // Remove also other dangerous string sequences 755 // '"' is dangerous because param in url can close the href= or src= and add javascript functions. 756 // '../' or '..\' is dangerous because it allows dir transversals 757 // Note &, '&', '&'... is a simple char like '&' alone but there is no reason to accept such way to encode input data. 758 $out = str_ireplace(array('&', '&', '&', '"', '"', '"', '"', '"', '/', '/', '\', '\', '/', '../', '..\\'), '', $out); 759 } while ($oldstringtoclean != $out); 760 // keep lines feed 761 } 762 break; 763 case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name <email>" 764 if (!is_array($out)) { 765 $out = trim($out); 766 do { 767 $oldstringtoclean = $out; 768 // Remove html tags 769 $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8'); 770 // '"' is dangerous because param in url can close the href= or src= and add javascript functions. 771 // '../' or '..\' is dangerous because it allows dir transversals 772 // Note &, '&', '&'... is a simple char like '&' alone but there is no reason to accept such way to encode input data. 773 $out = str_ireplace(array('&', '&', '&', '"', '"', '"', '"', '"', '/', '/', '\', '\', '/', '../', '..\\'), '', $out); 774 } while ($oldstringtoclean != $out); 775 } 776 break; 777 case 'restricthtml': // Recommended for most html textarea 778 case 'restricthtmlallowunvalid': 779 do { 780 $oldstringtoclean = $out; 781 782 if (!empty($out) && !empty($conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML) && $check != 'restricthtmlallowunvalid') { 783 try { 784 $dom = new DOMDocument; 785 // Add a trick to solve pb with text without parent tag 786 // like '<h1>Foo</h1><p>bar</p>' that ends up with '<h1>Foo<p>bar</p></h1>' 787 // like 'abc' that ends up with '<p>abc</p>' 788 $out = '<div class="tricktoremove">'.$out.'</div>'; 789 790 $dom->loadHTML($out, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL); 791 $out = trim($dom->saveHTML()); 792 793 // Remove the trick added to solve pb with text without parent tag 794 $out = preg_replace('/^<div class="tricktoremove">/', '', $out); 795 $out = preg_replace('/<\/div>$/', '', $out); 796 } catch (Exception $e) { 797 //print $e->getMessage(); 798 return 'InvalidHTMLString'; 799 } 800 } 801 802 // Ckeditor use the numeric entitic for apostrophe so we force it to text entity (all other special chars are 803 // encoded using text entities) so we can then exclude all numeric entities. 804 $out = preg_replace('/'/i', ''', $out); 805 806 // We replace chars from a/A to z/Z encoded with numeric HTML entities with the real char so we won't loose the chars at the next step (preg_replace). 807 // No need to use a loop here, this step is not to sanitize (this is done at next step, this is to try to save chars, even if they are 808 // using a non coventionnel way to be encoded, to not have them sanitized just after) 809 $out = preg_replace_callback('/&#(x?[0-9][0-9a-f]+;?)/i', 'realCharForNumericEntities', $out); 810 811 // Now we remove all remaining HTML entities starting with a number. We don't want such entities. 812 $out = preg_replace('/&#x?[0-9]+/i', '', $out); // For example if we have javascript with an entities without the ; to hide the 'a' of 'javascript'. 813 814 $out = dol_string_onlythesehtmltags($out, 0, 1, 1); 815 816 // We should also exclude non expected attributes 817 if (!empty($conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES)) { 818 // Warning, the function may add a LF so we are forced to trim to compare with old $out without having always a difference and an infinit loop. 819 $out = trim(dol_string_onlythesehtmlattributes($out)); 820 } 821 822 // Restore entity ' into ' (restricthtml is for html content so we can use html entity) 823 $out = preg_replace('/'/i', "'", $out); 824 } while ($oldstringtoclean != $out); 825 break; 826 case 'custom': 827 if (empty($filter)) { 828 return 'BadFourthParameterForGETPOST'; 829 } 830 $out = filter_var($out, $filter, $options); 831 break; 832 } 833 834 return $out; 835} 836 837 838if (!function_exists('dol_getprefix')) { 839 /** 840 * Return a prefix to use for this Dolibarr instance, for session/cookie names or email id. 841 * The prefix is unique for instance and avoid conflict between multi-instances, even when having two instances with same root dir 842 * or two instances in same virtual servers. 843 * 844 * @param string $mode '' (prefix for session name) or 'email' (prefix for email id) 845 * @return string A calculated prefix 846 */ 847 function dol_getprefix($mode = '') 848 { 849 // If prefix is for email (we need to have $conf alreayd loaded for this case) 850 if ($mode == 'email') { 851 global $conf; 852 853 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) 854 if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') { 855 return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID; 856 } elseif (isset($_SERVER["SERVER_NAME"])) { 857 return $_SERVER["SERVER_NAME"]; 858 } 859 } 860 861 // The recommended value (may be not defined for old versions) 862 if (!empty($conf->file->instance_unique_id)) { 863 return $conf->file->instance_unique_id; 864 } 865 866 // For backward compatibility 867 return dol_hash(DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3'); 868 } 869 870 // If prefix is for session (no need to have $conf loaded) 871 global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php 872 $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 873 874 // The recommended value (may be not defined for old versions) 875 if (!empty($tmp_instance_unique_id)) { 876 return $tmp_instance_unique_id; 877 } 878 879 // For backward compatibility 880 if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) { 881 return dol_hash($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3'); 882 } 883 884 return dol_hash(DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3'); 885 } 886} 887 888/** 889 * Make an include_once using default root and alternate root if it fails. 890 * To link to a core file, use include(DOL_DOCUMENT_ROOT.'/pathtofile') 891 * To link to a module file from a module file, use include './mymodulefile'; 892 * To link to a module file from a core file, then this function can be used (call by hook / trigger / speciales pages) 893 * 894 * @param string $relpath Relative path to file (Ie: mydir/myfile, ../myfile, ...) 895 * @param string $classname Class name (deprecated) 896 * @return bool True if load is a success, False if it fails 897 */ 898function dol_include_once($relpath, $classname = '') 899{ 900 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'] 901 902 $fullpath = dol_buildpath($relpath); 903 904 if (!file_exists($fullpath)) { 905 dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING); 906 return false; 907 } 908 909 if (!empty($classname) && !class_exists($classname)) { 910 return include $fullpath; 911 } else { 912 return include_once $fullpath; 913 } 914} 915 916 917/** 918 * Return path of url or filesystem. Can check into alternate dir or alternate dir + main dir depending on value of $returnemptyifnotfound. 919 * 920 * @param string $path Relative path to file (if mode=0) or relative url (if mode=1). Ie: mydir/myfile, ../myfile 921 * @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) 922 * @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) 923 * 1:If $type==0 and if file was not found into alternate dir, return empty string 924 * 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 925 * @return string Full filesystem path (if path=0) or '' if file not found, Full url path (if mode=1) 926 */ 927function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0) 928{ 929 global $conf; 930 931 $path = preg_replace('/^\//', '', $path); 932 933 if (empty($type)) { // For a filesystem path 934 $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path 935 if (is_array($conf->file->dol_document_root)) { 936 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...) 937 if ($key == 'main') { 938 continue; 939 } 940 if (file_exists($dirroot.'/'.$path)) { 941 $res = $dirroot.'/'.$path; 942 return $res; 943 } 944 } 945 } 946 if ($returnemptyifnotfound) { 947 // Not found into alternate dir 948 if ($returnemptyifnotfound == 1 || !file_exists($res)) { 949 return ''; 950 } 951 } 952 } else { 953 // For an url path 954 // We try to get local path of file on filesystem from url 955 // Note that trying to know if a file on disk exist by forging path on disk from url 956 // works only for some web server and some setup. This is bugged when 957 // using proxy, rewriting, virtual path, etc... 958 $res = ''; 959 if ($type == 1) { 960 $res = DOL_URL_ROOT.'/'.$path; // Standard value 961 } 962 if ($type == 2) { 963 $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value 964 } 965 if ($type == 3) { 966 $res = DOL_URL_ROOT.'/'.$path; 967 } 968 969 foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...) 970 if ($key == 'main') { 971 if ($type == 3) { 972 global $dolibarr_main_url_root; 973 974 // Define $urlwithroot 975 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); 976 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file 977 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current 978 979 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax 980 } 981 continue; 982 } 983 preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?' 984 if (!empty($regs[1])) { 985 //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n"; 986 if (file_exists($dirroot.'/'.$regs[1])) { 987 if ($type == 1) { 988 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path; 989 } 990 if ($type == 2) { 991 $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path; 992 } 993 if ($type == 3) { 994 global $dolibarr_main_url_root; 995 996 // Define $urlwithroot 997 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); 998 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file 999 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current 1000 1001 $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 1002 } 1003 break; 1004 } 1005 } 1006 } 1007 } 1008 1009 return $res; 1010} 1011 1012/** 1013 * Create a clone of instance of object (new instance with same value for properties) 1014 * With native = 0: Property that are reference are also new object (full isolation clone). This means $this->db of new object is not valid. 1015 * 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. 1016 * 1017 * @param object $object Object to clone 1018 * @param int $native 0=Full isolation method, 1=Native PHP method 1019 * @return object Clone object 1020 * @see https://php.net/manual/language.oop5.cloning.php 1021 */ 1022function dol_clone($object, $native = 0) 1023{ 1024 if (empty($native)) { 1025 $myclone = unserialize(serialize($object)); // serialize then unserialize is hack to be sure to have a new object for all fields 1026 } else { 1027 $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) 1028 } 1029 1030 return $myclone; 1031} 1032 1033/** 1034 * Optimize a size for some browsers (phone, smarphone, ...) 1035 * 1036 * @param int $size Size we want 1037 * @param string $type Type of optimizing: 1038 * '' = function used to define a size for truncation 1039 * 'width' = function is used to define a width 1040 * @return int New size after optimizing 1041 */ 1042function dol_size($size, $type = '') 1043{ 1044 global $conf; 1045 if (empty($conf->dol_optimize_smallscreen)) { 1046 return $size; 1047 } 1048 if ($type == 'width' && $size > 250) { 1049 return 250; 1050 } else { 1051 return 10; 1052 } 1053} 1054 1055 1056/** 1057 * Clean a string to use it as a file name. 1058 * Replace also '--' and ' -' strings, they are used for parameters separation (Note: ' - ' is allowed). 1059 * 1060 * @param string $str String to clean 1061 * @param string $newstr String to replace bad chars with. 1062 * @param int $unaccent 1=Remove also accent (default), 0 do not remove them 1063 * @return string String cleaned (a-zA-Z_) 1064 * 1065 * @see dol_string_nospecial(), dol_string_unaccent(), dol_sanitizePathName() 1066 */ 1067function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1) 1068{ 1069 // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file 1070 // Char '>' '<' '|' '$' and ';' are special chars for shells. 1071 // Char '/' and '\' are file delimiters. 1072 // -- 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 1073 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';'); 1074 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars); 1075 $tmp = preg_replace('/\-\-+/', '_', $tmp); 1076 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp); 1077 return $tmp; 1078} 1079 1080/** 1081 * Clean a string to use it as a path name. 1082 * Replace also '--' and ' -' strings, they are used for parameters separation (Note: ' - ' is allowed). 1083 * 1084 * @param string $str String to clean 1085 * @param string $newstr String to replace bad chars with 1086 * @param int $unaccent 1=Remove also accent (default), 0 do not remove them 1087 * @return string String cleaned (a-zA-Z_) 1088 * 1089 * @see dol_string_nospecial(), dol_string_unaccent(), dol_sanitizeFileName() 1090 */ 1091function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1) 1092{ 1093 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°'); 1094 $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars); 1095 $tmp = preg_replace('/\-\-+/', '_', $tmp); 1096 $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp); 1097 return $tmp; 1098} 1099 1100/** 1101 * Clean a string to use it as an URL (into a href or src attribute) 1102 * 1103 * @param string $stringtoclean String to clean 1104 * @param int $type 0=Accept all Url, 1=Clean external Url (keep only relative Url) 1105 * @return string Escaped string. 1106 */ 1107function dol_sanitizeUrl($stringtoclean, $type = 1) 1108{ 1109 // We clean string because some hacks try to obfuscate evil strings by inserting non printable chars. Example: 'java(ascci09)scr(ascii00)ipt' is processed like 'javascript' (whatever is place of evil ascii char) 1110 // We should use dol_string_nounprintableascii but function may not be yet loaded/available 1111 $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace 1112 // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1) 1113 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean); 1114 1115 $stringtoclean = str_replace('\\', '/', $stringtoclean); 1116 if ($type == 1) { 1117 // removing : should disable links to external url like http:aaa) 1118 // removing ';' should disable "named" html entities encode into an url (we should not have this into an url) 1119 $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean); 1120 } 1121 1122 do { 1123 $oldstringtoclean = $stringtoclean; 1124 // removing '&colon' should disable links to external url like http:aaa) 1125 // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url) 1126 $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean); 1127 } while ($oldstringtoclean != $stringtoclean); 1128 1129 if ($type == 1) { 1130 // removing '//' should disable links to external url like //aaa or http//) 1131 $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean); 1132 } 1133 1134 return $stringtoclean; 1135} 1136 1137/** 1138 * Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName 1139 * 1140 * @param string $str String to clean 1141 * @return string Cleaned string 1142 * 1143 * @see dol_sanitizeFilename(), dol_string_nospecial() 1144 */ 1145function dol_string_unaccent($str) 1146{ 1147 if (utf8_check($str)) { 1148 // See http://www.utf8-chartable.de/ 1149 $string = rawurlencode($str); 1150 $replacements = array( 1151 '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A', 1152 '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E', 1153 '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I', 1154 '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O', 1155 '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U', 1156 '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a', 1157 '%C3%A7' => 'c', 1158 '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e', 1159 '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i', 1160 '%C3%B1' => 'n', 1161 '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o', 1162 '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u', 1163 '%C3%BF' => 'y' 1164 ); 1165 $string = strtr($string, $replacements); 1166 return rawurldecode($string); 1167 } else { 1168 // See http://www.ascii-code.com/ 1169 $string = strtr( 1170 $str, 1171 "\xC0\xC1\xC2\xC3\xC4\xC5\xC7 1172 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1 1173 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD 1174 \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB 1175 \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8 1176 \xF9\xFA\xFB\xFC\xFD\xFF", 1177 "AAAAAAC 1178 EEEEIIIIDN 1179 OOOOOUUUY 1180 aaaaaaceeee 1181 iiiidnooooo 1182 uuuuyy" 1183 ); 1184 $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")); 1185 return $string; 1186 } 1187} 1188 1189/** 1190 * Clean a string from all punctuation characters to use it as a ref or login. 1191 * This is a more complete function than dol_sanitizeFileName. 1192 * 1193 * @param string $str String to clean 1194 * @param string $newstr String to replace forbidden chars with 1195 * @param array|string $badcharstoreplace List of forbidden characters to replace 1196 * @param array|string $badcharstoremove List of forbidden characters to remove 1197 * @return string Cleaned string 1198 * 1199 * @see dol_sanitizeFilename(), dol_string_unaccent(), dol_string_nounprintableascii() 1200 */ 1201function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '') 1202{ 1203 $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°'); // more complete than dol_sanitizeFileName 1204 $forbidden_chars_to_remove = array(); 1205 //$forbidden_chars_to_remove=array("(",")"); 1206 1207 if (is_array($badcharstoreplace)) { 1208 $forbidden_chars_to_replace = $badcharstoreplace; 1209 } 1210 if (is_array($badcharstoremove)) { 1211 $forbidden_chars_to_remove = $badcharstoremove; 1212 } 1213 1214 return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str)); 1215} 1216 1217 1218/** 1219 * Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F). It can also removes also Tab-CR-LF. UTF8 chars remains. 1220 * This can be used to sanitize a string and view its real content. Some hacks try to obfuscate attacks by inserting non printable chars. 1221 * Note, for information: UTF8 on 1 byte are: \x00-\7F 1222 * 2 bytes are: byte 1 \xc0-\xdf, byte 2 = \x80-\xbf 1223 * 3 bytes are: byte 1 \xe0-\xef, byte 2 = \x80-\xbf, byte 3 = \x80-\xbf 1224 * 4 bytes are: byte 1 \xf0-\xf7, byte 2 = \x80-\xbf, byte 3 = \x80-\xbf, byte 4 = \x80-\xbf 1225 * @param string $str String to clean 1226 * @param int $removetabcrlf Remove also CR-LF 1227 * @return string Cleaned string 1228 * 1229 * @see dol_sanitizeFilename(), dol_string_unaccent(), dol_string_nospecial() 1230 */ 1231function dol_string_nounprintableascii($str, $removetabcrlf = 1) 1232{ 1233 if ($removetabcrlf) { 1234 return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace 1235 } else { 1236 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 1237 } 1238} 1239 1240/** 1241 * Returns text escaped for inclusion into javascript code 1242 * 1243 * @param string $stringtoescape String to escape 1244 * @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 \ 1245 * @param int $noescapebackslashn 0=Escape also \n. 1=Do not escape \n. 1246 * @return string Escaped string. Both ' and " are escaped into ' if they are escaped. 1247 */ 1248function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0) 1249{ 1250 // escape quotes and backslashes, newlines, etc. 1251 $substitjs = array("'"=>"\\'", "\r"=>'\\r'); 1252 //$substitjs['</']='<\/'; // We removed this. Should be useless. 1253 if (empty($noescapebackslashn)) { 1254 $substitjs["\n"] = '\\n'; 1255 $substitjs['\\'] = '\\\\'; 1256 } 1257 if (empty($mode)) { 1258 $substitjs["'"] = "\\'"; 1259 $substitjs['"'] = "\\'"; 1260 } elseif ($mode == 1) { 1261 $substitjs["'"] = "\\'"; 1262 } elseif ($mode == 2) { 1263 $substitjs['"'] = '\\"'; 1264 } elseif ($mode == 3) { 1265 $substitjs["'"] = "\\'"; 1266 $substitjs['"'] = "\\\""; 1267 } 1268 return strtr($stringtoescape, $substitjs); 1269} 1270 1271/** 1272 * Returns text escaped for inclusion into javascript code 1273 * 1274 * @param string $stringtoescape String to escape 1275 * @return string Escaped string for json content. 1276 */ 1277function dol_escape_json($stringtoescape) 1278{ 1279 return str_replace('"', '\"', $stringtoescape); 1280} 1281 1282/** 1283 * Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields. 1284 * 1285 * @param string $stringtoescape String to escape 1286 * @param int $keepb 1=Keep b tags, 0=remove them completeley 1287 * @param int $keepn 1=Preserve \r\n strings (otherwise, replace them with escaped value). Set to 1 when escaping for a <textarea>. 1288 * @param string $noescapetags '' or 'common' or list of tags to not escape 1289 * @param int $escapeonlyhtmltags 1=Escape only html tags, not the special chars like accents. 1290 * @return string Escaped string 1291 * @see dol_string_nohtmltag(), dol_string_nospecial(), dol_string_unaccent() 1292 */ 1293function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0) 1294{ 1295 if ($noescapetags == 'common') { 1296 $noescapetags = 'html,body,a,b,em,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody'; 1297 } 1298 1299 // escape quotes and backslashes, newlines, etc. 1300 if ($escapeonlyhtmltags) { 1301 $tmp = htmlspecialchars_decode($stringtoescape, ENT_COMPAT); 1302 } else { 1303 $tmp = html_entity_decode($stringtoescape, ENT_COMPAT, 'UTF-8'); 1304 } 1305 if (!$keepb) { 1306 $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>'')); 1307 } 1308 if (!$keepn) { 1309 $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n')); 1310 } 1311 1312 if ($escapeonlyhtmltags) { 1313 return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8'); 1314 } else { 1315 // Escape tags to keep 1316 $tmparrayoftags = array(); 1317 if ($noescapetags) { 1318 $tmparrayoftags = explode(',', $noescapetags); 1319 } 1320 1321 if (count($tmparrayoftags)) { 1322 foreach ($tmparrayoftags as $tagtoreplace) { 1323 $tmp = str_ireplace('<'.$tagtoreplace.'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp); 1324 $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp); 1325 } 1326 } 1327 1328 $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8'); 1329 1330 if (count($tmparrayoftags)) { 1331 foreach ($tmparrayoftags as $tagtoreplace) { 1332 $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result); 1333 $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result); 1334 } 1335 } 1336 1337 return $result; 1338 } 1339} 1340 1341/** 1342 * Convert a string to lower. Never use strtolower because it does not works with UTF8 strings. 1343 * 1344 * @param string $string String to encode 1345 * @param string $encoding Character set encoding 1346 * @return string String converted 1347 */ 1348function dol_strtolower($string, $encoding = "UTF-8") 1349{ 1350 if (function_exists('mb_strtolower')) { 1351 return mb_strtolower($string, $encoding); 1352 } else { 1353 return strtolower($string); 1354 } 1355} 1356 1357/** 1358 * Convert a string to upper. Never use strtolower because it does not works with UTF8 strings. 1359 * 1360 * @param string $string String to encode 1361 * @param string $encoding Character set encoding 1362 * @return string String converted 1363 */ 1364function dol_strtoupper($string, $encoding = "UTF-8") 1365{ 1366 if (function_exists('mb_strtoupper')) { 1367 return mb_strtoupper($string, $encoding); 1368 } else { 1369 return strtoupper($string); 1370 } 1371} 1372 1373/** 1374 * Convert first character of the first word of a string to upper. Never use ucfirst because it does not works with UTF8 strings. 1375 * 1376 * @param string $string String to encode 1377 * @param string $encoding Character set encodign 1378 * @return string String converted 1379 */ 1380function dol_ucfirst($string, $encoding = "UTF-8") 1381{ 1382 if (function_exists('mb_substr')) { 1383 return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding); 1384 } else { 1385 return ucfirst($string); 1386 } 1387} 1388 1389/** 1390 * Convert first character of all the words of a string to upper. Never use ucfirst because it does not works with UTF8 strings. 1391 * 1392 * @param string $string String to encode 1393 * @param string $encoding Character set encodign 1394 * @return string String converted 1395 */ 1396function dol_ucwords($string, $encoding = "UTF-8") 1397{ 1398 if (function_exists('mb_convert_case')) { 1399 return mb_convert_case($string, MB_CASE_TITLE, $encoding); 1400 } else { 1401 return ucwords($string); 1402 } 1403} 1404 1405/** 1406 * Write log message into outputs. Possible outputs can be: 1407 * SYSLOG_HANDLERS = ["mod_syslog_file"] file name is then defined by SYSLOG_FILE 1408 * SYSLOG_HANDLERS = ["mod_syslog_syslog"] facility is then defined by SYSLOG_FACILITY 1409 * Warning, syslog functions are bugged on Windows, generating memory protection faults. To solve 1410 * this, use logging to files instead of syslog (see setup of module). 1411 * Note: If constant 'SYSLOG_FILE_NO_ERROR' defined, we never output any error message when writing to log fails. 1412 * Note: You can get log message into html sources by adding parameter &logtohtml=1 (constant MAIN_LOGTOHTML must be set) 1413 * This function works only if syslog module is enabled. 1414 * This must not use any call to other function calling dol_syslog (avoid infinite loop). 1415 * 1416 * @param string $message Line to log. ''=Show nothing 1417 * @param int $level Log level 1418 * 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 1419 * On Linux LOG_ERR=3, LOG_WARNING=4, LOG_NOTICE=5, LOG_INFO=6, LOG_DEBUG=7 1420 * @param int $ident 1=Increase ident of 1, -1=Decrease ident of 1 1421 * @param string $suffixinfilename When output is a file, append this suffix into default log filename. 1422 * @param string $restricttologhandler Force output of log only to this log handler 1423 * @param array|null $logcontext If defined, an array with extra informations (can be used by some log handlers) 1424 * @return void 1425 */ 1426function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null) 1427{ 1428 global $conf, $user, $debugbar; 1429 1430 // If syslog module enabled 1431 if (empty($conf->syslog->enabled)) { 1432 return; 1433 } 1434 1435 // Check if we are into execution of code of a website 1436 if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) { 1437 global $website, $websitekey; 1438 if (is_object($website) && !empty($website->ref)) { 1439 $suffixinfilename .= '_website_'.$website->ref; 1440 } elseif (!empty($websitekey)) { 1441 $suffixinfilename .= '_website_'.$websitekey; 1442 } 1443 } 1444 1445 if ($ident < 0) { 1446 foreach ($conf->loghandlers as $loghandlerinstance) { 1447 $loghandlerinstance->setIdent($ident); 1448 } 1449 } 1450 1451 if (!empty($message)) { 1452 // Test log level 1453 $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'); 1454 if (!array_key_exists($level, $logLevels)) { 1455 throw new Exception('Incorrect log level'); 1456 } 1457 if ($level > $conf->global->SYSLOG_LEVEL) { 1458 return; 1459 } 1460 1461 if (empty($conf->global->MAIN_SHOW_PASSWORD_INTO_LOG)) { 1462 $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log 1463 } 1464 1465 // If adding log inside HTML page is required 1466 if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML)) 1467 || (!empty($user->rights->debugbar->read) && is_object($debugbar))) { 1468 $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message; 1469 } 1470 1471 //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output 1472 // If html log tag enabled and url parameter log defined, we show output log on HTML comments 1473 if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) { 1474 print "\n\n<!-- Log start\n"; 1475 print dol_escape_htmltag($message)."\n"; 1476 print "Log end -->\n"; 1477 } 1478 1479 $data = array( 1480 'message' => $message, 1481 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false), 1482 'level' => $level, 1483 'user' => ((is_object($user) && $user->id) ? $user->login : false), 1484 'ip' => false 1485 ); 1486 1487 $remoteip = getUserRemoteIP(); // Get ip when page run on a web server 1488 if (!empty($remoteip)) { 1489 $data['ip'] = $remoteip; 1490 // This is when server run behind a reverse proxy 1491 if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) { 1492 $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip']; 1493 } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) { 1494 $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip']; 1495 } 1496 } elseif (!empty($_SERVER['SERVER_ADDR'])) { 1497 // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache) 1498 $data['ip'] = $_SERVER['SERVER_ADDR']; 1499 } elseif (!empty($_SERVER['COMPUTERNAME'])) { 1500 // 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). 1501 $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']); 1502 } elseif (!empty($_SERVER['LOGNAME'])) { 1503 // 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). 1504 $data['ip'] = '???@'.$_SERVER['LOGNAME']; 1505 } 1506 // Loop on each log handler and send output 1507 foreach ($conf->loghandlers as $loghandlerinstance) { 1508 if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) { 1509 continue; 1510 } 1511 $loghandlerinstance->export($data, $suffixinfilename); 1512 } 1513 unset($data); 1514 } 1515 1516 if ($ident > 0) { 1517 foreach ($conf->loghandlers as $loghandlerinstance) { 1518 $loghandlerinstance->setIdent($ident); 1519 } 1520 } 1521} 1522 1523/** 1524 * Return HTML code to output a button to open a dialog popup box. 1525 * Such buttons must be included inside a HTML form. 1526 * 1527 * @param string $name A name for the html component 1528 * @param string $label Label of button 1529 * @param string $buttonstring button string 1530 * @param string $url Url to open 1531 * @param string $disabled Disabled text 1532 * @return string HTML component with button 1533 */ 1534function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '') 1535{ 1536 if (strpos($url, '?') > 0) { 1537 $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup=1'; 1538 } else { 1539 $url .= '?dol_hide_menuinpopup=1&dol_hide_leftmenu=1&dol_openinpopup=1'; 1540 } 1541 1542 //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">'; 1543 $out = '<a class="button bordertransp button_'.$name.'"'.$disabled.' title="'.dol_escape_htmltag($label).'">'.$buttonstring.'</a>'; 1544 $out .= '<!-- Add js code to open dialog popup on dialog -->'; 1545 $out .= '<script language="javascript"> 1546 jQuery(document).ready(function () { 1547 jQuery(".button_'.$name.'").click(function () { 1548 console.log("Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'") 1549 var $dialog = $(\'<div></div>\').html(\'<iframe class="iframedialog" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\') 1550 .dialog({ 1551 autoOpen: false, 1552 modal: true, 1553 height: (window.innerHeight - 150), 1554 width: \'80%\', 1555 title: "'.dol_escape_js($label).'" 1556 }); 1557 $dialog.dialog(\'open\'); 1558 }); 1559 }); 1560 </script>'; 1561 return $out; 1562} 1563 1564/** 1565 * Show tab header of a card 1566 * 1567 * @param array $links Array of tabs. Currently initialized by calling a function xxx_admin_prepare_head 1568 * @param string $active Active tab name (document', 'info', 'ldap', ....) 1569 * @param string $title Title 1570 * @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) 1571 * @param string $picto Add a picto on tab title 1572 * @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. 1573 * @param string $morehtmlright Add more html content on right of tabs title 1574 * @param string $morecss More Css 1575 * @param int $limittoshow Limit number of tabs to show. Use 0 to use automatic default value. 1576 * @param string $moretabssuffix A suffix to use when you have several dol_get_fiche_head() in same page 1577 * @return void 1578 * @deprecated Use print dol_get_fiche_head() instead 1579 */ 1580function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '') 1581{ 1582 print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix); 1583} 1584 1585/** 1586 * Show tabs of a record 1587 * 1588 * @param array $links Array of tabs 1589 * @param string $active Active tab name 1590 * @param string $title Title 1591 * @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) 1592 * @param string $picto Add a picto on tab title 1593 * @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. 1594 * @param string $morehtmlright Add more html content on right of tabs title 1595 * @param string $morecss More Css 1596 * @param int $limittoshow Limit number of tabs to show. Use 0 to use automatic default value. 1597 * @param string $moretabssuffix A suffix to use when you have several dol_get_fiche_head() in same page 1598 * @return string 1599 */ 1600function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '') 1601{ 1602 global $conf, $langs, $hookmanager; 1603 1604 // Show title 1605 $showtitle = 1; 1606 if (!empty($conf->dol_optimize_smallscreen)) { 1607 $showtitle = 0; 1608 } 1609 1610 $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->'; 1611 1612 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) { 1613 $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n"; 1614 } 1615 1616 // Show right part 1617 if ($morehtmlright) { 1618 $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. 1619 } 1620 1621 // Show title 1622 if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { 1623 $limittitle = 30; 1624 $out .= '<a class="tabTitle">'; 1625 if ($picto) { 1626 $out .= img_picto($title, ($pictoisfullpath ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' '; 1627 } 1628 $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>'; 1629 $out .= '</a>'; 1630 } 1631 1632 // Show tabs 1633 1634 // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs). 1635 $maxkey = -1; 1636 if (is_array($links) && !empty($links)) { 1637 $keys = array_keys($links); 1638 if (count($keys)) { 1639 $maxkey = max($keys); 1640 } 1641 } 1642 1643 // Show tabs 1644 // if =0 we don't use the feature 1645 if (empty($limittoshow)) { 1646 $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD); 1647 } 1648 if (!empty($conf->dol_optimize_smallscreen)) { 1649 $limittoshow = 2; 1650 } 1651 1652 $displaytab = 0; 1653 $nbintab = 0; 1654 $popuptab = 0; 1655 $outmore = ''; 1656 for ($i = 0; $i <= $maxkey; $i++) { 1657 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) { 1658 // If active tab is already present 1659 if ($i >= $limittoshow) { 1660 $limittoshow--; 1661 } 1662 } 1663 } 1664 1665 for ($i = 0; $i <= $maxkey; $i++) { 1666 if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) { 1667 $isactive = true; 1668 } else { 1669 $isactive = false; 1670 } 1671 1672 if ($i < $limittoshow || $isactive) { 1673 // Add a new entry 1674 $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]).' -->'; 1675 1676 if (isset($links[$i][2]) && $links[$i][2] == 'image') { 1677 if (!empty($links[$i][0])) { 1678 $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n"; 1679 } else { 1680 $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n"; 1681 } 1682 } elseif (!empty($links[$i][1])) { 1683 //print "x $i $active ".$links[$i][2]." z"; 1684 $out .= '<div class="tab tab'.($isactive?'active':'unactive').'" style="margin: 0 !important">'; 1685 if (!empty($links[$i][0])) { 1686 $out .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="tab inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'; 1687 } 1688 $out .= $links[$i][1]; 1689 if (!empty($links[$i][0])) { 1690 $out .= '</a>'."\n"; 1691 } 1692 $out .= empty($links[$i][4]) ? '' : $links[$i][4]; 1693 $out .= '</div>'; 1694 } 1695 1696 $out .= '</div>'; 1697 } else { 1698 // The popup with the other tabs 1699 if (!$popuptab) { 1700 $popuptab = 1; 1701 $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup 1702 } 1703 $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">'; 1704 if (isset($links[$i][2]) && $links[$i][2] == 'image') { 1705 if (!empty($links[$i][0])) { 1706 $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n"; 1707 } else { 1708 $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n"; 1709 } 1710 } elseif (!empty($links[$i][1])) { 1711 $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'; 1712 $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. 1713 $outmore .= '</a>'."\n"; 1714 } 1715 $outmore .= '</div>'; 1716 1717 $nbintab++; 1718 } 1719 $displaytab = $i; 1720 } 1721 if ($popuptab) { 1722 $outmore .= '</div>'; 1723 } 1724 1725 if ($popuptab) { // If there is some tabs not shown 1726 $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left'); 1727 $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right'); 1728 $widthofpopup = 200; 1729 1730 $tabsname = $moretabssuffix; 1731 if (empty($tabsname)) { 1732 $tabsname = str_replace("@", "", $picto); 1733 } 1734 $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem">'; 1735 $out .= '<a href="#" class="tab moretab inline-block tabunactive"><span class="hideonsmartphone">'.$langs->trans("More").'</span>... ('.$nbintab.')</a>'; // Do not use "reposition" class in the "More". 1736 $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">'; 1737 $out .= $outmore; 1738 $out .= '</div>'; 1739 $out .= '<div></div>'; 1740 $out .= "</div>\n"; 1741 1742 $out .= "<script>"; 1743 $out .= "$('#moretabs".$tabsname."').mouseenter( function() { 1744 var x = this.offsetLeft, y = this.offsetTop; 1745 console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth); 1746 if ((window.innerWidth - x) < ".($widthofpopup + 10).") { 1747 $('#moretabsList".$tabsname."').css('".$right."','8px'); 1748 } 1749 $('#moretabsList".$tabsname."').css('".$left."','auto'); 1750 }); 1751 "; 1752 $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});"; 1753 $out .= "</script>"; 1754 } 1755 1756 if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) { 1757 $out .= "</div>\n"; 1758 } 1759 1760 if (!$notab || $notab == -1 || $notab == -2) { 1761 $out .= "\n".'<div class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ' tabBarWithBottom')).'">'."\n"; 1762 } 1763 1764 $parameters = array('tabname' => $active, 'out' => $out); 1765 $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead" 1766 if ($reshook > 0) { 1767 $out = $hookmanager->resPrint; 1768 } 1769 1770 return $out; 1771} 1772 1773/** 1774 * Show tab footer of a card 1775 * 1776 * @param int $notab -1 or 0=Add tab footer, 1=no tab footer 1777 * @return void 1778 * @deprecated Use print dol_get_fiche_end() instead 1779 */ 1780function dol_fiche_end($notab = 0) 1781{ 1782 print dol_get_fiche_end($notab); 1783} 1784 1785/** 1786 * Return tab footer of a card 1787 * 1788 * @param int $notab -1 or 0=Add tab footer, 1=no tab footer 1789 * @return string 1790 */ 1791function dol_get_fiche_end($notab = 0) 1792{ 1793 if (!$notab || $notab == -1) { 1794 return "\n</div>\n"; 1795 } else { 1796 return ''; 1797 } 1798} 1799 1800/** 1801 * Show tab footer of a card. 1802 * Note: $object->next_prev_filter can be set to restrict select to find next or previous record by $form->showrefnav. 1803 * 1804 * @param Object $object Object to show 1805 * @param string $paramid Name of parameter to use to name the id into the URL next/previous link 1806 * @param string $morehtml More html content to output just before the nav bar 1807 * @param int $shownav Show Condition (navigation is shown if value is 1) 1808 * @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. 1809 * @param string $fieldref Nom du champ objet ref (object->ref) a utiliser pour select next et previous 1810 * @param string $morehtmlref More html to show after the ref (see $morehtmlleft for before) 1811 * @param string $moreparam More param to add in nav link url. 1812 * @param int $nodbprefix Do not include DB prefix to forge table name 1813 * @param string $morehtmlleft More html code to show before the ref (see $morehtmlref for after) 1814 * @param string $morehtmlstatus More html code to show under navigation arrows 1815 * @param int $onlybanner Put this to 1, if the card will contains only a banner (this add css 'arearefnobottom' on div) 1816 * @param string $morehtmlright More html code to show before navigation arrows 1817 * @return void 1818 */ 1819function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '') 1820{ 1821 global $conf, $form, $user, $langs; 1822 1823 $error = 0; 1824 1825 $maxvisiblephotos = 1; 1826 $showimage = 1; 1827 $entity = (empty($object->entity) ? $conf->entity : $object->entity); 1828 $showbarcode = empty($conf->barcode->enabled) ? 0 : (empty($object->barcode) ? 0 : 1); 1829 if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) { 1830 $showbarcode = 0; 1831 } 1832 $modulepart = 'unknown'; 1833 1834 if ($object->element == 'societe' || $object->element == 'contact' || $object->element == 'product' || $object->element == 'ticket') { 1835 $modulepart = $object->element; 1836 } elseif ($object->element == 'member') { 1837 $modulepart = 'memberphoto'; 1838 } elseif ($object->element == 'user') { 1839 $modulepart = 'userphoto'; 1840 } 1841 1842 if (class_exists("Imagick")) { 1843 if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') { 1844 $modulepart = $object->element; 1845 } elseif ($object->element == 'fichinter') { 1846 $modulepart = 'ficheinter'; 1847 } elseif ($object->element == 'contrat') { 1848 $modulepart = 'contract'; 1849 } elseif ($object->element == 'order_supplier') { 1850 $modulepart = 'supplier_order'; 1851 } elseif ($object->element == 'invoice_supplier') { 1852 $modulepart = 'supplier_invoice'; 1853 } 1854 } 1855 1856 if ($object->element == 'product') { 1857 $width = 80; 1858 $cssclass = 'photoref'; 1859 $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]); 1860 $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5); 1861 if ($conf->browser->layout == 'phone') { 1862 $maxvisiblephotos = 1; 1863 } 1864 if ($showimage) { 1865 $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>'; 1866 } else { 1867 if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) { 1868 $nophoto = ''; 1869 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>'; 1870 } else { // Show no photo link 1871 $nophoto = '/public/theme/common/nophoto.png'; 1872 $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>'; 1873 } 1874 } 1875 } elseif ($object->element == 'ticket') { 1876 $width = 80; 1877 $cssclass = 'photoref'; 1878 $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref); 1879 $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2); 1880 if ($conf->browser->layout == 'phone') { 1881 $maxvisiblephotos = 1; 1882 } 1883 1884 if ($showimage) { 1885 $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0); 1886 if ($object->nbphoto > 0) { 1887 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>'; 1888 } else { 1889 $showimage = 0; 1890 } 1891 } 1892 if (!$showimage) { 1893 if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) { 1894 $nophoto = ''; 1895 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>'; 1896 } else { // Show no photo link 1897 $nophoto = img_picto('No photo', 'object_ticket'); 1898 $morehtmlleft .= '<!-- No photo to show -->'; 1899 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">'; 1900 $morehtmlleft .= $nophoto; 1901 $morehtmlleft .= '</div></div>'; 1902 } 1903 } 1904 } else { 1905 if ($showimage) { 1906 if ($modulepart != 'unknown') { 1907 $phototoshow = ''; 1908 // Check if a preview file is available 1909 if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) { 1910 $objectref = dol_sanitizeFileName($object->ref); 1911 $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/"; 1912 if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) { 1913 $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart); 1914 $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 1915 } else { 1916 $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart); 1917 } 1918 if (empty($subdir)) { 1919 $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path 1920 } 1921 1922 $filepath = $dir_output.$subdir."/"; 1923 1924 $filepdf = $filepath.$objectref.".pdf"; 1925 $relativepath = $subdir.'/'.$objectref.'.pdf'; 1926 1927 // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png") 1928 $fileimage = $filepdf.'_preview.png'; 1929 $relativepathimage = $relativepath.'_preview.png'; 1930 1931 $pdfexists = file_exists($filepdf); 1932 1933 // If PDF file exists 1934 if ($pdfexists) { 1935 // Conversion du PDF en image png si fichier png non existant 1936 if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) { 1937 if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) { // If you experience trouble with pdf thumb generation and imagick, you can disable here. 1938 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 1939 $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png 1940 if ($ret < 0) { 1941 $error++; 1942 } 1943 } 1944 } 1945 } 1946 1947 if ($pdfexists && !$error) { 1948 $heightforphotref = 80; 1949 if (!empty($conf->dol_optimize_smallscreen)) { 1950 $heightforphotref = 60; 1951 } 1952 // If the preview file is found 1953 if (file_exists($fileimage)) { 1954 $phototoshow = '<div class="photoref">'; 1955 $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&file='.urlencode($relativepathimage).'">'; 1956 $phototoshow .= '</div>'; 1957 } 1958 } 1959 } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' 1960 $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photoref', 'small', 1, 0, $maxvisiblephotos); 1961 } 1962 1963 if ($phototoshow) { 1964 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'; 1965 $morehtmlleft .= $phototoshow; 1966 $morehtmlleft .= '</div>'; 1967 } 1968 } 1969 1970 if (empty($phototoshow)) { // Show No photo link (picto of object) 1971 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'; 1972 if ($object->element == 'action') { 1973 $width = 80; 1974 $cssclass = 'photorefcenter'; 1975 $nophoto = img_picto('No photo', 'title_agenda'); 1976 } else { 1977 $width = 14; 1978 $cssclass = 'photorefcenter'; 1979 $picto = $object->picto; 1980 if ($object->element == 'project' && !$object->public) { 1981 $picto = 'project'; // instead of projectpub 1982 } 1983 $nophoto = img_picto('No photo', 'object_'.$picto); 1984 } 1985 $morehtmlleft .= '<!-- No photo to show -->'; 1986 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">'; 1987 $morehtmlleft .= $nophoto; 1988 $morehtmlleft .= '</div></div>'; 1989 1990 $morehtmlleft .= '</div>'; 1991 } 1992 } 1993 } 1994 1995 if ($showbarcode) { 1996 $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref').'</div>'; 1997 } 1998 1999 if ($object->element == 'societe') { 2000 if (!empty($conf->use_javascript_ajax) && $user->rights->societe->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) { 2001 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased'); 2002 } else { 2003 $morehtmlstatus .= $object->getLibStatut(6); 2004 } 2005 } elseif ($object->element == 'product') { 2006 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') '; 2007 if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) { 2008 $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell'); 2009 } else { 2010 $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>'; 2011 } 2012 $morehtmlstatus .= ' '; 2013 //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') '; 2014 if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) { 2015 $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy'); 2016 } else { 2017 $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>'; 2018 } 2019 } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva', 'salary'))) { 2020 $tmptxt = $object->getLibStatut(6, $object->totalpaye); 2021 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) { 2022 $tmptxt = $object->getLibStatut(5, $object->totalpaye); 2023 } 2024 $morehtmlstatus .= $tmptxt; 2025 } elseif ($object->element == 'contrat' || $object->element == 'contract') { 2026 if ($object->statut == 0) { 2027 $morehtmlstatus .= $object->getLibStatut(5); 2028 } else { 2029 $morehtmlstatus .= $object->getLibStatut(4); 2030 } 2031 } elseif ($object->element == 'facturerec') { 2032 if ($object->frequency == 0) { 2033 $morehtmlstatus .= $object->getLibStatut(2); 2034 } else { 2035 $morehtmlstatus .= $object->getLibStatut(5); 2036 } 2037 } elseif ($object->element == 'project_task') { 2038 $object->fk_statut = 1; 2039 if ($object->progress > 0) { 2040 $object->fk_statut = 2; 2041 } 2042 if ($object->progress >= 100) { 2043 $object->fk_statut = 3; 2044 } 2045 $tmptxt = $object->getLibStatut(5); 2046 $morehtmlstatus .= $tmptxt; // No status on task 2047 } else { // Generic case 2048 $tmptxt = $object->getLibStatut(6); 2049 if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) { 2050 $tmptxt = $object->getLibStatut(5); 2051 } 2052 $morehtmlstatus .= $tmptxt; 2053 } 2054 2055 // Add if object was dispatched "into accountancy" 2056 if (!empty($conf->accounting->enabled) && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) { 2057 // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank') 2058 if (method_exists($object, 'getVentilExportCompta')) { 2059 $accounted = $object->getVentilExportCompta(); 2060 $langs->load("accountancy"); 2061 $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>'; 2062 } 2063 } 2064 2065 // Add alias for thirdparty 2066 if (!empty($object->name_alias)) { 2067 $morehtmlref .= '<div class="refidno">'.$object->name_alias.'</div>'; 2068 } 2069 2070 // Add label 2071 if (in_array($object->element, array('product', 'bank_account', 'project_task'))) { 2072 if (!empty($object->label)) { 2073 $morehtmlref .= '<div class="refidno">'.$object->label.'</div>'; 2074 } 2075 } 2076 2077 if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) { 2078 $moreaddress = $object->getBannerAddress('refaddress', $object); 2079 if ($moreaddress) { 2080 $morehtmlref .= '<div class="refidno">'; 2081 $morehtmlref .= $moreaddress; 2082 $morehtmlref .= '</div>'; 2083 } 2084 } 2085 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)) { 2086 $morehtmlref .= '<div style="clear: both;"></div>'; 2087 $morehtmlref .= '<div class="refidno">'; 2088 $morehtmlref .= $langs->trans("TechnicalID").': '.$object->id; 2089 $morehtmlref .= '</div>'; 2090 } 2091 2092 print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">'; 2093 print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright); 2094 print '</div>'; 2095 print '<div class="underrefbanner clearboth"></div>'; 2096} 2097 2098/** 2099 * Show a string with the label tag dedicated to the HTML edit field. 2100 * 2101 * @param string $langkey Translation key 2102 * @param string $fieldkey Key of the html select field the text refers to 2103 * @param int $fieldrequired 1=Field is mandatory 2104 * @return string 2105 * @deprecated Form::editfieldkey 2106 */ 2107function fieldLabel($langkey, $fieldkey, $fieldrequired = 0) 2108{ 2109 global $langs; 2110 $ret = ''; 2111 if ($fieldrequired) { 2112 $ret .= '<span class="fieldrequired">'; 2113 } 2114 $ret .= '<label for="'.$fieldkey.'">'; 2115 $ret .= $langs->trans($langkey); 2116 $ret .= '</label>'; 2117 if ($fieldrequired) { 2118 $ret .= '</span>'; 2119 } 2120 return $ret; 2121} 2122 2123/** 2124 * Return string to add class property on html element with pair/impair. 2125 * 2126 * @param string $var 0 or 1 2127 * @param string $moreclass More class to add 2128 * @return string String to add class onto HTML element 2129 */ 2130function dol_bc($var, $moreclass = '') 2131{ 2132 global $bc; 2133 $ret = ' '.$bc[$var]; 2134 if ($moreclass) { 2135 $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret); 2136 } 2137 return $ret; 2138} 2139 2140/** 2141 * Return a formated address (part address/zip/town/state) according to country rules. 2142 * See https://en.wikipedia.org/wiki/Address 2143 * 2144 * @param Object $object A company or contact object 2145 * @param int $withcountry 1=Add country into address string 2146 * @param string $sep Separator to use to build string 2147 * @param Translate $outputlangs Object lang that contains language for text translation. 2148 * @param int $mode 0=Standard output, 1=Remove address 2149 * @param string $extralangcode User extralanguage $langcode as values for address, town 2150 * @return string Formated string 2151 * @see dol_print_address() 2152 */ 2153function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '') 2154{ 2155 global $conf, $langs; 2156 2157 $ret = ''; 2158 $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR'); // See also MAIN_FORCE_STATE_INTO_ADDRESS 2159 2160 // See format of addresses on https://en.wikipedia.org/wiki/Address 2161 // Address 2162 if (empty($mode)) { 2163 $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : $object->address)); 2164 } 2165 // Zip/Town/State 2166 if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) { 2167 // US: title firstname name \n address lines \n town, state, zip \n country 2168 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); 2169 $ret .= ($ret ? $sep : '').$town; 2170 if (!empty($object->state)) { 2171 $ret .= ($ret ? ", " : '').$object->state; 2172 } 2173 if (!empty($object->zip)) { 2174 $ret .= ($ret ? ", " : '').$object->zip; 2175 } 2176 } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) { 2177 // UK: title firstname name \n address lines \n town state \n zip \n country 2178 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); 2179 $ret .= ($ret ? $sep : '').$town; 2180 if (!empty($object->state)) { 2181 $ret .= ($ret ? ", " : '').$object->state; 2182 } 2183 if (!empty($object->zip)) { 2184 $ret .= ($ret ? $sep : '').$object->zip; 2185 } 2186 } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) { 2187 // ES: title firstname name \n address lines \n zip town \n state \n country 2188 $ret .= ($ret ? $sep : '').$object->zip; 2189 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); 2190 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : ''); 2191 if (!empty($object->state)) { 2192 $ret .= "\n".$object->state; 2193 } 2194 } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) { 2195 // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country 2196 // See https://www.sljfaq.org/afaq/addresses.html 2197 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); 2198 $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip; 2199 } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) { 2200 // IT: title firstname name\n address lines \n zip town state_code \n country 2201 $ret .= ($ret ? $sep : '').$object->zip; 2202 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); 2203 $ret .= ($town ? (($object->zip ? ' ' : '').$town) : ''); 2204 $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code)); 2205 } else { 2206 // Other: title firstname name \n address lines \n zip town[, state] \n country 2207 $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); 2208 $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : ''; 2209 $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : ''); 2210 if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) { 2211 $ret .= ($ret ? ", " : '').$object->state; 2212 } 2213 } 2214 if (!is_object($outputlangs)) { 2215 $outputlangs = $langs; 2216 } 2217 if ($withcountry) { 2218 $langs->load("dict"); 2219 $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code))); 2220 } 2221 2222 return $ret; 2223} 2224 2225 2226 2227/** 2228 * Format a string. 2229 * 2230 * @param string $fmt Format of strftime function (http://php.net/manual/fr/function.strftime.php) 2231 * @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) 2232 * @param int $is_gmt See comment of timestamp parameter 2233 * @return string A formatted string 2234 */ 2235function dol_strftime($fmt, $ts = false, $is_gmt = false) 2236{ 2237 if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range 2238 return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts); 2239 } else { 2240 return 'Error date into a not supported range'; 2241 } 2242} 2243 2244/** 2245 * Output date in a string format according to outputlangs (or langs if not defined). 2246 * Return charset is always UTF-8, except if encodetoouput is defined. In this case charset is output charset 2247 * 2248 * @param int $time GM Timestamps date 2249 * @param string $format Output date format (tag of strftime function) 2250 * "%d %b %Y", 2251 * "%d/%m/%Y %H:%M", 2252 * "%d/%m/%Y %H:%M:%S", 2253 * "%B"=Long text of month, "%A"=Long text of day, "%b"=Short text of month, "%a"=Short text of day 2254 * "day", "daytext", "dayhour", "dayhourldap", "dayhourtext", "dayrfc", "dayhourrfc", "...inputnoreduce", "...reduceformat" 2255 * @param string $tzoutput true or 'gmt' => string is for Greenwich location 2256 * false or 'tzserver' => output string is for local PHP server TZ usage 2257 * 'tzuser' => output string is for user TZ (current browser TZ with current dst) => In a future, we should have same behaviour than 'tzuserrel' 2258 * 'tzuserrel' => output string is for user TZ (current browser TZ with dst or not, depending on date position) (TODO not implemented yet) 2259 * @param Translate $outputlangs Object lang that contains language for text translation. 2260 * @param boolean $encodetooutput false=no convert into output pagecode 2261 * @return string Formated date or '' if time is null 2262 * 2263 * @see dol_mktime(), dol_stringtotime(), dol_getdate() 2264 */ 2265function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false) 2266{ 2267 global $conf, $langs; 2268 2269 if ($tzoutput === 'auto') { 2270 $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver')); 2271 } 2272 2273 // Clean parameters 2274 $to_gmt = false; 2275 $offsettz = $offsetdst = 0; 2276 if ($tzoutput) { 2277 $to_gmt = true; // For backward compatibility 2278 if (is_string($tzoutput)) { 2279 if ($tzoutput == 'tzserver') { 2280 $to_gmt = false; 2281 $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion' 2282 $offsettz = 0; // Timezone offset with server timezone, so 0 2283 $offsetdst = 0; // Dst offset with server timezone, so 0 2284 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') { 2285 $to_gmt = true; 2286 $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion' 2287 2288 if (class_exists('DateTimeZone')) { 2289 $user_date_tz = new DateTimeZone($offsettzstring); 2290 $user_dt = new DateTime(); 2291 $user_dt->setTimezone($user_date_tz); 2292 $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : $time); 2293 $offsettz = $user_dt->getOffset(); 2294 } else { // old method (The 'tzuser' was processed like the 'tzuserrel') 2295 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore 2296 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore 2297 } 2298 } 2299 } 2300 } 2301 if (!is_object($outputlangs)) { 2302 $outputlangs = $langs; 2303 } 2304 if (!$format) { 2305 $format = 'daytextshort'; 2306 } 2307 2308 // Do we have to reduce the length of date (year on 2 chars) to save space. 2309 // Note: dayinputnoreduce is same than day but no reduction of year length will be done 2310 $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param. 2311 $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day 2312 $formatwithoutreduce = preg_replace('/reduceformat/', '', $format); 2313 if ($formatwithoutreduce != $format) { 2314 $format = $formatwithoutreduce; 2315 $reduceformat = 1; 2316 } // so format 'dayreduceformat' is processed like day 2317 2318 // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default. 2319 // TODO Add format daysmallyear and dayhoursmallyear 2320 if ($format == 'day') { 2321 $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short); 2322 } elseif ($format == 'hour') { 2323 $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short); 2324 } elseif ($format == 'hourduration') { 2325 $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration); 2326 } elseif ($format == 'daytext') { 2327 $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text); 2328 } elseif ($format == 'daytextshort') { 2329 $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short); 2330 } elseif ($format == 'dayhour') { 2331 $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short); 2332 } elseif ($format == 'dayhoursec') { 2333 $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short); 2334 } elseif ($format == 'dayhourtext') { 2335 $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text); 2336 } elseif ($format == 'dayhourtextshort') { 2337 $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short); 2338 } elseif ($format == 'dayhourlog') { 2339 // Format not sensitive to language 2340 $format = '%Y%m%d%H%M%S'; 2341 } elseif ($format == 'dayhourldap') { 2342 $format = '%Y%m%d%H%M%SZ'; 2343 } elseif ($format == 'dayhourxcard') { 2344 $format = '%Y%m%dT%H%M%SZ'; 2345 } elseif ($format == 'dayxcard') { 2346 $format = '%Y%m%d'; 2347 } elseif ($format == 'dayrfc') { 2348 $format = '%Y-%m-%d'; // DATE_RFC3339 2349 } elseif ($format == 'dayhourrfc') { 2350 $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339 2351 } elseif ($format == 'standard') { 2352 $format = '%Y-%m-%d %H:%M:%S'; 2353 } 2354 2355 if ($reduceformat) { 2356 $format = str_replace('%Y', '%y', $format); 2357 $format = str_replace('yyyy', 'yy', $format); 2358 } 2359 2360 // If date undefined or "", we return "" 2361 if (dol_strlen($time) == 0) { 2362 return ''; // $time=0 allowed (it means 01/01/1970 00:00:00) 2363 } 2364 2365 // Clean format 2366 if (preg_match('/%b/i', $format)) { // There is some text to translate 2367 // We inhibate translation to text made by strftime functions. We will use trans instead later. 2368 $format = str_replace('%b', '__b__', $format); 2369 $format = str_replace('%B', '__B__', $format); 2370 } 2371 if (preg_match('/%a/i', $format)) { // There is some text to translate 2372 // We inhibate translation to text made by strftime functions. We will use trans instead later. 2373 $format = str_replace('%a', '__a__', $format); 2374 $format = str_replace('%A', '__A__', $format); 2375 } 2376 2377 2378 // Analyze date 2379 $reg = array(); 2380 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 2381 dol_print_error("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]); 2382 return ''; 2383 } 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 2384 // This part of code should not be used anymore. 2385 dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING); 2386 //if (function_exists('debug_print_backtrace')) debug_print_backtrace(); 2387 // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' 2388 $syear = (!empty($reg[1]) ? $reg[1] : ''); 2389 $smonth = (!empty($reg[2]) ? $reg[2] : ''); 2390 $sday = (!empty($reg[3]) ? $reg[3] : ''); 2391 $shour = (!empty($reg[4]) ? $reg[4] : ''); 2392 $smin = (!empty($reg[5]) ? $reg[5] : ''); 2393 $ssec = (!empty($reg[6]) ? $reg[6] : ''); 2394 2395 $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true); 2396 $ret = adodb_strftime($format, $time + $offsettz + $offsetdst, $to_gmt); 2397 } else { 2398 // Date is a timestamps 2399 if ($time < 100000000000) { // Protection against bad date values 2400 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring. 2401 2402 $ret = adodb_strftime($format, $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server 2403 } else { 2404 $ret = 'Bad value '.$time.' for date'; 2405 } 2406 } 2407 2408 if (preg_match('/__b__/i', $format)) { 2409 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring. 2410 2411 // Here ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs. 2412 $month = adodb_strftime('%m', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server 2413 $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'. 2414 if ($encodetooutput) { 2415 $monthtext = $outputlangs->transnoentities('Month'.$month); 2416 $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month); 2417 } else { 2418 $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month); 2419 $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month); 2420 } 2421 //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort; 2422 $ret = str_replace('__b__', $monthtextshort, $ret); 2423 $ret = str_replace('__B__', $monthtext, $ret); 2424 //print 'x'.$outputlangs->charset_output.'-'.$ret.'x'; 2425 //return $ret; 2426 } 2427 if (preg_match('/__a__/i', $format)) { 2428 //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring"; 2429 $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring. 2430 2431 $w = adodb_strftime('%w', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server 2432 $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w); 2433 $ret = str_replace('__A__', $dayweek, $ret); 2434 $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret); 2435 } 2436 2437 return $ret; 2438} 2439 2440 2441/** 2442 * Return an array with locale date info. 2443 * WARNING: This function use PHP server timezone by default to return locale informations. 2444 * Be aware to add the third parameter to "UTC" if you need to work on UTC. 2445 * 2446 * @param int $timestamp Timestamp 2447 * @param boolean $fast Fast mode. deprecated. 2448 * @param string $forcetimezone '' to use the PHP server timezone. Or use a form like 'gmt', 'Europe/Paris' or '+0200' to force timezone. 2449 * @return array Array of informations 2450 * 'seconds' => $secs, 2451 * 'minutes' => $min, 2452 * 'hours' => $hour, 2453 * 'mday' => $day, 2454 * 'wday' => $dow, 0=sunday, 6=saturday 2455 * 'mon' => $month, 2456 * 'year' => $year, 2457 * 'yday' => floor($secsInYear/$_day_power) 2458 * '0' => original timestamp 2459 * @see dol_print_date(), dol_stringtotime(), dol_mktime() 2460 */ 2461function dol_getdate($timestamp, $fast = false, $forcetimezone = '') 2462{ 2463 //$datetimeobj = new DateTime('@'.$timestamp); 2464 $datetimeobj = new DateTime(); 2465 $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone 2466 if ($forcetimezone) { 2467 $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered) 2468 } 2469 $arrayinfo = array( 2470 'year'=>((int) date_format($datetimeobj, 'Y')), 2471 'mon'=>((int) date_format($datetimeobj, 'm')), 2472 'mday'=>((int) date_format($datetimeobj, 'd')), 2473 'wday'=>((int) date_format($datetimeobj, 'w')), 2474 'yday'=>((int) date_format($datetimeobj, 'z')), 2475 'hours'=>((int) date_format($datetimeobj, 'H')), 2476 'minutes'=>((int) date_format($datetimeobj, 'i')), 2477 'seconds'=>((int) date_format($datetimeobj, 's')), 2478 '0'=>$timestamp 2479 ); 2480 2481 return $arrayinfo; 2482} 2483 2484/** 2485 * Return a timestamp date built from detailed informations (by default a local PHP server timestamp) 2486 * Replace function mktime not available under Windows if year < 1970 2487 * PHP mktime is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows 2488 * 2489 * @param int $hour Hour (can be -1 for undefined) 2490 * @param int $minute Minute (can be -1 for undefined) 2491 * @param int $second Second (can be -1 for undefined) 2492 * @param int $month Month (1 to 12) 2493 * @param int $day Day (1 to 31) 2494 * @param int $year Year 2495 * @param mixed $gm True or 1 or 'gmt'=Input informations are GMT values 2496 * False or 0 or 'tzserver' = local to server TZ 2497 * 'auto' 2498 * 'tzuser' = local to user TZ taking dst into account at the current date. Not yet implemented. 2499 * 'tzuserrel' = local to user TZ taking dst into account at the given date. Use this one to convert date input from user into a GMT date. 2500 * 'tz,TimeZone' = use specified timezone 2501 * @param int $check 0=No check on parameters (Can use day 32, etc...) 2502 * @return int|string Date as a timestamp, '' or false if error 2503 * @see dol_print_date(), dol_stringtotime(), dol_getdate() 2504 */ 2505function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1) 2506{ 2507 global $conf; 2508 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -"; 2509 //print 'gm:'.$gm.' gm==auto:'.($gm == 'auto').'<br>'; 2510 2511 if ($gm === 'auto') { 2512 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey); 2513 } 2514 2515 // Clean parameters 2516 if ($hour == -1 || empty($hour)) { 2517 $hour = 0; 2518 } 2519 if ($minute == -1 || empty($minute)) { 2520 $minute = 0; 2521 } 2522 if ($second == -1 || empty($second)) { 2523 $second = 0; 2524 } 2525 2526 // Check parameters 2527 if ($check) { 2528 if (!$month || !$day) { 2529 return ''; 2530 } 2531 if ($day > 31) { 2532 return ''; 2533 } 2534 if ($month > 12) { 2535 return ''; 2536 } 2537 if ($hour < 0 || $hour > 24) { 2538 return ''; 2539 } 2540 if ($minute < 0 || $minute > 60) { 2541 return ''; 2542 } 2543 if ($second < 0 || $second > 60) { 2544 return ''; 2545 } 2546 } 2547 2548 if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) { 2549 $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin' 2550 $localtz = new DateTimeZone($default_timezone); 2551 } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') { 2552 // We use dol_tz_string first because it is more reliable. 2553 $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin' 2554 try { 2555 $localtz = new DateTimeZone($default_timezone); 2556 } catch (Exception $e) { 2557 dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING); 2558 $default_timezone = @date_default_timezone_get(); 2559 } 2560 } elseif (strrpos($gm, "tz,") !== false) { 2561 $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin' 2562 try { 2563 $localtz = new DateTimeZone($timezone); 2564 } catch (Exception $e) { 2565 dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING); 2566 } 2567 } 2568 2569 if (empty($localtz)) { 2570 $localtz = new DateTimeZone('UTC'); 2571 } 2572 //var_dump($localtz); 2573 //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute); 2574 $dt = new DateTime(null, $localtz); 2575 $dt->setDate((int) $year, (int) $month, (int) $day); 2576 $dt->setTime((int) $hour, (int) $minute, (int) $second); 2577 $date = $dt->getTimestamp(); // should include daylight saving time 2578 //var_dump($date); 2579 return $date; 2580} 2581 2582 2583/** 2584 * Return date for now. In most cases, we use this function without parameters (that means GMT time). 2585 * 2586 * @param string $mode 'auto' => for backward compatibility (avoid this), 2587 * 'gmt' => we return GMT timestamp, 2588 * 'tzserver' => we add the PHP server timezone 2589 * 'tzref' => we add the company timezone. Not implemented. 2590 * 'tzuser' or 'tzuserrel' => we add the user timezone 2591 * @return int $date Timestamp 2592 */ 2593function dol_now($mode = 'auto') 2594{ 2595 $ret = 0; 2596 2597 if ($mode === 'auto') { 2598 $mode = 'gmt'; 2599 } 2600 2601 if ($mode == 'gmt') { 2602 $ret = time(); // Time for now at greenwich. 2603 } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added 2604 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; 2605 $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time 2606 $ret = (int) (dol_now('gmt') + ($tzsecond * 3600)); 2607 //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added 2608 // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; 2609 // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time 2610 // $ret=dol_now('gmt')+($tzsecond*3600); 2611 //} 2612 } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') { 2613 // Time for now with user timezone added 2614 //print 'time: '.time(); 2615 $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; 2616 $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; 2617 $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst)); 2618 } 2619 2620 return $ret; 2621} 2622 2623 2624/** 2625 * Return string with formated size 2626 * 2627 * @param int $size Size to print 2628 * @param int $shortvalue Tell if we want long value to use another unit (Ex: 1.5Kb instead of 1500b) 2629 * @param int $shortunit Use short label of size unit (for example 'b' instead of 'bytes') 2630 * @return string Link 2631 */ 2632function dol_print_size($size, $shortvalue = 0, $shortunit = 0) 2633{ 2634 global $conf, $langs; 2635 $level = 1024; 2636 2637 if (!empty($conf->dol_optimize_smallscreen)) { 2638 $shortunit = 1; 2639 } 2640 2641 // Set value text 2642 if (empty($shortvalue) || $size < ($level * 10)) { 2643 $ret = $size; 2644 $textunitshort = $langs->trans("b"); 2645 $textunitlong = $langs->trans("Bytes"); 2646 } else { 2647 $ret = round($size / $level, 0); 2648 $textunitshort = $langs->trans("Kb"); 2649 $textunitlong = $langs->trans("KiloBytes"); 2650 } 2651 // Use long or short text unit 2652 if (empty($shortunit)) { 2653 $ret .= ' '.$textunitlong; 2654 } else { 2655 $ret .= ' '.$textunitshort; 2656 } 2657 2658 return $ret; 2659} 2660 2661/** 2662 * Show Url link 2663 * 2664 * @param string $url Url to show 2665 * @param string $target Target for link 2666 * @param int $max Max number of characters to show 2667 * @param int $withpicto With picto 2668 * @return string HTML Link 2669 */ 2670function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0) 2671{ 2672 global $langs; 2673 2674 if (empty($url)) { 2675 return ''; 2676 } 2677 2678 $link = '<a href="'; 2679 if (!preg_match('/^http/i', $url)) { 2680 $link .= 'http://'; 2681 } 2682 $link .= $url; 2683 $link .= '"'; 2684 if ($target) { 2685 $link .= ' target="'.$target.'"'; 2686 } 2687 $link .= '>'; 2688 if (!preg_match('/^http/i', $url)) { 2689 $link .= 'http://'; 2690 } 2691 $link .= dol_trunc($url, $max); 2692 $link .= '</a>'; 2693 return '<div class="nospan float" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>'; 2694} 2695 2696/** 2697 * Show EMail link formatted for HTML output. 2698 * 2699 * @param string $email EMail to show (only email, without 'Name of recipient' before) 2700 * @param int $cid Id of contact if known 2701 * @param int $socid Id of third party if known 2702 * @param int $addlink 0=no link, 1=email has a html email link (+ link to create action if constant AGENDA_ADDACTIONFOREMAIL is on) 2703 * @param int $max Max number of characters to show 2704 * @param int $showinvalid 1=Show warning if syntax email is wrong 2705 * @param int $withpicto Show picto 2706 * @return string HTML Link 2707 */ 2708function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0) 2709{ 2710 global $conf, $user, $langs, $hookmanager; 2711 2712 $newemail = dol_escape_htmltag($email); 2713 2714 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) { 2715 $withpicto = 0; 2716 } 2717 2718 if (empty($email)) { 2719 return ' '; 2720 } 2721 2722 if (!empty($addlink)) { 2723 $newemail = '<a style="text-overflow: ellipsis;" href="'; 2724 if (!preg_match('/^mailto:/i', $email)) { 2725 $newemail .= 'mailto:'; 2726 } 2727 $newemail .= $email; 2728 $newemail .= '">'; 2729 $newemail .= dol_trunc($email, $max); 2730 $newemail .= '</a>'; 2731 if ($showinvalid && !isValidEmail($email)) { 2732 $langs->load("errors"); 2733 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email)); 2734 } 2735 2736 if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) { 2737 $type = 'AC_EMAIL'; 2738 $link = ''; 2739 if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) { 2740 $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>'; 2741 } 2742 if ($link) { 2743 $newemail = '<div>'.$newemail.' '.$link.'</div>'; 2744 } 2745 } 2746 } else { 2747 if ($showinvalid && !isValidEmail($email)) { 2748 $langs->load("errors"); 2749 $newemail .= img_warning($langs->trans("ErrorBadEMail", $email)); 2750 } 2751 } 2752 2753 //$rep = '<div class="nospan" style="margin-right: 10px">'; 2754 $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, 'object_email.png').' ' : '').$newemail; 2755 //$rep .= '</div>'; 2756 if ($hookmanager) { 2757 $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto); 2758 $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email); 2759 if ($reshook > 0) { 2760 $rep = ''; 2761 } 2762 $rep .= $hookmanager->resPrint; 2763 } 2764 2765 return $rep; 2766} 2767 2768/** 2769 * Get array of social network dictionary 2770 * 2771 * @return array Array of Social Networks Dictionary 2772 */ 2773function getArrayOfSocialNetworks() 2774{ 2775 global $conf, $db; 2776 2777 $socialnetworks = array(); 2778 // Enable caching of array 2779 require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php'; 2780 $cachekey = 'socialnetworks_' . $conf->entity; 2781 $dataretrieved = dol_getcache($cachekey); 2782 if (!is_null($dataretrieved)) { 2783 $socialnetworks = $dataretrieved; 2784 } else { 2785 $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks"; 2786 $sql .= " WHERE entity=".$conf->entity; 2787 $resql = $db->query($sql); 2788 if ($resql) { 2789 while ($obj = $db->fetch_object($resql)) { 2790 $socialnetworks[$obj->code] = array( 2791 'rowid' => $obj->rowid, 2792 'label' => $obj->label, 2793 'url' => $obj->url, 2794 'icon' => $obj->icon, 2795 'active' => $obj->active, 2796 ); 2797 } 2798 } 2799 dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result. 2800 } 2801 2802 return $socialnetworks; 2803} 2804 2805/** 2806 * Show social network link 2807 * 2808 * @param string $value Skype to show (only skype, without 'Name of recipient' before) 2809 * @param int $cid Id of contact if known 2810 * @param int $socid Id of third party if known 2811 * @param string $type 'skype','facebook',... 2812 * @param array $dictsocialnetworks socialnetworks availables 2813 * @return string HTML Link 2814 */ 2815function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array()) 2816{ 2817 global $conf, $user, $langs; 2818 2819 $htmllink = $value; 2820 2821 if (empty($value)) { 2822 return ' '; 2823 } 2824 2825 if (!empty($type)) { 2826 $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">'; 2827 // Use dictionary definition for picto $dictsocialnetworks[$type]['icon'] 2828 $htmllink .= '<span class="fa paddingright '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>'; 2829 if ($type == 'skype') { 2830 $htmllink .= $value; 2831 $htmllink .= ' '; 2832 $htmllink .= '<a href="skype:'; 2833 $htmllink .= $value; 2834 $htmllink .= '?call" alt="'.$langs->trans("Call").' '.$value.'" title="'.$langs->trans("Call").' '.$value.'">'; 2835 $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">'; 2836 $htmllink .= '</a><a href="skype:'; 2837 $htmllink .= $value; 2838 $htmllink .= '?chat" alt="'.$langs->trans("Chat").' '.$value.'" title="'.$langs->trans("Chat").' '.$value.'">'; 2839 $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">'; 2840 $htmllink .= '</a>'; 2841 if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) { 2842 $addlink = 'AC_SKYPE'; 2843 $link = ''; 2844 if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) { 2845 $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>'; 2846 } 2847 $htmllink .= ($link ? ' '.$link : ''); 2848 } 2849 } else { 2850 if (!empty($dictsocialnetworks[$type]['url'])) { 2851 $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']); 2852 $htmllink .= ' <a href="'.$link.'" target="_blank">'.$value.'</a>'; 2853 } else { 2854 $htmllink .= $value; 2855 } 2856 } 2857 $htmllink .= '</div>'; 2858 } else { 2859 $langs->load("errors"); 2860 $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value)); 2861 } 2862 return $htmllink; 2863} 2864 2865/** 2866 * Format phone numbers according to country 2867 * 2868 * @param string $phone Phone number to format 2869 * @param string $countrycode Country code to use for formatting 2870 * @param int $cid Id of contact if known 2871 * @param int $socid Id of third party if known 2872 * @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) 2873 * @param string $separ Separation between numbers for a better visibility example : xx.xx.xx.xx.xx 2874 * @param string $withpicto Show picto 2875 * @param string $titlealt Text to show on alt 2876 * @param int $adddivfloat Add div float around phone. 2877 * @return string Formated phone number 2878 */ 2879function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = " ", $withpicto = '', $titlealt = '', $adddivfloat = 0) 2880{ 2881 global $conf, $user, $langs, $mysoc, $hookmanager; 2882 2883 // Clean phone parameter 2884 $phone = preg_replace("/[\s.-]/", "", trim($phone)); 2885 if (empty($phone)) { 2886 return ''; 2887 } 2888 if (!empty($conf->global->MAIN_PHONE_SEPAR)) { 2889 $separ = $conf->global->MAIN_PHONE_SEPAR; 2890 } 2891 if (empty($countrycode)) { 2892 $countrycode = $mysoc->country_code; 2893 } 2894 2895 // Short format for small screens 2896 if ($conf->dol_optimize_smallscreen) { 2897 $separ = ''; 2898 } 2899 2900 $newphone = $phone; 2901 if (strtoupper($countrycode) == "FR") { 2902 // France 2903 if (dol_strlen($phone) == 10) { 2904 $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); 2905 } elseif (dol_strlen($phone) == 7) { 2906 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2); 2907 } elseif (dol_strlen($phone) == 9) { 2908 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2); 2909 } elseif (dol_strlen($phone) == 11) { 2910 $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); 2911 } elseif (dol_strlen($phone) == 12) { 2912 $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); 2913 } 2914 } elseif (strtoupper($countrycode) == "CA") { 2915 if (dol_strlen($phone) == 10) { 2916 $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4); 2917 } 2918 } elseif (strtoupper($countrycode) == "PT") {//Portugal 2919 if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI 2920 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); 2921 } 2922 } elseif (strtoupper($countrycode) == "SR") {//Suriname 2923 if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF 2924 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3); 2925 } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG 2926 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4); 2927 } 2928 } elseif (strtoupper($countrycode) == "DE") {//Allemagne 2929 if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK 2930 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3); 2931 } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ 2932 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3); 2933 } 2934 } elseif (strtoupper($countrycode) == "ES") {//Espagne 2935 if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI 2936 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); 2937 } 2938 } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso 2939 if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI 2940 $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); 2941 } 2942 } elseif (strtoupper($countrycode) == "RO") {// Roumanie 2943 if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI 2944 $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); 2945 } 2946 } elseif (strtoupper($countrycode) == "TR") {//Turquie 2947 if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ 2948 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4); 2949 } 2950 } elseif (strtoupper($countrycode) == "US") {//Etat-Unis 2951 if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ 2952 $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4); 2953 } 2954 } elseif (strtoupper($countrycode) == "MX") {//Mexique 2955 if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI 2956 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2); 2957 } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH 2958 $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); 2959 } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ 2960 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4); 2961 } 2962 } elseif (strtoupper($countrycode) == "ML") {//Mali 2963 if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH 2964 $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); 2965 } 2966 } elseif (strtoupper($countrycode) == "TH") {//Thaïlande 2967 if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH 2968 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3); 2969 } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI 2970 $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); 2971 } 2972 } elseif (strtoupper($countrycode) == "MU") { 2973 //Maurice 2974 if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG 2975 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2); 2976 } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH 2977 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2); 2978 } 2979 } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud 2980 if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI 2981 $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); 2982 } 2983 } elseif (strtoupper($countrycode) == "SY") {//Syrie 2984 if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH 2985 $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); 2986 } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI 2987 $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); 2988 } 2989 } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis 2990 if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH 2991 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2); 2992 } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI 2993 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); 2994 } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK 2995 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4); 2996 } 2997 } elseif (strtoupper($countrycode) == "DZ") {//Algérie 2998 if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI 2999 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); 3000 } 3001 } elseif (strtoupper($countrycode) == "BE") {//Belgique 3002 if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH 3003 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3); 3004 } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI 3005 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); 3006 } 3007 } elseif (strtoupper($countrycode) == "PF") {//Polynésie française 3008 if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH 3009 $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); 3010 } 3011 } elseif (strtoupper($countrycode) == "CO") {//Colombie 3012 if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ 3013 $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); 3014 } 3015 } elseif (strtoupper($countrycode) == "JO") {//Jordanie 3016 if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH 3017 $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); 3018 } 3019 } elseif (strtoupper($countrycode) == "JM") {//Jamaïque 3020 if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG 3021 $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4); 3022 } 3023 } elseif (strtoupper($countrycode) == "MG") {//Madagascar 3024 if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EF_GHI 3025 $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); 3026 } 3027 } elseif (strtoupper($countrycode) == "GB") {//Royaume uni 3028 if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ 3029 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); 3030 } 3031 } elseif (strtoupper($countrycode) == "CH") {//Suisse 3032 if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI 3033 $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); 3034 } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL 3035 $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); 3036 } 3037 } elseif (strtoupper($countrycode) == "TN") {//Tunisie 3038 if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH 3039 $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); 3040 } 3041 } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise 3042 if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau) 3043 $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); 3044 } 3045 } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe 3046 if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau) 3047 $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); 3048 } 3049 } elseif (strtoupper($countrycode) == "MQ") {//Martinique 3050 if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau) 3051 $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); 3052 } 3053 } elseif (strtoupper($countrycode) == "IT") {//Italie 3054 if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI 3055 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); 3056 } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ 3057 $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); 3058 } 3059 } elseif (strtoupper($countrycode) == "AU") { 3060 //Australie 3061 if (dol_strlen($phone) == 12) { 3062 //ex: +61_A_BCDE_FGHI 3063 $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4); 3064 } 3065 } 3066 if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set) 3067 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 3068 $newphoneform = $newphone; 3069 $newphone = '<a href="tel:'.$phone.'"'; 3070 $newphone .= '>'.$newphoneform.'</a>'; 3071 } elseif (!empty($conf->clicktodial->enabled) && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url 3072 if (empty($user->clicktodial_loaded)) { 3073 $user->fetch_clicktodial(); 3074 } 3075 3076 // Define urlmask 3077 $urlmask = 'ErrorClickToDialModuleNotConfigured'; 3078 if (!empty($conf->global->CLICKTODIAL_URL)) { 3079 $urlmask = $conf->global->CLICKTODIAL_URL; 3080 } 3081 if (!empty($user->clicktodial_url)) { 3082 $urlmask = $user->clicktodial_url; 3083 } 3084 3085 $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : ''); 3086 $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : ''); 3087 $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : ''); 3088 // This line is for backward compatibility 3089 $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password); 3090 // Thoose lines are for substitution 3091 $substitarray = array('__PHONEFROM__'=>$clicktodial_poste, 3092 '__PHONETO__'=>urlencode($phone), 3093 '__LOGIN__'=>$clicktodial_login, 3094 '__PASS__'=>$clicktodial_password); 3095 $url = make_substitutions($url, $substitarray); 3096 $newphonesav = $newphone; 3097 $newphone = '<a href="'.$url.'"'; 3098 if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) { 3099 $newphone .= ' target="_blank"'; 3100 } 3101 $newphone .= '>'.$newphonesav.'</a>'; 3102 } 3103 3104 //if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) 3105 if (!empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) { 3106 $type = 'AC_TEL'; 3107 $link = ''; 3108 if ($addlink == 'AC_FAX') { 3109 $type = 'AC_FAX'; 3110 } 3111 if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) { 3112 $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>'; 3113 } 3114 if ($link) { 3115 $newphone = '<div>'.$newphone.' '.$link.'</div>'; 3116 } 3117 } 3118 } 3119 3120 if (empty($titlealt)) { 3121 $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone")); 3122 } 3123 $rep = ''; 3124 3125 if ($hookmanager) { 3126 $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto); 3127 $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone); 3128 $rep .= $hookmanager->resPrint; 3129 } 3130 if (empty($reshook)) { 3131 $picto = ''; 3132 if ($withpicto) { 3133 if ($withpicto == 'fax') { 3134 $picto = 'phoning_fax'; 3135 } elseif ($withpicto == 'phone') { 3136 $picto = 'phoning'; 3137 } elseif ($withpicto == 'mobile') { 3138 $picto = 'phoning_mobile'; 3139 } else { 3140 $picto = ''; 3141 } 3142 } 3143 if ($adddivfloat) { 3144 $rep .= '<div class="nospan float" style="margin-right: 10px">'; 3145 } else { 3146 $rep .= '<span style="margin-right: 10px;">'; 3147 } 3148 $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone; 3149 if ($adddivfloat) { 3150 $rep .= '</div>'; 3151 } else { 3152 $rep .= '</span>'; 3153 } 3154 } 3155 3156 return $rep; 3157} 3158 3159/** 3160 * Return an IP formated to be shown on screen 3161 * 3162 * @param string $ip IP 3163 * @param int $mode 0=return IP + country/flag, 1=return only country/flag, 2=return only IP 3164 * @return string Formated IP, with country if GeoIP module is enabled 3165 */ 3166function dol_print_ip($ip, $mode = 0) 3167{ 3168 global $conf, $langs; 3169 3170 $ret = ''; 3171 3172 if (empty($mode)) { 3173 $ret .= $ip; 3174 } 3175 3176 if ($mode != 2) { 3177 $countrycode = dolGetCountryCodeFromIp($ip); 3178 if ($countrycode) { // If success, countrycode is us, fr, ... 3179 if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) { 3180 $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1); 3181 } else { 3182 $ret .= ' ('.$countrycode.')'; 3183 } 3184 } else { 3185 // Nothing 3186 } 3187 } 3188 3189 return $ret; 3190} 3191 3192/** 3193 * Return the IP of remote user. 3194 * Take HTTP_X_FORWARDED_FOR (defined when using proxy) 3195 * Then HTTP_CLIENT_IP if defined (rare) 3196 * Then REMOTE_ADDR (no way to be modified by user but may be wrong if user is using a proxy) 3197 * 3198 * @return string Ip of remote user. 3199 */ 3200function getUserRemoteIP() 3201{ 3202 if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) { 3203 if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) { 3204 if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) { 3205 $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client 3206 } else { 3207 $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client 3208 } 3209 } else { 3210 $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy 3211 } 3212 } else { 3213 $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy 3214 } 3215 return $ip; 3216} 3217 3218/** 3219 * Return if we are using a HTTPS connexion 3220 * Check HTTPS (no way to be modified by user but may be empty or wrong if user is using a proxy) 3221 * Take HTTP_X_FORWARDED_PROTO (defined when using proxy) 3222 * Then HTTP_X_FORWARDED_SSL 3223 * 3224 * @return boolean True if user is using HTTPS 3225 */ 3226function isHTTPS() 3227{ 3228 $isSecure = false; 3229 if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { 3230 $isSecure = true; 3231 } 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') { 3232 $isSecure = true; 3233 } 3234 return $isSecure; 3235} 3236 3237/** 3238 * Return a country code from IP. Empty string if not found. 3239 * 3240 * @param string $ip IP 3241 * @return string Country code ('us', 'fr', ...) 3242 */ 3243function dolGetCountryCodeFromIp($ip) 3244{ 3245 global $conf; 3246 3247 $countrycode = ''; 3248 3249 if (!empty($conf->geoipmaxmind->enabled)) { 3250 $datafile = $conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE; 3251 //$ip='24.24.24.24'; 3252 //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages) 3253 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php'; 3254 $geoip = new DolGeoIP('country', $datafile); 3255 //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n"; 3256 $countrycode = $geoip->getCountryCodeFromIP($ip); 3257 } 3258 3259 return $countrycode; 3260} 3261 3262 3263/** 3264 * Return country code for current user. 3265 * If software is used inside a local network, detection may fails (we need a public ip) 3266 * 3267 * @return string Country code (fr, es, it, us, ...) 3268 */ 3269function dol_user_country() 3270{ 3271 global $conf, $langs, $user; 3272 3273 //$ret=$user->xxx; 3274 $ret = ''; 3275 if (!empty($conf->geoipmaxmind->enabled)) { 3276 $ip = getUserRemoteIP(); 3277 $datafile = $conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE; 3278 //$ip='24.24.24.24'; 3279 //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat'; 3280 include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php'; 3281 $geoip = new DolGeoIP('country', $datafile); 3282 $countrycode = $geoip->getCountryCodeFromIP($ip); 3283 $ret = $countrycode; 3284 } 3285 return $ret; 3286} 3287 3288/** 3289 * Format address string 3290 * 3291 * @param string $address Address string, already formatted with dol_format_address() 3292 * @param int $htmlid Html ID (for example 'gmap') 3293 * @param int $element 'thirdparty'|'contact'|'member'|'other' 3294 * @param int $id Id of object 3295 * @param int $noprint No output. Result is the function return 3296 * @param string $charfornl Char to use instead of nl2br. '' means we use a standad nl2br. 3297 * @return string|void Nothing if noprint is 0, formatted address if noprint is 1 3298 * @see dol_format_address() 3299 */ 3300function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '') 3301{ 3302 global $conf, $user, $langs, $hookmanager; 3303 3304 $out = ''; 3305 3306 if ($address) { 3307 if ($hookmanager) { 3308 $parameters = array('element' => $element, 'id' => $id); 3309 $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address); 3310 $out .= $hookmanager->resPrint; 3311 } 3312 if (empty($reshook)) { 3313 if (empty($charfornl)) { 3314 $out .= nl2br($address); 3315 } else { 3316 $out .= preg_replace('/[\r\n]+/', $charfornl, $address); 3317 } 3318 3319 // TODO Remove this block, we can add this using the hook now 3320 $showgmap = $showomap = 0; 3321 if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) { 3322 $showgmap = 1; 3323 } 3324 if ($element == 'contact' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) { 3325 $showgmap = 1; 3326 } 3327 if ($element == 'member' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) { 3328 $showgmap = 1; 3329 } 3330 if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) { 3331 $showomap = 1; 3332 } 3333 if ($element == 'contact' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) { 3334 $showomap = 1; 3335 } 3336 if ($element == 'member' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) { 3337 $showomap = 1; 3338 } 3339 if ($showgmap) { 3340 $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1); 3341 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>'; 3342 } 3343 if ($showomap) { 3344 $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1); 3345 $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>'; 3346 } 3347 } 3348 } 3349 if ($noprint) { 3350 return $out; 3351 } else { 3352 print $out; 3353 } 3354} 3355 3356 3357/** 3358 * Return true if email syntax is ok. 3359 * 3360 * @param string $address email (Ex: "toto@examle.com". Long form "John Do <johndo@example.com>" will be false) 3361 * @param int $acceptsupervisorkey If 1, the special string '__SUPERVISOREMAIL__' is also accepted as valid 3362 * @return boolean true if email syntax is OK, false if KO or empty string 3363 * @see isValidMXRecord() 3364 */ 3365function isValidEmail($address, $acceptsupervisorkey = 0) 3366{ 3367 if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') { 3368 return true; 3369 } 3370 if (filter_var($address, FILTER_VALIDATE_EMAIL)) { 3371 return true; 3372 } 3373 3374 return false; 3375} 3376 3377/** 3378 * Return if the domain name has a valid MX record. 3379 * WARNING: This need function idn_to_ascii, checkdnsrr and getmxrr 3380 * 3381 * @param string $domain Domain name (Ex: "yahoo.com", "yhaoo.com", "dolibarr.fr") 3382 * @return int -1 if error (function not available), 0=Not valid, 1=Valid 3383 * @see isValidEmail() 3384 */ 3385function isValidMXRecord($domain) 3386{ 3387 if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) { 3388 if (!checkdnsrr(idn_to_ascii($domain), 'MX')) { 3389 return 0; 3390 } 3391 if (function_exists('getmxrr')) { 3392 $mxhosts = array(); 3393 $weight = array(); 3394 getmxrr(idn_to_ascii($domain), $mxhosts, $weight); 3395 if (count($mxhosts) > 1) { 3396 return 1; 3397 } 3398 if (count($mxhosts) == 1 && !empty($mxhosts[0])) { 3399 return 1; 3400 } 3401 3402 return 0; 3403 } 3404 } 3405 return -1; 3406} 3407 3408/** 3409 * Return true if phone number syntax is ok 3410 * TODO Decide what to do with this 3411 * 3412 * @param string $phone phone (Ex: "0601010101") 3413 * @return boolean true if phone syntax is OK, false if KO or empty string 3414 */ 3415function isValidPhone($phone) 3416{ 3417 return true; 3418} 3419 3420 3421/** 3422 * Make a strlen call. Works even if mbstring module not enabled 3423 * 3424 * @param string $string String to calculate length 3425 * @param string $stringencoding Encoding of string 3426 * @return int Length of string 3427 */ 3428function dol_strlen($string, $stringencoding = 'UTF-8') 3429{ 3430 if (function_exists('mb_strlen')) { 3431 return mb_strlen($string, $stringencoding); 3432 } else { 3433 return strlen($string); 3434 } 3435} 3436 3437/** 3438 * Make a substring. Works even if mbstring module is not enabled for better compatibility. 3439 * 3440 * @param string $string String to scan 3441 * @param string $start Start position 3442 * @param int $length Length (in nb of characters or nb of bytes depending on trunconbytes param) 3443 * @param string $stringencoding Page code used for input string encoding 3444 * @param int $trunconbytes 1=Length is max of bytes instead of max of characters 3445 * @return string substring 3446 */ 3447function dol_substr($string, $start, $length, $stringencoding = '', $trunconbytes = 0) 3448{ 3449 global $langs; 3450 3451 if (empty($stringencoding)) { 3452 $stringencoding = $langs->charset_output; 3453 } 3454 3455 $ret = ''; 3456 if (empty($trunconbytes)) { 3457 if (function_exists('mb_substr')) { 3458 $ret = mb_substr($string, $start, $length, $stringencoding); 3459 } else { 3460 $ret = substr($string, $start, $length); 3461 } 3462 } else { 3463 if (function_exists('mb_strcut')) { 3464 $ret = mb_strcut($string, $start, $length, $stringencoding); 3465 } else { 3466 $ret = substr($string, $start, $length); 3467 } 3468 } 3469 return $ret; 3470} 3471 3472 3473/** 3474 * Truncate a string to a particular length adding '…' if string larger than length. 3475 * If length = max length+1, we do no truncate to avoid having just 1 char replaced with '…'. 3476 * MAIN_DISABLE_TRUNC=1 can disable all truncings 3477 * 3478 * @param string $string String to truncate 3479 * @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 so it does not worse to replace with ...) 3480 * @param string $trunc Where to trunc: 'right', 'left', 'middle' (size must be a 2 power), 'wrap' 3481 * @param string $stringencoding Tell what is source string encoding 3482 * @param int $nodot Truncation do not add … after truncation. So it's an exact truncation. 3483 * @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) 3484 * @return string Truncated string. WARNING: length is never higher than $size if $nodot is set, but can be 3 chars higher otherwise. 3485 */ 3486function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0) 3487{ 3488 global $conf; 3489 3490 if (empty($size) || !empty($conf->global->MAIN_DISABLE_TRUNC)) { 3491 return $string; 3492 } 3493 3494 if (empty($stringencoding)) { 3495 $stringencoding = 'UTF-8'; 3496 } 3497 // reduce for small screen 3498 if ($conf->dol_optimize_smallscreen == 1 && $display == 1) { 3499 $size = round($size / 3); 3500 } 3501 3502 // We go always here 3503 if ($trunc == 'right') { 3504 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string; 3505 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) { 3506 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add … 3507 return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…'); 3508 } else { 3509 //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string; 3510 return $string; 3511 } 3512 } elseif ($trunc == 'middle') { 3513 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string; 3514 if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) { 3515 $size1 = round($size / 2); 3516 $size2 = round($size / 2); 3517 return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding); 3518 } else { 3519 return $string; 3520 } 3521 } elseif ($trunc == 'left') { 3522 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string; 3523 if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) { 3524 // If nodot is 0 and size is 1 chars more, we don't trunc and don't add … 3525 return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding); 3526 } else { 3527 return $string; 3528 } 3529 } elseif ($trunc == 'wrap') { 3530 $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string; 3531 if (dol_strlen($newstring, $stringencoding) > ($size + 1)) { 3532 return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc); 3533 } else { 3534 return $string; 3535 } 3536 } else { 3537 return 'BadParam3CallingDolTrunc'; 3538 } 3539} 3540 3541/** 3542 * Show picto whatever it's its name (generic function) 3543 * 3544 * @param string $titlealt Text on title tag for tooltip. Not used if param notitle is set to 1. 3545 * @param string $picto Name of image file to show ('filenew', ...) 3546 * If no extension provided, we use '.png'. Image must be stored into theme/xxx/img directory. 3547 * Example: picto.png if picto.png is stored into htdocs/theme/mytheme/img 3548 * Example: picto.png@mymodule if picto.png is stored into htdocs/mymodule/img 3549 * Example: /mydir/mysubdir/picto.png if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1) 3550 * @param string $moreatt Add more attribute on img tag (For example 'class="pictofixedwidth"') 3551 * @param boolean|int $pictoisfullpath If true or 1, image path is a full path 3552 * @param int $srconly Return only content of the src attribute of img. 3553 * @param int $notitle 1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip. 3554 * @param string $alt Force alt for bind people 3555 * @param string $morecss Add more class css on img tag (For example 'myclascss'). 3556 * @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. 3557 * @return string Return img tag 3558 * @see img_object(), img_picto_common() 3559 */ 3560function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2) 3561{ 3562 global $conf, $langs; 3563 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto 3564 $url = DOL_URL_ROOT; 3565 $theme = isset($conf->theme) ? $conf->theme : null; 3566 $path = 'theme/'.$theme; 3567 // Define fullpathpicto to use into src 3568 if ($pictoisfullpath) { 3569 // Clean parameters 3570 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) { 3571 $picto .= '.png'; 3572 } 3573 $fullpathpicto = $picto; 3574 $reg = array(); 3575 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) { 3576 $morecss .= ($morecss ? ' ' : '').$reg[1]; 3577 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt); 3578 } 3579 } else { 3580 $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto); 3581 $pictowithouttext = str_replace('object_', '', $pictowithouttext); 3582 if (empty($srconly) && in_array($pictowithouttext, array( 3583 '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected', 3584 'accountancy', 'account', 'accountline', 'action', 'add', 'address', 'angle-double-down', 'angle-double-up', 'asset', 3585 'bank_account', 'barcode', 'bank', 'bill', 'billa', 'billr', 'billd', 'bookmark', 'bom', 'bug', 'building', 3586 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype', 3587 'cash-register', 'category', 'chart', 'check', 'clock', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cubes', 3588 'multicurrency', 3589 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice', 3590 'edit', 'ellipsis-h', 'email', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 3591 'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 3592 'generate', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group', 3593 'help', 'holiday', 3594 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'knowledgemanagement', 3595 'label', 'language', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lot', 'long-arrow-alt-right', 3596 'margin', 'map-marker-alt', 'member', 'meeting', 'money-bill-alt', 'movement', 'mrp', 'note', 'next', 3597 'off', 'on', 'order', 3598 'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'stock', 'resize', 'service', 'stats', 'trip', 3599 'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'tools', 'unlink', 'uparrow', 'user', 'vcard', 'wrench', 3600 'github', 'jabber', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp', 3601 'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies', 3602 'generic', 'home', 'hrm', 'members', 'products', 'invoicing', 3603 'partnership', 'payment', 'pencil-ruler', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region', 3604 'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced', 3605 'technic', 'ticket', 3606 'error', 'warning', 3607 'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'resource', 3608 'shapes', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice', 3609 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 3610 'uncheck', 'user-cog', 'website', 'workstation', 3611 'conferenceorbooth', 'eventorganization' 3612 ))) { 3613 $fakey = $pictowithouttext; 3614 $facolor = ''; 3615 $fasize = ''; 3616 $fa = 'fas'; 3617 if (in_array($pictowithouttext, array('clock', 'establishment', 'generic', 'minus-square', 'object_generic', 'pdf', 'plus-square', 'timespent', 'note', 'off', 'on', 'object_bookmark', 'bookmark', 'vcard'))) { 3618 $fa = 'far'; 3619 } 3620 if (in_array($pictowithouttext, array('black-tie', 'github', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) { 3621 $fa = 'fab'; 3622 } 3623 3624 $arrayconvpictotofa = array( 3625 'account'=>'university', 'accountline'=>'receipt', 'accountancy'=>'search-dollar', 'action'=>'calendar-alt', 'add'=>'plus-circle', 'address'=> 'address-book', 'asset'=>'money-check-alt', 'autofill'=>'fill', 3626 'bank_account'=>'university', 3627 'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'billd'=>'file-medical', 3628 'supplier_invoice'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'supplier_invoicer'=>'file-invoice-dollar', 'supplier_invoiced'=>'file-medical', 3629 'bom'=>'shapes', 3630 'chart'=>'chart-line', 'company'=>'building', 'contact'=>'address-book', 'contract'=>'suitcase', 'collab'=>'people-arrows', 'conversation'=>'comments', 'country'=>'globe-americas', 'cron'=>'business-time', 3631 'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd', 3632 'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins', 3633 'accounting'=>'search-dollar', 'category'=>'tag', 'dollyrevert'=>'dolly', 3634 'generate'=>'plus-square', 'hrm'=>'user-tie', 'incoterm'=>'truck-loading', 3635 'margin'=>'calculator', 'members'=>'user-friends', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode', 3636 'email'=>'at', 'establishment'=>'building', 3637 'edit'=>'pencil-alt', 'graph'=>'chart-line', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle', 3638 'generic'=>'file', 'holiday'=>'umbrella-beach', 3639 'info'=>'info-circle', 'inventory'=>'boxes', 'intracommreport'=>'globe-europe', 'knowledgemanagement'=>'ticket-alt', 'label'=>'layer-group', 'loan'=>'money-bill-alt', 3640 'member'=>'user-alt', 'meeting'=>'chalkboard-teacher', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right', 3641 'trip'=>'wallet', 'expensereport'=>'wallet', 'group'=>'users', 'movement'=>'people-carry', 3642 'sign-out'=>'sign-out-alt', 3643 'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star', 'bookmark'=>'star', 3644 'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'edit'=>'pencil-alt', 'filter'=>'filter', 3645 'list-alt'=>'list-alt', 'calendar'=>'calendar-alt', 'calendarmonth'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarmonth'=>'calendar-alt', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table', 3646 'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice', 3647 'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle', 3648 'other'=>'square', 3649 'playdisabled'=>'play', 'pdf'=>'file-pdf', 'poll'=>'check-double', 'pos'=>'cash-register', 'preview'=>'binoculars', 'project'=>'project-diagram', 'projectpub'=>'project-diagram', 'projecttask'=>'tasks', 'propal'=>'file-signature', 3650 'partnership'=>'handshake', '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', 3651 'recent' => 'question', 'reception'=>'dolly', 'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge', 3652 'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature', 3653 'refresh'=>'redo', 'region'=>'map-marked', 'resource'=>'laptop-house', 3654 'state'=>'map-marked-alt', 'security'=>'key', 'salary'=>'wallet', 'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'stripe'=>'stripe-s', 3655 'supplier'=>'building', 'supplier_invoice'=>'file-invoice-dollar', 'technic'=>'cogs', 'ticket'=>'ticket-alt', 3656 'timespent'=>'clock', 'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach', 3657 'title_agenda'=>'calendar-alt', 3658 'uncheck'=>'times', 'uparrow'=>'share', 'vcard'=>'address-card', 3659 'jabber'=>'comment-o', 3660 'website'=>'globe-americas', 'workstation'=>'pallet', 3661 'conferenceorbooth'=>'chalkboard-teacher', 'eventorganization'=>'project-diagram' 3662 ); 3663 if ($pictowithouttext == 'off') { 3664 $fakey = 'fa-square'; 3665 $fasize = '1.3em'; 3666 } elseif ($pictowithouttext == 'on') { 3667 $fakey = 'fa-check-square'; 3668 $fasize = '1.3em'; 3669 } elseif ($pictowithouttext == 'listlight') { 3670 $fakey = 'fa-download'; 3671 $marginleftonlyshort = 1; 3672 } elseif ($pictowithouttext == 'printer') { 3673 $fakey = 'fa-print'; 3674 $fasize = '1.2em'; 3675 } elseif ($pictowithouttext == 'note') { 3676 $fakey = 'fa-sticky-note'; 3677 $marginleftonlyshort = 1; 3678 } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) { 3679 $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'); 3680 $fakey = 'fa-'.$convertarray[$pictowithouttext]; 3681 if (preg_match('/selected/', $pictowithouttext)) { 3682 $facolor = '#888'; 3683 } 3684 $marginleftonlyshort = 1; 3685 } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) { 3686 $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext]; 3687 } else { 3688 $fakey = 'fa-'.$pictowithouttext; 3689 } 3690 3691 if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment'))) { 3692 $morecss .= ' em092'; 3693 } 3694 if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) { 3695 $morecss .= ' em088'; 3696 } 3697 if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) { 3698 $morecss .= ' em080'; 3699 } 3700 3701 // Define $marginleftonlyshort 3702 $arrayconvpictotomarginleftonly = array( 3703 'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber', 3704 'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_off', 3705 'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected' 3706 ); 3707 if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) { 3708 $marginleftonlyshort = 0; 3709 } 3710 3711 // Add CSS 3712 $arrayconvpictotomorcess = array( 3713 'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account', 'asset'=>'infobox-bank_account', 3714 'bank_account'=>'bg-infobox-bank_account', 3715 'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billr'=>'infobox-commande', 'billd'=>'infobox-commande', 3716 'margin'=>'infobox-bank_account', 'conferenceorbooth'=>'infobox-project', 3717 'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'collab'=>'infobox-action', 'conversation'=>'infobox-contrat', 3718 'donation'=>'infobox-commande', 'dolly'=>'infobox-commande', 'dollyrevert'=>'flip infobox-order_supplier', 3719 'ecm'=>'infobox-action', 'eventorganization'=>'infobox-project', 3720 'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat', 3721 'incoterm'=>'infobox-supplier_proposal', 3722 'multicurrency'=>'infobox-bank_account', 3723 'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account', 3724 'order'=>'infobox-commande', 3725 'user'=>'infobox-adherent', 'users'=>'infobox-adherent', 3726 'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 3727 'holiday'=>'infobox-holiday', 'info'=>'opacityhigh', 'invoice'=>'infobox-commande', 3728 'knowledgemanagement'=>'infobox-contrat rotate90', 'loan'=>'infobox-bank_account', 3729 'payment'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'pos'=>'infobox-bank_account', 'project'=>'infobox-project', 'projecttask'=>'infobox-project', 'propal'=>'infobox-propal', 3730 'reception'=>'flip', 'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent', 3731 'resource'=>'infobox-action', 3732 'salary'=>'infobox-bank_account', 'shipment'=>'infobox-commande', 'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier', 3733 'supplier'=>'infobox-order_supplier', 'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal', 3734 'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'expensereport'=>'infobox-expensereport', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action', 3735 //'title_setup'=>'infobox-action', 'tools'=>'infobox-action', 3736 'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode' 3737 ); 3738 if (!empty($arrayconvpictotomorcess[$pictowithouttext])) { 3739 $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext]; 3740 } 3741 3742 // Define $color 3743 $arrayconvpictotocolor = array( 3744 'address'=>'#6c6aa8', 'building'=>'#6c6aa8', 'bom'=>'#a69944', 3745 'cog'=>'#999', 'companies'=>'#6c6aa8', 'company'=>'#6c6aa8', 'contact'=>'#6c6aa8', 'cron'=>'#555', 3746 'dynamicprice'=>'#a69944', 3747 'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999', 'language'=>'#555', 3748 //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944', 3749 'lot'=>'#a69944', 3750 'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'inventory'=>'#a69944', 'stock'=>'#a69944', 'movement'=>'#a69944', 3751 'other'=>'#ddd', 3752 'partnership'=>'#6c6aa8', 'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'reception'=>'#a69944', 'resize'=>'#444', 'rss'=>'#cba', 3753 //'shipment'=>'#a69944', 3754 'security'=>'#999', 'stats'=>'#444', 'switch_off'=>'#999', 'technic'=>'#999', 'timespent'=>'#555', 3755 'uncheck'=>'#800', 'uparrow'=>'#555', 'user-cog'=>'#999', 'country'=>'#aaa', 'globe-americas'=>'#aaa', 'region'=>'#aaa', 'state'=>'#aaa', 3756 'website'=>'#304', 'workstation'=>'#a69944' 3757 ); 3758 if (isset($arrayconvpictotocolor[$pictowithouttext])) { 3759 $facolor = $arrayconvpictotocolor[$pictowithouttext]; 3760 } 3761 3762 // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only. 3763 // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes. 3764 $morestyle = ''; 3765 $reg = array(); 3766 if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) { 3767 $morecss .= ($morecss ? ' ' : '').$reg[1]; 3768 $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt); 3769 } 3770 if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) { 3771 $morestyle = $reg[1]; 3772 $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt); 3773 } 3774 $moreatt = trim($moreatt); 3775 3776 $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : ''); 3777 $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>'; 3778 /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { 3779 $enabledisablehtml .= $titlealt; 3780 }*/ 3781 $enabledisablehtml .= '</span>'; 3782 3783 return $enabledisablehtml; 3784 } 3785 3786 if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) { 3787 $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module 3788 } elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) { 3789 $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 3790 } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) { 3791 $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module 3792 } 3793 3794 // If we ask an image into $url/$mymodule/img (instead of default path) 3795 $regs = array(); 3796 if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) { 3797 $picto = $regs[1]; 3798 $path = $regs[2]; // $path is $mymodule 3799 } 3800 3801 // Clean parameters 3802 if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) { 3803 $picto .= '.png'; 3804 } 3805 // If alt path are defined, define url where img file is, according to physical path 3806 // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...) 3807 foreach ($conf->file->dol_document_root as $type => $dirroot) { 3808 if ($type == 'main') { 3809 continue; 3810 } 3811 // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded 3812 if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) { 3813 $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type]; 3814 break; 3815 } 3816 } 3817 3818 // $url is '' or '/custom', $path is current theme or 3819 $fullpathpicto = $url.'/'.$path.'/img/'.$picto; 3820 } 3821 3822 if ($srconly) { 3823 return $fullpathpicto; 3824 } 3825 // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people 3826 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 3827} 3828 3829/** 3830 * Show a picto called object_picto (generic function) 3831 * 3832 * @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. 3833 * @param string $picto Name of image to show object_picto (example: user, group, action, bill, contract, propal, product, ...) 3834 * For external modules use imagename@mymodule to search into directory "img" of module. 3835 * @param string $moreatt Add more attribute on img tag (ie: class="datecallink") 3836 * @param int $pictoisfullpath If 1, image path is a full path 3837 * @param int $srconly Return only content of the src attribute of img. 3838 * @param int $notitle 1=Disable tag title. Use it if you add js tooltip, to avoid duplicate tooltip. 3839 * @return string Return img tag 3840 * @see img_picto(), img_picto_common() 3841 */ 3842function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0) 3843{ 3844 if (strpos($picto, '^') === 0) { 3845 return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle); 3846 } else { 3847 return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle); 3848 } 3849} 3850 3851/** 3852 * Show weather picto 3853 * 3854 * @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. 3855 * @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). 3856 * @param string $moreatt Add more attribute on img tag 3857 * @param int $pictoisfullpath If 1, image path is a full path 3858 * @param string $morecss More CSS 3859 * @return string Return img tag 3860 * @see img_object(), img_picto() 3861 */ 3862function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '') 3863{ 3864 global $conf; 3865 3866 if (is_numeric($picto)) { 3867 //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png'); 3868 //$picto = $leveltopicto[$picto]; 3869 return '<i class="fa fa-weather-level'.$picto.'"></i>'; 3870 } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) { 3871 $picto .= '.png'; 3872 } 3873 3874 $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto; 3875 3876 return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss); 3877} 3878 3879/** 3880 * Show picto (generic function) 3881 * 3882 * @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. 3883 * @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. 3884 * @param string $moreatt Add more attribute on img tag 3885 * @param int $pictoisfullpath If 1, image path is a full path 3886 * @return string Return img tag 3887 * @see img_object(), img_picto() 3888 */ 3889function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0) 3890{ 3891 global $conf; 3892 3893 if (!preg_match('/(\.png|\.gif)$/i', $picto)) { 3894 $picto .= '.png'; 3895 } 3896 3897 if ($pictoisfullpath) { 3898 $path = $picto; 3899 } else { 3900 $path = DOL_URL_ROOT.'/theme/common/'.$picto; 3901 3902 if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) { 3903 $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto; 3904 3905 if (file_exists($themepath)) { 3906 $path = $themepath; 3907 } 3908 } 3909 } 3910 3911 return img_picto($titlealt, $path, $moreatt, 1); 3912} 3913 3914/** 3915 * Show logo action 3916 * 3917 * @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. 3918 * @param string $numaction Action id or code to show 3919 * @param string $picto Name of image file to show ('filenew', ...) 3920 * If no extension provided, we use '.png'. Image must be stored into theme/xxx/img directory. 3921 * Example: picto.png if picto.png is stored into htdocs/theme/mytheme/img 3922 * Example: picto.png@mymodule if picto.png is stored into htdocs/mymodule/img 3923 * Example: /mydir/mysubdir/picto.png if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1) 3924 * @return string Return an img tag 3925 */ 3926function img_action($titlealt, $numaction, $picto = '') 3927{ 3928 global $langs; 3929 3930 if (empty($titlealt) || $titlealt == 'default') { 3931 if ($numaction == '-1' || $numaction == 'ST_NO') { 3932 $numaction = -1; 3933 $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact'); 3934 } elseif ($numaction == '0' || $numaction == 'ST_NEVER') { 3935 $numaction = 0; 3936 $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted'); 3937 } elseif ($numaction == '1' || $numaction == 'ST_TODO') { 3938 $numaction = 1; 3939 $titlealt = $langs->transnoentitiesnoconv('ChangeToContact'); 3940 } elseif ($numaction == '2' || $numaction == 'ST_PEND') { 3941 $numaction = 2; 3942 $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess'); 3943 } elseif ($numaction == '3' || $numaction == 'ST_DONE') { 3944 $numaction = 3; 3945 $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone'); 3946 } else { 3947 $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction); 3948 $numaction = 0; 3949 } 3950 } 3951 if (!is_numeric($numaction)) { 3952 $numaction = 0; 3953 } 3954 3955 return img_picto($titlealt, !empty($picto) ? $picto : 'stcomm'.$numaction.'.png'); 3956} 3957 3958/** 3959 * Show pdf logo 3960 * 3961 * @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. 3962 * @param int $size Taille de l'icone : 3 = 16x16px , 2 = 14x14px 3963 * @return string Retourne tag img 3964 */ 3965function img_pdf($titlealt = 'default', $size = 3) 3966{ 3967 global $langs; 3968 3969 if ($titlealt == 'default') { 3970 $titlealt = $langs->trans('Show'); 3971 } 3972 3973 return img_picto($titlealt, 'pdf'.$size.'.png'); 3974} 3975 3976/** 3977 * Show logo + 3978 * 3979 * @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. 3980 * @param string $other Add more attributes on img 3981 * @return string Return tag img 3982 */ 3983function img_edit_add($titlealt = 'default', $other = '') 3984{ 3985 global $langs; 3986 3987 if ($titlealt == 'default') { 3988 $titlealt = $langs->trans('Add'); 3989 } 3990 3991 return img_picto($titlealt, 'edit_add.png', $other); 3992} 3993/** 3994 * Show logo - 3995 * 3996 * @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. 3997 * @param string $other Add more attributes on img 3998 * @return string Return tag img 3999 */ 4000function img_edit_remove($titlealt = 'default', $other = '') 4001{ 4002 global $langs; 4003 4004 if ($titlealt == 'default') { 4005 $titlealt = $langs->trans('Remove'); 4006 } 4007 4008 return img_picto($titlealt, 'edit_remove.png', $other); 4009} 4010 4011/** 4012 * Show logo editer/modifier fiche 4013 * 4014 * @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. 4015 * @param integer $float If you have to put the style "float: right" 4016 * @param string $other Add more attributes on img 4017 * @return string Return tag img 4018 */ 4019function img_edit($titlealt = 'default', $float = 0, $other = '') 4020{ 4021 global $langs; 4022 4023 if ($titlealt == 'default') { 4024 $titlealt = $langs->trans('Modify'); 4025 } 4026 4027 return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : '')); 4028} 4029 4030/** 4031 * Show logo view card 4032 * 4033 * @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. 4034 * @param integer $float If you have to put the style "float: right" 4035 * @param string $other Add more attributes on img 4036 * @return string Return tag img 4037 */ 4038function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"') 4039{ 4040 global $langs; 4041 4042 if ($titlealt == 'default') { 4043 $titlealt = $langs->trans('View'); 4044 } 4045 4046 $moreatt = ($float ? 'style="float: right" ' : '').$other; 4047 4048 return img_picto($titlealt, 'view.png', $moreatt); 4049} 4050 4051/** 4052 * Show delete logo 4053 * 4054 * @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. 4055 * @param string $other Add more attributes on img 4056 * @param string $morecss More CSS 4057 * @return string Retourne tag img 4058 */ 4059function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '') 4060{ 4061 global $langs; 4062 4063 if ($titlealt == 'default') { 4064 $titlealt = $langs->trans('Delete'); 4065 } 4066 4067 return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss); 4068} 4069 4070/** 4071 * Show printer logo 4072 * 4073 * @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. 4074 * @param string $other Add more attributes on img 4075 * @return string Retourne tag img 4076 */ 4077function img_printer($titlealt = "default", $other = '') 4078{ 4079 global $langs; 4080 if ($titlealt == "default") { 4081 $titlealt = $langs->trans("Print"); 4082 } 4083 return img_picto($titlealt, 'printer.png', $other); 4084} 4085 4086/** 4087 * Show split logo 4088 * 4089 * @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. 4090 * @param string $other Add more attributes on img 4091 * @return string Retourne tag img 4092 */ 4093function img_split($titlealt = 'default', $other = 'class="pictosplit"') 4094{ 4095 global $langs; 4096 4097 if ($titlealt == 'default') { 4098 $titlealt = $langs->trans('Split'); 4099 } 4100 4101 return img_picto($titlealt, 'split.png', $other); 4102} 4103 4104/** 4105 * Show help logo with cursor "?" 4106 * 4107 * @param int $usehelpcursor 1=Use help cursor, 2=Use click pointer cursor, 0=No specific cursor 4108 * @param int|string $usealttitle Text to use as alt title 4109 * @return string Return tag img 4110 */ 4111function img_help($usehelpcursor = 1, $usealttitle = 1) 4112{ 4113 global $langs; 4114 4115 if ($usealttitle) { 4116 if (is_string($usealttitle)) { 4117 $usealttitle = dol_escape_htmltag($usealttitle); 4118 } else { 4119 $usealttitle = $langs->trans('Info'); 4120 } 4121 } 4122 4123 return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"'); 4124} 4125 4126/** 4127 * Show info logo 4128 * 4129 * @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. 4130 * @return string Return img tag 4131 */ 4132function img_info($titlealt = 'default') 4133{ 4134 global $langs; 4135 4136 if ($titlealt == 'default') { 4137 $titlealt = $langs->trans('Informations'); 4138 } 4139 4140 return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"'); 4141} 4142 4143/** 4144 * Show warning logo 4145 * 4146 * @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. 4147 * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"'). If 1, add float: right. Can't be "class" attribute. 4148 * @param string $morecss Add more CSS 4149 * @return string Return img tag 4150 */ 4151function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning') 4152{ 4153 global $langs; 4154 4155 if ($titlealt == 'default') { 4156 $titlealt = $langs->trans('Warning'); 4157 } 4158 4159 //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>'; 4160 return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : '')); 4161} 4162 4163/** 4164 * Show error logo 4165 * 4166 * @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. 4167 * @return string Return img tag 4168 */ 4169function img_error($titlealt = 'default') 4170{ 4171 global $langs; 4172 4173 if ($titlealt == 'default') { 4174 $titlealt = $langs->trans('Error'); 4175 } 4176 4177 return img_picto($titlealt, 'error.png'); 4178} 4179 4180/** 4181 * Show next logo 4182 * 4183 * @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. 4184* @param string $moreatt Add more attribute on img tag (For example 'style="float: right"') 4185 * @return string Return img tag 4186 */ 4187function img_next($titlealt = 'default', $moreatt = '') 4188{ 4189 global $langs; 4190 4191 if ($titlealt == 'default') { 4192 $titlealt = $langs->trans('Next'); 4193 } 4194 4195 //return img_picto($titlealt, 'next.png', $moreatt); 4196 return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>'; 4197} 4198 4199/** 4200 * Show previous logo 4201 * 4202 * @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. 4203 * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"') 4204 * @return string Return img tag 4205 */ 4206function img_previous($titlealt = 'default', $moreatt = '') 4207{ 4208 global $langs; 4209 4210 if ($titlealt == 'default') { 4211 $titlealt = $langs->trans('Previous'); 4212 } 4213 4214 //return img_picto($titlealt, 'previous.png', $moreatt); 4215 return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>'; 4216} 4217 4218/** 4219 * Show down arrow logo 4220 * 4221 * @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. 4222 * @param int $selected Selected 4223 * @param string $moreclass Add more CSS classes 4224 * @return string Return img tag 4225 */ 4226function img_down($titlealt = 'default', $selected = 0, $moreclass = '') 4227{ 4228 global $langs; 4229 4230 if ($titlealt == 'default') { 4231 $titlealt = $langs->trans('Down'); 4232 } 4233 4234 return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"'); 4235} 4236 4237/** 4238 * Show top arrow logo 4239 * 4240 * @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. 4241 * @param int $selected Selected 4242 * @param string $moreclass Add more CSS classes 4243 * @return string Return img tag 4244 */ 4245function img_up($titlealt = 'default', $selected = 0, $moreclass = '') 4246{ 4247 global $langs; 4248 4249 if ($titlealt == 'default') { 4250 $titlealt = $langs->trans('Up'); 4251 } 4252 4253 return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"'); 4254} 4255 4256/** 4257 * Show left arrow logo 4258 * 4259 * @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. 4260 * @param int $selected Selected 4261 * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"') 4262 * @return string Return img tag 4263 */ 4264function img_left($titlealt = 'default', $selected = 0, $moreatt = '') 4265{ 4266 global $langs; 4267 4268 if ($titlealt == 'default') { 4269 $titlealt = $langs->trans('Left'); 4270 } 4271 4272 return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt); 4273} 4274 4275/** 4276 * Show right arrow logo 4277 * 4278 * @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. 4279 * @param int $selected Selected 4280 * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"') 4281 * @return string Return img tag 4282 */ 4283function img_right($titlealt = 'default', $selected = 0, $moreatt = '') 4284{ 4285 global $langs; 4286 4287 if ($titlealt == 'default') { 4288 $titlealt = $langs->trans('Right'); 4289 } 4290 4291 return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt); 4292} 4293 4294/** 4295 * Show tick logo if allowed 4296 * 4297 * @param string $allow Allow 4298 * @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. 4299 * @return string Return img tag 4300 */ 4301function img_allow($allow, $titlealt = 'default') 4302{ 4303 global $langs; 4304 4305 if ($titlealt == 'default') { 4306 $titlealt = $langs->trans('Active'); 4307 } 4308 4309 if ($allow == 1) { 4310 return img_picto($titlealt, 'tick.png'); 4311 } 4312 4313 return '-'; 4314} 4315 4316/** 4317 * Return image of a credit card according to its brand name 4318 * 4319 * @param string $brand Brand name of credit card 4320 * @param string $morecss More CSS 4321 * @return string Return img tag 4322 */ 4323function img_credit_card($brand, $morecss = null) 4324{ 4325 if (is_null($morecss)) { 4326 $morecss = 'fa-2x'; 4327 } 4328 4329 if ($brand == 'visa' || $brand == 'Visa') { 4330 $brand = 'cc-visa'; 4331 } elseif ($brand == 'mastercard' || $brand == 'MasterCard') { 4332 $brand = 'cc-mastercard'; 4333 } elseif ($brand == 'amex' || $brand == 'American Express') { 4334 $brand = 'cc-amex'; 4335 } elseif ($brand == 'discover' || $brand == 'Discover') { 4336 $brand = 'cc-discover'; 4337 } elseif ($brand == 'jcb' || $brand == 'JCB') { 4338 $brand = 'cc-jcb'; 4339 } elseif ($brand == 'diners' || $brand == 'Diners club') { 4340 $brand = 'cc-diners-club'; 4341 } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) { 4342 $brand = 'credit-card'; 4343 } 4344 4345 return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>'; 4346} 4347 4348/** 4349 * Show MIME img of a file 4350 * 4351 * @param string $file Filename 4352 * @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. 4353 * @param string $morecss More css 4354 * @return string Return img tag 4355 */ 4356function img_mime($file, $titlealt = '', $morecss = '') 4357{ 4358 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 4359 4360 $mimetype = dol_mimetype($file, '', 1); 4361 $mimeimg = dol_mimetype($file, '', 2); 4362 $mimefa = dol_mimetype($file, '', 4); 4363 4364 if (empty($titlealt)) { 4365 $titlealt = 'Mime type: '.$mimetype; 4366 } 4367 4368 //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"'); 4369 return '<i class="fa fa-'.$mimefa.' paddingright"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>'; 4370} 4371 4372 4373/** 4374 * Show search logo 4375 * 4376 * @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. 4377 * @param string $other Add more attributes on img 4378 * @return string Retourne tag img 4379 */ 4380function img_search($titlealt = 'default', $other = '') 4381{ 4382 global $conf, $langs; 4383 4384 if ($titlealt == 'default') { 4385 $titlealt = $langs->trans('Search'); 4386 } 4387 4388 $img = img_picto($titlealt, 'search.png', $other, false, 1); 4389 4390 $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" '; 4391 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >'; 4392 4393 return $input; 4394} 4395 4396/** 4397 * Show search logo 4398 * 4399 * @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. 4400 * @param string $other Add more attributes on img 4401 * @return string Retourne tag img 4402 */ 4403function img_searchclear($titlealt = 'default', $other = '') 4404{ 4405 global $conf, $langs; 4406 4407 if ($titlealt == 'default') { 4408 $titlealt = $langs->trans('Search'); 4409 } 4410 4411 $img = img_picto($titlealt, 'searchclear.png', $other, false, 1); 4412 4413 $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" '; 4414 $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >'; 4415 4416 return $input; 4417} 4418 4419/** 4420 * Show information for admin users or standard users 4421 * 4422 * @param string $text Text info 4423 * @param integer $infoonimgalt Info is shown only on alt of star picto, otherwise it is show on output after the star picto 4424 * @param int $nodiv No div 4425 * @param string $admin '1'=Info for admin users. '0'=Info for standard users (change only the look), 'error', 'warning', 'xxx'=Other 4426 * @param string $morecss More CSS ('', 'warning', 'error') 4427 * @param string $textfordropdown Show a text to click to dropdown the info box. 4428 * @return string String with info text 4429 */ 4430function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = '', $textfordropdown = '') 4431{ 4432 global $conf, $langs; 4433 4434 if ($infoonimgalt) { 4435 $result = img_picto($text, 'info', 'class="hideonsmartphone'.($morecss ? ' '.$morecss : '').'"'); 4436 } else { 4437 if (empty($conf->use_javascript_ajax)) { 4438 $textfordropdown = ''; 4439 } 4440 4441 $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin)); 4442 $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>'); 4443 4444 if ($textfordropdown) { 4445 $tmpresult .= '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>'; 4446 $tmpresult .= '<script type="text/javascript" language="javascript"> 4447 jQuery(document).ready(function() { 4448 jQuery(".'.$class.'text").click(function() { 4449 console.log("toggle text"); 4450 jQuery(".'.$class.'").toggle(); 4451 }); 4452 }); 4453 </script>'; 4454 4455 $result = $tmpresult.$result; 4456 } 4457 } 4458 4459 return $result; 4460} 4461 4462 4463/** 4464 * Displays error message system with all the information to facilitate the diagnosis and the escalation of the bugs. 4465 * This function must be called when a blocking technical error is encountered. 4466 * However, one must try to call it only within php pages, classes must return their error through their property "error". 4467 * 4468 * @param DoliDB $db Database handler 4469 * @param string|string[] $error String or array of errors strings to show 4470 * @param array $errors Array of errors 4471 * @return void 4472 * @see dol_htmloutput_errors() 4473 */ 4474function dol_print_error($db = '', $error = '', $errors = null) 4475{ 4476 global $conf, $langs, $argv; 4477 global $dolibarr_main_prod; 4478 4479 $out = ''; 4480 $syslog = ''; 4481 4482 // If error occurs before the $lang object was loaded 4483 if (!$langs) { 4484 require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php'; 4485 $langs = new Translate('', $conf); 4486 $langs->load("main"); 4487 } 4488 4489 // Load translation files required by the error messages 4490 $langs->loadLangs(array('main', 'errors')); 4491 4492 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web 4493 $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n"; 4494 if (!empty($conf->global->MAIN_FEATURES_LEVEL)) { 4495 $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"; 4496 } 4497 $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n"; 4498 4499 $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n"; 4500 $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n"; 4501 if (isset($conf->global->MAIN_FEATURES_LEVEL)) { 4502 $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".dol_htmlentities($conf->global->MAIN_FEATURES_LEVEL, ENT_COMPAT)."<br>\n"; 4503 } 4504 if (function_exists("phpversion")) { 4505 $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n"; 4506 } 4507 $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n"; 4508 if (function_exists("php_uname")) { 4509 $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n"; 4510 } 4511 $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n"; 4512 $out .= "<br>\n"; 4513 $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n"; 4514 $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n"; 4515 $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n"; 4516 $out .= "<br>\n"; 4517 $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]); 4518 $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]); 4519 } else // Mode CLI 4520 { 4521 $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n"; 4522 $syslog .= "pid=".dol_getmypid(); 4523 } 4524 4525 if (!empty($conf->modules)) { 4526 $out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n"; 4527 } 4528 4529 if (is_object($db)) { 4530 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web 4531 $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n"; 4532 $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror() ? dol_escape_htmltag($db->lastqueryerror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n"; 4533 $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n"; 4534 $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n"; 4535 $out .= "<br>\n"; 4536 } else // Mode CLI 4537 { 4538 // No dol_escape_htmltag for output, we are in CLI mode 4539 $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n"; 4540 $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n"; 4541 $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n"; 4542 $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n"; 4543 } 4544 $syslog .= ", sql=".$db->lastquery(); 4545 $syslog .= ", db_error=".$db->lasterror(); 4546 } 4547 4548 if ($error || $errors) { 4549 $langs->load("errors"); 4550 4551 // Merge all into $errors array 4552 if (is_array($error) && is_array($errors)) { 4553 $errors = array_merge($error, $errors); 4554 } elseif (is_array($error)) { 4555 $errors = $error; 4556 } elseif (is_array($errors)) { 4557 $errors = array_merge(array($error), $errors); 4558 } else { 4559 $errors = array_merge(array($error)); 4560 } 4561 4562 foreach ($errors as $msg) { 4563 if (empty($msg)) { 4564 continue; 4565 } 4566 if ($_SERVER['DOCUMENT_ROOT']) { // Mode web 4567 $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n"; 4568 } else // Mode CLI 4569 { 4570 $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n"; 4571 } 4572 $syslog .= ", msg=".$msg; 4573 } 4574 } 4575 if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) { 4576 xdebug_print_function_stack(); 4577 $out .= '<b>XDebug informations:</b>'."<br>\n"; 4578 $out .= 'File: '.xdebug_call_file()."<br>\n"; 4579 $out .= 'Line: '.xdebug_call_line()."<br>\n"; 4580 $out .= 'Function: '.xdebug_call_function()."<br>\n"; 4581 $out .= "<br>\n"; 4582 } 4583 4584 // Return a http error code if possible 4585 if (!headers_sent()) { 4586 http_response_code(500); 4587 } 4588 4589 if (empty($dolibarr_main_prod)) { 4590 print $out; 4591 } else { 4592 if (empty($langs->defaultlang)) { 4593 $langs->setDefaultLang(); 4594 } 4595 $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. 4596 // This should not happen, except if there is a bug somewhere. Enabled and check log in such case. 4597 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 ('.dol_print_date(dol_now(), 'dayhourrfc').') are on next line...<br><br>'."\n"; 4598 print $langs->trans("DolibarrHasDetectedError").'. '; 4599 print $langs->trans("YouCanSetOptionDolibarrMainProdToZero"); 4600 define("MAIN_CORE_ERROR", 1); 4601 } 4602 4603 dol_syslog("Error ".$syslog, LOG_ERR); 4604} 4605 4606/** 4607 * Show a public email and error code to contact if technical error 4608 * 4609 * @param string $prefixcode Prefix of public error code 4610 * @param string $errormessage Complete error message 4611 * @param array $errormessages Array of error messages 4612 * @param string $morecss More css 4613 * @param string $email Email 4614 * @return void 4615 */ 4616function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '') 4617{ 4618 global $langs, $conf; 4619 4620 if (empty($email)) { 4621 $email = $conf->global->MAIN_INFO_SOCIETE_MAIL; 4622 } 4623 4624 $langs->load("errors"); 4625 $now = dol_now(); 4626 4627 print '<br><div class="center login_main_message"><div class="'.$morecss.'">'; 4628 print $langs->trans("ErrorContactEMail", $email, $prefixcode.dol_print_date($now, '%Y%m%d%H%M%S')); 4629 if ($errormessage) { 4630 print '<br><br>'.$errormessage; 4631 } 4632 if (is_array($errormessages) && count($errormessages)) { 4633 foreach ($errormessages as $mesgtoshow) { 4634 print '<br><br>'.$mesgtoshow; 4635 } 4636 } 4637 print '</div></div>'; 4638} 4639 4640/** 4641 * Show title line of an array 4642 * 4643 * @param string $name Label of field 4644 * @param string $file Url used when we click on sort picto 4645 * @param string $field Field to use for new sorting 4646 * @param string $begin ("" by defaut) 4647 * @param string $moreparam Add more parameters on sort url links ("" by default) 4648 * @param string $moreattrib Options of attribute td ("" by defaut, example: 'align="center"') 4649 * @param string $sortfield Current field used to sort 4650 * @param string $sortorder Current sort order 4651 * @param string $prefix Prefix for css. Use space after prefix to add your own CSS tag. 4652 * @param string $tooltip Tooltip 4653 * @param string $forcenowrapcolumntitle No need for use 'wrapcolumntitle' css style 4654 * @return void 4655 */ 4656function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0) 4657{ 4658 print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle); 4659} 4660 4661/** 4662 * Get title line of an array 4663 * 4664 * @param string $name Translation key of field to show or complete HTML string to show 4665 * @param int $thead 0=To use with standard table format, 1=To use inside <thead><tr>, 2=To use with <div> 4666 * @param string $file Url used when we click on sort picto 4667 * @param string $field Field to use for new sorting. Empty if this field is not sortable. Example "t.abc" or "t.abc,t.def" 4668 * @param string $begin ("" by defaut) 4669 * @param string $moreparam Add more parameters on sort url links ("" by default) 4670 * @param string $moreattrib Add more attributes on th ("" by defaut, example: 'align="center"'). To add more css class, use param $prefix. 4671 * @param string $sortfield Current field used to sort (Ex: 'd.datep,d.id') 4672 * @param string $sortorder Current sort order (Ex: 'asc,desc') 4673 * @param string $prefix Prefix for css. Use space after prefix to add your own CSS tag, for example 'mycss '. 4674 * @param string $disablesortlink 1=Disable sort link 4675 * @param string $tooltip Tooltip 4676 * @param string $forcenowrapcolumntitle No need for use 'wrapcolumntitle' css style 4677 * @return string 4678 */ 4679function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0) 4680{ 4681 global $conf, $langs, $form; 4682 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n"; 4683 4684 if ($moreattrib == 'class="right"') { 4685 $prefix .= 'right '; // For backward compatibility 4686 } 4687 4688 $sortorder = strtoupper($sortorder); 4689 $out = ''; 4690 $sortimg = ''; 4691 4692 $tag = 'th'; 4693 if ($thead == 2) { 4694 $tag = 'div'; 4695 } 4696 4697 $tmpsortfield = explode(',', $sortfield); 4698 $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep' 4699 $tmpfield = explode(',', $field); 4700 $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep' 4701 4702 if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle)) { 4703 $prefix = 'wrapcolumntitle '.$prefix; 4704 } 4705 4706 //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1); 4707 // If field is used as sort criteria we use a specific css class liste_titre_sel 4708 // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom") 4709 $liste_titre = 'liste_titre'; 4710 if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) { 4711 $liste_titre = 'liste_titre_sel'; 4712 } 4713 $out .= '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib; 4714 //$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)).'"' : ''); 4715 $out .= ($name && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : ''; 4716 $out .= '>'; 4717 4718 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field 4719 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : '')); 4720 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options); 4721 $options = preg_replace('/&+/i', '&', $options); 4722 if (!preg_match('/^&/', $options)) { 4723 $options = '&'.$options; 4724 } 4725 4726 $sortordertouseinlink = ''; 4727 if ($field1 != $sortfield1) { // We are on another field than current sorted field 4728 if (preg_match('/^DESC/i', $sortorder)) { 4729 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field))); 4730 } else // We reverse the var $sortordertouseinlink 4731 { 4732 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field))); 4733 } 4734 } else // We are on field that is the first current sorting criteria 4735 { 4736 if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink 4737 $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field))); 4738 } else { 4739 $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field))); 4740 } 4741 } 4742 $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink); 4743 $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"'; 4744 //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : ''); 4745 $out .= '>'; 4746 } 4747 4748 if ($tooltip) { 4749 // You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click. 4750 $tmptooltip = explode(':', $tooltip); 4751 $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1])); 4752 } else { 4753 $out .= $langs->trans($name); 4754 } 4755 4756 if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field 4757 $out .= '</a>'; 4758 } 4759 4760 if (empty($thead) && $field) { // If this is a sort field 4761 $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : '')); 4762 $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options); 4763 $options = preg_replace('/&+/i', '&', $options); 4764 if (!preg_match('/^&/', $options)) { 4765 $options = '&'.$options; 4766 } 4767 4768 if (!$sortorder || $field1 != $sortfield1) { 4769 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>'; 4770 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>'; 4771 } else { 4772 if (preg_match('/^DESC/', $sortorder)) { 4773 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>'; 4774 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>'; 4775 $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingleft').'</span>'; 4776 } 4777 if (preg_match('/^ASC/', $sortorder)) { 4778 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>'; 4779 //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>'; 4780 $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingleft').'</span>'; 4781 } 4782 } 4783 } 4784 4785 $out .= $sortimg; 4786 4787 $out .= '</'.$tag.'>'; 4788 4789 return $out; 4790} 4791 4792/** 4793 * Show a title. 4794 * 4795 * @param string $title Title to show 4796 * @return string Title to show 4797 * @deprecated Use load_fiche_titre instead 4798 * @see load_fiche_titre() 4799 */ 4800function print_titre($title) 4801{ 4802 dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING); 4803 4804 print '<div class="titre">'.$title.'</div>'; 4805} 4806 4807/** 4808 * Show a title with picto 4809 * 4810 * @param string $title Title to show 4811 * @param string $mesg Added message to show on right 4812 * @param string $picto Icon to use before title (should be a 32x32 transparent png file) 4813 * @param int $pictoisfullpath 1=Icon name is a full absolute url of image 4814 * @param int $id To force an id on html objects 4815 * @return void 4816 * @deprecated Use print load_fiche_titre instead 4817 */ 4818function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '') 4819{ 4820 print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id); 4821} 4822 4823/** 4824 * Load a title with picto 4825 * 4826 * @param string $titre Title to show 4827 * @param string $morehtmlright Added message to show on right 4828 * @param string $picto Icon to use before title (should be a 32x32 transparent png file) 4829 * @param int $pictoisfullpath 1=Icon name is a full absolute url of image 4830 * @param string $id To force an id on html objects 4831 * @param string $morecssontable More css on table 4832 * @param string $morehtmlcenter Added message to show on center 4833 * @return string 4834 * @see print_barre_liste() 4835 */ 4836function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '') 4837{ 4838 global $conf; 4839 4840 $return = ''; 4841 4842 if ($picto == 'setup') { 4843 $picto = 'generic'; 4844 } 4845 4846 $return .= "\n"; 4847 $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list 4848 $return .= '<tr class="titre">'; 4849 if ($picto) { 4850 $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>'; 4851 } 4852 $return .= '<td class="nobordernopadding valignmiddle col-title">'; 4853 $return .= '<div class="titre inline-block">'.$titre.'</div>'; 4854 $return .= '</td>'; 4855 if (dol_strlen($morehtmlcenter)) { 4856 $return .= '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>'; 4857 } 4858 if (dol_strlen($morehtmlright)) { 4859 $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle">'.$morehtmlright.'</td>'; 4860 } 4861 $return .= '</tr></table>'."\n"; 4862 4863 return $return; 4864} 4865 4866/** 4867 * Print a title with navigation controls for pagination 4868 * 4869 * @param string $titre Title to show (required) 4870 * @param int $page Numero of page to show in navigation links (required) 4871 * @param string $file Url of page (required) 4872 * @param string $options More parameters for links ('' by default, does not include sortfield neither sortorder). Value must be 'urlencoded' before calling function. 4873 * @param string $sortfield Field to sort on ('' by default) 4874 * @param string $sortorder Order to sort ('' by default) 4875 * @param string $morehtmlcenter String in the middle ('' by default). We often find here string $massaction comming from $form->selectMassAction() 4876 * @param int $num Number of records found by select with limit+1 4877 * @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. 4878 * @param string $picto Icon to use before title (should be a 32x32 transparent png file) 4879 * @param int $pictoisfullpath 1=Icon name is a full absolute url of image 4880 * @param string $morehtmlright More html to show (after arrows) 4881 * @param string $morecss More css to the table 4882 * @param int $limit Max number of lines (-1 = use default, 0 = no limit, > 0 = limit). 4883 * @param int $hideselectlimit Force to hide select limit 4884 * @param int $hidenavigation Force to hide all navigation tools 4885 * @param int $pagenavastextinput 1=Do not suggest list of pages to navigate but suggest the page number into an input field. 4886 * @param string $morehtmlrightbeforearrow More html to show (before arrows) 4887 * @return void 4888 */ 4889function 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 = '') 4890{ 4891 global $conf, $langs; 4892 4893 $savlimit = $limit; 4894 $savtotalnboflines = $totalnboflines; 4895 $totalnboflines = abs((int) $totalnboflines); 4896 4897 if ($picto == 'setup') { 4898 $picto = 'title_setup.png'; 4899 } 4900 if (($conf->browser->name == 'ie') && $picto == 'generic') { 4901 $picto = 'title.gif'; 4902 } 4903 if ($limit < 0) { 4904 $limit = $conf->liste_limit; 4905 } 4906 if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) { 4907 $nextpage = 1; 4908 } else { 4909 $nextpage = 0; 4910 } 4911 //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage; 4912 4913 print "\n"; 4914 print "<!-- Begin title -->\n"; 4915 print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire 4916 4917 // Left 4918 4919 if ($picto && $titre) { 4920 print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>'; 4921 } 4922 print '<td class="nobordernopadding valignmiddle col-title">'; 4923 print '<div class="titre inline-block">'.$titre; 4924 if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') { 4925 print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>'; 4926 } 4927 print '</div></td>'; 4928 4929 // Center 4930 if ($morehtmlcenter) { 4931 print '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>'; 4932 } 4933 4934 // Right 4935 print '<td class="nobordernopadding valignmiddle right">'; 4936 print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">'; 4937 if ($sortfield) { 4938 $options .= "&sortfield=".urlencode($sortfield); 4939 } 4940 if ($sortorder) { 4941 $options .= "&sortorder=".urlencode($sortorder); 4942 } 4943 // Show navigation bar 4944 $pagelist = ''; 4945 if ($savlimit != 0 && ($page > 0 || $num > $limit)) { 4946 if ($totalnboflines) { // If we know total nb of lines 4947 // Define nb of extra page links before and after selected page + ... + first or last 4948 $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0); 4949 4950 if ($limit > 0) { 4951 $nbpages = ceil($totalnboflines / $limit); 4952 } else { 4953 $nbpages = 1; 4954 } 4955 $cpt = ($page - $maxnbofpage); 4956 if ($cpt < 0) { 4957 $cpt = 0; 4958 } 4959 4960 if ($cpt >= 1) { 4961 if (empty($pagenavastextinput)) { 4962 $pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>'; 4963 if ($cpt > 2) { 4964 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>'; 4965 } elseif ($cpt == 2) { 4966 $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>'; 4967 } 4968 } 4969 } 4970 4971 do { 4972 if ($pagenavastextinput) { 4973 if ($cpt == $page) { 4974 $pagelist .= '<li class="pagination"><input type="text" class="width25 center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>'; 4975 $pagelist .= '/'; 4976 //if (($cpt + 1) < $nbpages) $pagelist .= '/'; 4977 } 4978 } else { 4979 if ($cpt == $page) { 4980 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>'; 4981 } else { 4982 $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>'; 4983 } 4984 } 4985 $cpt++; 4986 } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage)); 4987 4988 if (empty($pagenavastextinput)) { 4989 if ($cpt < $nbpages) { 4990 if ($cpt < $nbpages - 2) { 4991 $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>'; 4992 } elseif ($cpt == $nbpages - 2) { 4993 $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>'; 4994 } 4995 $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>'; 4996 } 4997 } else { 4998 //var_dump($page.' '.$cpt.' '.$nbpages); 4999 //if (($page + 1) < $nbpages) { 5000 $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>'; 5001 //} 5002 } 5003 } else { 5004 $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>"; 5005 } 5006 } 5007 5008 if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) { 5009 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 5010 } 5011 5012 // js to autoselect page field on focus 5013 if ($pagenavastextinput) { 5014 print ajax_autoselect('.pageplusone'); 5015 } 5016 5017 print '</td>'; 5018 5019 print '</tr></table>'."\n"; 5020 print "<!-- End title -->\n\n"; 5021} 5022 5023/** 5024 * Function to show navigation arrows into lists 5025 * 5026 * @param int $page Number of page 5027 * @param string $file Page URL (in most cases provided with $_SERVER["PHP_SELF"]) 5028 * @param string $options Other url parameters to propagate ("" by default, may include sortfield and sortorder) 5029 * @param integer $nextpage Do we show a next page button 5030 * @param string $betweenarrows HTML content to show between arrows. MUST contains '<li> </li>' tags or '<li><span> </span></li>'. 5031 * @param string $afterarrows HTML content to show after arrows. Must NOT contains '<li> </li>' tags. 5032 * @param int $limit Max nb of record to show (-1 = no combo with limit, 0 = no limit, > 0 = limit) 5033 * @param int $totalnboflines Total number of records/lines for all pages (if known) 5034 * @param int $hideselectlimit Force to hide select limit 5035 * @param string $beforearrows HTML content to show before arrows. Must NOT contains '<li> </li>' tags. 5036 * @return void 5037 */ 5038function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '') 5039{ 5040 global $conf, $langs; 5041 5042 print '<div class="pagination"><ul>'; 5043 if ($beforearrows) { 5044 print '<li class="paginationbeforearrows">'; 5045 print $beforearrows; 5046 print '</li>'; 5047 } 5048 if ((int) $limit > 0 && empty($hideselectlimit)) { 5049 $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'; 5050 //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported 5051 //$pagesizechoices.=',2:2'; 5052 if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) { 5053 $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES; 5054 } 5055 5056 print '<li class="pagination">'; 5057 print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">'; 5058 $tmpchoice = explode(',', $pagesizechoices); 5059 $tmpkey = $limit.':'.$limit; 5060 if (!in_array($tmpkey, $tmpchoice)) { 5061 $tmpchoice[] = $tmpkey; 5062 } 5063 $tmpkey = $conf->liste_limit.':'.$conf->liste_limit; 5064 if (!in_array($tmpkey, $tmpchoice)) { 5065 $tmpchoice[] = $tmpkey; 5066 } 5067 asort($tmpchoice, SORT_NUMERIC); 5068 foreach ($tmpchoice as $val) { 5069 $selected = ''; 5070 $tmp = explode(':', $val); 5071 $key = $tmp[0]; 5072 $val = $tmp[1]; 5073 if ($key != '' && $val != '') { 5074 if ((int) $key == (int) $limit) { 5075 $selected = ' selected="selected"'; 5076 } 5077 print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n"; 5078 } 5079 } 5080 print '</select>'; 5081 if ($conf->use_javascript_ajax) { 5082 print '<!-- JS CODE TO ENABLE select limit to launch submit of page --> 5083 <script> 5084 jQuery(document).ready(function () { 5085 jQuery(".selectlimit").change(function() { 5086 console.log("Change limit. Send submit"); 5087 $(this).parents(\'form:first\').submit(); 5088 }); 5089 }); 5090 </script> 5091 '; 5092 } 5093 print '</li>'; 5094 } 5095 if ($page > 0) { 5096 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>'; 5097 } 5098 if ($betweenarrows) { 5099 print '<!--<div class="betweenarrows nowraponall inline-block">-->'; 5100 print $betweenarrows; 5101 print '<!--</div>-->'; 5102 } 5103 if ($nextpage > 0) { 5104 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>'; 5105 } 5106 if ($afterarrows) { 5107 print '<li class="paginationafterarrows">'; 5108 print $afterarrows; 5109 print '</li>'; 5110 } 5111 print '</ul></div>'."\n"; 5112} 5113 5114 5115/** 5116 * Return a string with VAT rate label formated for view output 5117 * Used into pdf and HTML pages 5118 * 5119 * @param string $rate Rate value to format ('19.6', '19,6', '19.6%', '19,6%', '19.6 (CODEX)', ...) 5120 * @param boolean $addpercent Add a percent % sign in output 5121 * @param int $info_bits Miscellaneous information on vat (0=Default, 1=French NPR vat) 5122 * @param int $usestarfornpr -1=Never show, 0 or 1=Use '*' for NPR vat rates 5123 * @return string String with formated amounts ('19,6' or '19,6%' or '8.5% (NPR)' or '8.5% *' or '19,6 (CODEX)') 5124 */ 5125function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0) 5126{ 5127 $morelabel = ''; 5128 5129 if (preg_match('/%/', $rate)) { 5130 $rate = str_replace('%', '', $rate); 5131 $addpercent = true; 5132 } 5133 if (preg_match('/\((.*)\)/', $rate, $reg)) { 5134 $morelabel = ' ('.$reg[1].')'; 5135 $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate); 5136 } 5137 if (preg_match('/\*/', $rate)) { 5138 $rate = str_replace('*', '', $rate); 5139 $info_bits |= 1; 5140 } 5141 5142 // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price() 5143 if (!preg_match('/\//', $rate)) { 5144 $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : ''); 5145 } else { 5146 // TODO Split on / and output with a price2num to have clean numbers without ton of 000. 5147 $ret = $rate.($addpercent ? '%' : ''); 5148 } 5149 if (($info_bits & 1) && $usestarfornpr >= 0) { 5150 $ret .= ' *'; 5151 } 5152 $ret .= $morelabel; 5153 return $ret; 5154} 5155 5156 5157/** 5158 * Function to format a value into an amount for visual output 5159 * Function used into PDF and HTML pages 5160 * 5161 * @param float $amount Amount to format 5162 * @param integer $form Type of format, HTML or not (not by default) 5163 * @param Translate $outlangs Object langs for output 5164 * @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. 5165 * @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) 5166 * @param int $forcerounding Force the number of decimal to forcerounding decimal (-1=do not force) 5167 * @param string $currency_code To add currency symbol (''=add nothing, 'auto'=Use default currency, 'XXX'=add currency symbols for XXX currency) 5168 * @return string Chaine avec montant formate 5169 * 5170 * @see price2num() Revert function of price 5171 */ 5172function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '') 5173{ 5174 global $langs, $conf; 5175 5176 // Clean parameters 5177 if (empty($amount)) { 5178 $amount = 0; // To have a numeric value if amount not defined or = '' 5179 } 5180 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number) 5181 if ($rounding < 0) { 5182 $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT); 5183 } 5184 $nbdecimal = $rounding; 5185 5186 // Output separators by default (french) 5187 $dec = ','; 5188 $thousand = ' '; 5189 5190 // If $outlangs not forced, we use use language 5191 if (!is_object($outlangs)) { 5192 $outlangs = $langs; 5193 } 5194 5195 if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") { 5196 $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal"); 5197 } 5198 if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") { 5199 $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand"); 5200 } 5201 if ($thousand == 'None') { 5202 $thousand = ''; 5203 } elseif ($thousand == 'Space') { 5204 $thousand = ' '; 5205 } 5206 //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>"; 5207 5208 //print "amount=".$amount."-"; 5209 $amount = str_replace(',', '.', $amount); // should be useless 5210 //print $amount."-"; 5211 $datas = explode('.', $amount); 5212 $decpart = isset($datas[1]) ? $datas[1] : ''; 5213 $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale 5214 //print "decpart=".$decpart."<br>"; 5215 $end = ''; 5216 5217 // We increase nbdecimal if there is more decimal than asked (to not loose information) 5218 if (dol_strlen($decpart) > $nbdecimal) { 5219 $nbdecimal = dol_strlen($decpart); 5220 } 5221 // Si on depasse max 5222 if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN) { 5223 $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN; 5224 if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN)) { 5225 // Si un affichage est tronque, on montre des ... 5226 $end = '...'; 5227 } 5228 } 5229 5230 // If force rounding 5231 if ($forcerounding >= 0) { 5232 $nbdecimal = $forcerounding; 5233 } 5234 5235 // Format number 5236 $output = number_format($amount, $nbdecimal, $dec, $thousand); 5237 if ($form) { 5238 $output = preg_replace('/\s/', ' ', $output); 5239 $output = preg_replace('/\'/', ''', $output); 5240 } 5241 // Add symbol of currency if requested 5242 $cursymbolbefore = $cursymbolafter = ''; 5243 if ($currency_code) { 5244 if ($currency_code == 'auto') { 5245 $currency_code = $conf->currency; 5246 } 5247 5248 $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD'); 5249 $listoflanguagesbefore = array('nl_NL'); 5250 if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) { 5251 $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code); 5252 } else { 5253 $tmpcur = $outlangs->getCurrencySymbol($currency_code); 5254 $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur); 5255 } 5256 } 5257 $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter; 5258 5259 return $output; 5260} 5261 5262/** 5263 * Function that return a number with universal decimal format (decimal separator is '.') from an amount typed by a user. 5264 * Function to use on each input amount before any numeric test or database insert. A better name for this function 5265 * should be roundtext2num(). 5266 * 5267 * @param string|float $amount Amount to convert/clean or round 5268 * @param string|int $rounding ''=No rounding 5269 * 'MU'=Round to Max unit price (MAIN_MAX_DECIMALS_UNIT) 5270 * 'MT'=Round to Max for totals with Tax (MAIN_MAX_DECIMALS_TOT) 5271 * 'MS'=Round to Max for stock quantity (MAIN_MAX_DECIMALS_STOCK) 5272 * 'CU'=Round to Max unit price of foreign currency accuracy 5273 * 'CT'=Round to Max for totals with Tax of foreign currency accuracy 5274 * Numeric = Nb of digits for rounding (For example 2 for a percentage) 5275 * @param int $option Put 1 if you know that content is already universal format number (so no correction on decimal will be done) 5276 * Put 2 if you know that number is a user input (so we know we don't have to fix decimal separator). 5277 * @return string Amount with universal numeric format (Example: '99.99999'). 5278 * If conversion fails, it return text unchanged if ($rounding = '' and $option = 1) or '0' if ($rounding is defined and $option = 1). 5279 * If amount is null or '', it returns '' if $rounding = '' or '0' if $rounding is defined.. 5280 * 5281 * @see price() Opposite function of price2num 5282 */ 5283function price2num($amount, $rounding = '', $option = 0) 5284{ 5285 global $langs, $conf; 5286 5287 // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56' 5288 // Numbers must be '1234.56' 5289 // Decimal delimiter for PHP and database SQL requests must be '.' 5290 $dec = ','; 5291 $thousand = ' '; 5292 if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") { 5293 $dec = $langs->transnoentitiesnoconv("SeparatorDecimal"); 5294 } 5295 if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") { 5296 $thousand = $langs->transnoentitiesnoconv("SeparatorThousand"); 5297 } 5298 if ($thousand == 'None') { 5299 $thousand = ''; 5300 } elseif ($thousand == 'Space') { 5301 $thousand = ' '; 5302 } 5303 //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>"; 5304 5305 // Convert value to universal number format (no thousand separator, '.' as decimal separator) 5306 if ($option != 1) { // If not a PHP number or unknown, we change or clean format 5307 //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>'; 5308 if (!is_numeric($amount)) { 5309 $amount = preg_replace('/[a-zA-Z\/\\\*\(\)\<\>\_]/', '', $amount); 5310 } 5311 5312 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 5313 $amount = str_replace($thousand, '', $amount); 5314 } 5315 5316 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number 5317 // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup. 5318 // So if number was already a good number, it is converted into local Dolibarr setup. 5319 if (is_numeric($amount)) { 5320 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10 5321 $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000 5322 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1 5323 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0." 5324 $amount = number_format($amount, $nbofdec, $dec, $thousand); 5325 } 5326 //print "QQ".$amount."<br>\n"; 5327 5328 // Now make replace (the main goal of function) 5329 if ($thousand != ',' && $thousand != '.') { 5330 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users 5331 } 5332 $amount = str_replace(' ', '', $amount); // To avoid spaces 5333 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is . 5334 $amount = str_replace($dec, '.', $amount); 5335 } 5336 //print ' XX'.$amount.' '.$rounding; 5337 5338 // Now, make a rounding if required 5339 if ($rounding) { 5340 $nbofdectoround = ''; 5341 if ($rounding == 'MU') { 5342 $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT; 5343 } elseif ($rounding == 'MT') { 5344 $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT; 5345 } elseif ($rounding == 'MS') { 5346 $nbofdectoround = empty($conf->global->MAIN_MAX_DECIMALS_STOCK) ? 5 : $conf->global->MAIN_MAX_DECIMALS_STOCK; 5347 } elseif ($rounding == 'CU') { 5348 $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_UNIT, 8); // TODO Use param of currency 5349 } elseif ($rounding == 'CT') { 5350 $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_TOT, 8); // TODO Use param of currency 5351 } elseif (is_numeric($rounding)) { 5352 $nbofdectoround = (int) $rounding; 5353 } 5354 //print " RR".$amount.' - '.$nbofdectoround.'<br>'; 5355 if (dol_strlen($nbofdectoround)) { 5356 $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0. 5357 } else { 5358 return 'ErrorBadParameterProvidedToFunction'; 5359 } 5360 //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>'; 5361 5362 // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number 5363 // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup. 5364 if (is_numeric($amount)) { 5365 // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10 5366 $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000 5367 $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1 5368 $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0." 5369 $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand 5370 } 5371 //print "TT".$amount.'<br>'; 5372 5373 // Always make replace because each math function (like round) replace 5374 // with local values and we want a number that has a SQL string format x.y 5375 if ($thousand != ',' && $thousand != '.') { 5376 $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users 5377 } 5378 $amount = str_replace(' ', '', $amount); // To avoid spaces 5379 $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is . 5380 $amount = str_replace($dec, '.', $amount); 5381 } 5382 5383 return $amount; 5384} 5385 5386/** 5387 * Output a dimension with best unit 5388 * 5389 * @param float $dimension Dimension 5390 * @param int $unit Unit scale of dimension (Example: 0=kg, -3=g, -6=mg, 98=ounce, 99=pound, ...) 5391 * @param string $type 'weight', 'volume', ... 5392 * @param Translate $outputlangs Translate language object 5393 * @param int $round -1 = non rounding, x = number of decimal 5394 * @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) 5395 * @return string String to show dimensions 5396 */ 5397function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no') 5398{ 5399 require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; 5400 5401 if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) { 5402 $dimension = $dimension * 1000000; 5403 $unit = $unit - 6; 5404 } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) { 5405 $dimension = $dimension * 1000; 5406 $unit = $unit - 3; 5407 } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) { 5408 $dimension = $dimension / 1000000; 5409 $unit = $unit + 6; 5410 } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) { 5411 $dimension = $dimension / 1000; 5412 $unit = $unit + 3; 5413 } 5414 // Special case when we want output unit into pound or ounce 5415 /* TODO 5416 if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99)) 5417 { 5418 $dimension = // convert dimension from standard unit into ounce or pound 5419 $unit = $forceunitoutput; 5420 } 5421 if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90) 5422 { 5423 $dimension = // convert dimension from standard unit into ounce or pound 5424 $unit = $forceunitoutput; 5425 }*/ 5426 5427 $ret = price($dimension, 0, $outputlangs, 0, 0, $round).' '.measuringUnitString(0, $type, $unit); 5428 5429 return $ret; 5430} 5431 5432 5433/** 5434 * Return localtax rate for a particular vat, when selling a product with vat $vatrate, from a $thirdparty_buyer to a $thirdparty_seller 5435 * Note: This function applies same rules than get_default_tva 5436 * 5437 * @param float $vatrate Vat rate. Can be '8.5' or '8.5 (VATCODEX)' for example 5438 * @param int $local Local tax to search and return (1 or 2 return only tax rate 1 or tax rate 2) 5439 * @param Societe $thirdparty_buyer Object of buying third party 5440 * @param Societe $thirdparty_seller Object of selling third party ($mysoc if not defined) 5441 * @param int $vatnpr If vat rate is NPR or not 5442 * @return mixed 0 if not found, localtax rate if found 5443 * @see get_default_tva() 5444 */ 5445function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0) 5446{ 5447 global $db, $conf, $mysoc; 5448 5449 if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) { 5450 $thirdparty_seller = $mysoc; 5451 } 5452 5453 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); 5454 5455 $vatratecleaned = $vatrate; 5456 $reg = array(); 5457 if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "xx (yy)" 5458 $vatratecleaned = trim($reg[1]); 5459 $vatratecode = $reg[2]; 5460 } 5461 5462 /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code) 5463 { 5464 return 0; 5465 }*/ 5466 5467 // Some test to guess with no need to make database access 5468 if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax 5469 if ($local == 1) { 5470 if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") { 5471 return 0; 5472 } 5473 if ($thirdparty_seller->id == $mysoc->id) { 5474 if (!$thirdparty_buyer->localtax1_assuj) { 5475 return 0; 5476 } 5477 } else { 5478 if (!$thirdparty_seller->localtax1_assuj) { 5479 return 0; 5480 } 5481 } 5482 } 5483 5484 if ($local == 2) { 5485 //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0; 5486 if (!$mysoc->localtax2_assuj) { 5487 return 0; // If main vat is 0, IRPF may be different than 0. 5488 } 5489 if ($thirdparty_seller->id == $mysoc->id) { 5490 if (!$thirdparty_buyer->localtax2_assuj) { 5491 return 0; 5492 } 5493 } else { 5494 if (!$thirdparty_seller->localtax2_assuj) { 5495 return 0; 5496 } 5497 } 5498 } 5499 } else { 5500 if ($local == 1 && !$thirdparty_seller->localtax1_assuj) { 5501 return 0; 5502 } 5503 if ($local == 2 && !$thirdparty_seller->localtax2_assuj) { 5504 return 0; 5505 } 5506 } 5507 5508 // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on. 5509 if (in_array($mysoc->country_code, array('ES'))) { 5510 $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1; 5511 } 5512 5513 // Search local taxes 5514 if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) { 5515 if ($local == 1) { 5516 if ($thirdparty_seller != $mysoc) { 5517 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate 5518 return $thirdparty_seller->localtax1_value; 5519 } 5520 } else { // i am the seller 5521 if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat. 5522 return $conf->global->MAIN_INFO_VALUE_LOCALTAX1; 5523 } 5524 } 5525 } 5526 if ($local == 2) { 5527 if ($thirdparty_seller != $mysoc) { 5528 if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate 5529 // TODO We should also return value defined on thirdparty only if defined 5530 return $thirdparty_seller->localtax2_value; 5531 } 5532 } else { // i am the seller 5533 if (in_array($mysoc->country_code, array('ES'))) { 5534 return $thirdparty_buyer->localtax2_value; 5535 } else { 5536 return $conf->global->MAIN_INFO_VALUE_LOCALTAX2; 5537 } 5538 } 5539 } 5540 } 5541 5542 // By default, search value of local tax on line of common tax 5543 $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type"; 5544 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; 5545 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'"; 5546 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1"; 5547 if (!empty($vatratecode)) { 5548 $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority 5549 } else { 5550 $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'"; 5551 } 5552 5553 $resql = $db->query($sql); 5554 5555 if ($resql) { 5556 $obj = $db->fetch_object($resql); 5557 if ($obj) { 5558 if ($local == 1) { 5559 return $obj->localtax1; 5560 } elseif ($local == 2) { 5561 return $obj->localtax2; 5562 } 5563 } 5564 } 5565 5566 return 0; 5567} 5568 5569 5570/** 5571 * Return true if LocalTax (1 or 2) is unique. 5572 * Example: If localtax1 is 5 on line with highest common vat rate, return true 5573 * Example: If localtax1 is 5:8:15 on line with highest common vat rate, return false 5574 * 5575 * @param int $local Local tax to test (1 or 2) 5576 * @return boolean True if LocalTax have multiple values, False if not 5577 */ 5578function isOnlyOneLocalTax($local) 5579{ 5580 $tax = get_localtax_by_third($local); 5581 5582 $valors = explode(":", $tax); 5583 5584 if (count($valors) > 1) { 5585 return false; 5586 } else { 5587 return true; 5588 } 5589} 5590 5591/** 5592 * Get values of localtaxes (1 or 2) for company country for the common vat with the highest value 5593 * 5594 * @param int $local LocalTax to get 5595 * @return number Values of localtax 5596 */ 5597function get_localtax_by_third($local) 5598{ 5599 global $db, $mysoc; 5600 $sql = "SELECT t.localtax1, t.localtax2 "; 5601 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=t.fk_pays"; 5602 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux=("; 5603 $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"; 5604 $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1"; 5605 $sql .= " )"; 5606 5607 $resql = $db->query($sql); 5608 if ($resql) { 5609 $obj = $db->fetch_object($resql); 5610 if ($local == 1) { 5611 return $obj->localtax1; 5612 } elseif ($local == 2) { 5613 return $obj->localtax2; 5614 } 5615 } 5616 5617 return 0; 5618} 5619 5620 5621/** 5622 * Get tax (VAT) main information from Id. 5623 * You can also call getLocalTaxesFromRate() after to get only localtax fields. 5624 * 5625 * @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. 5626 * @param Societe $buyer Company object 5627 * @param Societe $seller Company object 5628 * @param int $firstparamisid 1 if first param is id into table (use this if you can) 5629 * @return array array('rowid'=> , 'code'=> ...) 5630 * @see getLocalTaxesFromRate() 5631 */ 5632function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1) 5633{ 5634 global $db, $mysoc; 5635 5636 dol_syslog("getTaxesFromId vat id or rate = ".$vatrate); 5637 5638 // Search local taxes 5639 $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,"; 5640 $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type"; 5641 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t"; 5642 if ($firstparamisid) { 5643 $sql .= " WHERE t.rowid = ".(int) $vatrate; 5644 } else { 5645 $vatratecleaned = $vatrate; 5646 $vatratecode = ''; 5647 $reg = array(); 5648 if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "xx (yy)" 5649 $vatratecleaned = $reg[1]; 5650 $vatratecode = $reg[2]; 5651 } 5652 5653 $sql .= ", ".MAIN_DB_PREFIX."c_country as c"; 5654 /*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 ?? 5655 else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/ 5656 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'"; 5657 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1"; 5658 if ($vatratecode) { 5659 $sql .= " AND t.code = '".$db->escape($vatratecode)."'"; 5660 } 5661 } 5662 5663 $resql = $db->query($sql); 5664 if ($resql) { 5665 $obj = $db->fetch_object($resql); 5666 if ($obj) { 5667 return array( 5668 'rowid'=>$obj->rowid, 5669 'code'=>$obj->code, 5670 'rate'=>$obj->rate, 5671 'localtax1'=>$obj->localtax1, 5672 'localtax1_type'=>$obj->localtax1_type, 5673 'localtax2'=>$obj->localtax2, 5674 'localtax2_type'=>$obj->localtax2_type, 5675 'npr'=>$obj->npr, 5676 'accountancy_code_sell'=>$obj->accountancy_code_sell, 5677 'accountancy_code_buy'=>$obj->accountancy_code_buy 5678 ); 5679 } else { 5680 return array(); 5681 } 5682 } else { 5683 dol_print_error($db); 5684 } 5685 5686 return array(); 5687} 5688 5689/** 5690 * Get type and rate of localtaxes for a particular vat rate/country of a thirdparty. 5691 * This does not take into account the seller setup if subject to vat or not, only country. 5692 * 5693 * TODO This function is ALSO called to retrieve type for building PDF. Such call of function must be removed. 5694 * Instead this function must be called when adding a line to get the array of possible values for localtax and type, and then 5695 * provide the selected value to the function calcul_price_total. 5696 * 5697 * @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. 5698 * @param int $local Number of localtax (1 or 2, or 0 to return 1 & 2) 5699 * @param Societe $buyer Company object 5700 * @param Societe $seller Company object 5701 * @param int $firstparamisid 1 if first param is ID into table instead of Rate+code (use this if you can) 5702 * @return array array(localtax_type1(1-6 or 0 if not found), rate localtax1, localtax_type2, rate localtax2, accountancycodecust, accountancycodesupp) 5703 * @see getTaxesFromId() 5704 */ 5705function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0) 5706{ 5707 global $db, $mysoc; 5708 5709 dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local); 5710 5711 // Search local taxes 5712 $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"; 5713 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t"; 5714 if ($firstparamisid) { 5715 $sql .= " WHERE t.rowid = ".(int) $vatrate; 5716 } else { 5717 $vatratecleaned = $vatrate; 5718 $vatratecode = ''; 5719 $reg = array(); 5720 if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "x.x (yy)" 5721 $vatratecleaned = $reg[1]; 5722 $vatratecode = $reg[2]; 5723 } 5724 5725 $sql .= ", ".MAIN_DB_PREFIX."c_country as c"; 5726 if ($mysoc->country_code == 'ES') { 5727 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // local tax in spain use the buyer country ?? 5728 } else { 5729 $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape(empty($seller->country_code) ? $mysoc->country_code : $seller->country_code)."'"; 5730 } 5731 $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1"; 5732 if ($vatratecode) { 5733 $sql .= " AND t.code = '".$db->escape($vatratecode)."'"; 5734 } 5735 } 5736 5737 $resql = $db->query($sql); 5738 if ($resql) { 5739 $obj = $db->fetch_object($resql); 5740 5741 if ($obj) { 5742 $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : ''); 5743 5744 if ($local == 1) { 5745 return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy); 5746 } elseif ($local == 2) { 5747 return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy); 5748 } else { 5749 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); 5750 } 5751 } 5752 } 5753 5754 return array(); 5755} 5756 5757/** 5758 * Return vat rate of a product in a particular country, or default country vat if product is unknown. 5759 * Function called by get_default_tva(). 5760 * 5761 * @param int $idprod Id of product or 0 if not a predefined product 5762 * @param Societe $thirdpartytouse Thirdparty with a ->country_code defined (FR, US, IT, ...) 5763 * @param int $idprodfournprice Id product_fournisseur_price (for "supplier" proposal/order/invoice) 5764 * @return float|string Vat rate to use with format 5.0 or '5.0 (XXX)' 5765 * @see get_product_localtax_for_country() 5766 */ 5767function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0) 5768{ 5769 global $db, $conf, $mysoc; 5770 5771 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; 5772 5773 $ret = 0; 5774 $found = 0; 5775 5776 if ($idprod > 0) { 5777 // Load product 5778 $product = new Product($db); 5779 $result = $product->fetch($idprod); 5780 5781 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If country to consider is ours 5782 if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object 5783 $product->get_buyprice($idprodfournprice, 0, 0, 0); 5784 $ret = $product->vatrate_supplier; 5785 if ($product->default_vat_code) { 5786 $ret .= ' ('.$product->default_vat_code.')'; 5787 } 5788 } else { 5789 $ret = $product->tva_tx; // Default vat of product we defined 5790 if ($product->default_vat_code) { 5791 $ret .= ' ('.$product->default_vat_code.')'; 5792 } 5793 } 5794 $found = 1; 5795 } else { 5796 // TODO Read default product vat according to product and another countrycode. 5797 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule. 5798 } 5799 } 5800 5801 if (!$found) { 5802 if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) { 5803 // If vat of product for the country not found or not defined, we return the first higher vat of country. 5804 $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code"; 5805 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; 5806 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'"; 5807 $sql .= " ORDER BY t.taux DESC, t.code ASC, t.recuperableonly ASC"; 5808 $sql .= $db->plimit(1); 5809 5810 $resql = $db->query($sql); 5811 if ($resql) { 5812 $obj = $db->fetch_object($resql); 5813 if ($obj) { 5814 $ret = $obj->vat_rate; 5815 if ($obj->default_vat_code) { 5816 $ret .= ' ('.$obj->default_vat_code.')'; 5817 } 5818 } 5819 $db->free($sql); 5820 } else { 5821 dol_print_error($db); 5822 } 5823 } else { 5824 $ret = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS; // Forced value if autodetect fails 5825 } 5826 } 5827 5828 dol_syslog("get_product_vat_for_country: ret=".$ret); 5829 return $ret; 5830} 5831 5832/** 5833 * Return localtax vat rate of a product in a particular country or default country vat if product is unknown 5834 * 5835 * @param int $idprod Id of product 5836 * @param int $local 1 for localtax1, 2 for localtax 2 5837 * @param Societe $thirdpartytouse Thirdparty with a ->country_code defined (FR, US, IT, ...) 5838 * @return int <0 if KO, Vat rate if OK 5839 * @see get_product_vat_for_country() 5840 */ 5841function get_product_localtax_for_country($idprod, $local, $thirdpartytouse) 5842{ 5843 global $db, $mysoc; 5844 5845 if (!class_exists('Product')) { 5846 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; 5847 } 5848 5849 $ret = 0; 5850 $found = 0; 5851 5852 if ($idprod > 0) { 5853 // Load product 5854 $product = new Product($db); 5855 $result = $product->fetch($idprod); 5856 5857 if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours 5858 /* Not defined yet, so we don't use this 5859 if ($local==1) $ret=$product->localtax1_tx; 5860 elseif ($local==2) $ret=$product->localtax2_tx; 5861 $found=1; 5862 */ 5863 } else { 5864 // TODO Read default product vat according to product and another countrycode. 5865 // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule. 5866 } 5867 } 5868 5869 if (!$found) { 5870 // If vat of product for the country not found or not defined, we return higher vat of country. 5871 $sql = "SELECT taux as vat_rate, localtax1, localtax2"; 5872 $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; 5873 $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'"; 5874 $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC"; 5875 $sql .= $db->plimit(1); 5876 5877 $resql = $db->query($sql); 5878 if ($resql) { 5879 $obj = $db->fetch_object($resql); 5880 if ($obj) { 5881 if ($local == 1) { 5882 $ret = $obj->localtax1; 5883 } elseif ($local == 2) { 5884 $ret = $obj->localtax2; 5885 } 5886 } 5887 } else { 5888 dol_print_error($db); 5889 } 5890 } 5891 5892 dol_syslog("get_product_localtax_for_country: ret=".$ret); 5893 return $ret; 5894} 5895 5896/** 5897 * Function that return vat rate of a product line (according to seller, buyer and product vat rate) 5898 * VATRULE 1: Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle. 5899 * VATRULE 2: Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle. 5900 * VATRULE 3: 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. 5901 * VATRULE 4: Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle 5902 * VATRULE 5: Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle 5903 * VATRULE 6: Sinon TVA proposee par defaut=0. Fin de regle. 5904 * 5905 * @param Societe $thirdparty_seller Objet societe vendeuse 5906 * @param Societe $thirdparty_buyer Objet societe acheteuse 5907 * @param int $idprod Id product 5908 * @param int $idprodfournprice Id product_fournisseur_price (for supplier order/invoice) 5909 * @return float|string Vat rate to use with format 5.0 or '5.0 (XXX)', -1 if we can't guess it 5910 * @see get_default_npr(), get_default_localtax() 5911 */ 5912function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0) 5913{ 5914 global $conf; 5915 5916 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; 5917 5918 // Note: possible values for tva_assuj are 0/1 or franchise/reel 5919 $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; 5920 5921 $seller_country_code = $thirdparty_seller->country_code; 5922 $seller_in_cee = isInEEC($thirdparty_seller); 5923 5924 $buyer_country_code = $thirdparty_buyer->country_code; 5925 $buyer_in_cee = isInEEC($thirdparty_buyer); 5926 5927 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 : '')); 5928 5929 // 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) 5930 // we use the buyer VAT. 5931 if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) { 5932 if ($seller_in_cee && $buyer_in_cee) { 5933 $isacompany = $thirdparty_buyer->isACompany(); 5934 if ($isacompany && !empty($conf->global->MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL)) { 5935 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; 5936 if (!isValidVATID($thirdparty_buyer)) { 5937 $isacompany = 0; 5938 } 5939 } 5940 5941 if (!$isacompany) { 5942 //print 'VATRULE 0'; 5943 return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice); 5944 } 5945 } 5946 } 5947 5948 // If seller does not use VAT 5949 if (!$seller_use_vat) { 5950 //print 'VATRULE 1'; 5951 return 0; 5952 } 5953 5954 // 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. 5955 5956 // Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle. 5957 if (($seller_country_code == $buyer_country_code) 5958 || (in_array($seller_country_code, array('FR,MC')) && in_array($buyer_country_code, array('FR', 'MC')))) { // Warning ->country_code not always defined 5959 //print 'VATRULE 2'; 5960 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice); 5961 } 5962 5963 // 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. 5964 // 'VATRULE 3' - Not supported 5965 5966 // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle 5967 // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle 5968 if (($seller_in_cee && $buyer_in_cee)) { 5969 $isacompany = $thirdparty_buyer->isACompany(); 5970 if ($isacompany && !empty($conf->global->MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL)) { 5971 require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; 5972 if (!isValidVATID($thirdparty_buyer)) { 5973 $isacompany = 0; 5974 } 5975 } 5976 5977 if (!$isacompany) { 5978 //print 'VATRULE 4'; 5979 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice); 5980 } else { 5981 //print 'VATRULE 5'; 5982 return 0; 5983 } 5984 } 5985 5986 // Si (vendeur dans Communaute europeene et acheteur hors Communaute europeenne et acheteur particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle 5987 // I don't see any use case that need this rule. 5988 if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee)) { 5989 $isacompany = $thirdparty_buyer->isACompany(); 5990 if (!$isacompany) { 5991 return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice); 5992 //print 'VATRULE extra'; 5993 } 5994 } 5995 5996 // Sinon la TVA proposee par defaut=0. Fin de regle. 5997 // Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe 5998 //print 'VATRULE 6'; 5999 return 0; 6000} 6001 6002 6003/** 6004 * Fonction qui renvoie si tva doit etre tva percue recuperable 6005 * 6006 * @param Societe $thirdparty_seller Thirdparty seller 6007 * @param Societe $thirdparty_buyer Thirdparty buyer 6008 * @param int $idprod Id product 6009 * @param int $idprodfournprice Id supplier price for product 6010 * @return float 0 or 1 6011 * @see get_default_tva(), get_default_localtax() 6012 */ 6013function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0) 6014{ 6015 global $db; 6016 6017 if ($idprodfournprice > 0) { 6018 if (!class_exists('ProductFournisseur')) { 6019 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php'; 6020 } 6021 $prodprice = new ProductFournisseur($db); 6022 $prodprice->fetch_product_fournisseur_price($idprodfournprice); 6023 return $prodprice->fourn_tva_npr; 6024 } elseif ($idprod > 0) { 6025 if (!class_exists('Product')) { 6026 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; 6027 } 6028 $prod = new Product($db); 6029 $prod->fetch($idprod); 6030 return $prod->tva_npr; 6031 } 6032 6033 return 0; 6034} 6035 6036/** 6037 * Function that return localtax of a product line (according to seller, buyer and product vat rate) 6038 * Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle. 6039 * Si le (pays vendeur = pays acheteur) alors TVA par defaut=TVA du produit vendu. Fin de regle. 6040 * Sinon TVA proposee par defaut=0. Fin de regle. 6041 * 6042 * @param Societe $thirdparty_seller Thirdparty seller 6043 * @param Societe $thirdparty_buyer Thirdparty buyer 6044 * @param int $local Localtax to process (1 or 2) 6045 * @param int $idprod Id product 6046 * @return integer localtax, -1 si ne peut etre determine 6047 * @see get_default_tva(), get_default_npr() 6048 */ 6049function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0) 6050{ 6051 global $mysoc; 6052 6053 if (!is_object($thirdparty_seller)) { 6054 return -1; 6055 } 6056 if (!is_object($thirdparty_buyer)) { 6057 return -1; 6058 } 6059 6060 if ($local == 1) { // Localtax 1 6061 if ($mysoc->country_code == 'ES') { 6062 if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) { 6063 return 0; 6064 } 6065 } else { 6066 // Si vendeur non assujeti a Localtax1, localtax1 par default=0 6067 if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) { 6068 return 0; 6069 } 6070 if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') { 6071 return 0; 6072 } 6073 } 6074 } elseif ($local == 2) { //I Localtax 2 6075 // Si vendeur non assujeti a Localtax2, localtax2 par default=0 6076 if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) { 6077 return 0; 6078 } 6079 if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') { 6080 return 0; 6081 } 6082 } 6083 6084 if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) { 6085 return get_product_localtax_for_country($idprod, $local, $thirdparty_seller); 6086 } 6087 6088 return 0; 6089} 6090 6091/** 6092 * Return yes or no in current language 6093 * 6094 * @param string|int $yesno Value to test (1, 'yes', 'true' or 0, 'no', 'false') 6095 * @param integer $case 1=Yes/No, 0=yes/no, 2=Disabled checkbox, 3=Disabled checkbox + Yes/No 6096 * @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. 6097 * @return string HTML string 6098 */ 6099function yn($yesno, $case = 1, $color = 0) 6100{ 6101 global $langs; 6102 $result = 'unknown'; 6103 $classname = ''; 6104 if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') { // A mettre avant test sur no a cause du == 0 6105 $result = $langs->trans('yes'); 6106 if ($case == 1 || $case == 3) { 6107 $result = $langs->trans("Yes"); 6108 } 6109 if ($case == 2) { 6110 $result = '<input type="checkbox" value="1" checked disabled>'; 6111 } 6112 if ($case == 3) { 6113 $result = '<input type="checkbox" value="1" checked disabled> '.$result; 6114 } 6115 6116 $classname = 'ok'; 6117 } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') { 6118 $result = $langs->trans("no"); 6119 if ($case == 1 || $case == 3) { 6120 $result = $langs->trans("No"); 6121 } 6122 if ($case == 2) { 6123 $result = '<input type="checkbox" value="0" disabled>'; 6124 } 6125 if ($case == 3) { 6126 $result = '<input type="checkbox" value="0" disabled> '.$result; 6127 } 6128 6129 if ($color == 2) { 6130 $classname = 'ok'; 6131 } else { 6132 $classname = 'error'; 6133 } 6134 } 6135 if ($color) { 6136 return '<font class="'.$classname.'">'.$result.'</font>'; 6137 } 6138 return $result; 6139} 6140 6141/** 6142 * Return a path to have a the directory according to object where files are stored. 6143 * New usage: $conf->module->multidir_output[$object->entity].'/'.get_exdir(0, 0, 0, 1, $object, '').'/' 6144 * or: $conf->module->dir_output.'/'.get_exdir(0, 0, 0, 0, $object, '') if multidir_output not defined. 6145 * Example out with new usage: $object is invoice -> 'INYYMM-ABCD' 6146 * 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/" 6147 * 6148 * @param string|int $num Id of object (deprecated, $object will be used in future) 6149 * @param int $level Level of subdirs to return (1, 2 or 3 levels). (deprecated, global option will be used in future) 6150 * @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) 6151 * @param int $withoutslash 0=With slash at end (except if '/', we return ''), 1=without slash at end 6152 * @param Object $object Object to use to get ref to forge the path. 6153 * @param string $modulepart Type of object ('invoice_supplier, 'donation', 'invoice', ...'). Use '' for autodetect from $object. 6154 * @return string Dir to use ending. Example '' or '1/' or '1/2/' 6155 */ 6156function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '') 6157{ 6158 global $conf; 6159 6160 if (empty($modulepart) && !empty($object->module)) { 6161 $modulepart = $object->module; 6162 } 6163 6164 $path = ''; 6165 6166 $arrayforoldpath = array('cheque', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment'); 6167 if (!empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) { 6168 $arrayforoldpath[] = 'product'; 6169 } 6170 if (!empty($level) && in_array($modulepart, $arrayforoldpath)) { 6171 // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided. 6172 if (empty($alpha)) { 6173 $num = preg_replace('/([^0-9])/i', '', $num); 6174 } else { 6175 $num = preg_replace('/^.*\-/i', '', $num); 6176 } 6177 $num = substr("000".$num, -$level); 6178 if ($level == 1) { 6179 $path = substr($num, 0, 1); 6180 } 6181 if ($level == 2) { 6182 $path = substr($num, 1, 1).'/'.substr($num, 0, 1); 6183 } 6184 if ($level == 3) { 6185 $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1); 6186 } 6187 } else { 6188 // We will enhance here a common way of forging path for document storage. 6189 // In a future, we may distribute directories on several levels depending on setup and object. 6190 // Here, $object->id, $object->ref and $modulepart are required. 6191 //var_dump($modulepart); 6192 $path = dol_sanitizeFileName(empty($object->ref) ? (string) $object->id : $object->ref); 6193 } 6194 6195 if (empty($withoutslash) && !empty($path)) { 6196 $path .= '/'; 6197 } 6198 6199 return $path; 6200} 6201 6202/** 6203 * Creation of a directory (this can create recursive subdir) 6204 * 6205 * @param string $dir Directory to create (Separator must be '/'. Example: '/mydir/mysubdir') 6206 * @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) 6207 * @param string|null $newmask Mask for new file (Defaults to $conf->global->MAIN_UMASK or 0755 if unavailable). Example: '0444' 6208 * @return int < 0 if KO, 0 = already exists, > 0 if OK 6209 */ 6210function dol_mkdir($dir, $dataroot = '', $newmask = null) 6211{ 6212 global $conf; 6213 6214 dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO); 6215 6216 $dir_osencoded = dol_osencode($dir); 6217 if (@is_dir($dir_osencoded)) { 6218 return 0; 6219 } 6220 6221 $nberr = 0; 6222 $nbcreated = 0; 6223 6224 $ccdir = ''; 6225 if (!empty($dataroot)) { 6226 // Remove data root from loop 6227 $dir = str_replace($dataroot.'/', '', $dir); 6228 $ccdir = $dataroot.'/'; 6229 } 6230 6231 $cdir = explode("/", $dir); 6232 $num = count($cdir); 6233 for ($i = 0; $i < $num; $i++) { 6234 if ($i > 0) { 6235 $ccdir .= '/'.$cdir[$i]; 6236 } else { 6237 $ccdir .= $cdir[$i]; 6238 } 6239 if (preg_match("/^.:$/", $ccdir, $regs)) { 6240 continue; // Si chemin Windows incomplet, on poursuit par rep suivant 6241 } 6242 6243 // Attention, le is_dir() peut echouer bien que le rep existe. 6244 // (ex selon config de open_basedir) 6245 if ($ccdir) { 6246 $ccdir_osencoded = dol_osencode($ccdir); 6247 if (!@is_dir($ccdir_osencoded)) { 6248 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG); 6249 6250 umask(0); 6251 $dirmaskdec = octdec($newmask); 6252 if (empty($newmask)) { 6253 $dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK); 6254 } 6255 $dirmaskdec |= octdec('0111'); // Set x bit required for directories 6256 if (!@mkdir($ccdir_osencoded, $dirmaskdec)) { 6257 // Si le is_dir a renvoye une fausse info, alors on passe ici. 6258 dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING); 6259 $nberr++; 6260 } else { 6261 dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG); 6262 $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore 6263 $nbcreated++; 6264 } 6265 } else { 6266 $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores 6267 } 6268 } 6269 } 6270 return ($nberr ? -$nberr : $nbcreated); 6271} 6272 6273 6274/** 6275 * Return picto saying a field is required 6276 * 6277 * @return string Chaine avec picto obligatoire 6278 */ 6279function picto_required() 6280{ 6281 return '<span class="fieldrequired">*</span>'; 6282} 6283 6284 6285/** 6286 * Clean a string from all HTML tags and entities. 6287 * This function differs from strip_tags because: 6288 * - <br> are replaced with \n if removelinefeed=0 or 1 6289 * - if entities are found, they are decoded BEFORE the strip 6290 * - you can decide to convert line feed into a space 6291 * 6292 * @param string $stringtoclean String to clean 6293 * @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..." 6294 * @param string $pagecodeto Encoding of input/output string 6295 * @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') 6296 * @param integer $removedoublespaces Replace double space into one space 6297 * @return string String cleaned 6298 * 6299 * @see dol_escape_htmltag() strip_tags() dol_string_onlythesehtmltags() dol_string_neverthesehtmltags(), dolStripPhpCode() 6300 */ 6301function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1) 6302{ 6303 if ($removelinefeed == 2) { 6304 $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean); 6305 } 6306 $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean); 6307 6308 // 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) 6309 $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto); 6310 6311 $temp = str_replace('< ', '__ltspace__', $temp); 6312 6313 if ($strip_tags) { 6314 $temp = strip_tags($temp); 6315 } else { 6316 $temp = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning 6317 $pattern = "/<[^<>]+>/"; 6318 // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a> 6319 $temp = preg_replace($pattern, "", $temp); // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021 6320 $temp = preg_replace($pattern, "", $temp); // pass 2 - $temp after pass 2: 0000-021 6321 // Remove '<' into remainging, so remove non closing html tags like '<abc' or '<<abc'. Note: '<123abc' is not a html tag (can be kept), but '<abc123' is (must be removed). 6322 $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp); 6323 } 6324 6325 $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto); 6326 6327 // Remove also carriage returns 6328 if ($removelinefeed == 1) { 6329 $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp); 6330 } 6331 6332 // And double quotes 6333 if ($removedoublespaces) { 6334 while (strpos($temp, " ")) { 6335 $temp = str_replace(" ", " ", $temp); 6336 } 6337 } 6338 6339 $temp = str_replace('__ltspace__', '< ', $temp); 6340 6341 return trim($temp); 6342} 6343 6344/** 6345 * Clean a string to keep only desirable HTML tags. 6346 * WARNING: This also clean HTML comments (used to obfuscate tag name). 6347 * 6348 * @param string $stringtoclean String to clean 6349 * @param int $cleanalsosomestyles Remove absolute/fixed positioning from inline styles 6350 * @param int $removeclassattribute 1=Remove the class attribute from tags 6351 * @param int $cleanalsojavascript Remove also occurence of 'javascript:'. 6352 * @param int $allowiframe Allow iframe tags. 6353 * @return string String cleaned 6354 * 6355 * @see dol_escape_htmltag() strip_tags() dol_string_nohtmltag() dol_string_neverthesehtmltags() 6356 */ 6357function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0) 6358{ 6359 $allowed_tags = array( 6360 "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li", "link", 6361 "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" 6362 ); 6363 if ($allowiframe) { 6364 $allowed_tags[] = "iframe"; 6365 } 6366 6367 $allowed_tags_string = join("><", $allowed_tags); 6368 $allowed_tags_string = '<'.$allowed_tags_string.'>'; 6369 6370 $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags 6371 6372 $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0); 6373 6374 $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean); 6375 6376 $stringtoclean = preg_replace('/:/i', ':', $stringtoclean); 6377 $stringtoclean = preg_replace('/:|�+58|:/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...' 6378 $stringtoclean = preg_replace('/javascript\s*:/i', '', $stringtoclean); 6379 6380 $temp = strip_tags($stringtoclean, $allowed_tags_string); // Warning: This remove also undesired </> changing string obfuscated with </> that pass injection detection into harmfull string 6381 6382 if ($cleanalsosomestyles) { // Clean for remaining html tags 6383 $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 6384 } 6385 if ($removeclassattribute) { // Clean for remaining html tags 6386 $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp); 6387 } 6388 6389 // Remove 'javascript:' that we should not find into a text with 6390 // 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 a GETPOST(.., powerfullfilter)). 6391 if ($cleanalsojavascript) { 6392 $temp = preg_replace('/javascript\s*:/i', '', $temp); 6393 } 6394 6395 $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE 6396 6397 return $temp; 6398} 6399 6400 6401/** 6402 * Clean a string from some undesirable HTML tags. 6403 * Note. Not as secured as dol_string_onlythesehtmltags(). 6404 * 6405 * @param string $stringtoclean String to clean 6406 * @param array $allowed_attributes Array of tags not allowed 6407 * @return string String cleaned 6408 * 6409 * @see dol_escape_htmltag() strip_tags() dol_string_nohtmltag() dol_string_onlythesehtmltags() dol_string_neverthesehtmltags() 6410 */ 6411function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = array("allow", "allowfullscreen", "alt", "class", "contenteditable", "data-html", "frameborder", "height", "href", "id", "name", "src", "style", "target", "title", "width")) 6412{ 6413 if (class_exists('DOMDocument') && !empty($stringtoclean)) { 6414 $stringtoclean = '<html><body>'.$stringtoclean.'</body></html>'; 6415 6416 $dom = new DOMDocument(); 6417 $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL); 6418 if (is_object($dom)) { 6419 for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) { 6420 for ($attrs = $els->item($i)->attributes, $ii = $attrs->length - 1; $ii >= 0; $ii--) { 6421 // Delete attribute if not into allowed_attributes 6422 if (! empty($attrs->item($ii)->name) && ! in_array($attrs->item($ii)->name, $allowed_attributes)) { 6423 $els->item($i)->removeAttribute($attrs->item($ii)->name); 6424 } 6425 } 6426 } 6427 } 6428 6429 $return = $dom->saveHTML(); 6430 //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>"; 6431 6432 $return = preg_replace('/^<html><body>/', '', $return); 6433 $return = preg_replace('/<\/body><\/html>$/', '', $return); 6434 return $return; 6435 } else { 6436 return $stringtoclean; 6437 } 6438} 6439 6440/** 6441 * Clean a string from some undesirable HTML tags. 6442 * Note. Not as secured as dol_string_onlythesehtmltags(). 6443 * 6444 * @param string $stringtoclean String to clean 6445 * @param array $disallowed_tags Array of tags not allowed 6446 * @param string $cleanalsosomestyles Clean also some tags 6447 * @return string String cleaned 6448 * 6449 * @see dol_escape_htmltag() strip_tags() dol_string_nohtmltag() dol_string_onlythesehtmltags() dol_string_onlythesehtmlattributes() 6450 */ 6451function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0) 6452{ 6453 $temp = $stringtoclean; 6454 foreach ($disallowed_tags as $tagtoremove) { 6455 $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp); 6456 $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp); 6457 } 6458 6459 if ($cleanalsosomestyles) { 6460 $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 6461 } 6462 6463 return $temp; 6464} 6465 6466 6467/** 6468 * Return first line of text. Cut will depends if content is HTML or not. 6469 * 6470 * @param string $text Input text 6471 * @param int $nboflines Nb of lines to get (default is 1 = first line only) 6472 * @param string $charset Charset of $text string (UTF-8 by default) 6473 * @return string Output text 6474 * @see dol_nboflines_bis(), dol_string_nohtmltag(), dol_escape_htmltag() 6475 */ 6476function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8') 6477{ 6478 if ($nboflines == 1) { 6479 if (dol_textishtml($text)) { 6480 $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters 6481 $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters 6482 } else { 6483 $firstline = preg_replace('/[\n\r].*/', '', $text); 6484 } 6485 return $firstline.((strlen($firstline) != strlen($text)) ? '...' : ''); 6486 } else { 6487 $ishtml = 0; 6488 if (dol_textishtml($text)) { 6489 $text = preg_replace('/\n/', '', $text); 6490 $ishtml = 1; 6491 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " "); 6492 } else { 6493 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " "); 6494 } 6495 6496 $text = strtr($text, $repTable); 6497 if ($charset == 'UTF-8') { 6498 $pattern = '/(<br[^>]*>)/Uu'; 6499 } else { 6500 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support 6501 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag. 6502 } 6503 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 6504 6505 $firstline = ''; 6506 $i = 0; 6507 $nba = count($a); // 2x nb of lines in $a because $a contains also a line for each new line separator 6508 while (($i < $nba) && ($i < ($nboflines * 2))) { 6509 if ($i % 2 == 0) { 6510 $firstline .= $a[$i]; 6511 } elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) { 6512 $firstline .= ($ishtml ? "<br>\n" : "\n"); 6513 } 6514 $i++; 6515 } 6516 unset($a); 6517 return $firstline.(($i < $nba) ? '...' : ''); 6518 } 6519} 6520 6521 6522/** 6523 * Replace CRLF in string with a HTML BR tag. 6524 * 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. 6525 * 6526 * @param string $stringtoencode String to encode 6527 * @param int $nl2brmode 0=Adding br before \n, 1=Replacing \n by br 6528 * @param bool $forxml false=Use <br>, true=Use <br /> 6529 * @return string String encoded 6530 * @see dol_nboflines(), dolGetFirstLineOfText() 6531 */ 6532function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false) 6533{ 6534 if (!$nl2brmode) { 6535 return nl2br($stringtoencode, $forxml); 6536 } else { 6537 $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode); 6538 return $ret; 6539 } 6540} 6541 6542 6543/** 6544 * This function is called to encode a string into a HTML string but differs from htmlentities because 6545 * a detection is done before to see if text is already HTML or not. Also, all entities but &,<,>," are converted. 6546 * This permits to encode special chars to entities with no double encoding for already encoded HTML strings. 6547 * This function also remove last EOL or BR if $removelasteolbr=1 (default). 6548 * For PDF usage, you can show text by 2 ways: 6549 * - writeHTMLCell -> param must be encoded into HTML. 6550 * - MultiCell -> param must not be encoded into HTML. 6551 * Because writeHTMLCell convert also \n into <br>, if function 6552 * is used to build PDF, nl2brmode must be 1. 6553 * 6554 * @param string $stringtoencode String to encode 6555 * @param int $nl2brmode 0=Adding br before \n, 1=Replacing \n by br (for use with FPDF writeHTMLCell function for example) 6556 * @param string $pagecodefrom Pagecode stringtoencode is encoded 6557 * @param int $removelasteolbr 1=Remove last br or lasts \n (default), 0=Do nothing 6558 * @return string String encoded 6559 */ 6560function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1) 6561{ 6562 $newstring = $stringtoencode; 6563 if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not 6564 $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. 6565 if ($removelasteolbr) { 6566 $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one) 6567 } 6568 $newstring = strtr($newstring, array('&'=>'__and__', '<'=>'__lt__', '>'=>'__gt__', '"'=>'__dquot__')); 6569 $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding 6570 $newstring = strtr($newstring, array('__and__'=>'&', '__lt__'=>'<', '__gt__'=>'>', '__dquot__'=>'"')); 6571 } else { 6572 if ($removelasteolbr) { 6573 $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several) 6574 } 6575 $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode); 6576 } 6577 // Other substitutions that htmlentities does not do 6578 //$newstring=str_replace(chr(128),'€',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages 6579 return $newstring; 6580} 6581 6582/** 6583 * This function is called to decode a HTML string (it decodes entities and br tags) 6584 * 6585 * @param string $stringtodecode String to decode 6586 * @param string $pagecodeto Page code for result 6587 * @return string String decoded 6588 */ 6589function dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto = 'UTF-8') 6590{ 6591 $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto); 6592 $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret); 6593 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret); 6594 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret); 6595 $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret); 6596 return $ret; 6597} 6598 6599/** 6600 * This function remove all ending \n and br at end 6601 * 6602 * @param string $stringtodecode String to decode 6603 * @return string String decoded 6604 */ 6605function dol_htmlcleanlastbr($stringtodecode) 6606{ 6607 $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $stringtodecode); 6608 return $ret; 6609} 6610 6611/** 6612 * Replace html_entity_decode functions to manage errors 6613 * 6614 * @param string $a Operand a 6615 * @param string $b Operand b (ENT_QUOTES|ENT_HTML5=convert simple, double quotes, colon, e accent, ...) 6616 * @param string $c Operand c 6617 * @param string $keepsomeentities Entities but &, <, >, " are not converted. 6618 * @return string String decoded 6619 */ 6620function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0) 6621{ 6622 $newstring = $a; 6623 if ($keepsomeentities) { 6624 $newstring = strtr($newstring, array('&'=>'__andamp__', '<'=>'__andlt__', '>'=>'__andgt__', '"'=>'__dquot__')); 6625 } 6626 $newstring = html_entity_decode($newstring, $b, $c); 6627 if ($keepsomeentities) { 6628 $newstring = strtr($newstring, array('__andamp__'=>'&', '__andlt__'=>'<', '__andgt__'=>'>', '__dquot__'=>'"')); 6629 } 6630 return $newstring; 6631} 6632 6633/** 6634 * Replace htmlentities functions. 6635 * Goal of this function is to be sure to have default values of htmlentities that match what we need. 6636 * 6637 * @param string $string The input string to encode 6638 * @param int $flags Flags (see PHP doc above) 6639 * @param string $encoding Encoding page code 6640 * @param bool $double_encode When double_encode is turned off, PHP will not encode existing html entities 6641 * @return string $ret Encoded string 6642 */ 6643function dol_htmlentities($string, $flags = null, $encoding = 'UTF-8', $double_encode = false) 6644{ 6645 return htmlentities($string, $flags, $encoding, $double_encode); 6646} 6647 6648/** 6649 * Check if a string is a correct iso string 6650 * If not, it will we considered not HTML encoded even if it is by FPDF. 6651 * Example, if string contains euro symbol that has ascii code 128 6652 * 6653 * @param string $s String to check 6654 * @param string $clean Clean if it is not an ISO. Warning, if file is utf8, you will get a bad formated file. 6655 * @return int|string 0 if bad iso, 1 if good iso, Or the clean string if $clean is 1 6656 */ 6657function dol_string_is_good_iso($s, $clean = 0) 6658{ 6659 $len = dol_strlen($s); 6660 $out = ''; 6661 $ok = 1; 6662 for ($scursor = 0; $scursor < $len; $scursor++) { 6663 $ordchar = ord($s[$scursor]); 6664 //print $scursor.'-'.$ordchar.'<br>'; 6665 if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) { 6666 $ok = 0; 6667 break; 6668 } elseif ($ordchar > 126 && $ordchar < 160) { 6669 $ok = 0; 6670 break; 6671 } elseif ($clean) { 6672 $out .= $s[$scursor]; 6673 } 6674 } 6675 if ($clean) { 6676 return $out; 6677 } 6678 return $ok; 6679} 6680 6681/** 6682 * Return nb of lines of a clear text 6683 * 6684 * @param string $s String to check 6685 * @param int $maxchar Not yet used 6686 * @return int Number of lines 6687 * @see dol_nboflines_bis(), dolGetFirstLineOfText() 6688 */ 6689function dol_nboflines($s, $maxchar = 0) 6690{ 6691 if ($s == '') { 6692 return 0; 6693 } 6694 $arraystring = explode("\n", $s); 6695 $nb = count($arraystring); 6696 6697 return $nb; 6698} 6699 6700 6701/** 6702 * Return nb of lines of a formated text with \n and <br> (WARNING: string must not have mixed \n and br separators) 6703 * 6704 * @param string $text Text 6705 * @param int $maxlinesize Largeur de ligne en caracteres (ou 0 si pas de limite - defaut) 6706 * @param string $charset Give the charset used to encode the $text variable in memory. 6707 * @return int Number of lines 6708 * @see dol_nboflines(), dolGetFirstLineOfText() 6709 */ 6710function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8') 6711{ 6712 $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " "); 6713 if (dol_textishtml($text)) { 6714 $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " "); 6715 } 6716 6717 $text = strtr($text, $repTable); 6718 if ($charset == 'UTF-8') { 6719 $pattern = '/(<br[^>]*>)/Uu'; 6720 } else { 6721 // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support 6722 $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag. 6723 } 6724 $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 6725 6726 $nblines = (int) floor((count($a) + 1) / 2); 6727 // count possible auto line breaks 6728 if ($maxlinesize) { 6729 foreach ($a as $line) { 6730 if (dol_strlen($line) > $maxlinesize) { 6731 //$line_dec = html_entity_decode(strip_tags($line)); 6732 $line_dec = html_entity_decode($line); 6733 if (dol_strlen($line_dec) > $maxlinesize) { 6734 $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true); 6735 $nblines += substr_count($line_dec, '\n'); 6736 } 6737 } 6738 } 6739 } 6740 6741 unset($a); 6742 return $nblines; 6743} 6744 6745/** 6746 * Return if a text is a html content 6747 * 6748 * @param string $msg Content to check 6749 * @param int $option 0=Full detection, 1=Fast check 6750 * @return boolean true/false 6751 * @see dol_concatdesc() 6752 */ 6753function dol_textishtml($msg, $option = 0) 6754{ 6755 if ($option == 1) { 6756 if (preg_match('/<html/i', $msg)) { 6757 return true; 6758 } elseif (preg_match('/<body/i', $msg)) { 6759 return true; 6760 } elseif (preg_match('/<\/textarea/i', $msg)) { 6761 return true; 6762 } elseif (preg_match('/<(b|em|i|u)>/i', $msg)) { 6763 return true; 6764 } elseif (preg_match('/<br/i', $msg)) { 6765 return true; 6766 } 6767 return false; 6768 } else { 6769 if (preg_match('/<html/i', $msg)) { 6770 return true; 6771 } elseif (preg_match('/<body/i', $msg)) { 6772 return true; 6773 } elseif (preg_match('/<\/textarea/i', $msg)) { 6774 return true; 6775 } elseif (preg_match('/<(b|em|i|u)>/i', $msg)) { 6776 return true; 6777 } elseif (preg_match('/<br\/>/i', $msg)) { 6778 return true; 6779 } elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i', $msg)) { 6780 return true; 6781 } elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) { 6782 return true; 6783 } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) { 6784 return true; // must accept <img src="http://example.com/aaa.png" /> 6785 } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) { 6786 return true; // must accept <a href="http://example.com/aaa.png" /> 6787 } elseif (preg_match('/<h[0-9]>/i', $msg)) { 6788 return true; 6789 } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) { 6790 return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp) 6791 } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) { 6792 return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp) 6793 } 6794 6795 return false; 6796 } 6797} 6798 6799/** 6800 * Concat 2 descriptions with a new line between them (second operand after first one with appropriate new line separator) 6801 * text1 html + text2 html => text1 + '<br>' + text2 6802 * text1 html + text2 txt => text1 + '<br>' + dol_nl2br(text2) 6803 * text1 txt + text2 html => dol_nl2br(text1) + '<br>' + text2 6804 * text1 txt + text2 txt => text1 + '\n' + text2 6805 * 6806 * @param string $text1 Text 1 6807 * @param string $text2 Text 2 6808 * @param bool $forxml true=Use <br /> instead of <br> if we have to add a br tag 6809 * @param bool $invert invert order of description lines (we often use config MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION in this parameter) 6810 * @return string Text 1 + new line + Text2 6811 * @see dol_textishtml() 6812 */ 6813function dol_concatdesc($text1, $text2, $forxml = false, $invert = false) 6814{ 6815 if (!empty($invert)) { 6816 $tmp = $text1; 6817 $text1 = $text2; 6818 $text2 = $tmp; 6819 } 6820 6821 $ret = ''; 6822 $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1; 6823 $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br \>\n" : "<br>\n") : "\n") : ""; 6824 $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2; 6825 return $ret; 6826} 6827 6828 6829 6830/** 6831 * Return array of possible common substitutions. This includes several families like: 'system', 'mycompany', 'object', 'objectamount', 'date', 'user' 6832 * 6833 * @param Translate $outputlangs Output language 6834 * @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) 6835 * @param array $exclude Array of family keys we want to exclude. For example array('system', 'mycompany', 'object', 'objectamount', 'date', 'user', ...) 6836 * @param Object $object Object for keys on object 6837 * @return array Array of substitutions 6838 * @see setSubstitFromObject() 6839 */ 6840function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null) 6841{ 6842 global $db, $conf, $mysoc, $user, $extrafields; 6843 6844 $substitutionarray = array(); 6845 6846 if (empty($exclude) || !in_array('user', $exclude)) { 6847 // Add SIGNATURE into substitutionarray first, so, when we will make the substitution, 6848 // this will include signature content first and then replace var found into content of signature 6849 $signature = $user->signature; 6850 $substitutionarray = array_merge($substitutionarray, array( 6851 '__USER_SIGNATURE__' => (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '') 6852 )); 6853 6854 if (is_object($user)) { 6855 $substitutionarray = array_merge($substitutionarray, array( 6856 '__USER_ID__' => (string) $user->id, 6857 '__USER_LOGIN__' => (string) $user->login, 6858 '__USER_EMAIL__' => (string) $user->email, 6859 '__USER_LASTNAME__' => (string) $user->lastname, 6860 '__USER_FIRSTNAME__' => (string) $user->firstname, 6861 '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs), 6862 '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'), 6863 '__USER_REMOTE_IP__' => (string) getUserRemoteIP() 6864 )); 6865 } 6866 } 6867 if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc)) { 6868 $substitutionarray = array_merge($substitutionarray, array( 6869 '__MYCOMPANY_NAME__' => $mysoc->name, 6870 '__MYCOMPANY_EMAIL__' => $mysoc->email, 6871 '__MYCOMPANY_PHONE__' => $mysoc->phone, 6872 '__MYCOMPANY_FAX__' => $mysoc->fax, 6873 '__MYCOMPANY_PROFID1__' => $mysoc->idprof1, 6874 '__MYCOMPANY_PROFID2__' => $mysoc->idprof2, 6875 '__MYCOMPANY_PROFID3__' => $mysoc->idprof3, 6876 '__MYCOMPANY_PROFID4__' => $mysoc->idprof4, 6877 '__MYCOMPANY_PROFID5__' => $mysoc->idprof5, 6878 '__MYCOMPANY_PROFID6__' => $mysoc->idprof6, 6879 '__MYCOMPANY_CAPITAL__' => $mysoc->capital, 6880 '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass 6881 '__MYCOMPANY_ADDRESS__' => $mysoc->address, 6882 '__MYCOMPANY_ZIP__' => $mysoc->zip, 6883 '__MYCOMPANY_TOWN__' => $mysoc->town, 6884 '__MYCOMPANY_COUNTRY__' => $mysoc->country, 6885 '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id, 6886 '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code, 6887 '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency 6888 )); 6889 } 6890 6891 if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude))) { 6892 if ($onlykey) { 6893 $substitutionarray['__ID__'] = '__ID__'; 6894 $substitutionarray['__REF__'] = '__REF__'; 6895 $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__'; 6896 $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__'; 6897 $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__'; 6898 $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__'; 6899 $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__'; 6900 6901 if (!empty($conf->societe->enabled)) { // Most objects are concerned 6902 $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__'; 6903 $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__'; 6904 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__'; 6905 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__'; 6906 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__'; 6907 $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__'; 6908 $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__'; 6909 $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__'; 6910 $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__'; 6911 $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__'; 6912 $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__'; 6913 $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__'; 6914 $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__'; 6915 $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__'; 6916 $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__'; 6917 $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__'; 6918 $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__'; 6919 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__'; 6920 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__'; 6921 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__'; 6922 } 6923 if (!empty($conf->adherent->enabled) && (!is_object($object) || $object->element == 'adherent')) { 6924 $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__'; 6925 $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__'; 6926 $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__'; 6927 $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__'; 6928 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account'; 6929 /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__'; 6930 $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/ 6931 } 6932 if (!empty($conf->recruitment->enabled) && (!is_object($object) || $object->element == 'candidature')) { 6933 $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__'; 6934 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__'; 6935 $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__'; 6936 } 6937 if (!empty($conf->projet->enabled)) { // Most objects 6938 $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__'; 6939 $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__'; 6940 $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__'; 6941 /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__'; 6942 $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/ 6943 } 6944 if (!empty($conf->contrat->enabled) && (!is_object($object) || $object->element == 'contract')) { 6945 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start'; 6946 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start'; 6947 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service'; 6948 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service'; 6949 } 6950 $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable'; 6951 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable'; 6952 $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)'; 6953 $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)'; 6954 $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order'; 6955 $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice'; 6956 $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract'; 6957 6958 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal'; 6959 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order'; 6960 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice'; 6961 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract'; 6962 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal'; 6963 6964 if (!empty($conf->expedition->enabled) && (!is_object($object) || $object->element == 'shipping')) { 6965 $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number'; 6966 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url'; 6967 } 6968 if (!empty($conf->reception->enabled) && (!is_object($object) || $object->element == 'reception')) { 6969 $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shippin tracking number of shipment'; 6970 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url'; 6971 } 6972 } else { 6973 $substitutionarray['__ID__'] = $object->id; 6974 $substitutionarray['__REF__'] = $object->ref; 6975 $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null)); 6976 $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null); 6977 $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null); 6978 $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null); 6979 $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : ''); 6980 $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%d") : ''); 6981 $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%A") : ''); 6982 $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%m") : ''); 6983 $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%b") : ''); 6984 $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%Y") : ''); 6985 $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%H") : ''); 6986 $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%M") : ''); 6987 $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%S") : ''); 6988 6989 // For backward compatibility 6990 $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null)); 6991 $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null); 6992 $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : ''); 6993 $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 : '')) : ''); 6994 6995 if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) { 6996 $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day')); 6997 6998 $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : ''); 6999 if (method_exists($object, 'getCivilityLabel')) { 7000 $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel(); 7001 } 7002 $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : ''); 7003 $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : ''); 7004 $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = ''; 7005 if (method_exists($object, 'getFullName')) { 7006 $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs); 7007 } 7008 $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : ''); 7009 $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : ''); 7010 $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : ''); 7011 $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : ''); 7012 $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : ''); 7013 $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : ''); 7014 $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : ''); 7015 $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : ''); 7016 $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : ''); 7017 $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : ''); 7018 $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? $object->phone : ''); 7019 $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? $object->phone_perso : ''); 7020 $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? $object->phone_mobile : ''); 7021 $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : ''); 7022 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'dayrfc'); 7023 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->first_subscription_date_start, 'dayrfc'); 7024 $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->first_subscription_date_end, 'dayrfc'); 7025 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'dayrfc'); 7026 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc'); 7027 $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc'); 7028 } 7029 7030 if (is_object($object) && $object->element == 'societe') { 7031 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : ''); 7032 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : ''); 7033 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : ''); 7034 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : ''); 7035 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : ''); 7036 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : ''); 7037 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? $object->phone : ''); 7038 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? $object->fax : ''); 7039 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : ''); 7040 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : ''); 7041 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : ''); 7042 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : ''); 7043 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : ''); 7044 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : ''); 7045 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : ''); 7046 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : ''); 7047 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : ''); 7048 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : ''); 7049 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : ''); 7050 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : ''); 7051 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : ''); 7052 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : ''); 7053 } elseif (is_object($object->thirdparty)) { 7054 $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : ''); 7055 $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : ''); 7056 $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : ''); 7057 $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : ''); 7058 $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : ''); 7059 $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : ''); 7060 $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? $object->thirdparty->phone : ''); 7061 $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? $object->thirdparty->fax : ''); 7062 $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : ''); 7063 $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : ''); 7064 $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : ''); 7065 $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : ''); 7066 $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : ''); 7067 $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : ''); 7068 $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : ''); 7069 $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : ''); 7070 $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : ''); 7071 $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : ''); 7072 $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : ''); 7073 $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : ''); 7074 $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : ''); 7075 $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : ''); 7076 } 7077 7078 if (is_object($object) && $object->element == 'recruitmentcandidature') { 7079 $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs); 7080 $substitutionarray['__CANDIDATE_FIRSTNAME__'] = $object->firstname; 7081 $substitutionarray['__CANDIDATE_LASTNAME__'] = $object->lastname; 7082 } 7083 7084 if (is_object($object->project)) { 7085 $substitutionarray['__PROJECT_ID__'] = (is_object($object->project) ? $object->project->id : ''); 7086 $substitutionarray['__PROJECT_REF__'] = (is_object($object->project) ? $object->project->ref : ''); 7087 $substitutionarray['__PROJECT_NAME__'] = (is_object($object->project) ? $object->project->title : ''); 7088 } 7089 if (is_object($object->projet)) { // Deprecated, for backward compatibility 7090 $substitutionarray['__PROJECT_ID__'] = (is_object($object->projet) ? $object->projet->id : ''); 7091 $substitutionarray['__PROJECT_REF__'] = (is_object($object->projet) ? $object->projet->ref : ''); 7092 $substitutionarray['__PROJECT_NAME__'] = (is_object($object->projet) ? $object->projet->title : ''); 7093 } 7094 7095 if (is_object($object) && $object->element == 'shipping') { 7096 $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number; 7097 $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url; 7098 } 7099 if (is_object($object) && $object->element == 'reception') { 7100 $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number; 7101 $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url; 7102 } 7103 7104 if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) { 7105 $dateplannedstart = ''; 7106 $datenextexpiration = ''; 7107 foreach ($object->lines as $line) { 7108 if ($line->date_ouverture_prevue > $dateplannedstart) { 7109 $dateplannedstart = $line->date_ouverture_prevue; 7110 } 7111 if ($line->statut == 4 && $line->date_fin_prevue && (!$datenextexpiration || $line->date_fin_prevue < $datenextexpiration)) { 7112 $datenextexpiration = $line->date_fin_prevue; 7113 } 7114 } 7115 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'dayrfc'); 7116 $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard'); 7117 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'dayrfc'); 7118 $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard'); 7119 } 7120 7121 // Create dynamic tags for __EXTRAFIELD_FIELD__ 7122 if ($object->table_element && $object->id > 0) { 7123 if (!is_object($extrafields)) { 7124 $extrafields = new ExtraFields($db); 7125 } 7126 $extrafields->fetch_name_optionals_label($object->table_element, true); 7127 7128 if ($object->fetch_optionals() > 0) { 7129 if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) { 7130 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) { 7131 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key]; 7132 if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') { 7133 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day'); 7134 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs); 7135 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc'); 7136 } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') { 7137 $datetime = $object->array_options['options_'.$key]; 7138 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : ''); 7139 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : ''); 7140 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : ''); 7141 $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : ''); 7142 } 7143 } 7144 } 7145 } 7146 } 7147 7148 // Complete substitution array with the url to make online payment 7149 $paymenturl = ''; 7150 if (empty($substitutionarray['__REF__'])) { 7151 $paymenturl = ''; 7152 } else { 7153 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key 7154 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php'; 7155 $outputlangs->loadLangs(array('paypal', 'other')); 7156 $typeforonlinepayment = 'free'; 7157 if (is_object($object) && $object->element == 'commande') { 7158 $typeforonlinepayment = 'order'; 7159 } 7160 if (is_object($object) && $object->element == 'facture') { 7161 $typeforonlinepayment = 'invoice'; 7162 } 7163 if (is_object($object) && $object->element == 'member') { 7164 $typeforonlinepayment = 'member'; 7165 } 7166 if (is_object($object) && $object->element == 'contrat') { 7167 $typeforonlinepayment = 'contract'; 7168 } 7169 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__']); 7170 $paymenturl = $url; 7171 } 7172 7173 if ($object->id > 0) { 7174 $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ?str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : ''); 7175 $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl; 7176 7177 if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal') { 7178 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element); 7179 } else { 7180 $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = ''; 7181 } 7182 if (!empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'commande') { 7183 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element); 7184 } else { 7185 $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = ''; 7186 } 7187 if (!empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'facture') { 7188 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element); 7189 } else { 7190 $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = ''; 7191 } 7192 if (!empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'contrat') { 7193 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element); 7194 } else { 7195 $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = ''; 7196 } 7197 if (!empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'supplier_proposal') { 7198 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element); 7199 } else { 7200 $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = ''; 7201 } 7202 7203 if (is_object($object) && $object->element == 'propal') { 7204 $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id; 7205 } 7206 if (is_object($object) && $object->element == 'commande') { 7207 $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id; 7208 } 7209 if (is_object($object) && $object->element == 'facture') { 7210 $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id; 7211 } 7212 if (is_object($object) && $object->element == 'contrat') { 7213 $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id; 7214 } 7215 if (is_object($object) && $object->element == 'supplier_proposal') { 7216 $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id; 7217 } 7218 } 7219 7220 if (is_object($object) && $object->element == 'action') { 7221 $substitutionarray['__EVENT_LABEL__'] = $object->label; 7222 $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, '%A %d %b %Y'); 7223 $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, '%H:%M:%S'); 7224 } 7225 } 7226 } 7227 if (empty($exclude) || !in_array('objectamount', $exclude)) { 7228 include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php'; 7229 7230 $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : ''; 7231 $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : ''; 7232 7233 $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : ''; 7234 $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : ''; 7235 $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : ''; 7236 $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : ''; 7237 $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : ''; 7238 $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)) : ''; 7239 $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)) : ''; 7240 if ($onlykey != 2 || $mysoc->useLocalTax(1)) { 7241 $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : ''; 7242 } 7243 if ($onlykey != 2 || $mysoc->useLocalTax(2)) { 7244 $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : ''; 7245 } 7246 7247 $substitutionarray['__AMOUNT_FORMATED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : ''; 7248 $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : ''; 7249 $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)) : ''; 7250 if ($onlykey != 2 || $mysoc->useLocalTax(1)) { 7251 $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : ''; 7252 } 7253 if ($onlykey != 2 || $mysoc->useLocalTax(2)) { 7254 $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : ''; 7255 } 7256 7257 $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : ''; 7258 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : ''; 7259 $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : ''; 7260 // TODO Add other keys for foreign multicurrency 7261 7262 // For backward compatibility 7263 if ($onlykey != 2) { 7264 $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : ''; 7265 $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : ''; 7266 $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : ''; 7267 } 7268 } 7269 7270 //var_dump($substitutionarray['__AMOUNT_FORMATED__']); 7271 if (empty($exclude) || !in_array('date', $exclude)) { 7272 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; 7273 7274 $tmp = dol_getdate(dol_now(), true); 7275 $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']); 7276 $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']); 7277 $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']); 7278 $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']); 7279 7280 $daytext = $outputlangs->trans('Day'.$tmp['wday']); 7281 7282 $substitutionarray = array_merge($substitutionarray, array( 7283 '__DAY__' => (string) $tmp['mday'], 7284 '__DAY_TEXT__' => $daytext, // Monday 7285 '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon 7286 '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M 7287 '__MONTH__' => (string) $tmp['mon'], 7288 '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])), 7289 '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])), 7290 '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])), 7291 '__YEAR__' => (string) $tmp['year'], 7292 '__PREVIOUS_DAY__' => (string) $tmp2['day'], 7293 '__PREVIOUS_MONTH__' => (string) $tmp3['month'], 7294 '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1), 7295 '__NEXT_DAY__' => (string) $tmp4['day'], 7296 '__NEXT_MONTH__' => (string) $tmp5['month'], 7297 '__NEXT_YEAR__' => (string) ($tmp['year'] + 1), 7298 )); 7299 } 7300 7301 if (!empty($conf->multicompany->enabled)) { 7302 $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity)); 7303 } 7304 if (empty($exclude) || !in_array('system', $exclude)) { 7305 $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT; 7306 $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey'); 7307 $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)'; 7308 $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey'); 7309 } 7310 7311 return $substitutionarray; 7312} 7313 7314/** 7315 * Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newval), 7316 * and texts like __(TranslationKey|langfile)__ and __[ConstantKey]__ are also replaced. 7317 * Example of usage: 7318 * $substitutionarray = getCommonSubstitutionArray($langs, 0, null, $thirdparty); 7319 * complete_substitutions_array($substitutionarray, $langs, $thirdparty); 7320 * $mesg = make_substitutions($mesg, $substitutionarray, $langs); 7321 * 7322 * @param string $text Source string in which we must do substitution 7323 * @param array $substitutionarray Array with key->val to substitute. Example: array('__MYKEY__' => 'MyVal', ...) 7324 * @param Translate $outputlangs Output language 7325 * @param int $converttextinhtmlifnecessary 0=Convert only value into HTML if text is already in HTML 7326 * 1=Will also convert initial $text into HTML if we try to insert one value that is HTML 7327 * @return string Output string after substitutions 7328 * @see complete_substitutions_array(), getCommonSubstitutionArray() 7329 */ 7330function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0) 7331{ 7332 global $conf, $langs; 7333 7334 if (!is_array($substitutionarray)) { 7335 return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions'; 7336 } 7337 7338 if (empty($outputlangs)) { 7339 $outputlangs = $langs; 7340 } 7341 7342 // Is initial text HTML or simple text ? 7343 $msgishtml = 0; 7344 if (dol_textishtml($text, 1)) { 7345 $msgishtml = 1; 7346 } 7347 7348 // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__ 7349 if (is_object($outputlangs)) { 7350 $reg = array(); 7351 while (preg_match('/__\(([^\)]+)\)__/', $text, $reg)) { 7352 // If key is __(TranslationKey|langfile)__, then force load of langfile.lang 7353 $tmp = explode('|', $reg[1]); 7354 if (!empty($tmp[1])) { 7355 $outputlangs->load($tmp[1]); 7356 } 7357 7358 $value = $outputlangs->transnoentitiesnoconv($reg[1]); 7359 7360 if (empty($converttextinhtmlifnecessary)) { 7361 // convert $newval into HTML is necessary 7362 $text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text); 7363 } else { 7364 if (! $msgishtml) { 7365 $valueishtml = dol_textishtml($value, 1); 7366 7367 if ($valueishtml) { 7368 $text = dol_htmlentitiesbr($text); 7369 $msgishtml = 1; 7370 } 7371 } else { 7372 $value = dol_nl2br("$value"); 7373 } 7374 7375 $text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $value, $text); 7376 } 7377 } 7378 } 7379 7380 // Make substitution for constant keys. 7381 // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted. 7382 $reg = array(); 7383 while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) { 7384 $keyfound = $reg[1]; 7385 if (isASecretKey($keyfound)) { 7386 $value = '*****forbidden*****'; 7387 } else { 7388 $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound; 7389 } 7390 7391 if (empty($converttextinhtmlifnecessary)) { 7392 // convert $newval into HTML is necessary 7393 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text); 7394 } else { 7395 if (! $msgishtml) { 7396 $valueishtml = dol_textishtml($value, 1); 7397 7398 if ($valueishtml) { 7399 $text = dol_htmlentitiesbr($text); 7400 $msgishtml = 1; 7401 } 7402 } else { 7403 $value = dol_nl2br("$value"); 7404 } 7405 7406 $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text); 7407 } 7408 } 7409 7410 // Make substitition for array $substitutionarray 7411 foreach ($substitutionarray as $key => $value) { 7412 if (!isset($value)) { 7413 continue; // If value is null, it same than not having substitution key at all into array, we do not replace. 7414 } 7415 7416 if ($key == '__USER_SIGNATURE__' && (!empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) { 7417 $value = ''; // Protection 7418 } 7419 7420 if (empty($converttextinhtmlifnecessary)) { 7421 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example 7422 } else { 7423 if (! $msgishtml) { 7424 $valueishtml = dol_textishtml($value, 1); 7425 7426 if ($valueishtml) { 7427 $text = dol_htmlentitiesbr($text); 7428 $msgishtml = 1; 7429 } 7430 } else { 7431 $value = dol_nl2br("$value"); 7432 } 7433 $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example 7434 } 7435 } 7436 7437 return $text; 7438} 7439 7440/** 7441 * Complete the $substitutionarray with more entries coming from external module that had set the "substitutions=1" into module_part array. 7442 * In this case, method completesubstitutionarray provided by module is called. 7443 * 7444 * @param array $substitutionarray Array substitution old value => new value value 7445 * @param Translate $outputlangs Output language 7446 * @param Object $object Source object 7447 * @param mixed $parameters Add more parameters (useful to pass product lines) 7448 * @param string $callfunc What is the name of the custom function that will be called? (default: completesubstitutionarray) 7449 * @return void 7450 * @see make_substitutions() 7451 */ 7452function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray") 7453{ 7454 global $conf, $user; 7455 7456 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 7457 7458 // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array. 7459 7460 // Check if there is external substitution to do, requested by plugins 7461 $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']); 7462 7463 foreach ($dirsubstitutions as $reldir) { 7464 $dir = dol_buildpath($reldir, 0); 7465 7466 // Check if directory exists 7467 if (!dol_is_dir($dir)) { 7468 continue; 7469 } 7470 7471 $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_'); 7472 foreach ($substitfiles as $substitfile) { 7473 $reg = array(); 7474 if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) { 7475 $module = $reg[1]; 7476 7477 dol_syslog("Library ".$substitfile['name']." found into ".$dir); 7478 // Include the user's functions file 7479 require_once $dir.$substitfile['name']; 7480 // Call the user's function, and only if it is defined 7481 $function_name = $module."_".$callfunc; 7482 if (function_exists($function_name)) { 7483 $function_name($substitutionarray, $outputlangs, $object, $parameters); 7484 } 7485 } 7486 } 7487 } 7488 if (!empty($conf->global->ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS)) { 7489 // to list all tags in odt template 7490 $tags = ''; 7491 foreach ($substitutionarray as $key => $value) { 7492 $tags .= '{'.$key.'} => '.$value."\n"; 7493 } 7494 $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags)); 7495 } 7496} 7497 7498/** 7499 * Format output for start and end date 7500 * 7501 * @param int $date_start Start date 7502 * @param int $date_end End date 7503 * @param string $format Output format 7504 * @param Translate $outputlangs Output language 7505 * @return void 7506 */ 7507function print_date_range($date_start, $date_end, $format = '', $outputlangs = '') 7508{ 7509 print get_date_range($date_start, $date_end, $format, $outputlangs); 7510} 7511 7512/** 7513 * Format output for start and end date 7514 * 7515 * @param int $date_start Start date 7516 * @param int $date_end End date 7517 * @param string $format Output format 7518 * @param Translate $outputlangs Output language 7519 * @param integer $withparenthesis 1=Add parenthesis, 0=no parenthesis 7520 * @return string String 7521 */ 7522function get_date_range($date_start, $date_end, $format = '', $outputlangs = '', $withparenthesis = 1) 7523{ 7524 global $langs; 7525 7526 $out = ''; 7527 7528 if (!is_object($outputlangs)) { 7529 $outputlangs = $langs; 7530 } 7531 7532 if ($date_start && $date_end) { 7533 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : ''); 7534 } 7535 if ($date_start && !$date_end) { 7536 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : ''); 7537 } 7538 if (!$date_start && $date_end) { 7539 $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : ''); 7540 } 7541 7542 return $out; 7543} 7544 7545/** 7546 * Return firstname and lastname in correct order 7547 * 7548 * @param string $firstname Firstname 7549 * @param string $lastname Lastname 7550 * @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 7551 * @return string Firstname + lastname or Lastname + firstname 7552 */ 7553function dolGetFirstLastname($firstname, $lastname, $nameorder = -1) 7554{ 7555 global $conf; 7556 7557 $ret = ''; 7558 // If order not defined, we use the setup 7559 if ($nameorder < 0) { 7560 $nameorder = (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION) ? 1 : 0); 7561 } 7562 if ($nameorder == 1) { 7563 $ret .= $firstname; 7564 if ($firstname && $lastname) { 7565 $ret .= ' '; 7566 } 7567 $ret .= $lastname; 7568 } elseif ($nameorder == 2 || $nameorder == 3) { 7569 $ret .= $firstname; 7570 if (empty($ret) && $nameorder == 3) { 7571 $ret .= $lastname; 7572 } 7573 } else { // 0, 4 or 5 7574 $ret .= $lastname; 7575 if (empty($ret) && $nameorder == 5) { 7576 $ret .= $firstname; 7577 } 7578 if ($nameorder == 0) { 7579 if ($firstname && $lastname) { 7580 $ret .= ' '; 7581 } 7582 $ret .= $firstname; 7583 } 7584 } 7585 return $ret; 7586} 7587 7588 7589/** 7590 * Set event message in dol_events session object. Will be output by calling dol_htmloutput_events. 7591 * Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function. 7592 * Note: Prefer to use setEventMessages instead. 7593 * 7594 * @param string|string[] $mesgs Message string or array 7595 * @param string $style Which style to use ('mesgs' by default, 'warnings', 'errors') 7596 * @return void 7597 * @see dol_htmloutput_events() 7598 */ 7599function setEventMessage($mesgs, $style = 'mesgs') 7600{ 7601 //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function 7602 if (!is_array($mesgs)) { 7603 // If mesgs is a string 7604 if ($mesgs) { 7605 $_SESSION['dol_events'][$style][] = $mesgs; 7606 } 7607 } else { 7608 // If mesgs is an array 7609 foreach ($mesgs as $mesg) { 7610 if ($mesg) { 7611 $_SESSION['dol_events'][$style][] = $mesg; 7612 } 7613 } 7614 } 7615} 7616 7617/** 7618 * Set event messages in dol_events session object. Will be output by calling dol_htmloutput_events. 7619 * Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function. 7620 * 7621 * @param string $mesg Message string 7622 * @param array $mesgs Message array 7623 * @param string $style Which style to use ('mesgs' by default, 'warnings', 'errors') 7624 * @param string $messagekey A key to be used to allow the feature "Never show this message again" 7625 * @return void 7626 * @see dol_htmloutput_events() 7627 */ 7628function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '') 7629{ 7630 if (empty($mesg) && empty($mesgs)) { 7631 dol_syslog("Try to add a message in stack with empty message", LOG_WARNING); 7632 } else { 7633 if ($messagekey) { 7634 // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey; 7635 // TODO 7636 $mesg .= ''; 7637 } 7638 if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) { 7639 if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) { 7640 dol_print_error('', 'Bad parameter style='.$style.' for setEventMessages'); 7641 } 7642 if (empty($mesgs)) { 7643 setEventMessage($mesg, $style); 7644 } else { 7645 if (!empty($mesg) && !in_array($mesg, $mesgs)) { 7646 setEventMessage($mesg, $style); // Add message string if not already into array 7647 } 7648 setEventMessage($mesgs, $style); 7649 } 7650 } 7651 } 7652} 7653 7654/** 7655 * Print formated messages to output (Used to show messages on html output). 7656 * Note: Calling dol_htmloutput_events is done into pages by standard llxFooter() function, so there is 7657 * no need to call it explicitely. 7658 * 7659 * @param int $disabledoutputofmessages Clear all messages stored into session without diplaying them 7660 * @return void 7661 * @see dol_htmloutput_mesg() 7662 */ 7663function dol_htmloutput_events($disabledoutputofmessages = 0) 7664{ 7665 // Show mesgs 7666 if (isset($_SESSION['dol_events']['mesgs'])) { 7667 if (empty($disabledoutputofmessages)) { 7668 dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']); 7669 } 7670 unset($_SESSION['dol_events']['mesgs']); 7671 } 7672 7673 // Show errors 7674 if (isset($_SESSION['dol_events']['errors'])) { 7675 if (empty($disabledoutputofmessages)) { 7676 dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error'); 7677 } 7678 unset($_SESSION['dol_events']['errors']); 7679 } 7680 7681 // Show warnings 7682 if (isset($_SESSION['dol_events']['warnings'])) { 7683 if (empty($disabledoutputofmessages)) { 7684 dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning'); 7685 } 7686 unset($_SESSION['dol_events']['warnings']); 7687 } 7688} 7689 7690/** 7691 * Get formated messages to output (Used to show messages on html output). 7692 * This include also the translation of the message key. 7693 * 7694 * @param string $mesgstring Message string or message key 7695 * @param string[] $mesgarray Array of message strings or message keys 7696 * @param string $style Style of message output ('ok' or 'error') 7697 * @param int $keepembedded Set to 1 in error message must be kept embedded into its html place (this disable jnotify) 7698 * @return string Return html output 7699 * 7700 * @see dol_print_error() 7701 * @see dol_htmloutput_errors() 7702 * @see setEventMessages() 7703 */ 7704function get_htmloutput_mesg($mesgstring = '', $mesgarray = '', $style = 'ok', $keepembedded = 0) 7705{ 7706 global $conf, $langs; 7707 7708 $ret = 0; 7709 $return = ''; 7710 $out = ''; 7711 $divstart = $divend = ''; 7712 7713 // If inline message with no format, we add it. 7714 if ((empty($conf->use_javascript_ajax) || !empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) { 7715 $divstart = '<div class="'.$style.' clearboth">'; 7716 $divend = '</div>'; 7717 } 7718 7719 if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) { 7720 $langs->load("errors"); 7721 $out .= $divstart; 7722 if (is_array($mesgarray) && count($mesgarray)) { 7723 foreach ($mesgarray as $message) { 7724 $ret++; 7725 $out .= $langs->trans($message); 7726 if ($ret < count($mesgarray)) { 7727 $out .= "<br>\n"; 7728 } 7729 } 7730 } 7731 if ($mesgstring) { 7732 $langs->load("errors"); 7733 $ret++; 7734 $out .= $langs->trans($mesgstring); 7735 } 7736 $out .= $divend; 7737 } 7738 7739 if ($out) { 7740 if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded)) { 7741 $return = '<script> 7742 $(document).ready(function() { 7743 var block = '.(!empty($conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false").' 7744 if (block) { 7745 $.dolEventValid("","'.dol_escape_js($out).'"); 7746 } else { 7747 /* jnotify(message, preset of message type, keepmessage) */ 7748 $.jnotify("'.dol_escape_js($out).'", 7749 "'.($style == "ok" ? 3000 : $style).'", 7750 '.($style == "ok" ? "false" : "true").', 7751 { remove: function (){} } ); 7752 } 7753 }); 7754 </script>'; 7755 } else { 7756 $return = $out; 7757 } 7758 } 7759 7760 return $return; 7761} 7762 7763/** 7764 * Get formated error messages to output (Used to show messages on html output). 7765 * 7766 * @param string $mesgstring Error message 7767 * @param array $mesgarray Error messages array 7768 * @param int $keepembedded Set to 1 in error message must be kept embedded into its html place (this disable jnotify) 7769 * @return string Return html output 7770 * 7771 * @see dol_print_error() 7772 * @see dol_htmloutput_mesg() 7773 */ 7774function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0) 7775{ 7776 return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded); 7777} 7778 7779/** 7780 * Print formated messages to output (Used to show messages on html output). 7781 * 7782 * @param string $mesgstring Message string or message key 7783 * @param string[] $mesgarray Array of message strings or message keys 7784 * @param string $style Which style to use ('ok', 'warning', 'error') 7785 * @param int $keepembedded Set to 1 if message must be kept embedded into its html place (this disable jnotify) 7786 * @return void 7787 * 7788 * @see dol_print_error() 7789 * @see dol_htmloutput_errors() 7790 * @see setEventMessages() 7791 */ 7792function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0) 7793{ 7794 if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) { 7795 return; 7796 } 7797 7798 $iserror = 0; 7799 $iswarning = 0; 7800 if (is_array($mesgarray)) { 7801 foreach ($mesgarray as $val) { 7802 if ($val && preg_match('/class="error"/i', $val)) { 7803 $iserror++; 7804 break; 7805 } 7806 if ($val && preg_match('/class="warning"/i', $val)) { 7807 $iswarning++; 7808 break; 7809 } 7810 } 7811 } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) { 7812 $iserror++; 7813 } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) { 7814 $iswarning++; 7815 } 7816 if ($style == 'error') { 7817 $iserror++; 7818 } 7819 if ($style == 'warning') { 7820 $iswarning++; 7821 } 7822 7823 if ($iserror || $iswarning) { 7824 // Remove div from texts 7825 $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring); 7826 $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring); 7827 $mesgstring = preg_replace('/<\/div>/', '', $mesgstring); 7828 // Remove div from texts array 7829 if (is_array($mesgarray)) { 7830 $newmesgarray = array(); 7831 foreach ($mesgarray as $val) { 7832 if (is_string($val)) { 7833 $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val); 7834 $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring); 7835 $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring); 7836 $newmesgarray[] = $tmpmesgstring; 7837 } else { 7838 dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING); 7839 } 7840 } 7841 $mesgarray = $newmesgarray; 7842 } 7843 print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded); 7844 } else { 7845 print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded); 7846 } 7847} 7848 7849/** 7850 * Print formated error messages to output (Used to show messages on html output). 7851 * 7852 * @param string $mesgstring Error message 7853 * @param array $mesgarray Error messages array 7854 * @param int $keepembedded Set to 1 in error message must be kept embedded into its html place (this disable jnotify) 7855 * @return void 7856 * 7857 * @see dol_print_error() 7858 * @see dol_htmloutput_mesg() 7859 */ 7860function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0) 7861{ 7862 dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded); 7863} 7864 7865/** 7866 * Advanced sort array by second index function, which produces ascending (default) 7867 * or descending output and uses optionally natural case insensitive sorting (which 7868 * can be optionally case sensitive as well). 7869 * 7870 * @param array $array Array to sort (array of array('key1'=>val1,'key2'=>val2,'key3'...) or array of objects) 7871 * @param string $index Key in array to use for sorting criteria 7872 * @param int $order Sort order ('asc' or 'desc') 7873 * @param int $natsort 1=use "natural" sort (natsort) for a search criteria thats is strings or unknown, 0=use "standard" sort (asort) for numbers 7874 * @param int $case_sensitive 1=sort is case sensitive, 0=not case sensitive 7875 * @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. 7876 * @return array Sorted array 7877 */ 7878function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0) 7879{ 7880 // Clean parameters 7881 $order = strtolower($order); 7882 7883 if (is_array($array)) { 7884 $sizearray = count($array); 7885 if ($sizearray > 0) { 7886 $temp = array(); 7887 foreach (array_keys($array) as $key) { 7888 if (is_object($array[$key])) { 7889 $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index; 7890 } else { 7891 $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index]; 7892 } 7893 } 7894 7895 if (!$natsort) { 7896 if ($order == 'asc') { 7897 asort($temp); 7898 } else { 7899 arsort($temp); 7900 } 7901 } else { 7902 if ($case_sensitive) { 7903 natsort($temp); 7904 } else { 7905 natcasesort($temp); // natecasesort is not sensible to case 7906 } 7907 if ($order != 'asc') { 7908 $temp = array_reverse($temp, true); 7909 } 7910 } 7911 7912 $sorted = array(); 7913 7914 foreach (array_keys($temp) as $key) { 7915 (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key]; 7916 } 7917 7918 return $sorted; 7919 } 7920 } 7921 return $array; 7922} 7923 7924 7925/** 7926 * Check if a string is in UTF8 7927 * 7928 * @param string $str String to check 7929 * @return boolean True if string is UTF8 or ISO compatible with UTF8, False if not (ISO with special char or Binary) 7930 */ 7931function utf8_check($str) 7932{ 7933 $str = (string) $str; // Sometimes string is an int. 7934 7935 // We must use here a binary strlen function (so not dol_strlen) 7936 $strLength = dol_strlen($str); 7937 for ($i = 0; $i < $strLength; $i++) { 7938 if (ord($str[$i]) < 0x80) { 7939 continue; // 0bbbbbbb 7940 } elseif ((ord($str[$i]) & 0xE0) == 0xC0) { 7941 $n = 1; // 110bbbbb 7942 } elseif ((ord($str[$i]) & 0xF0) == 0xE0) { 7943 $n = 2; // 1110bbbb 7944 } elseif ((ord($str[$i]) & 0xF8) == 0xF0) { 7945 $n = 3; // 11110bbb 7946 } elseif ((ord($str[$i]) & 0xFC) == 0xF8) { 7947 $n = 4; // 111110bb 7948 } elseif ((ord($str[$i]) & 0xFE) == 0xFC) { 7949 $n = 5; // 1111110b 7950 } else { 7951 return false; // Does not match any model 7952 } 7953 for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ? 7954 if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) { 7955 return false; 7956 } 7957 } 7958 } 7959 return true; 7960} 7961 7962/** 7963 * Check if a string is in ASCII 7964 * 7965 * @param string $str String to check 7966 * @return boolean True if string is ASCII, False if not (byte value > 0x7F) 7967 */ 7968function ascii_check($str) 7969{ 7970 if (function_exists('mb_check_encoding')) { 7971 //if (mb_detect_encoding($str, 'ASCII', true) return false; 7972 if (!mb_check_encoding($str, 'ASCII')) { 7973 return false; 7974 } 7975 } else { 7976 if (preg_match('/[^\x00-\x7f]/', $str)) { 7977 return false; // Contains a byte > 7f 7978 } 7979 } 7980 7981 return true; 7982} 7983 7984 7985/** 7986 * Return a string encoded into OS filesystem encoding. This function is used to define 7987 * value to pass to filesystem PHP functions. 7988 * 7989 * @param string $str String to encode (UTF-8) 7990 * @return string Encoded string (UTF-8, ISO-8859-1) 7991 */ 7992function dol_osencode($str) 7993{ 7994 global $conf; 7995 7996 $tmp = ini_get("unicode.filesystem_encoding"); // Disponible avec PHP 6.0 7997 if (empty($tmp) && !empty($_SERVER["WINDIR"])) { 7998 $tmp = 'iso-8859-1'; // By default for windows 7999 } 8000 if (empty($tmp)) { 8001 $tmp = 'utf-8'; // By default for other 8002 } 8003 if (!empty($conf->global->MAIN_FILESYSTEM_ENCODING)) { 8004 $tmp = $conf->global->MAIN_FILESYSTEM_ENCODING; 8005 } 8006 8007 if ($tmp == 'iso-8859-1') { 8008 return utf8_decode($str); 8009 } 8010 return $str; 8011} 8012 8013 8014/** 8015 * Return an id or code from a code or id. 8016 * Store also Code-Id into a cache to speed up next request on same key. 8017 * 8018 * @param DoliDB $db Database handler 8019 * @param string $key Code or Id to get Id or Code 8020 * @param string $tablename Table name without prefix 8021 * @param string $fieldkey Field to search the key into 8022 * @param string $fieldid Field to get 8023 * @param int $entityfilter Filter by entity 8024 * @return int <0 if KO, Id of code if OK 8025 * @see $langs->getLabelFromKey 8026 */ 8027function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0) 8028{ 8029 global $cache_codes; 8030 8031 // If key empty 8032 if ($key == '') { 8033 return ''; 8034 } 8035 8036 // Check in cache 8037 if (isset($cache_codes[$tablename][$key][$fieldid])) { // Can be defined to 0 or '' 8038 return $cache_codes[$tablename][$key][$fieldid]; // Found in cache 8039 } 8040 8041 dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG); 8042 8043 $sql = "SELECT ".$fieldid." as valuetoget"; 8044 $sql .= " FROM ".MAIN_DB_PREFIX.$tablename; 8045 $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'"; 8046 if (!empty($entityfilter)) { 8047 $sql .= " AND entity IN (".getEntity($tablename).")"; 8048 } 8049 8050 $resql = $db->query($sql); 8051 if ($resql) { 8052 $obj = $db->fetch_object($resql); 8053 if ($obj) { 8054 $cache_codes[$tablename][$key][$fieldid] = $obj->valuetoget; 8055 } else { 8056 $cache_codes[$tablename][$key][$fieldid] = ''; 8057 } 8058 $db->free($resql); 8059 return $cache_codes[$tablename][$key][$fieldid]; 8060 } else { 8061 return -1; 8062 } 8063} 8064 8065/** 8066 * Verify if condition in string is ok or not 8067 * 8068 * @param string $strRights String with condition to check 8069 * @return boolean True or False. Return True if strRights is '' 8070 */ 8071function verifCond($strRights) 8072{ 8073 global $user, $conf, $langs; 8074 global $leftmenu; 8075 global $rights; // To export to dol_eval function 8076 8077 //print $strRights."<br>\n"; 8078 $rights = true; 8079 if ($strRights != '') { 8080 $str = 'if(!('.$strRights.')) { $rights = false; }'; 8081 dol_eval($str); // The dol_eval must contains all the global $xxx used into a condition 8082 } 8083 return $rights; 8084} 8085 8086/** 8087 * Replace eval function to add more security. 8088 * This function is called by verifCond() or trans() and transnoentitiesnoconv(). 8089 * 8090 * @param string $s String to evaluate 8091 * @param int $returnvalue 0=No return (used to execute eval($a=something)). 1=Value of eval is returned (used to eval($something)). 8092 * @param int $hideerrors 1=Hide errors 8093 * @return mixed Nothing or return result of eval 8094 */ 8095function dol_eval($s, $returnvalue = 0, $hideerrors = 1) 8096{ 8097 // Only global variables can be changed by eval function and returned to caller 8098 global $db, $langs, $user, $conf, $website, $websitepage; 8099 global $action, $mainmenu, $leftmenu; 8100 global $rights; 8101 global $object; 8102 global $mysoc; 8103 8104 global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object 8105 global $soc; // For backward compatibility 8106 8107 // Replace dangerous char (used for RCE), we allow only PHP variable testing. 8108 if (strpos($s, '`') !== false) { 8109 return 'Bad string syntax to evaluate: '.$s; 8110 } 8111 8112 // We block using of php exec or php file functions 8113 $forbiddenphpstrings = array("exec(", "passthru(", "shell_exec(", "system(", "proc_open(", "popen(", "eval(", "dol_eval(", "executeCLI("); 8114 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array("fopen(", "file_put_contents(", "fputs(", "fputscsv(", "fwrite(", "fpassthru(", "unlink(", "mkdir(", "rmdir(", "symlink(", "touch(", "umask(")); 8115 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('function(', '$$', 'call_user_func(')); 8116 $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST')); 8117 $forbiddenphpregex = 'global\s+\$'; 8118 do { 8119 $oldstringtoclean = $s; 8120 $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s); 8121 $s = preg_replace('/'.$forbiddenphpregex.'/', '__forbiddenstring__', $s); 8122 //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\(/i', '', $s); // Remove $function( call and $mycall->mymethod( 8123 } while ($oldstringtoclean != $s); 8124 8125 if (strpos($s, '__forbiddenstring__') !== false) { 8126 dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING); 8127 return 'Bad string syntax to evaluate: '.$s; 8128 } 8129 8130 //print $s."<br>\n"; 8131 if ($returnvalue) { 8132 if ($hideerrors) { 8133 return @eval('return '.$s.';'); 8134 } else { 8135 return eval('return '.$s.';'); 8136 } 8137 } else { 8138 if ($hideerrors) { 8139 @eval($s); 8140 } else { 8141 eval($s); 8142 } 8143 } 8144} 8145 8146/** 8147 * Return if var element is ok 8148 * 8149 * @param string $element Variable to check 8150 * @return boolean Return true of variable is not empty 8151 */ 8152function dol_validElement($element) 8153{ 8154 return (trim($element) != ''); 8155} 8156 8157/** 8158 * Return img flag of country for a language code or country code. 8159 * 8160 * @param string $codelang Language code ('en_IN', 'fr_CA', ...) or ISO Country code on 2 characters in uppercase ('IN', 'FR') 8161 * @param string $moreatt Add more attribute on img tag (For example 'style="float: right"' or 'class="saturatemedium"') 8162 * @return string HTML img string with flag. 8163 */ 8164function picto_from_langcode($codelang, $moreatt = '') 8165{ 8166 if (empty($codelang)) { 8167 return ''; 8168 } 8169 8170 if ($codelang == 'auto') { 8171 return '<span class="fa fa-language"></span>'; 8172 } 8173 8174 $langtocountryflag = array( 8175 'ar_AR' => '', 8176 'ca_ES' => 'catalonia', 8177 'da_DA' => 'dk', 8178 'fr_CA' => 'mq', 8179 'sv_SV' => 'se', 8180 'sw_SW' => 'unknown', 8181 'AQ' => 'unknown', 8182 'CW' => 'unknown', 8183 'IM' => 'unknown', 8184 'JE' => 'unknown', 8185 'MF' => 'unknown', 8186 'BL' => 'unknown', 8187 'SX' => 'unknown' 8188 ); 8189 8190 if (isset($langtocountryflag[$codelang])) { 8191 $flagImage = $langtocountryflag[$codelang]; 8192 } else { 8193 $tmparray = explode('_', $codelang); 8194 $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1]; 8195 } 8196 8197 return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt); 8198} 8199 8200/** 8201 * Return default language from country code. 8202 * Return null if not found. 8203 * 8204 * @param string $countrycode Country code like 'US', 'FR', 'CA', 'ES', 'IN', 'MX', ... 8205 * @return string Value of locale like 'en_US', 'fr_FR', ... or null if not found 8206 */ 8207function getLanguageCodeFromCountryCode($countrycode) 8208{ 8209 global $mysoc; 8210 8211 if (empty($countrycode)) { 8212 return null; 8213 } 8214 8215 if (strtoupper($countrycode) == 'MQ') { 8216 return 'fr_CA'; 8217 } 8218 if (strtoupper($countrycode) == 'SE') { 8219 return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country 8220 } 8221 if (strtoupper($countrycode) == 'CH') { 8222 if ($mysoc->country_code == 'FR') { 8223 return 'fr_CH'; 8224 } 8225 if ($mysoc->country_code == 'DE') { 8226 return 'de_CH'; 8227 } 8228 if ($mysoc->country_code == 'IT') { 8229 return 'it_CH'; 8230 } 8231 } 8232 8233 // Locale list taken from: 8234 // http://stackoverflow.com/questions/3191664/ 8235 // list-of-all-locales-and-their-short-codes 8236 $locales = array( 8237 'af-ZA', 8238 'am-ET', 8239 'ar-AE', 8240 'ar-BH', 8241 'ar-DZ', 8242 'ar-EG', 8243 'ar-IQ', 8244 'ar-JO', 8245 'ar-KW', 8246 'ar-LB', 8247 'ar-LY', 8248 'ar-MA', 8249 'ar-OM', 8250 'ar-QA', 8251 'ar-SA', 8252 'ar-SY', 8253 'ar-TN', 8254 'ar-YE', 8255 //'as-IN', // Moved after en-IN 8256 'ba-RU', 8257 'be-BY', 8258 'bg-BG', 8259 'bn-BD', 8260 //'bn-IN', // Moved after en-IN 8261 'bo-CN', 8262 'br-FR', 8263 'ca-ES', 8264 'co-FR', 8265 'cs-CZ', 8266 'cy-GB', 8267 'da-DK', 8268 'de-AT', 8269 'de-CH', 8270 'de-DE', 8271 'de-LI', 8272 'de-LU', 8273 'dv-MV', 8274 'el-GR', 8275 'en-AU', 8276 'en-BZ', 8277 'en-CA', 8278 'en-GB', 8279 'en-IE', 8280 'en-IN', 8281 'as-IN', // as-IN must be after en-IN (en in priority if country is IN) 8282 'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN) 8283 'en-JM', 8284 'en-MY', 8285 'en-NZ', 8286 'en-PH', 8287 'en-SG', 8288 'en-TT', 8289 'en-US', 8290 'en-ZA', 8291 'en-ZW', 8292 'es-AR', 8293 'es-BO', 8294 'es-CL', 8295 'es-CO', 8296 'es-CR', 8297 'es-DO', 8298 'es-EC', 8299 'es-ES', 8300 'es-GT', 8301 'es-HN', 8302 'es-MX', 8303 'es-NI', 8304 'es-PA', 8305 'es-PE', 8306 'es-PR', 8307 'es-PY', 8308 'es-SV', 8309 'es-US', 8310 'es-UY', 8311 'es-VE', 8312 'et-EE', 8313 'eu-ES', 8314 'fa-IR', 8315 'fi-FI', 8316 'fo-FO', 8317 'fr-BE', 8318 'fr-CA', 8319 'fr-CH', 8320 'fr-FR', 8321 'fr-LU', 8322 'fr-MC', 8323 'fy-NL', 8324 'ga-IE', 8325 'gd-GB', 8326 'gl-ES', 8327 'gu-IN', 8328 'he-IL', 8329 'hi-IN', 8330 'hr-BA', 8331 'hr-HR', 8332 'hu-HU', 8333 'hy-AM', 8334 'id-ID', 8335 'ig-NG', 8336 'ii-CN', 8337 'is-IS', 8338 'it-CH', 8339 'it-IT', 8340 'ja-JP', 8341 'ka-GE', 8342 'kk-KZ', 8343 'kl-GL', 8344 'km-KH', 8345 'kn-IN', 8346 'ko-KR', 8347 'ky-KG', 8348 'lb-LU', 8349 'lo-LA', 8350 'lt-LT', 8351 'lv-LV', 8352 'mi-NZ', 8353 'mk-MK', 8354 'ml-IN', 8355 'mn-MN', 8356 'mr-IN', 8357 'ms-BN', 8358 'ms-MY', 8359 'mt-MT', 8360 'nb-NO', 8361 'ne-NP', 8362 'nl-BE', 8363 'nl-NL', 8364 'nn-NO', 8365 'oc-FR', 8366 'or-IN', 8367 'pa-IN', 8368 'pl-PL', 8369 'ps-AF', 8370 'pt-BR', 8371 'pt-PT', 8372 'rm-CH', 8373 'ro-MD', 8374 'ro-RO', 8375 'ru-RU', 8376 'rw-RW', 8377 'sa-IN', 8378 'se-FI', 8379 'se-NO', 8380 'se-SE', 8381 'si-LK', 8382 'sk-SK', 8383 'sl-SI', 8384 'sq-AL', 8385 'sv-FI', 8386 'sv-SE', 8387 'sw-KE', 8388 'ta-IN', 8389 'te-IN', 8390 'th-TH', 8391 'tk-TM', 8392 'tn-ZA', 8393 'tr-TR', 8394 'tt-RU', 8395 'ug-CN', 8396 'uk-UA', 8397 'ur-PK', 8398 'vi-VN', 8399 'wo-SN', 8400 'xh-ZA', 8401 'yo-NG', 8402 'zh-CN', 8403 'zh-HK', 8404 'zh-MO', 8405 'zh-SG', 8406 'zh-TW', 8407 'zu-ZA', 8408 ); 8409 8410 $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode); 8411 if (in_array($buildprimarykeytotest, $locales)) { 8412 return strtolower($countrycode).'_'.strtoupper($countrycode); 8413 } 8414 8415 if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl 8416 foreach ($locales as $locale) { 8417 $locale_language = locale_get_primary_language($locale); 8418 $locale_region = locale_get_region($locale); 8419 if (strtoupper($countrycode) == $locale_region) { 8420 //var_dump($locale.' - '.$locale_language.' - '.$locale_region); 8421 return strtolower($locale_language).'_'.strtoupper($locale_region); 8422 } 8423 } 8424 } else { 8425 dol_syslog("Warning Exention php-intl is not available", LOG_WARNING); 8426 } 8427 8428 return null; 8429} 8430 8431/** 8432 * Complete or removed entries into a head array (used to build tabs). 8433 * For example, with value added by external modules. Such values are declared into $conf->modules_parts['tab']. 8434 * Or by change using hook completeTabsHead 8435 * 8436 * @param Conf $conf Object conf 8437 * @param Translate $langs Object langs 8438 * @param object|null $object Object object 8439 * @param array $head Object head 8440 * @param int $h New position to fill 8441 * @param string $type Value for object where objectvalue can be 8442 * 'thirdparty' to add a tab in third party view 8443 * 'intervention' to add a tab in intervention view 8444 * 'supplier_order' to add a tab in supplier order view 8445 * 'supplier_invoice' to add a tab in supplier invoice view 8446 * 'invoice' to add a tab in customer invoice view 8447 * 'order' to add a tab in customer order view 8448 * 'contract' to add a tabl in contract view 8449 * 'product' to add a tab in product view 8450 * 'propal' to add a tab in propal view 8451 * 'user' to add a tab in user view 8452 * 'group' to add a tab in group view 8453 * 'member' to add a tab in fundation member view 8454 * 'categories_x' to add a tab in category view ('x': type of category (0=product, 1=supplier, 2=customer, 3=member) 8455 * 'ecm' to add a tab for another ecm view 8456 * 'stock' to add a tab for warehouse view 8457 * @param string $mode 'add' to complete head, 'remove' to remove entries 8458 * @return void 8459 */ 8460function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add') 8461{ 8462 global $hookmanager; 8463 8464 if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) { 8465 foreach ($conf->modules_parts['tabs'][$type] as $value) { 8466 $values = explode(':', $value); 8467 8468 if ($mode == 'add' && !preg_match('/^\-/', $values[1])) { 8469 if (count($values) == 6) { // new declaration with permissions: $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__' 8470 if ($values[0] != $type) { 8471 continue; 8472 } 8473 8474 if (verifCond($values[4])) { 8475 if ($values[3]) { 8476 $langs->load($values[3]); 8477 } 8478 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) { 8479 $substitutionarray = array(); 8480 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2])); 8481 $label = make_substitutions($reg[1], $substitutionarray); 8482 } else { 8483 $label = $langs->trans($values[2]); 8484 } 8485 8486 $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1); 8487 $head[$h][1] = $label; 8488 $head[$h][2] = str_replace('+', '', $values[1]); 8489 $h++; 8490 } 8491 } elseif (count($values) == 5) { // deprecated 8492 dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING); 8493 8494 if ($values[0] != $type) { 8495 continue; 8496 } 8497 if ($values[3]) { 8498 $langs->load($values[3]); 8499 } 8500 if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) { 8501 $substitutionarray = array(); 8502 complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2])); 8503 $label = make_substitutions($reg[1], $substitutionarray); 8504 } else { 8505 $label = $langs->trans($values[2]); 8506 } 8507 8508 $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1); 8509 $head[$h][1] = $label; 8510 $head[$h][2] = str_replace('+', '', $values[1]); 8511 $h++; 8512 } 8513 } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) { 8514 if ($values[0] != $type) { 8515 continue; 8516 } 8517 $tabname = str_replace('-', '', $values[1]); 8518 foreach ($head as $key => $val) { 8519 $condition = (!empty($values[3]) ? verifCond($values[3]) : 1); 8520 //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition); 8521 if ($head[$key][2] == $tabname && $condition) { 8522 unset($head[$key]); 8523 break; 8524 } 8525 } 8526 } 8527 } 8528 } 8529 8530 // No need to make a return $head. Var is modified as a reference 8531 if (!empty($hookmanager)) { 8532 $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head); 8533 $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters); 8534 if ($reshook > 0) { // Hook ask to replace completely the array 8535 $head = $hookmanager->resArray; 8536 } else { // Hook 8537 $head = array_merge($head, $hookmanager->resArray); 8538 } 8539 $h = count($head); 8540 } 8541} 8542 8543/** 8544 * Print common footer : 8545 * conf->global->MAIN_HTML_FOOTER 8546 * js for switch of menu hider 8547 * js for conf->global->MAIN_GOOGLE_AN_ID 8548 * js for conf->global->MAIN_SHOW_TUNING_INFO or $_SERVER["MAIN_SHOW_TUNING_INFO"] 8549 * js for conf->logbuffer 8550 * 8551 * @param string $zone 'private' (for private pages) or 'public' (for public pages) 8552 * @return void 8553 */ 8554function printCommonFooter($zone = 'private') 8555{ 8556 global $conf, $hookmanager, $user, $debugbar; 8557 global $action; 8558 global $micro_start_time; 8559 8560 if ($zone == 'private') { 8561 print "\n".'<!-- Common footer for private page -->'."\n"; 8562 } else { 8563 print "\n".'<!-- Common footer for public page -->'."\n"; 8564 } 8565 8566 // A div to store page_y POST parameter so we can read it using javascript 8567 print "\n<!-- A div to store page_y POST parameter -->\n"; 8568 print '<div id="page_y" style="display: none;">'.(empty($_POST['page_y']) ? '' : $_POST['page_y']).'</div>'."\n"; 8569 8570 $parameters = array(); 8571 $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks 8572 if (empty($reshook)) { 8573 if (!empty($conf->global->MAIN_HTML_FOOTER)) { 8574 print $conf->global->MAIN_HTML_FOOTER."\n"; 8575 } 8576 8577 print "\n"; 8578 if (!empty($conf->use_javascript_ajax)) { 8579 print '<script>'."\n"; 8580 print 'jQuery(document).ready(function() {'."\n"; 8581 8582 if ($zone == 'private' && empty($conf->dol_use_jmobile)) { 8583 print "\n"; 8584 print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n"; 8585 print 'jQuery("li.menuhider").click(function(event) {'; 8586 print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n"; 8587 print ' console.log("We click on .menuhider");'."\n"; 8588 print ' $("body").toggleClass("sidebar-collapse")'."\n"; 8589 print '});'."\n"; 8590 } 8591 8592 // Management of focus and mandatory for fields 8593 if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"])))) { 8594 print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n"; 8595 $relativepathstring = $_SERVER["PHP_SELF"]; 8596 // Clean $relativepathstring 8597 if (constant('DOL_URL_ROOT')) { 8598 $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring); 8599 } 8600 $relativepathstring = preg_replace('/^\//', '', $relativepathstring); 8601 $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring); 8602 //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); 8603 if (!empty($user->default_values[$relativepathstring]['focus'])) { 8604 foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) { 8605 $qualified = 0; 8606 if ($defkey != '_noquery_') { 8607 $tmpqueryarraytohave = explode('&', $defkey); 8608 $foundintru = 0; 8609 foreach ($tmpqueryarraytohave as $tmpquerytohave) { 8610 $tmpquerytohaveparam = explode('=', $tmpquerytohave); 8611 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');"; 8612 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) { 8613 $foundintru = 1; 8614 } 8615 } 8616 if (!$foundintru) { 8617 $qualified = 1; 8618 } 8619 //var_dump($defkey.'-'.$qualified); 8620 } else { 8621 $qualified = 1; 8622 } 8623 8624 if ($qualified) { 8625 foreach ($defval as $paramkey => $paramval) { 8626 // Set focus on field 8627 print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n"; 8628 print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n"; 8629 print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really usefull, but we keep it in case of. 8630 } 8631 } 8632 } 8633 } 8634 if (!empty($user->default_values[$relativepathstring]['mandatory'])) { 8635 foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) { 8636 $qualified = 0; 8637 if ($defkey != '_noquery_') { 8638 $tmpqueryarraytohave = explode('&', $defkey); 8639 $foundintru = 0; 8640 foreach ($tmpqueryarraytohave as $tmpquerytohave) { 8641 $tmpquerytohaveparam = explode('=', $tmpquerytohave); 8642 //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');"; 8643 if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) { 8644 $foundintru = 1; 8645 } 8646 } 8647 if (!$foundintru) { 8648 $qualified = 1; 8649 } 8650 //var_dump($defkey.'-'.$qualified); 8651 } else { 8652 $qualified = 1; 8653 } 8654 8655 if ($qualified) { 8656 foreach ($defval as $paramkey => $paramval) { 8657 // Add property 'required' on input 8658 print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; 8659 print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; 8660 print '// required on a select works only if key is "", so we add the required attributes but also we reset the key -1 or 0 to an empty string'."\n"; 8661 print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; 8662 print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n"; 8663 print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n"; 8664 } 8665 } 8666 } 8667 } 8668 } 8669 8670 print '});'."\n"; 8671 8672 // End of tuning 8673 if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || !empty($conf->global->MAIN_SHOW_TUNING_INFO)) { 8674 print "\n"; 8675 print "/* JS CODE TO ENABLE to add memory info */\n"; 8676 print 'window.console && console.log("'; 8677 if (!empty($conf->global->MEMCACHED_SERVER)) { 8678 print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - '; 8679 } 8680 print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED) ? $conf->global->MAIN_OPTIMIZE_SPEED : 'off'); 8681 if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable. 8682 $micro_end_time = microtime(true); 8683 print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms'; 8684 } 8685 8686 if (function_exists("memory_get_usage")) { 8687 print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount 8688 } 8689 if (function_exists("memory_get_peak_usage")) { 8690 print ' - Real mem peak: '.memory_get_peak_usage(true); 8691 } 8692 if (function_exists("zend_loader_file_encoded")) { 8693 print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no'); 8694 } 8695 print '");'."\n"; 8696 } 8697 8698 print "\n".'</script>'."\n"; 8699 8700 // Google Analytics 8701 // TODO Add a hook here 8702 if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AN_ID)) { 8703 $tmptagarray = explode(',', $conf->global->MAIN_GOOGLE_AN_ID); 8704 foreach ($tmptagarray as $tmptag) { 8705 print "\n"; 8706 print "<!-- JS CODE TO ENABLE for google analtics tag -->\n"; 8707 print " 8708 <!-- Global site tag (gtag.js) - Google Analytics --> 8709 <script async src=\"https://www.googletagmanager.com/gtag/js?id=".trim($tmptag)."\"></script> 8710 <script> 8711 window.dataLayer = window.dataLayer || []; 8712 function gtag(){dataLayer.push(arguments);} 8713 gtag('js', new Date()); 8714 8715 gtag('config', '".trim($tmptag)."'); 8716 </script>"; 8717 print "\n"; 8718 } 8719 } 8720 } 8721 8722 // Add Xdebug coverage of code 8723 if (defined('XDEBUGCOVERAGE')) { 8724 print_r(xdebug_get_code_coverage()); 8725 } 8726 8727 // Add DebugBar data 8728 if (!empty($user->rights->debugbar->read) && is_object($debugbar)) { 8729 $debugbar['time']->stopMeasure('pageaftermaster'); 8730 print '<!-- Output debugbar data -->'."\n"; 8731 $renderer = $debugbar->getRenderer(); 8732 print $debugbar->getRenderer()->render(); 8733 } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show 8734 print "\n"; 8735 print "<!-- Start of log output\n"; 8736 //print '<div class="hidden">'."\n"; 8737 foreach ($conf->logbuffer as $logline) { 8738 print $logline."<br>\n"; 8739 } 8740 //print '</div>'."\n"; 8741 print "End of log output -->\n"; 8742 } 8743 } 8744} 8745 8746/** 8747 * Split a string with 2 keys into key array. 8748 * For example: "A=1;B=2;C=2" is exploded into array('A'=>1,'B'=>2,'C'=>3) 8749 * 8750 * @param string $string String to explode 8751 * @param string $delimiter Delimiter between each couple of data 8752 * @param string $kv Delimiter between key and value 8753 * @return array Array of data exploded 8754 */ 8755function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=') 8756{ 8757 if ($a = explode($delimiter, $string)) { 8758 $ka = array(); 8759 foreach ($a as $s) { // each part 8760 if ($s) { 8761 if ($pos = strpos($s, $kv)) { // key/value delimiter 8762 $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv))); 8763 } else { // key delimiter not found 8764 $ka[] = trim($s); 8765 } 8766 } 8767 } 8768 return $ka; 8769 } 8770 return array(); 8771} 8772 8773 8774/** 8775 * Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag) 8776 * 8777 * @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. 8778 * @return string HTML code to set focus 8779 */ 8780function dol_set_focus($selector) 8781{ 8782 print "\n".'<!-- Set focus onto a specific field -->'."\n"; 8783 print '<script>jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n"; 8784} 8785 8786 8787/** 8788 * Return getmypid() or random PID when function is disabled 8789 * Some web hosts disable this php function for security reasons 8790 * and sometimes we can't redeclare function 8791 * 8792 * @return int 8793 */ 8794function dol_getmypid() 8795{ 8796 if (!function_exists('getmypid')) { 8797 return mt_rand(1, 32768); 8798 } else { 8799 return getmypid(); 8800 } 8801} 8802 8803 8804/** 8805 * Generate natural SQL search string for a criteria (this criteria can be tested on one or several fields) 8806 * 8807 * @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") 8808 * @param string $value The value to look for. 8809 * If param $mode is 0, can contains several keywords separated with a space or | 8810 * like "keyword1 keyword2" = We want record field like keyword1 AND field like keyword2 8811 * or like "keyword1|keyword2" = We want record field like keyword1 OR field like keyword2 8812 * If param $mode is 1, can contains an operator <, > or = like "<10" or ">=100.5 < 1000" 8813 * If param $mode is 2, can contains a list of int id separated by comma like "1,3,4" 8814 * If param $mode is 3, can contains a list of string separated by comma like "a,b,c" 8815 * @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') 8816 * 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' 8817 * @param integer $nofirstand 1=Do not output the first 'AND' 8818 * @return string $res The statement to append to the SQL query 8819 */ 8820function natural_search($fields, $value, $mode = 0, $nofirstand = 0) 8821{ 8822 global $db, $langs; 8823 8824 $value = trim($value); 8825 8826 if ($mode == 0) { 8827 $value = preg_replace('/\*/', '%', $value); // Replace * with % 8828 } 8829 if ($mode == 1) { 8830 $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 8831 } 8832 8833 $value = preg_replace('/\s*\|\s*/', '|', $value); 8834 8835 $crits = explode(' ', $value); 8836 $res = ''; 8837 if (!is_array($fields)) { 8838 $fields = array($fields); 8839 } 8840 8841 $j = 0; 8842 foreach ($crits as $crit) { 8843 $crit = trim($crit); 8844 $i = 0; 8845 $i2 = 0; 8846 $newres = ''; 8847 foreach ($fields as $field) { 8848 if ($mode == 1) { 8849 $operator = '='; 8850 $newcrit = preg_replace('/([<>=]+)/', '', $crit); 8851 8852 $reg = array(); 8853 preg_match('/([<>=]+)/', $crit, $reg); 8854 if ($reg[1]) { 8855 $operator = $reg[1]; 8856 } 8857 if ($newcrit != '') { 8858 $numnewcrit = price2num($newcrit); 8859 if (is_numeric($numnewcrit)) { 8860 $newres .= ($i2 > 0 ? ' OR ' : '').$field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric 8861 } else { 8862 $newres .= ($i2 > 0 ? ' OR ' : '').'1 = 2'; // force false 8863 } 8864 $i2++; // a criteria was added to string 8865 } 8866 } elseif ($mode == 2 || $mode == -2) { 8867 $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer 8868 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : ''); 8869 $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)"; 8870 if ($mode == -2) { 8871 $newres .= ' OR '.$field.' IS NULL'; 8872 } 8873 $i2++; // a criteria was added to string 8874 } elseif ($mode == 3 || $mode == -3) { 8875 $tmparray = explode(',', $crit); 8876 if (count($tmparray)) { 8877 $listofcodes = ''; 8878 foreach ($tmparray as $val) { 8879 $val = trim($val); 8880 if ($val) { 8881 $listofcodes .= ($listofcodes ? ',' : ''); 8882 $listofcodes .= "'".$db->escape($val)."'"; 8883 } 8884 } 8885 $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")"; 8886 $i2++; // a criteria was added to string 8887 } 8888 if ($mode == -3) { 8889 $newres .= ' OR '.$field.' IS NULL'; 8890 } 8891 } elseif ($mode == 4) { 8892 $tmparray = explode(',', $crit); 8893 if (count($tmparray)) { 8894 $listofcodes = ''; 8895 foreach ($tmparray as $val) { 8896 $val = trim($val); 8897 if ($val) { 8898 $newres .= ($i2 > 0 ? ' OR (' : '(').$field.' LIKE \''.$db->escape($val).',%\''; 8899 $newres .= ' OR '.$field.' = \''.$db->escape($val).'\''; 8900 $newres .= ' OR '.$field.' LIKE \'%,'.$db->escape($val).'\''; 8901 $newres .= ' OR '.$field.' LIKE \'%,'.$db->escape($val).',%\''; 8902 $newres .= ')'; 8903 $i2++; 8904 } 8905 } 8906 } 8907 } else // $mode=0 8908 { 8909 $tmpcrits = explode('|', $crit); 8910 $i3 = 0; 8911 foreach ($tmpcrits as $tmpcrit) { 8912 if ($tmpcrit !== '0' && empty($tmpcrit)) { 8913 continue; 8914 } 8915 8916 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : ''); 8917 8918 if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field 8919 $newres .= $field." = ".(is_numeric(trim($tmpcrit)) ? ((float) trim($tmpcrit)) : '0'); 8920 } else { 8921 $newres .= $field." LIKE '"; 8922 8923 $tmpcrit = trim($tmpcrit); 8924 $tmpcrit2 = $tmpcrit; 8925 $tmpbefore = '%'; 8926 $tmpafter = '%'; 8927 if (preg_match('/^[\^\$]/', $tmpcrit)) { 8928 $tmpbefore = ''; 8929 $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2); 8930 } 8931 if (preg_match('/[\^\$]$/', $tmpcrit)) { 8932 $tmpafter = ''; 8933 $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2); 8934 } 8935 $newres .= $tmpbefore; 8936 $newres .= $db->escape($tmpcrit2); 8937 $newres .= $tmpafter; 8938 $newres .= "'"; 8939 if ($tmpcrit2 == '') { 8940 $newres .= ' OR '.$field." IS NULL"; 8941 } 8942 } 8943 8944 $i3++; 8945 } 8946 $i2++; // a criteria was added to string 8947 } 8948 $i++; 8949 } 8950 if ($newres) { 8951 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : ''); 8952 } 8953 $j++; 8954 } 8955 $res = ($nofirstand ? "" : " AND ")."(".$res.")"; 8956 //print 'xx'.$res.'yy'; 8957 return $res; 8958} 8959 8960/** 8961 * Return string with full Url. The file qualified is the one defined by relative path in $object->last_main_doc 8962 * 8963 * @param Object $object Object 8964 * @return string Url string 8965 */ 8966function showDirectDownloadLink($object) 8967{ 8968 global $conf, $langs; 8969 8970 $out = ''; 8971 $url = $object->getLastMainDocLink($object->element); 8972 8973 $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>'; 8974 if ($url) { 8975 $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>'; 8976 $out .= ajax_autoselect("directdownloadlink", 0); 8977 } else { 8978 $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>'; 8979 } 8980 8981 return $out; 8982} 8983 8984/** 8985 * Return the filename of file to get the thumbs 8986 * 8987 * @param string $file Original filename (full or relative path) 8988 * @param string $extName Extension to differenciate thumb file name ('', '_small', '_mini') 8989 * @param string $extImgTarget Force image extension for thumbs. Use '' to keep same extension than original image (default). 8990 * @return string New file name (full or relative path, including the thumbs/) 8991 */ 8992function getImageFileNameForSize($file, $extName, $extImgTarget = '') 8993{ 8994 $dirName = dirname($file); 8995 if ($dirName == '.') { 8996 $dirName = ''; 8997 } 8998 8999 $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case 9000 $fileName = basename($fileName); 9001 9002 if (empty($extImgTarget)) { 9003 $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : ''); 9004 } 9005 if (empty($extImgTarget)) { 9006 $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : ''); 9007 } 9008 if (empty($extImgTarget)) { 9009 $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : ''); 9010 } 9011 if (empty($extImgTarget)) { 9012 $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : ''); 9013 } 9014 if (empty($extImgTarget)) { 9015 $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : ''); 9016 } 9017 if (empty($extImgTarget)) { 9018 $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : ''); 9019 } 9020 9021 if (!$extImgTarget) { 9022 return $file; 9023 } 9024 9025 $subdir = ''; 9026 if ($extName) { 9027 $subdir = 'thumbs/'; 9028 } 9029 9030 return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb 9031} 9032 9033 9034/** 9035 * Return URL we can use for advanced preview links 9036 * 9037 * @param string $modulepart propal, facture, facture_fourn, ... 9038 * @param string $relativepath Relative path of docs. 9039 * @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) 9040 * @param string $param More param on http links 9041 * @return string|array Output string with href link or array with all components of link 9042 */ 9043function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '') 9044{ 9045 global $conf, $langs; 9046 9047 if (empty($conf->use_javascript_ajax)) { 9048 return ''; 9049 } 9050 9051 $isAllowedForPreview = dolIsAllowedForPreview($relativepath); 9052 9053 if ($alldata == 1) { 9054 if ($isAllowedForPreview) { 9055 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)); 9056 } else { 9057 return array(); 9058 } 9059 } 9060 9061 // old behavior, return a string 9062 if ($isAllowedForPreview) { 9063 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')).'\')'; 9064 } else { 9065 return ''; 9066 } 9067} 9068 9069 9070/** 9071 * Make content of an input box selected when we click into input field. 9072 * 9073 * @param string $htmlname Id of html object ('#idvalue' or '.classvalue') 9074 * @param string $addlink Add a 'link to' after 9075 * @return string 9076 */ 9077function ajax_autoselect($htmlname, $addlink = '') 9078{ 9079 global $langs; 9080 $out = '<script> 9081 jQuery(document).ready(function () { 9082 jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } ); 9083 }); 9084 </script>'; 9085 if ($addlink) { 9086 $out .= ' <a href="'.$addlink.'" target="_blank">'.$langs->trans("Link").'</a>'; 9087 } 9088 return $out; 9089} 9090 9091/** 9092 * Return if a file is qualified for preview 9093 * 9094 * @param string $file Filename we looking for information 9095 * @return int 1 If allowed, 0 otherwise 9096 * @see dol_mimetype(), image_format_supported() from images.lib.php 9097 */ 9098function dolIsAllowedForPreview($file) 9099{ 9100 global $conf; 9101 9102 // Check .noexe extension in filename 9103 if (preg_match('/\.noexe$/i', $file)) { 9104 return 0; 9105 } 9106 9107 // Check mime types 9108 $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp'); 9109 if (!empty($conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES)) { 9110 $mime_preview[] = 'svg+xml'; 9111 } 9112 //$mime_preview[]='vnd.oasis.opendocument.presentation'; 9113 //$mime_preview[]='archive'; 9114 $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview); 9115 if ($num_mime !== false) { 9116 return 1; 9117 } 9118 9119 // By default, not allowed for preview 9120 return 0; 9121} 9122 9123 9124/** 9125 * Return mime type of a file 9126 * 9127 * @param string $file Filename we looking for MIME type 9128 * @param string $default Default mime type if extension not found in known list 9129 * @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 9130 * @return string Return a mime type family (text/xxx, application/xxx, image/xxx, audio, video, archive) 9131 * @see dolIsAllowedForPreview(), image_format_supported() from images.lib.php 9132 */ 9133function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0) 9134{ 9135 $mime = $default; 9136 $imgmime = 'other.png'; 9137 $famime = 'file-o'; 9138 $srclang = ''; 9139 9140 $tmpfile = preg_replace('/\.noexe$/', '', $file); 9141 9142 // Plain text files 9143 if (preg_match('/\.txt$/i', $tmpfile)) { 9144 $mime = 'text/plain'; 9145 $imgmime = 'text.png'; 9146 $famime = 'file-text-o'; 9147 } 9148 if (preg_match('/\.rtx$/i', $tmpfile)) { 9149 $mime = 'text/richtext'; 9150 $imgmime = 'text.png'; 9151 $famime = 'file-text-o'; 9152 } 9153 if (preg_match('/\.csv$/i', $tmpfile)) { 9154 $mime = 'text/csv'; 9155 $imgmime = 'text.png'; 9156 $famime = 'file-text-o'; 9157 } 9158 if (preg_match('/\.tsv$/i', $tmpfile)) { 9159 $mime = 'text/tab-separated-values'; 9160 $imgmime = 'text.png'; 9161 $famime = 'file-text-o'; 9162 } 9163 if (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) { 9164 $mime = 'text/plain'; 9165 $imgmime = 'text.png'; 9166 $famime = 'file-text-o'; 9167 } 9168 if (preg_match('/\.ini$/i', $tmpfile)) { 9169 $mime = 'text/plain'; 9170 $imgmime = 'text.png'; 9171 $srclang = 'ini'; 9172 $famime = 'file-text-o'; 9173 } 9174 if (preg_match('/\.md$/i', $tmpfile)) { 9175 $mime = 'text/plain'; 9176 $imgmime = 'text.png'; 9177 $srclang = 'md'; 9178 $famime = 'file-text-o'; 9179 } 9180 if (preg_match('/\.css$/i', $tmpfile)) { 9181 $mime = 'text/css'; 9182 $imgmime = 'css.png'; 9183 $srclang = 'css'; 9184 $famime = 'file-text-o'; 9185 } 9186 if (preg_match('/\.lang$/i', $tmpfile)) { 9187 $mime = 'text/plain'; 9188 $imgmime = 'text.png'; 9189 $srclang = 'lang'; 9190 $famime = 'file-text-o'; 9191 } 9192 // Certificate files 9193 if (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) { 9194 $mime = 'text/plain'; 9195 $imgmime = 'text.png'; 9196 $famime = 'file-text-o'; 9197 } 9198 // XML based (HTML/XML/XAML) 9199 if (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) { 9200 $mime = 'text/html'; 9201 $imgmime = 'html.png'; 9202 $srclang = 'html'; 9203 $famime = 'file-text-o'; 9204 } 9205 if (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) { 9206 $mime = 'text/xml'; 9207 $imgmime = 'other.png'; 9208 $srclang = 'xml'; 9209 $famime = 'file-text-o'; 9210 } 9211 if (preg_match('/\.xaml$/i', $tmpfile)) { 9212 $mime = 'text/xml'; 9213 $imgmime = 'other.png'; 9214 $srclang = 'xaml'; 9215 $famime = 'file-text-o'; 9216 } 9217 // Languages 9218 if (preg_match('/\.bas$/i', $tmpfile)) { 9219 $mime = 'text/plain'; 9220 $imgmime = 'text.png'; 9221 $srclang = 'bas'; 9222 $famime = 'file-code-o'; 9223 } 9224 if (preg_match('/\.(c)$/i', $tmpfile)) { 9225 $mime = 'text/plain'; 9226 $imgmime = 'text.png'; 9227 $srclang = 'c'; 9228 $famime = 'file-code-o'; 9229 } 9230 if (preg_match('/\.(cpp)$/i', $tmpfile)) { 9231 $mime = 'text/plain'; 9232 $imgmime = 'text.png'; 9233 $srclang = 'cpp'; 9234 $famime = 'file-code-o'; 9235 } 9236 if (preg_match('/\.cs$/i', $tmpfile)) { 9237 $mime = 'text/plain'; 9238 $imgmime = 'text.png'; 9239 $srclang = 'cs'; 9240 $famime = 'file-code-o'; 9241 } 9242 if (preg_match('/\.(h)$/i', $tmpfile)) { 9243 $mime = 'text/plain'; 9244 $imgmime = 'text.png'; 9245 $srclang = 'h'; 9246 $famime = 'file-code-o'; 9247 } 9248 if (preg_match('/\.(java|jsp)$/i', $tmpfile)) { 9249 $mime = 'text/plain'; 9250 $imgmime = 'text.png'; 9251 $srclang = 'java'; 9252 $famime = 'file-code-o'; 9253 } 9254 if (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) { 9255 $mime = 'text/plain'; 9256 $imgmime = 'php.png'; 9257 $srclang = 'php'; 9258 $famime = 'file-code-o'; 9259 } 9260 if (preg_match('/\.phtml$/i', $tmpfile)) { 9261 $mime = 'text/plain'; 9262 $imgmime = 'php.png'; 9263 $srclang = 'php'; 9264 $famime = 'file-code-o'; 9265 } 9266 if (preg_match('/\.(pl|pm)$/i', $tmpfile)) { 9267 $mime = 'text/plain'; 9268 $imgmime = 'pl.png'; 9269 $srclang = 'perl'; 9270 $famime = 'file-code-o'; 9271 } 9272 if (preg_match('/\.sql$/i', $tmpfile)) { 9273 $mime = 'text/plain'; 9274 $imgmime = 'text.png'; 9275 $srclang = 'sql'; 9276 $famime = 'file-code-o'; 9277 } 9278 if (preg_match('/\.js$/i', $tmpfile)) { 9279 $mime = 'text/x-javascript'; 9280 $imgmime = 'jscript.png'; 9281 $srclang = 'js'; 9282 $famime = 'file-code-o'; 9283 } 9284 // Open office 9285 if (preg_match('/\.odp$/i', $tmpfile)) { 9286 $mime = 'application/vnd.oasis.opendocument.presentation'; 9287 $imgmime = 'ooffice.png'; 9288 $famime = 'file-powerpoint-o'; 9289 } 9290 if (preg_match('/\.ods$/i', $tmpfile)) { 9291 $mime = 'application/vnd.oasis.opendocument.spreadsheet'; 9292 $imgmime = 'ooffice.png'; 9293 $famime = 'file-excel-o'; 9294 } 9295 if (preg_match('/\.odt$/i', $tmpfile)) { 9296 $mime = 'application/vnd.oasis.opendocument.text'; 9297 $imgmime = 'ooffice.png'; 9298 $famime = 'file-word-o'; 9299 } 9300 // MS Office 9301 if (preg_match('/\.mdb$/i', $tmpfile)) { 9302 $mime = 'application/msaccess'; 9303 $imgmime = 'mdb.png'; 9304 $famime = 'file-o'; 9305 } 9306 if (preg_match('/\.doc(x|m)?$/i', $tmpfile)) { 9307 $mime = 'application/msword'; 9308 $imgmime = 'doc.png'; 9309 $famime = 'file-word-o'; 9310 } 9311 if (preg_match('/\.dot(x|m)?$/i', $tmpfile)) { 9312 $mime = 'application/msword'; 9313 $imgmime = 'doc.png'; 9314 $famime = 'file-word-o'; 9315 } 9316 if (preg_match('/\.xlt(x)?$/i', $tmpfile)) { 9317 $mime = 'application/vnd.ms-excel'; 9318 $imgmime = 'xls.png'; 9319 $famime = 'file-excel-o'; 9320 } 9321 if (preg_match('/\.xla(m)?$/i', $tmpfile)) { 9322 $mime = 'application/vnd.ms-excel'; 9323 $imgmime = 'xls.png'; 9324 $famime = 'file-excel-o'; 9325 } 9326 if (preg_match('/\.xls$/i', $tmpfile)) { 9327 $mime = 'application/vnd.ms-excel'; 9328 $imgmime = 'xls.png'; 9329 $famime = 'file-excel-o'; 9330 } 9331 if (preg_match('/\.xls(b|m|x)$/i', $tmpfile)) { 9332 $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; 9333 $imgmime = 'xls.png'; 9334 $famime = 'file-excel-o'; 9335 } 9336 if (preg_match('/\.pps(m|x)?$/i', $tmpfile)) { 9337 $mime = 'application/vnd.ms-powerpoint'; 9338 $imgmime = 'ppt.png'; 9339 $famime = 'file-powerpoint-o'; 9340 } 9341 if (preg_match('/\.ppt(m|x)?$/i', $tmpfile)) { 9342 $mime = 'application/x-mspowerpoint'; 9343 $imgmime = 'ppt.png'; 9344 $famime = 'file-powerpoint-o'; 9345 } 9346 // Other 9347 if (preg_match('/\.pdf$/i', $tmpfile)) { 9348 $mime = 'application/pdf'; 9349 $imgmime = 'pdf.png'; 9350 $famime = 'file-pdf-o'; 9351 } 9352 // Scripts 9353 if (preg_match('/\.bat$/i', $tmpfile)) { 9354 $mime = 'text/x-bat'; 9355 $imgmime = 'script.png'; 9356 $srclang = 'dos'; 9357 $famime = 'file-code-o'; 9358 } 9359 if (preg_match('/\.sh$/i', $tmpfile)) { 9360 $mime = 'text/x-sh'; 9361 $imgmime = 'script.png'; 9362 $srclang = 'bash'; 9363 $famime = 'file-code-o'; 9364 } 9365 if (preg_match('/\.ksh$/i', $tmpfile)) { 9366 $mime = 'text/x-ksh'; 9367 $imgmime = 'script.png'; 9368 $srclang = 'bash'; 9369 $famime = 'file-code-o'; 9370 } 9371 if (preg_match('/\.bash$/i', $tmpfile)) { 9372 $mime = 'text/x-bash'; 9373 $imgmime = 'script.png'; 9374 $srclang = 'bash'; 9375 $famime = 'file-code-o'; 9376 } 9377 // Images 9378 if (preg_match('/\.ico$/i', $tmpfile)) { 9379 $mime = 'image/x-icon'; 9380 $imgmime = 'image.png'; 9381 $famime = 'file-image-o'; 9382 } 9383 if (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) { 9384 $mime = 'image/jpeg'; 9385 $imgmime = 'image.png'; 9386 $famime = 'file-image-o'; 9387 } 9388 if (preg_match('/\.png$/i', $tmpfile)) { 9389 $mime = 'image/png'; 9390 $imgmime = 'image.png'; 9391 $famime = 'file-image-o'; 9392 } 9393 if (preg_match('/\.gif$/i', $tmpfile)) { 9394 $mime = 'image/gif'; 9395 $imgmime = 'image.png'; 9396 $famime = 'file-image-o'; 9397 } 9398 if (preg_match('/\.bmp$/i', $tmpfile)) { 9399 $mime = 'image/bmp'; 9400 $imgmime = 'image.png'; 9401 $famime = 'file-image-o'; 9402 } 9403 if (preg_match('/\.(tif|tiff)$/i', $tmpfile)) { 9404 $mime = 'image/tiff'; 9405 $imgmime = 'image.png'; 9406 $famime = 'file-image-o'; 9407 } 9408 if (preg_match('/\.svg$/i', $tmpfile)) { 9409 $mime = 'image/svg+xml'; 9410 $imgmime = 'image.png'; 9411 $famime = 'file-image-o'; 9412 } 9413 if (preg_match('/\.webp$/i', $tmpfile)) { 9414 $mime = 'image/webp'; 9415 $imgmime = 'image.png'; 9416 $famime = 'file-image-o'; 9417 } 9418 // Calendar 9419 if (preg_match('/\.vcs$/i', $tmpfile)) { 9420 $mime = 'text/calendar'; 9421 $imgmime = 'other.png'; 9422 $famime = 'file-text-o'; 9423 } 9424 if (preg_match('/\.ics$/i', $tmpfile)) { 9425 $mime = 'text/calendar'; 9426 $imgmime = 'other.png'; 9427 $famime = 'file-text-o'; 9428 } 9429 // Other 9430 if (preg_match('/\.torrent$/i', $tmpfile)) { 9431 $mime = 'application/x-bittorrent'; 9432 $imgmime = 'other.png'; 9433 $famime = 'file-o'; 9434 } 9435 // Audio 9436 if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) { 9437 $mime = 'audio'; 9438 $imgmime = 'audio.png'; 9439 $famime = 'file-audio-o'; 9440 } 9441 // Video 9442 if (preg_match('/\.mp4$/i', $tmpfile)) { 9443 $mime = 'video/mp4'; 9444 $imgmime = 'video.png'; 9445 $famime = 'file-video-o'; 9446 } 9447 if (preg_match('/\.ogv$/i', $tmpfile)) { 9448 $mime = 'video/ogg'; 9449 $imgmime = 'video.png'; 9450 $famime = 'file-video-o'; 9451 } 9452 if (preg_match('/\.webm$/i', $tmpfile)) { 9453 $mime = 'video/webm'; 9454 $imgmime = 'video.png'; 9455 $famime = 'file-video-o'; 9456 } 9457 if (preg_match('/\.avi$/i', $tmpfile)) { 9458 $mime = 'video/x-msvideo'; 9459 $imgmime = 'video.png'; 9460 $famime = 'file-video-o'; 9461 } 9462 if (preg_match('/\.divx$/i', $tmpfile)) { 9463 $mime = 'video/divx'; 9464 $imgmime = 'video.png'; 9465 $famime = 'file-video-o'; 9466 } 9467 if (preg_match('/\.xvid$/i', $tmpfile)) { 9468 $mime = 'video/xvid'; 9469 $imgmime = 'video.png'; 9470 $famime = 'file-video-o'; 9471 } 9472 if (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) { 9473 $mime = 'video'; 9474 $imgmime = 'video.png'; 9475 $famime = 'file-video-o'; 9476 } 9477 // Archive 9478 if (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) { 9479 $mime = 'archive'; 9480 $imgmime = 'archive.png'; 9481 $famime = 'file-archive-o'; 9482 } // application/xxx where zzz is zip, ... 9483 // Exe 9484 if (preg_match('/\.(exe|com)$/i', $tmpfile)) { 9485 $mime = 'application/octet-stream'; 9486 $imgmime = 'other.png'; 9487 $famime = 'file-o'; 9488 } 9489 // Lib 9490 if (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) { 9491 $mime = 'library'; 9492 $imgmime = 'library.png'; 9493 $famime = 'file-o'; 9494 } 9495 // Err 9496 if (preg_match('/\.err$/i', $tmpfile)) { 9497 $mime = 'error'; 9498 $imgmime = 'error.png'; 9499 $famime = 'file-text-o'; 9500 } 9501 9502 // Return string 9503 if ($mode == 1) { 9504 $tmp = explode('/', $mime); 9505 return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]); 9506 } 9507 if ($mode == 2) { 9508 return $imgmime; 9509 } 9510 if ($mode == 3) { 9511 return $srclang; 9512 } 9513 if ($mode == 4) { 9514 return $famime; 9515 } 9516 return $mime; 9517} 9518 9519/** 9520 * Return value from dictionary 9521 * 9522 * @param string $tablename name of dictionary 9523 * @param string $field the value to return 9524 * @param int $id id of line 9525 * @param bool $checkentity add filter on entity 9526 * @param string $rowidfield name of the column rowid 9527 * @return string 9528 */ 9529function getDictvalue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid') 9530{ 9531 global $dictvalues, $db, $langs; 9532 9533 if (!isset($dictvalues[$tablename])) { 9534 $dictvalues[$tablename] = array(); 9535 9536 $sql = 'SELECT * FROM '.$tablename.' WHERE 1 = 1'; // Here select * is allowed as it is generic code and we don't have list of fields 9537 if ($checkentity) { 9538 $sql .= ' AND entity IN (0,'.getEntity($tablename).')'; 9539 } 9540 9541 $resql = $db->query($sql); 9542 if ($resql) { 9543 while ($obj = $db->fetch_object($resql)) { 9544 $dictvalues[$tablename][$obj->{$rowidfield}] = $obj; 9545 } 9546 } else { 9547 dol_print_error($db); 9548 } 9549 } 9550 9551 if (!empty($dictvalues[$tablename][$id])) { 9552 return $dictvalues[$tablename][$id]->{$field}; // Found 9553 } else // Not found 9554 { 9555 if ($id > 0) { 9556 return $id; 9557 } 9558 return ''; 9559 } 9560} 9561 9562/** 9563 * Return true if the color is light 9564 * 9565 * @param string $stringcolor String with hex (FFFFFF) or comma RGB ('255,255,255') 9566 * @return int -1 : Error with argument passed |0 : color is dark | 1 : color is light 9567 */ 9568function colorIsLight($stringcolor) 9569{ 9570 $stringcolor = str_replace('#', '', $stringcolor); 9571 $res = -1; 9572 if (!empty($stringcolor)) { 9573 $res = 0; 9574 $tmp = explode(',', $stringcolor); 9575 if (count($tmp) > 1) { // This is a comma RGB ('255','255','255') 9576 $r = $tmp[0]; 9577 $g = $tmp[1]; 9578 $b = $tmp[2]; 9579 } else { 9580 $hexr = $stringcolor[0].$stringcolor[1]; 9581 $hexg = $stringcolor[2].$stringcolor[3]; 9582 $hexb = $stringcolor[4].$stringcolor[5]; 9583 $r = hexdec($hexr); 9584 $g = hexdec($hexg); 9585 $b = hexdec($hexb); 9586 } 9587 $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm 9588 if ($bright > 0.6) { 9589 $res = 1; 9590 } 9591 } 9592 return $res; 9593} 9594 9595/** 9596 * Function to test if an entry is enabled or not 9597 * 9598 * @param string $type_user 0=We test for internal user, 1=We test for external user 9599 * @param array $menuentry Array for feature entry to test 9600 * @param array $listofmodulesforexternal Array with list of modules allowed to external users 9601 * @return int 0=Hide, 1=Show, 2=Show gray 9602 */ 9603function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal) 9604{ 9605 global $conf; 9606 9607 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms']; 9608 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal); 9609 if (empty($menuentry['enabled'])) { 9610 return 0; // Entry disabled by condition 9611 } 9612 if ($type_user && $menuentry['module']) { 9613 $tmploops = explode('|', $menuentry['module']); 9614 $found = 0; 9615 foreach ($tmploops as $tmploop) { 9616 if (in_array($tmploop, $listofmodulesforexternal)) { 9617 $found++; 9618 break; 9619 } 9620 } 9621 if (!$found) { 9622 return 0; // Entry is for menus all excluded to external users 9623 } 9624 } 9625 if (!$menuentry['perms'] && $type_user) { 9626 return 0; // No permissions and user is external 9627 } 9628 if (!$menuentry['perms'] && !empty($conf->global->MAIN_MENU_HIDE_UNAUTHORIZED)) { 9629 return 0; // No permissions and option to hide when not allowed, even for internal user, is on 9630 } 9631 if (!$menuentry['perms']) { 9632 return 2; // No permissions and user is external 9633 } 9634 return 1; 9635} 9636 9637/** 9638 * Round to next multiple. 9639 * 9640 * @param double $n Number to round up 9641 * @param integer $x Multiple. For example 60 to round up to nearest exact minute for a date with seconds. 9642 * @return integer Value rounded. 9643 */ 9644function roundUpToNextMultiple($n, $x = 5) 9645{ 9646 return (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x; 9647} 9648 9649/** 9650 * Function dolGetBadge 9651 * 9652 * @param string $label label of badge no html : use in alt attribute for accessibility 9653 * @param string $html optional : label of badge with html 9654 * @param string $type type of badge : Primary Secondary Success Danger Warning Info Light Dark status0 status1 status2 status3 status4 status5 status6 status7 status8 status9 9655 * @param string $mode default '' , 'pill', 'dot' 9656 * @param string $url the url for link 9657 * @param array $params various params for future : recommended rather than adding more fuction arguments. array('attr'=>array('title'=>'abc')) 9658 * @return string Html badge 9659 */ 9660function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array()) 9661{ 9662 $attr = array( 9663 'class'=>'badge '.(!empty($mode) ? ' badge-'.$mode : '').(!empty($type) ? ' badge-'.$type : '').(empty($params['css']) ? '' : ' '.$params['css']) 9664 ); 9665 9666 if (empty($html)) { 9667 $html = $label; 9668 } 9669 9670 if (!empty($url)) { 9671 $attr['href'] = $url; 9672 } 9673 9674 if ($mode === 'dot') { 9675 $attr['class'] .= ' classfortooltip'; 9676 $attr['title'] = $html; 9677 $attr['aria-label'] = $label; 9678 $html = ''; 9679 } 9680 9681 // Override attr 9682 if (!empty($params['attr']) && is_array($params['attr'])) { 9683 foreach ($params['attr'] as $key => $value) { 9684 if ($key == 'class') { 9685 $attr['class'] .= ' '.$value; 9686 } elseif ($key == 'classOverride') { 9687 $attr['class'] = $value; 9688 } else { 9689 $attr[$key] = $value; 9690 } 9691 } 9692 } 9693 9694 // TODO: add hook 9695 9696 // escape all attribute 9697 $attr = array_map('dol_escape_htmltag', $attr); 9698 9699 $TCompiledAttr = array(); 9700 foreach ($attr as $key => $value) { 9701 $TCompiledAttr[] = $key.'="'.$value.'"'; 9702 } 9703 9704 $compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : ''; 9705 9706 $tag = !empty($url) ? 'a' : 'span'; 9707 9708 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>'; 9709} 9710 9711 9712/** 9713 * Output the badge of a status. 9714 * 9715 * @param string $statusLabel Label of badge no html : use in alt attribute for accessibility 9716 * @param string $statusLabelShort Short label of badge no html 9717 * @param string $html Optional : label of badge with html 9718 * @param string $statusType status0 status1 status2 status3 status4 status5 status6 status7 status8 status9 : image name or badge name 9719 * @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 9720 * @param string $url The url for link 9721 * @param array $params Various params. Example: array('tooltip'=>'no|...', 'badgeParams'=>...) 9722 * @return string Html status string 9723 */ 9724function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array()) 9725{ 9726 global $conf; 9727 9728 $return = ''; 9729 $dolGetBadgeParams = array(); 9730 9731 if (!empty($params['badgeParams'])) { 9732 $dolGetBadgeParams = $params['badgeParams']; 9733 } 9734 9735 // TODO : add a hook 9736 if ($displayMode == 0) { 9737 $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)); 9738 } elseif ($displayMode == 1) { 9739 $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort); 9740 } elseif (!empty($conf->global->MAIN_STATUS_USES_IMAGES)) { 9741 // Use status with images (for backward compatibility) 9742 $return = ''; 9743 $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : ''); 9744 $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>' : ''); 9745 9746 // For small screen, we always use the short label instead of long label. 9747 if (!empty($conf->dol_optimize_smallscreen)) { 9748 if ($displayMode == 0) { 9749 $displayMode = 1; 9750 } elseif ($displayMode == 4) { 9751 $displayMode = 2; 9752 } elseif ($displayMode == 6) { 9753 $displayMode = 5; 9754 } 9755 } 9756 9757 // For backward compatibility. Image's filename are still in French, so we use this array to convert 9758 $statusImg = array( 9759 'status0' => 'statut0', 9760 'status1' => 'statut1', 9761 'status2' => 'statut2', 9762 'status3' => 'statut3', 9763 'status4' => 'statut4', 9764 'status5' => 'statut5', 9765 'status6' => 'statut6', 9766 'status7' => 'statut7', 9767 'status8' => 'statut8', 9768 'status9' => 'statut9' 9769 ); 9770 9771 if (!empty($statusImg[$statusType])) { 9772 $htmlImg = img_picto($statusLabel, $statusImg[$statusType]); 9773 } else { 9774 $htmlImg = img_picto($statusLabel, $statusType); 9775 } 9776 9777 if ($displayMode === 2) { 9778 $return = $htmlImg.' '.$htmlLabelShort; 9779 } elseif ($displayMode === 3) { 9780 $return = $htmlImg; 9781 } elseif ($displayMode === 4) { 9782 $return = $htmlImg.' '.$htmlLabel; 9783 } elseif ($displayMode === 5) { 9784 $return = $htmlLabelShort.' '.$htmlImg; 9785 } else { // $displayMode >= 6 9786 $return = $htmlLabel.' '.$htmlImg; 9787 } 9788 } elseif (empty($conf->global->MAIN_STATUS_USES_IMAGES) && !empty($displayMode)) { 9789 // Use new badge 9790 $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort); 9791 9792 $dolGetBadgeParams['attr']['class'] = 'badge-status'; 9793 $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : ''); 9794 9795 if ($displayMode == 3) { 9796 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams); 9797 } elseif ($displayMode === 5) { 9798 $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams); 9799 } else { 9800 $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams); 9801 } 9802 } 9803 9804 return $return; 9805} 9806 9807 9808/** 9809 * Function dolGetButtonAction 9810 * 9811 * @param string $label label of button without HTML : use in alt attribute for accessibility $html is not empty 9812 * @param string $html optional : content with html 9813 * @param string $actionType default, delete, danger 9814 * @param string $url the url for link 9815 * @param string $id attribute id of button 9816 * @param int $userRight user action right 9817 * @param array $params various params for future : recommended rather than adding more function arguments 9818 * @return string html button 9819 */ 9820function dolGetButtonAction($label, $html = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array()) 9821{ 9822 $class = 'butAction'; 9823 if ($actionType == 'danger' || $actionType == 'delete') { 9824 $class = 'butActionDelete'; 9825 if (strpos($url, 'token=') === false) $url .= '&token='.newToken(); 9826 } 9827 9828 $attr = array( 9829 'class' => $class, 9830 'href' => empty($url) ? '' : $url, 9831 'title' => $label 9832 ); 9833 9834 if (empty($html)) { 9835 $html = $label; 9836 } else { 9837 $attr['aria-label'] = $label; 9838 } 9839 9840 if (empty($userRight)) { 9841 $attr['class'] = 'butActionRefused'; 9842 $attr['href'] = ''; 9843 } 9844 9845 if (!empty($id)) { 9846 $attr['id'] = $id; 9847 } 9848 9849 // Override attr 9850 if (!empty($params['attr']) && is_array($params['attr'])) { 9851 foreach ($params['attr'] as $key => $value) { 9852 if ($key == 'class') { 9853 $attr['class'] .= ' '.$value; 9854 } elseif ($key == 'classOverride') { 9855 $attr['class'] = $value; 9856 } else { 9857 $attr[$key] = $value; 9858 } 9859 } 9860 } 9861 9862 if (isset($attr['href']) && empty($attr['href'])) { 9863 unset($attr['href']); 9864 } 9865 9866 // TODO : add a hook 9867 9868 // escape all attribute 9869 $attr = array_map('dol_escape_htmltag', $attr); 9870 9871 $TCompiledAttr = array(); 9872 foreach ($attr as $key => $value) { 9873 $TCompiledAttr[] = $key.'="'.$value.'"'; 9874 } 9875 9876 $compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : ''; 9877 9878 $tag = !empty($attr['href']) ? 'a' : 'span'; 9879 9880 return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>'; 9881} 9882 9883/** 9884 * Add space between dolGetButtonTitle 9885 * 9886 * @param string $moreClass more css class label 9887 * @return string html of title separator 9888 */ 9889function dolGetButtonTitleSeparator($moreClass = "") 9890{ 9891 return '<span class="button-title-separator '.$moreClass.'" ></span>'; 9892} 9893 9894/** 9895 * Function dolGetButtonTitle : this kind of buttons are used in title in list 9896 * 9897 * @param string $label label of button 9898 * @param string $helpText optional : content for help tooltip 9899 * @param string $iconClass class for icon element (Example: 'fa fa-file') 9900 * @param string $url the url for link 9901 * @param string $id attribute id of button 9902 * @param int $status 0 no user rights, 1 active, 2 current action or selected, -1 Feature Disabled, -2 disable Other reason use helpText as tooltip 9903 * @param array $params various params for future : recommended rather than adding more function arguments 9904 * @return string html button 9905 */ 9906function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array()) 9907{ 9908 global $langs, $conf, $user; 9909 9910 // Actually this conf is used in css too for external module compatibility and smooth transition to this function 9911 if (!empty($conf->global->MAIN_BUTTON_HIDE_UNAUTHORIZED) && (!$user->admin) && $status <= 0) { 9912 return ''; 9913 } 9914 9915 $class = 'btnTitle'; 9916 if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-comment-dots'))) { 9917 $class .= ' btnTitlePlus'; 9918 } 9919 $useclassfortooltip = 1; 9920 9921 if (!empty($params['morecss'])) { 9922 $class .= ' '.$params['morecss']; 9923 } 9924 9925 $attr = array( 9926 'class' => $class, 9927 'href' => empty($url) ? '' : $url 9928 ); 9929 9930 if (!empty($helpText)) { 9931 $attr['title'] = dol_escape_htmltag($helpText); 9932 } elseif (empty($attr['title']) && $label) { 9933 $attr['title'] = $label; 9934 $useclassfortooltip = 0; 9935 } 9936 9937 if ($status == 2) { 9938 $attr['class'] .= ' btnTitleSelected'; 9939 } elseif ($status <= 0) { 9940 $attr['class'] .= ' refused'; 9941 9942 $attr['href'] = ''; 9943 9944 if ($status == -1) { // disable 9945 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled")); 9946 } elseif ($status == 0) { // Not enough permissions 9947 $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions")); 9948 } 9949 } 9950 9951 if (!empty($attr['title']) && $useclassfortooltip) { 9952 $attr['class'] .= ' classfortooltip'; 9953 } 9954 9955 if (!empty($id)) { 9956 $attr['id'] = $id; 9957 } 9958 9959 // Override attr 9960 if (!empty($params['attr']) && is_array($params['attr'])) { 9961 foreach ($params['attr'] as $key => $value) { 9962 if ($key == 'class') { 9963 $attr['class'] .= ' '.$value; 9964 } elseif ($key == 'classOverride') { 9965 $attr['class'] = $value; 9966 } else { 9967 $attr[$key] = $value; 9968 } 9969 } 9970 } 9971 9972 if (isset($attr['href']) && empty($attr['href'])) { 9973 unset($attr['href']); 9974 } 9975 9976 // TODO : add a hook 9977 9978 // escape all attribute 9979 $attr = array_map('dol_escape_htmltag', $attr); 9980 9981 $TCompiledAttr = array(); 9982 foreach ($attr as $key => $value) { 9983 $TCompiledAttr[] = $key.'="'.$value.'"'; 9984 } 9985 9986 $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr)); 9987 9988 $tag = (empty($attr['href']) ? 'span' : 'a'); 9989 9990 $button = '<'.$tag.' '.$compiledAttributes.'>'; 9991 $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>'; 9992 if (!empty($params['forcenohideoftext'])) { 9993 $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>'; 9994 } 9995 $button .= '</'.$tag.'>'; 9996 9997 return $button; 9998} 9999 10000/** 10001 * Get an array with properties of an element. 10002 * Called by fetchObjectByElement. 10003 * 10004 * @param string $element_type Element type (Value of $object->element). Example: 'action', 'facture', 'project_task' or 'object@mymodule'... 10005 * @return array (module, classpath, element, subelement, classfile, classname) 10006 */ 10007function getElementProperties($element_type) 10008{ 10009 $regs = array(); 10010 10011 $classfile = $classname = $classpath = ''; 10012 10013 // Parse element/subelement (ex: project_task) 10014 $module = $element_type; 10015 $element = $element_type; 10016 $subelement = $element_type; 10017 10018 // If we ask an resource form external module (instead of default path) 10019 if (preg_match('/^([^@]+)@([^@]+)$/i', $element_type, $regs)) { 10020 $element = $subelement = $regs[1]; 10021 $module = $regs[2]; 10022 } 10023 10024 //print '<br>1. element : '.$element.' - module : '.$module .'<br>'; 10025 if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) { 10026 $module = $element = $regs[1]; 10027 $subelement = $regs[2]; 10028 } 10029 10030 // For compat 10031 if ($element_type == "action") { 10032 $classpath = 'comm/action/class'; 10033 $subelement = 'Actioncomm'; 10034 $module = 'agenda'; 10035 } 10036 10037 // To work with non standard path 10038 if ($element_type == 'facture' || $element_type == 'invoice') { 10039 $classpath = 'compta/facture/class'; 10040 $module = 'facture'; 10041 $subelement = 'facture'; 10042 } 10043 if ($element_type == 'commande' || $element_type == 'order') { 10044 $classpath = 'commande/class'; 10045 $module = 'commande'; 10046 $subelement = 'commande'; 10047 } 10048 if ($element_type == 'propal') { 10049 $classpath = 'comm/propal/class'; 10050 } 10051 if ($element_type == 'supplier_proposal') { 10052 $classpath = 'supplier_proposal/class'; 10053 } 10054 if ($element_type == 'shipping') { 10055 $classpath = 'expedition/class'; 10056 $subelement = 'expedition'; 10057 $module = 'expedition_bon'; 10058 } 10059 if ($element_type == 'delivery') { 10060 $classpath = 'delivery/class'; 10061 $subelement = 'delivery'; 10062 $module = 'delivery_note'; 10063 } 10064 if ($element_type == 'contract') { 10065 $classpath = 'contrat/class'; 10066 $module = 'contrat'; 10067 $subelement = 'contrat'; 10068 } 10069 if ($element_type == 'member') { 10070 $classpath = 'adherents/class'; 10071 $module = 'adherent'; 10072 $subelement = 'adherent'; 10073 } 10074 if ($element_type == 'cabinetmed_cons') { 10075 $classpath = 'cabinetmed/class'; 10076 $module = 'cabinetmed'; 10077 $subelement = 'cabinetmedcons'; 10078 } 10079 if ($element_type == 'fichinter') { 10080 $classpath = 'fichinter/class'; 10081 $module = 'ficheinter'; 10082 $subelement = 'fichinter'; 10083 } 10084 if ($element_type == 'dolresource' || $element_type == 'resource') { 10085 $classpath = 'resource/class'; 10086 $module = 'resource'; 10087 $subelement = 'dolresource'; 10088 } 10089 if ($element_type == 'propaldet') { 10090 $classpath = 'comm/propal/class'; 10091 $module = 'propal'; 10092 $subelement = 'propaleligne'; 10093 } 10094 if ($element_type == 'order_supplier') { 10095 $classpath = 'fourn/class'; 10096 $module = 'fournisseur'; 10097 $subelement = 'commandefournisseur'; 10098 $classfile = 'fournisseur.commande'; 10099 } 10100 if ($element_type == 'invoice_supplier') { 10101 $classpath = 'fourn/class'; 10102 $module = 'fournisseur'; 10103 $subelement = 'facturefournisseur'; 10104 $classfile = 'fournisseur.facture'; 10105 } 10106 if ($element_type == "service") { 10107 $classpath = 'product/class'; 10108 $subelement = 'product'; 10109 } 10110 10111 if (empty($classfile)) { 10112 $classfile = strtolower($subelement); 10113 } 10114 if (empty($classname)) { 10115 $classname = ucfirst($subelement); 10116 } 10117 if (empty($classpath)) { 10118 $classpath = $module.'/class'; 10119 } 10120 10121 $element_properties = array( 10122 'module' => $module, 10123 'classpath' => $classpath, 10124 'element' => $element, 10125 'subelement' => $subelement, 10126 'classfile' => $classfile, 10127 'classname' => $classname 10128 ); 10129 return $element_properties; 10130} 10131 10132/** 10133 * Fetch an object from its id and element_type 10134 * Inclusion of classes is automatic 10135 * 10136 * @param int $element_id Element id 10137 * @param string $element_type Element type 10138 * @param string $element_ref Element ref (Use this or element_id but not both) 10139 * @return int|object object || 0 || -1 if error 10140 */ 10141function fetchObjectByElement($element_id, $element_type, $element_ref = '') 10142{ 10143 global $conf, $db; 10144 10145 $element_prop = getElementProperties($element_type); 10146 if (is_array($element_prop) && $conf->{$element_prop['module']}->enabled) { 10147 dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php'); 10148 10149 $objecttmp = new $element_prop['classname']($db); 10150 $ret = $objecttmp->fetch($element_id, $element_ref); 10151 if ($ret >= 0) { 10152 return $objecttmp; 10153 } 10154 } 10155 return 0; 10156} 10157 10158/** 10159 * Return if a file can contains executable content 10160 * 10161 * @param string $filename File name to test 10162 * @return boolean True if yes, False if no 10163 */ 10164function isAFileWithExecutableContent($filename) 10165{ 10166 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)) { 10167 return true; 10168 } 10169 10170 return false; 10171} 10172 10173/** 10174 * Return the value of token currently saved into session with name 'newtoken'. 10175 * This token must be send by any POST as it will be used by next page for comparison with value in session. 10176 * 10177 * @return string 10178 */ 10179function newToken() 10180{ 10181 return $_SESSION['newtoken']; 10182} 10183 10184/** 10185 * Return the value of token currently saved into session with name 'token'. 10186 * 10187 * @return string 10188 */ 10189function currentToken() 10190{ 10191 return $_SESSION['token']; 10192} 10193 10194/** 10195 * Start a table with headers and a optinal clickable number (don't forget to use "finishSimpleTable()" after the last table row) 10196 * 10197 * @param string $header The first left header of the table (automatic translated) 10198 * @param string $link (optional) The link to a internal dolibarr page, when click on the number (without the first "/") 10199 * @param string $arguments (optional) Additional arguments for the link (e.g. "search_status=0") 10200 * @param integer $emptyRows (optional) The count of empty rows after the first header 10201 * @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" 10202 * @return void 10203 * 10204 * @see finishSimpleTable() 10205 */ 10206function startSimpleTable($header, $link = "", $arguments = "", $emptyRows = 0, $number = -1) 10207{ 10208 global $langs; 10209 10210 print '<div class="div-table-responsive-no-min">'; 10211 print '<table class="noborder centpercent">'; 10212 print '<tr class="liste_titre">'; 10213 10214 print $emptyRows < 1 ? '<th>' : '<th colspan="'.($emptyRows + 1).'">'; 10215 10216 print $langs->trans($header); 10217 10218 // extra space between the first header and the number 10219 if ($number > -1) { 10220 print ' '; 10221 } 10222 10223 if (!empty($link)) { 10224 if (!empty($arguments)) { 10225 print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">'; 10226 } else { 10227 print '<a href="'.DOL_URL_ROOT.'/'.$link.'">'; 10228 } 10229 } 10230 10231 if ($number > -1) { 10232 print '<span class="badge">'.$number.'</span>'; 10233 } 10234 10235 if (!empty($link)) { 10236 print '</a>'; 10237 } 10238 10239 print '</th>'; 10240 10241 if ($number < 0 && !empty($link)) { 10242 print '<th class="right">'; 10243 10244 if (!empty($arguments)) { 10245 print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">'; 10246 } else { 10247 print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'">'; 10248 } 10249 10250 print $langs->trans("FullList"); 10251 print '</a>'; 10252 print '</th>'; 10253 } 10254 10255 print '</tr>'; 10256} 10257 10258/** 10259 * Add the correct HTML close tags for "startSimpleTable(...)" (use after the last table line) 10260 * 10261 * @param bool $addLineBreak (optional) Add a extra line break after the complete table (\<br\>) 10262 * @return void 10263 * 10264 * @see startSimpleTable() 10265 */ 10266function finishSimpleTable($addLineBreak = false) 10267{ 10268 print '</table>'; 10269 print '</div>'; 10270 10271 if ($addLineBreak) { 10272 print '<br>'; 10273 } 10274} 10275 10276/** 10277 * Add a summary line to the current open table ("None", "XMoreLines" or "Total xxx") 10278 * 10279 * @param integer $tableColumnCount The complete count columns of the table 10280 * @param integer $num The count of the rows of the table, when it is zero (0) the "$noneWord" is shown instead 10281 * @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) 10282 * @param integer $total (optional) The total value thaht is shown after when the table has minimum of one entire 10283 * @param string $noneWord (optional) The word that is shown when the table has no entires ($num === 0) 10284 * @param boolean $extraRightColumn (optional) Add a addtional column after the summary word and total number 10285 * @return void 10286 */ 10287function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false) 10288{ 10289 global $langs; 10290 10291 if ($num === 0) { 10292 print '<tr class="oddeven">'; 10293 print '<td colspan="'.$tableColumnCount.'" class="opacitymedium">'.$langs->trans($noneWord).'</td>'; 10294 print '</tr>'; 10295 return; 10296 } 10297 10298 if ($nbofloop === 0) { 10299 // don't show a summary line 10300 return; 10301 } 10302 10303 if ($num === 0) { 10304 $colspan = $tableColumnCount; 10305 } elseif ($num > $nbofloop) { 10306 $colspan = $tableColumnCount; 10307 } else { 10308 $colspan = $tableColumnCount - 1; 10309 } 10310 10311 if ($extraRightColumn) { 10312 $colspan--; 10313 } 10314 10315 print '<tr class="liste_total">'; 10316 10317 if ($nbofloop > 0 && $num > $nbofloop) { 10318 print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>'; 10319 } else { 10320 print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>'; 10321 print '<td class="right" width="100">'.price($total).'</td>'; 10322 } 10323 10324 if ($extraRightColumn) { 10325 print '<td></td>'; 10326 } 10327 10328 print '</tr>'; 10329} 10330 10331/** 10332 * Return a file on output using a low memory. It can return very large files with no need of memory. 10333 * WARNING: This close output buffers. 10334 * 10335 * @param string $fullpath_original_file_osencoded Full path of file to return. 10336 * @param int $method -1 automatic, 0=readfile, 1=fread, 2=stream_copy_to_stream 10337 * @return void 10338 */ 10339function readfileLowMemory($fullpath_original_file_osencoded, $method = -1) 10340{ 10341 global $conf; 10342 10343 if ($method == -1) { 10344 $method = 0; 10345 if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_FREAD)) { 10346 $method = 1; 10347 } 10348 if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_STREAM_COPY)) { 10349 $method = 2; 10350 } 10351 } 10352 10353 // Be sure we don't have output buffering enabled to have readfile working correctly 10354 while (ob_get_level()) { 10355 ob_end_flush(); 10356 } 10357 10358 // Solution 0 10359 if ($method == 0) { 10360 readfile($fullpath_original_file_osencoded); 10361 } elseif ($method == 1) { 10362 // Solution 1 10363 $handle = fopen($fullpath_original_file_osencoded, "rb"); 10364 while (!feof($handle)) { 10365 print fread($handle, 8192); 10366 } 10367 fclose($handle); 10368 } elseif ($method == 2) { 10369 // Solution 2 10370 $handle1 = fopen($fullpath_original_file_osencoded, "rb"); 10371 $handle2 = fopen("php://output", "wb"); 10372 stream_copy_to_stream($handle1, $handle2); 10373 fclose($handle1); 10374 fclose($handle2); 10375 } 10376} 10377 10378/** 10379 * Create a button to copy $valuetocopy in the clipboard. 10380 * Code that handle the click is inside lib_foot.jsp.php 10381 * 10382 * @param string $valuetocopy The value to print 10383 * @param int $showonlyonhover Show the copy-paste button only on hover 10384 * @param string $texttoshow Replace the value to show with this text 10385 * @return string The string to print for the button 10386 */ 10387function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '') 10388{ 10389 /* 10390 global $conf; 10391 10392 if (!empty($conf->dol_no_mouse_hover)) { 10393 $showonlyonhover = 0; 10394 }*/ 10395 10396 if ($texttoshow) { 10397 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'"><span class="clipboardCPValue hidewithsize">'.$valuetocopy.'</span><span class="clipboardCPValueToPrint">'.$texttoshow.'</span><span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft paddingright"></span><span class="clipboardCPText opacitymedium"></span></span>'; 10398 } else { 10399 $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'"><span class="clipboardCPValue">'.$valuetocopy.'</span><span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft paddingright"></span><span class="clipboardCPText opacitymedium"></span></span>'; 10400 } 10401 10402 return $result; 10403} 10404 10405 10406/** 10407 * Decode an encode string. The string can be encoded in json format (recommended) or with serialize (avoid this) 10408 * 10409 * @param string $stringtodecode String to decode (json or serialize coded) 10410 * @return mixed The decoded object. 10411 */ 10412function jsonOrUnserialize($stringtodecode) 10413{ 10414 $result = json_decode($stringtodecode); 10415 if ($result === null) { 10416 $result = unserialize($stringtodecode); 10417 } 10418 10419 return $result; 10420} 10421