* Copyright (C) 2003 Jean-Louis Bergamo * Copyright (C) 2004-2018 Laurent Destailleur * Copyright (C) 2004 Sebastien Di Cintio * Copyright (C) 2004 Benoit Mortier * Copyright (C) 2004 Christophe Combelles * Copyright (C) 2005-2019 Regis Houssin * Copyright (C) 2008 Raphael Bertrand (Resultic) * Copyright (C) 2010-2018 Juanjo Menent * Copyright (C) 2013 Cédric Salvador * Copyright (C) 2013-2021 Alexandre Spangaro * Copyright (C) 2014 Cédric GROSS * Copyright (C) 2014-2015 Marcos García * Copyright (C) 2015 Jean-François Ferry * Copyright (C) 2018-2022 Frédéric France * Copyright (C) 2019 Thibault Foucart * Copyright (C) 2020 Open-Dsi * Copyright (C) 2021 Gauthier VERDOL * Copyright (C) 2022 Anthony Berton * Copyright (C) 2022 Ferran Marcet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * or see https://www.gnu.org/ */ /** * \file htdocs/core/lib/functions.lib.php * \brief A set of functions for Dolibarr * This file contains all frequently used functions. */ include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php'; if (!function_exists('utf8_encode')) { /** * Implement utf8_encode for PHP that does not support it. * * @param mixed $elements PHP Object to json encode * @return string Json encoded string */ function utf8_encode($elements) { return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1'); } } if (!function_exists('utf8_decode')) { /** * Implement utf8_decode for PHP that does not support it. * * @param mixed $elements PHP Object to json encode * @return string Json encoded string */ function utf8_decode($elements) { return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8'); } } /** * Return dolibarr global constant string value * @param string $key key to return value, return '' if not set * @param string $default value to return * @return string */ function getDolGlobalString($key, $default = '') { global $conf; // return $conf->global->$key ?? $default; return (string) (empty($conf->global->$key) ? $default : $conf->global->$key); } /** * Return dolibarr global constant int value * @param string $key key to return value, return 0 if not set * @param int $default value to return * @return int */ function getDolGlobalInt($key, $default = 0) { global $conf; // return $conf->global->$key ?? $default; return (int) (empty($conf->global->$key) ? $default : $conf->global->$key); } /** * Is Dolibarr module enabled * @param string $module module name to check * @return int */ function isModEnabled($module) { global $conf; return !empty($conf->$module->enabled); } /** * Return a DoliDB instance (database handler). * * @param string $type Type of database (mysql, pgsql...) * @param string $host Address of database server * @param string $user Authorized username * @param string $pass Password * @param string $name Name of database * @param int $port Port of database server * @return DoliDB A DoliDB instance */ function getDoliDBInstance($type, $host, $user, $pass, $name, $port) { require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php'; $class = 'DoliDB'.ucfirst($type); $dolidb = new $class($type, $host, $user, $pass, $name, $port); return $dolidb; } /** * Get list of entity id to use. * * @param string $element Current element * 'societe', 'socpeople', 'actioncomm', 'agenda', 'resource', * 'product', 'productprice', 'stock', 'bom', 'mo', * 'propal', 'supplier_proposal', 'invoice', 'supplier_invoice', 'payment_various', * 'categorie', 'bank_account', 'bank_account', 'adherent', 'user', * 'commande', 'supplier_order', 'expedition', 'intervention', 'survey', * 'contract', 'tax', 'expensereport', 'holiday', 'multicurrency', 'project', * 'email_template', 'event', 'donation' * 'c_paiement', 'c_payment_term', ... * @param int $shared 0=Return id of current entity only, * 1=Return id of current entity + shared entities (default) * @param object $currentobject Current object if needed * @return mixed Entity id(s) to use ( eg. entity IN ('.getEntity(elementname).')' ) */ function getEntity($element, $shared = 1, $currentobject = null) { global $conf, $mc, $hookmanager, $object, $action, $db; if (! is_object($hookmanager)) { $hookmanager = new HookManager($db); } // fix different element names (France to English) switch ($element) { case 'contrat': $element = 'contract'; break; // "/contrat/class/contrat.class.php" case 'order_supplier': $element = 'supplier_order'; break; // "/fourn/class/fournisseur.commande.class.php" case 'invoice_supplier': $element = 'supplier_invoice'; break; // "/fourn/class/fournisseur.facture.class.php" } if (is_object($mc)) { $out = $mc->getEntity($element, $shared, $currentobject); } else { $out = ''; $addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values'); if (in_array($element, $addzero)) { $out .= '0,'; } $out .= ((int) $conf->entity); } // Manipulate entities to query on the fly $parameters = array( 'element' => $element, 'shared' => $shared, 'object' => $object, 'currentobject' => $currentobject, 'out' => $out ); $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks if (is_numeric($reshook)) { if ($reshook == 0 && !empty($hookmanager->resPrint)) { $out .= ','.$hookmanager->resPrint; // add } elseif ($reshook == 1) { $out = $hookmanager->resPrint; // replace } } return $out; } /** * Set entity id to use when to create an object * * @param object $currentobject Current object * @return mixed Entity id to use ( eg. entity = '.setEntity($object) ) */ function setEntity($currentobject) { global $conf, $mc; if (is_object($mc) && method_exists($mc, 'setEntity')) { return $mc->setEntity($currentobject); } else { return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity); } } /** * Return if string has a name dedicated to store a secret * * @param string $keyname Name of key to test * @return boolean True if key is used to store a secret */ function isASecretKey($keyname) { return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname); } /** * Return a numeric value into an Excel like column number. So 0 return 'A', 1 returns 'B'..., 26 return 'AA' * * @param int|string $n Numeric value * @return string Column in Excel format */ function num2Alpha($n) { for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) $r = chr($n % 26 + 0x41) . $r; return $r; } /** * Return information about user browser * * Returns array with the following format: * array( * 'browsername' => Browser name (firefox|chrome|iceweasel|epiphany|safari|opera|ie|unknown) * 'browserversion' => Browser version. Empty if unknown * 'browseros' => Set with mobile OS (android|blackberry|ios|palm|symbian|webos|maemo|windows|unknown) * 'layout' => (tablet|phone|classic) * 'phone' => empty if not mobile, (android|blackberry|ios|palm|unknown) if mobile * 'tablet' => true/false * ) * * @param string $user_agent Content of $_SERVER["HTTP_USER_AGENT"] variable * @return array Check function documentation */ function getBrowserInfo($user_agent) { include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php'; $name = 'unknown'; $version = ''; $os = 'unknown'; $phone = ''; $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent $detectmobile = new Mobile_Detect(null, $user_agent); $tablet = $detectmobile->isTablet(); if ($detectmobile->isMobile()) { $phone = 'unknown'; // If phone/smartphone, we set phone os name. if ($detectmobile->is('AndroidOS')) { $os = $phone = 'android'; } elseif ($detectmobile->is('BlackBerryOS')) { $os = $phone = 'blackberry'; } elseif ($detectmobile->is('iOS')) { $os = 'ios'; $phone = 'iphone'; } elseif ($detectmobile->is('PalmOS')) { $os = $phone = 'palm'; } elseif ($detectmobile->is('SymbianOS')) { $os = 'symbian'; } elseif ($detectmobile->is('webOS')) { $os = 'webos'; } elseif ($detectmobile->is('MaemoOS')) { $os = 'maemo'; } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) { $os = 'windows'; } } // OS if (preg_match('/linux/i', $user_agent)) { $os = 'linux'; } elseif (preg_match('/macintosh/i', $user_agent)) { $os = 'macintosh'; } elseif (preg_match('/windows/i', $user_agent)) { $os = 'windows'; } // Name $reg = array(); if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'firefox'; $version = $reg[2]; } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'edge'; $version = $reg[2]; } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) { $name = 'chrome'; $version = $reg[2]; } elseif (preg_match('/chrome/i', $user_agent, $reg)) { // we can have 'chrome (Mozilla...) chrome x.y' in one string $name = 'chrome'; } elseif (preg_match('/iceweasel/i', $user_agent)) { $name = 'iceweasel'; } elseif (preg_match('/epiphany/i', $user_agent)) { $name = 'epiphany'; } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name = 'safari'; $version = $reg[2]; } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { // Safari is often present in string for mobile but its not. $name = 'opera'; $version = $reg[2]; } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) { $name = 'ie'; $version = end($reg); } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) { // MS products at end $name = 'ie'; $version = end($reg); } elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) { // MS products at end $name = 'lynxlinks'; $version = $reg[4]; } if ($tablet) { $layout = 'tablet'; } elseif ($phone) { $layout = 'phone'; } else { $layout = 'classic'; } return array( 'browsername' => $name, 'browserversion' => $version, 'browseros' => $os, 'layout' => $layout, 'phone' => $phone, 'tablet' => $tablet ); } /** * Function called at end of web php process * * @return void */ function dol_shutdown() { global $conf, $user, $langs, $db; $disconnectdone = false; $depth = 0; if (is_object($db) && !empty($db->connected)) { $depth = $db->transaction_opened; $disconnectdone = $db->close(); } dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO)); } /** * Return true if we are in a context of submitting the parameter $paramname from a POST of a form. * * @param string $paramname Name or parameter to test * @return boolean True if we have just submit a POST or GET request with the parameter provided (even if param is empty) */ function GETPOSTISSET($paramname) { $isset = false; $relativepathstring = $_SERVER["PHP_SELF"]; // Clean $relativepathstring if (constant('DOL_URL_ROOT')) { $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring); } $relativepathstring = preg_replace('/^\//', '', $relativepathstring); $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring); //var_dump($relativepathstring); //var_dump($user->default_values); // Code for search criteria persistence. // Retrieve values if restore_lastsearch_values if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true); if (is_array($tmp)) { foreach ($tmp as $key => $val) { if ($key == $paramname) { // We are on the requested parameter $isset = true; break; } } } } // If there is saved contextpage, limit, page or mode if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) { $isset = true; } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) { $isset = true; } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) { $isset = true; } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) { $isset = true; } } else { $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here } return $isset; } /** * Return true if the parameter $paramname is submit from a POST OR GET as an array. * Can be used before GETPOST to know if the $check param of GETPOST need to check an array or a string * * @param string $paramname Name or parameter to test * @param int $method Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get) * @return bool True if we have just submit a POST or GET request with the parameter provided (even if param is empty) */ function GETPOSTISARRAY($paramname, $method = 0) { // for $method test need return the same $val as GETPOST if (empty($method)) { $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : ''); } elseif ($method == 1) { $val = isset($_GET[$paramname]) ? $_GET[$paramname] : ''; } elseif ($method == 2) { $val = isset($_POST[$paramname]) ? $_POST[$paramname] : ''; } elseif ($method == 3) { $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : ''); } else { $val = 'BadFirstParameterForGETPOST'; } return is_array($val); } /** * Return value of a param into GET or POST supervariable. * Use the property $user->default_values[path]['createform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder'] * Note: The property $user->default_values is loaded by main.php when loading the user. * * @param string $paramname Name of parameter to found * @param string $check Type of check * ''=no check (deprecated) * 'none'=no check (only for param that should have very rich content like passwords) * 'array', 'array:restricthtml' or 'array:aZ09' to check it's an array * 'int'=check it's numeric (integer or float) * 'intcomma'=check it's integer+comma ('1,2,3,4...') * 'alpha'=Same than alphanohtml since v13 * 'alphawithlgt'=alpha with lgt * 'alphanohtml'=check there is no html content and no " and no ../ * 'aZ'=check it's a-z only * 'aZ09'=check it's simple alpha string (recommended for keys) * 'san_alpha'=Use filter_var with FILTER_SANITIZE_STRING (do not use this for free text string) * 'nohtml'=check there is no html content and no " and no ../ * 'restricthtml'=check html content is restricted to some tags only * 'custom'= custom filter specify $filter and $options) * @param int $method Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get) * @param int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) * @param mixed $options Options to pass to filter_var when $check is set to 'custom' * @param string $noreplace Force disable of replacement of __xxx__ strings. * @return string|array Value found (string or array), or '' if check fails */ function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0) { global $mysoc, $user, $conf; if (empty($paramname)) { return 'BadFirstParameterForGETPOST'; } if (empty($check)) { dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING); // Enable this line to know who call the GETPOST with '' $check parameter. //var_dump(debug_backtrace()[0]); } if (empty($method)) { $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : ''); } elseif ($method == 1) { $out = isset($_GET[$paramname]) ? $_GET[$paramname] : ''; } elseif ($method == 2) { $out = isset($_POST[$paramname]) ? $_POST[$paramname] : ''; } elseif ($method == 3) { $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : ''); } else { return 'BadThirdParameterForGETPOST'; } if (empty($method) || $method == 3 || $method == 4) { $relativepathstring = $_SERVER["PHP_SELF"]; // Clean $relativepathstring if (constant('DOL_URL_ROOT')) { $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring); } $relativepathstring = preg_replace('/^\//', '', $relativepathstring); $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring); //var_dump($relativepathstring); //var_dump($user->default_values); // Code for search criteria persistence. // Retrieve values if restore_lastsearch_values if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true); if (is_array($tmp)) { foreach ($tmp as $key => $val) { if ($key == $paramname) { // We are on the requested parameter $out = $val; break; } } } } // If there is saved contextpage, page or limit if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) { $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring]; } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) { $out = $_SESSION['lastsearch_limit_'.$relativepathstring]; } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) { $out = $_SESSION['lastsearch_page_'.$relativepathstring]; } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) { $out = $_SESSION['lastsearch_mode_'.$relativepathstring]; } } elseif (!isset($_GET['sortfield'])) { // Else, retrieve default values if we are not doing a sort // 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 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) { // Search default value from $object->field global $object; if (is_object($object) && isset($object->fields[$paramname]['default'])) { $out = $object->fields[$paramname]['default']; } } if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) { if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) { // Now search in setup to overwrite default values if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values' if (isset($user->default_values[$relativepathstring]['createform'])) { foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) { $qualified = 0; if ($defkey != '_noquery_') { $tmpqueryarraytohave = explode('&', $defkey); $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); $foundintru = 0; foreach ($tmpqueryarraytohave as $tmpquerytohave) { if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) { $foundintru = 1; } } if (!$foundintru) { $qualified = 1; } //var_dump($defkey.'-'.$qualified); } else { $qualified = 1; } if ($qualified) { if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) { $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname]; break; } } } } } } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) { // Management of default search_filters and sort order if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values' //var_dump($user->default_values[$relativepathstring]); if ($paramname == 'sortfield' || $paramname == 'sortorder') { // Sorted on which fields ? ASC or DESC ? if (isset($user->default_values[$relativepathstring]['sortorder'])) { // Even if paramname is sortfield, data are stored into ['sortorder...'] foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) { $qualified = 0; if ($defkey != '_noquery_') { $tmpqueryarraytohave = explode('&', $defkey); $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); $foundintru = 0; foreach ($tmpqueryarraytohave as $tmpquerytohave) { if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) { $foundintru = 1; } } if (!$foundintru) { $qualified = 1; } //var_dump($defkey.'-'.$qualified); } else { $qualified = 1; } if ($qualified) { $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) { if ($out) { $out .= ', '; } if ($paramname == 'sortfield') { $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace); } if ($paramname == 'sortorder') { $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace); } } //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?) } } } } elseif (isset($user->default_values[$relativepathstring]['filters'])) { foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter continue; } $qualified = 0; if ($defkey != '_noquery_') { $tmpqueryarraytohave = explode('&', $defkey); $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING'])); $foundintru = 0; foreach ($tmpqueryarraytohave as $tmpquerytohave) { if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) { $foundintru = 1; } } if (!$foundintru) { $qualified = 1; } //var_dump($defkey.'-'.$qualified); } else { $qualified = 1; } if ($qualified) { // We must keep $_POST and $_GET here if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) { // We made a search from quick search menu, do we still use default filter ? if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) { $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace); } } else { $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and , $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace); } break; } } } } } } } } // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters) // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ... // 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. if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) { $reg = array(); $maxloop = 20; $loopnb = 0; // Protection against infinite loop 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. $loopnb++; $newout = ''; if ($reg[1] == 'DAY') { $tmp = dol_getdate(dol_now(), true); $newout = $tmp['mday']; } elseif ($reg[1] == 'MONTH') { $tmp = dol_getdate(dol_now(), true); $newout = $tmp['mon']; } elseif ($reg[1] == 'YEAR') { $tmp = dol_getdate(dol_now(), true); $newout = $tmp['year']; } elseif ($reg[1] == 'PREVIOUS_DAY') { $tmp = dol_getdate(dol_now(), true); $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; } elseif ($reg[1] == 'PREVIOUS_MONTH') { $tmp = dol_getdate(dol_now(), true); $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']); $newout = $tmp2['month']; } elseif ($reg[1] == 'PREVIOUS_YEAR') { $tmp = dol_getdate(dol_now(), true); $newout = ($tmp['year'] - 1); } elseif ($reg[1] == 'NEXT_DAY') { $tmp = dol_getdate(dol_now(), true); $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; } elseif ($reg[1] == 'NEXT_MONTH') { $tmp = dol_getdate(dol_now(), true); $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']); $newout = $tmp2['month']; } elseif ($reg[1] == 'NEXT_YEAR') { $tmp = dol_getdate(dol_now(), true); $newout = ($tmp['year'] + 1); } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') { $newout = $mysoc->country_id; } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') { $newout = $user->id; } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') { $newout = $user->fk_user; } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') { $newout = $conf->entity; } else { $newout = ''; // Key not found, we replace with empty string } //var_dump('__'.$reg[1].'__ -> '.$newout); $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out); } } // Check rule if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma' if (!is_array($out) || empty($out)) { $out = array(); } else { $tmparray = explode(':', $check); if (!empty($tmparray[1])) { $tmpcheck = $tmparray[1]; } else { $tmpcheck = 'alphanohtml'; } foreach ($out as $outkey => $outval) { $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options); } } } else { $out = sanitizeVal($out, $check, $filter, $options); } // Sanitizing for special parameters. // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed. if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') { $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements. $out = str_replace(array(':', ';', '@', "\t", ' '), '', $out); // Can be before the loop because only 1 char is replaced. No risk to retreive it after other replacements. do { $oldstringtoclean = $out; $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out); $out = preg_replace(array('/^[^\?]*%/'), '', $out); // We remove any % chars before the ?. Example in url: '/product/stock/card.php?action=create&backtopage=%2Fdolibarr_dev%2Fhtdocs%2Fpro%25duct%2Fcard.php%3Fid%3Dabc' $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL } while ($oldstringtoclean != $out); } // Code for search criteria persistence. // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year' if (empty($method) || $method == 3 || $method == 4) { if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) { //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]); // We save search key only if $out not empty that means: // - posted value not empty, or // - 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). if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out; } } } return $out; } /** * Return value of a param into GET or POST supervariable. * Use the property $user->default_values[path]['creatform'] and/or $user->default_values[path]['filters'] and/or $user->default_values[path]['sortorder'] * Note: The property $user->default_values is loaded by main.php when loading the user. * * @param string $paramname Name of parameter to found * @param int $method Type of method (0 = get then post, 1 = only get, 2 = only post, 3 = post then get) * @return int Value found (int) */ function GETPOSTINT($paramname, $method = 0) { return (int) GETPOST($paramname, 'int', $method, null, null, 0); } /** * Return a sanitized or empty value after checking value against a rule. * * @deprecated * @param string|array $out Value to check/clear. * @param string $check Type of check/sanitizing * @param int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) * @param mixed $options Options to pass to filter_var when $check is set to 'custom' * @return string|array Value sanitized (string or array). It may be '' if format check fails. */ function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null) { return sanitizeVal($out, $check, $filter, $options); } /** * Return a sanitized or empty value after checking value against a rule. * * @param string|array $out Value to check/clear. * @param string $check Type of check/sanitizing * @param int $filter Filter to apply when $check is set to 'custom'. (See http://php.net/manual/en/filter.filters.php for détails) * @param mixed $options Options to pass to filter_var when $check is set to 'custom' * @return string|array Value sanitized (string or array). It may be '' if format check fails. */ function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null) { global $conf; // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize // Check is done after replacement switch ($check) { case 'none': break; case 'int': // Check param is a numeric value (integer but also float or hexadecimal) if (!is_numeric($out)) { $out = ''; } break; case 'intcomma': if (preg_match('/[^0-9,-]+/i', $out)) { $out = ''; } break; case 'san_alpha': $out = filter_var($out, FILTER_SANITIZE_STRING); break; case 'email': $out = filter_var($out, FILTER_SANITIZE_EMAIL); break; case 'aZ': if (!is_array($out)) { $out = trim($out); if (preg_match('/[^a-z]+/i', $out)) { $out = ''; } } break; case 'aZ09': if (!is_array($out)) { $out = trim($out); if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) { $out = ''; } } break; case 'aZ09comma': // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh if (!is_array($out)) { $out = trim($out); if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) { $out = ''; } } break; case 'nohtml': // No html $out = dol_string_nohtmltag($out, 0); break; case 'alpha': // No html and no ../ and " case 'alphanohtml': // Recommended for most scalar parameters and search parameters if (!is_array($out)) { $out = trim($out); do { $oldstringtoclean = $out; // Remove html tags $out = dol_string_nohtmltag($out, 0); // Remove also other dangerous string sequences // '"' is dangerous because param in url can close the href= or src= and add javascript functions. // '../' or '..\' is dangerous because it allows dir transversals // Note &, '&', '&'... is a simple char like '&' alone but there is no reason to accept such way to encode input data. $out = str_ireplace(array('&', '&', '&', '"', '"', '"', '"', '"', '/', '/', '\', '\', '/', '../', '..\\'), '', $out); } while ($oldstringtoclean != $out); // keep lines feed } break; case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name ". Less secured than 'alphanohtml' if (!is_array($out)) { $out = trim($out); do { $oldstringtoclean = $out; // Remove html tags $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8'); // '"' is dangerous because param in url can close the href= or src= and add javascript functions. // '../' or '..\' is dangerous because it allows dir transversals // Note &, '&', '&'... is a simple char like '&' alone but there is no reason to accept such way to encode input data. $out = str_ireplace(array('&', '&', '&', '"', '"', '"', '"', '"', '/', '/', '\', '\', '/', '../', '..\\'), '', $out); } while ($oldstringtoclean != $out); } break; case 'restricthtml': // Recommended for most html textarea case 'restricthtmlallowunvalid': do { $oldstringtoclean = $out; if (!empty($out) && !empty($conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML) && $check != 'restricthtmlallowunvalid') { try { $dom = new DOMDocument; // Add a trick to solve pb with text without parent tag // like '

Foo

bar

' that ends up with '

Foo

bar

' // like 'abc' that ends up with '

abc

' $out = '
'.$out.'
'; $dom->loadHTML($out, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL); $out = trim($dom->saveHTML()); // Remove the trick added to solve pb with text without parent tag $out = preg_replace('/^
/', '', $out); $out = preg_replace('/<\/div>$/', '', $out); } catch (Exception $e) { //print $e->getMessage(); return 'InvalidHTMLString'; } } // Ckeditor use the numeric entitic for apostrophe so we force it to text entity (all other special chars are // encoded using text entities) so we can then exclude all numeric entities. $out = preg_replace('/'/i', ''', $out); // 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). // 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 // using a non coventionnel way to be encoded, to not have them sanitized just after) //$out = preg_replace_callback('/&#(x?[0-9][0-9a-f]+;?)/i', 'realCharForNumericEntities', $out); $out = preg_replace_callback('/&#(x?[0-9][0-9a-f]+;?)/i', function ($m) { return realCharForNumericEntities($m); }, $out); // Now we remove all remaining HTML entities starting with a number. We don't want such entities. $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'. $out = dol_string_onlythesehtmltags($out, 0, 1, 1); // We should also exclude non expected HTML attributes and clean content of some attributes. if (!empty($conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES)) { // 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. $out = dol_string_onlythesehtmlattributes($out); } // Restore entity ' into ' (restricthtml is for html content so we can use html entity) $out = preg_replace('/'/i', "'", $out); } while ($oldstringtoclean != $out); break; case 'custom': if (empty($filter)) { return 'BadFourthParameterForGETPOST'; } $out = filter_var($out, $filter, $options); break; } return $out; } if (!function_exists('dol_getprefix')) { /** * Return a prefix to use for this Dolibarr instance, for session/cookie names or email id. * The prefix is unique for instance and avoid conflict between multi-instances, even when having two instances with same root dir * or two instances in same virtual servers. * This function must not use dol_hash (that is used for password hash) and need to have all context $conf loaded. * * @param string $mode '' (prefix for session name) or 'email' (prefix for email id) * @return string A calculated prefix */ function dol_getprefix($mode = '') { // If prefix is for email (we need to have $conf already loaded for this case) if ($mode == 'email') { global $conf; if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) { // If MAIL_PREFIX_FOR_EMAIL_ID is set if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') { return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID; } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME' return $_SERVER["SERVER_NAME"]; } } // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions) if (!empty($conf->file->instance_unique_id)) { return sha1('dolibarr'.$conf->file->instance_unique_id); } // For backward compatibility when instance_unique_id is not set return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT); } // If prefix is for session (no need to have $conf loaded) global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php $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 // The recommended value (may be not defined for old versions) if (!empty($tmp_instance_unique_id)) { return sha1('dolibarr'.$tmp_instance_unique_id); } // For backward compatibility when instance_unique_id is not set if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) { return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT); } else { return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT); } } } /** * Make an include_once using default root and alternate root if it fails. * To link to a core file, use include(DOL_DOCUMENT_ROOT.'/pathtofile') * To link to a module file from a module file, use include './mymodulefile'; * To link to a module file from a core file, then this function can be used (call by hook / trigger / speciales pages) * * @param string $relpath Relative path to file (Ie: mydir/myfile, ../myfile, ...) * @param string $classname Class name (deprecated) * @return bool True if load is a success, False if it fails */ function dol_include_once($relpath, $classname = '') { 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'] $fullpath = dol_buildpath($relpath); if (!file_exists($fullpath)) { dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING); return false; } if (!empty($classname) && !class_exists($classname)) { return include $fullpath; } else { return include_once $fullpath; } } /** * Return path of url or filesystem. Can check into alternate dir or alternate dir + main dir depending on value of $returnemptyifnotfound. * * @param string $path Relative path to file (if mode=0) or relative url (if mode=1). Ie: mydir/myfile, ../myfile * @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) * @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) * 1:If $type==0 and if file was not found into alternate dir, return empty string * 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 * @return string Full filesystem path (if path=0) or '' if file not found, Full url path (if mode=1) */ function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0) { global $conf; $path = preg_replace('/^\//', '', $path); if (empty($type)) { // For a filesystem path $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path if (is_array($conf->file->dol_document_root)) { foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...) if ($key == 'main') { continue; } if (file_exists($dirroot.'/'.$path)) { $res = $dirroot.'/'.$path; return $res; } } } if ($returnemptyifnotfound) { // Not found into alternate dir if ($returnemptyifnotfound == 1 || !file_exists($res)) { return ''; } } } else { // For an url path // We try to get local path of file on filesystem from url // Note that trying to know if a file on disk exist by forging path on disk from url // works only for some web server and some setup. This is bugged when // using proxy, rewriting, virtual path, etc... $res = ''; if ($type == 1) { $res = DOL_URL_ROOT.'/'.$path; // Standard value } if ($type == 2) { $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value } if ($type == 3) { $res = DOL_URL_ROOT.'/'.$path; } foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...) if ($key == 'main') { if ($type == 3) { global $dolibarr_main_url_root; // Define $urlwithroot $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax } continue; } preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?' if (!empty($regs[1])) { //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'
'."\n"; if (file_exists($dirroot.'/'.$regs[1])) { if ($type == 1) { $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path; } if ($type == 2) { $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path; } if ($type == 3) { global $dolibarr_main_url_root; // Define $urlwithroot $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current $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 } break; } } } } return $res; } /** * Create a clone of instance of object (new instance with same value for properties) * With native = 0: Property that are reference are also new object (full isolation clone). This means $this->db of new object is not valid. * 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. * * @param object $object Object to clone * @param int $native 0=Full isolation method, 1=Native PHP method * @return object Clone object * @see https://php.net/manual/language.oop5.cloning.php */ function dol_clone($object, $native = 0) { if (empty($native)) { $tmpsavdb = null; if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') { $tmpsavdb = $object->db; unset($object->db); // Such property can not be serialized when PgSql/Connection } $myclone = unserialize(serialize($object)); // serialize then unserialize is hack to be sure to have a new object for all fields if ($tmpsavdb) { $object->db = $tmpsavdb; } } else { $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) } return $myclone; } /** * Optimize a size for some browsers (phone, smarphone, ...) * * @param int $size Size we want * @param string $type Type of optimizing: * '' = function used to define a size for truncation * 'width' = function is used to define a width * @return int New size after optimizing */ function dol_size($size, $type = '') { global $conf; if (empty($conf->dol_optimize_smallscreen)) { return $size; } if ($type == 'width' && $size > 250) { return 250; } else { return 10; } } /** * Clean a string to use it as a file name. * Replace also '--' and ' -' strings, they are used for parameters separation (Note: ' - ' is allowed). * * @param string $str String to clean * @param string $newstr String to replace bad chars with. * @param int $unaccent 1=Remove also accent (default), 0 do not remove them * @return string String cleaned (a-zA-Z_) * * @see dol_string_nospecial(), dol_string_unaccent(), dol_sanitizePathName() */ function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1) { // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file // Char '>' '<' '|' '$' and ';' are special chars for shells. // Char '/' and '\' are file delimiters. // Chars '--' 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 $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';'); $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars); $tmp = preg_replace('/\-\-+/', '_', $tmp); $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp); $tmp = str_replace('..', '', $tmp); return $tmp; } /** * Clean a string to use it as a path name. * Replace also '--' and ' -' strings, they are used for parameters separation (Note: ' - ' is allowed). * * @param string $str String to clean * @param string $newstr String to replace bad chars with * @param int $unaccent 1=Remove also accent (default), 0 do not remove them * @return string String cleaned (a-zA-Z_) * * @see dol_string_nospecial(), dol_string_unaccent(), dol_sanitizeFileName() */ function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1) { // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file // Char '>' '<' '|' '$' and ';' are special chars for shells. // Chars '--' 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 $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';'); $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars); $tmp = preg_replace('/\-\-+/', '_', $tmp); $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp); $tmp = str_replace('..', '', $tmp); return $tmp; } /** * Clean a string to use it as an URL (into a href or src attribute) * * @param string $stringtoclean String to clean * @param int $type 0=Accept all Url, 1=Clean external Url (keep only relative Url) * @return string Escaped string. */ function dol_sanitizeUrl($stringtoclean, $type = 1) { // 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) // We should use dol_string_nounprintableascii but function may not be yet loaded/available $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: onerror=alert(1) $stringtoclean = preg_replace('//', '', $stringtoclean); $stringtoclean = str_replace('\\', '/', $stringtoclean); if ($type == 1) { // removing : should disable links to external url like http:aaa) // removing ';' should disable "named" html entities encode into an url (we should not have this into an url) $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean); } do { $oldstringtoclean = $stringtoclean; // removing '&colon' should disable links to external url like http:aaa) // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url) $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean); } while ($oldstringtoclean != $stringtoclean); if ($type == 1) { // removing '//' should disable links to external url like //aaa or http//) $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean); } return $stringtoclean; } /** * Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName * * @param string $str String to clean * @return string Cleaned string * * @see dol_sanitizeFilename(), dol_string_nospecial() */ function dol_string_unaccent($str) { global $conf; if (utf8_check($str)) { if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) { $transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', \Transliterator::FORWARD); return $transliterator->transliterate($str); } // See http://www.utf8-chartable.de/ $string = rawurlencode($str); $replacements = array( '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A', '%C3%87' => 'C', '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E', '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I', '%C3%91' => 'N', '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O', '%C5%A0' => 'S', '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U', '%C3%9D' => 'Y', '%C5%B8' => 'y', '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a', '%C3%A7' => 'c', '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e', '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i', '%C3%B1' => 'n', '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o', '%C5%A1' => 's', '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u', '%C3%BD' => 'y', '%C3%BF' => 'y' ); $string = strtr($string, $replacements); return rawurldecode($string); } else { // See http://www.ascii-code.com/ $string = strtr( $str, "\xC0\xC1\xC2\xC3\xC4\xC5\xC7 \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1 \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8 \xF9\xFA\xFB\xFC\xFD\xFF", "AAAAAAC EEEEIIIIDN OOOOOUUUY aaaaaaceeee iiiidnooooo uuuuyy" ); $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")); return $string; } } /** * Clean a string from all punctuation characters to use it as a ref or login. * This is a more complete function than dol_sanitizeFileName. * * @param string $str String to clean * @param string $newstr String to replace forbidden chars with * @param array|string $badcharstoreplace Array of forbidden characters to replace. Use '' to keep default list. * @param array|string $badcharstoremove Array of forbidden characters to remove. Use '' to keep default list. * @return string Cleaned string * * @see dol_sanitizeFilename(), dol_string_unaccent(), dol_string_nounprintableascii() */ function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '') { $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°'); // more complete than dol_sanitizeFileName $forbidden_chars_to_remove = array(); //$forbidden_chars_to_remove=array("(",")"); if (is_array($badcharstoreplace)) { $forbidden_chars_to_replace = $badcharstoreplace; } if (is_array($badcharstoremove)) { $forbidden_chars_to_remove = $badcharstoremove; } return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str)); } /** * Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F). It can also removes also Tab-CR-LF. UTF8 chars remains. * This can be used to sanitize a string and view its real content. Some hacks try to obfuscate attacks by inserting non printable chars. * Note, for information: UTF8 on 1 byte are: \x00-\7F * 2 bytes are: byte 1 \xc0-\xdf, byte 2 = \x80-\xbf * 3 bytes are: byte 1 \xe0-\xef, byte 2 = \x80-\xbf, byte 3 = \x80-\xbf * 4 bytes are: byte 1 \xf0-\xf7, byte 2 = \x80-\xbf, byte 3 = \x80-\xbf, byte 4 = \x80-\xbf * @param string $str String to clean * @param int $removetabcrlf Remove also CR-LF * @return string Cleaned string * * @see dol_sanitizeFilename(), dol_string_unaccent(), dol_string_nospecial() */ function dol_string_nounprintableascii($str, $removetabcrlf = 1) { if ($removetabcrlf) { return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace } else { 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 } } /** * Returns text escaped for inclusion into javascript code * * @param string $stringtoescape String to escape * @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 \ * @param int $noescapebackslashn 0=Escape also \n. 1=Do not escape \n. * @return string Escaped string. Both ' and " are escaped into ' if they are escaped. */ function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0) { // escape quotes and backslashes, newlines, etc. $substitjs = array("'"=>"\\'", "\r"=>'\\r'); //$substitjs['. * @param string $noescapetags '' or 'common' or list of tags to not escape. TODO Does not works yet when there is attributes to tag. * @param int $escapeonlyhtmltags 1=Escape only html tags, not the special chars like accents. * @return string Escaped string * @see dol_string_nohtmltag(), dol_string_nospecial(), dol_string_unaccent() */ function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0) { if ($noescapetags == 'common') { $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody'; } // escape quotes and backslashes, newlines, etc. if ($escapeonlyhtmltags) { $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT); } else { $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8'); } if (!$keepb) { $tmp = strtr($tmp, array(""=>'', ''=>'')); } if (!$keepn) { $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n')); } if ($escapeonlyhtmltags) { return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8'); } else { // Escape tags to keep // TODO Does not works yet when there is attributes to tag $tmparrayoftags = array(); if ($noescapetags) { $tmparrayoftags = explode(',', $noescapetags); } if (count($tmparrayoftags)) { foreach ($tmparrayoftags as $tagtoreplace) { $tmp = str_ireplace('<'.$tagtoreplace.'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp); $tmp = str_ireplace('', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp); $tmp = str_ireplace('<'.$tagtoreplace.' />', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp); } } $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8'); if (count($tmparrayoftags)) { foreach ($tmparrayoftags as $tagtoreplace) { $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result); $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '', $result); $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result); } } return $result; } } /** * Convert a string to lower. Never use strtolower because it does not works with UTF8 strings. * * @param string $string String to encode * @param string $encoding Character set encoding * @return string String converted */ function dol_strtolower($string, $encoding = "UTF-8") { if (function_exists('mb_strtolower')) { return mb_strtolower($string, $encoding); } else { return strtolower($string); } } /** * Convert a string to upper. Never use strtolower because it does not works with UTF8 strings. * * @param string $string String to encode * @param string $encoding Character set encoding * @return string String converted */ function dol_strtoupper($string, $encoding = "UTF-8") { if (function_exists('mb_strtoupper')) { return mb_strtoupper($string, $encoding); } else { return strtoupper($string); } } /** * Convert first character of the first word of a string to upper. Never use ucfirst because it does not works with UTF8 strings. * * @param string $string String to encode * @param string $encoding Character set encodign * @return string String converted */ function dol_ucfirst($string, $encoding = "UTF-8") { if (function_exists('mb_substr')) { return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding); } else { return ucfirst($string); } } /** * Convert first character of all the words of a string to upper. Never use ucfirst because it does not works with UTF8 strings. * * @param string $string String to encode * @param string $encoding Character set encodign * @return string String converted */ function dol_ucwords($string, $encoding = "UTF-8") { if (function_exists('mb_convert_case')) { return mb_convert_case($string, MB_CASE_TITLE, $encoding); } else { return ucwords($string); } } /** * Write log message into outputs. Possible outputs can be: * SYSLOG_HANDLERS = ["mod_syslog_file"] file name is then defined by SYSLOG_FILE * SYSLOG_HANDLERS = ["mod_syslog_syslog"] facility is then defined by SYSLOG_FACILITY * Warning, syslog functions are bugged on Windows, generating memory protection faults. To solve * this, use logging to files instead of syslog (see setup of module). * Note: If constant 'SYSLOG_FILE_NO_ERROR' defined, we never output any error message when writing to log fails. * Note: You can get log message into html sources by adding parameter &logtohtml=1 (constant MAIN_LOGTOHTML must be set) * This function works only if syslog module is enabled. * This must not use any call to other function calling dol_syslog (avoid infinite loop). * * @param string $message Line to log. ''=Show nothing * @param int $level Log level * 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 * On Linux LOG_ERR=3, LOG_WARNING=4, LOG_NOTICE=5, LOG_INFO=6, LOG_DEBUG=7 * @param int $ident 1=Increase ident of 1, -1=Decrease ident of 1 * @param string $suffixinfilename When output is a file, append this suffix into default log filename. * @param string $restricttologhandler Force output of log only to this log handler * @param array|null $logcontext If defined, an array with extra informations (can be used by some log handlers) * @return void */ function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null) { global $conf, $user, $debugbar; // If syslog module enabled if (empty($conf->syslog->enabled)) { return; } // Check if we are into execution of code of a website if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) { global $website, $websitekey; if (is_object($website) && !empty($website->ref)) { $suffixinfilename .= '_website_'.$website->ref; } elseif (!empty($websitekey)) { $suffixinfilename .= '_website_'.$websitekey; } } // Check if we have a forced suffix if (defined('USESUFFIXINLOG')) { $suffixinfilename .= constant('USESUFFIXINLOG'); } if ($ident < 0) { foreach ($conf->loghandlers as $loghandlerinstance) { $loghandlerinstance->setIdent($ident); } } if (!empty($message)) { // Test log level $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'); if (!array_key_exists($level, $logLevels)) { throw new Exception('Incorrect log level'); } if ($level > $conf->global->SYSLOG_LEVEL) { return; } if (empty($conf->global->MAIN_SHOW_PASSWORD_INTO_LOG)) { $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log } // If adding log inside HTML page is required if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML)) || (!empty($user->rights->debugbar->read) && is_object($debugbar))) { $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message; } //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output // If html log tag enabled and url parameter log defined, we show output log on HTML comments if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) { print "\n\n\n"; } $data = array( 'message' => $message, 'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false), 'level' => $level, 'user' => ((is_object($user) && $user->id) ? $user->login : false), 'ip' => false ); $remoteip = getUserRemoteIP(); // Get ip when page run on a web server if (!empty($remoteip)) { $data['ip'] = $remoteip; // This is when server run behind a reverse proxy if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) { $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip']; } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) { $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip']; } } elseif (!empty($_SERVER['SERVER_ADDR'])) { // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache) $data['ip'] = $_SERVER['SERVER_ADDR']; } elseif (!empty($_SERVER['COMPUTERNAME'])) { // 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). $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']); } elseif (!empty($_SERVER['LOGNAME'])) { // 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). $data['ip'] = '???@'.$_SERVER['LOGNAME']; } // Loop on each log handler and send output foreach ($conf->loghandlers as $loghandlerinstance) { if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) { continue; } $loghandlerinstance->export($data, $suffixinfilename); } unset($data); } if ($ident > 0) { foreach ($conf->loghandlers as $loghandlerinstance) { $loghandlerinstance->setIdent($ident); } } } /** * Return HTML code to output a button to open a dialog popup box. * Such buttons must be included inside a HTML form. * * @param string $name A name for the html component * @param string $label Label shown in Popup title top bar * @param string $buttonstring button string * @param string $url Url to open * @param string $disabled Disabled text * @param string $morecss More CSS * @param string $backtopagejsfields The back to page must be managed using javascript instead of a redirect. * Value is 'keyforpopupid:Name_of_html_component_to_set_with id,Name_of_html_component_to_set_with_label' * @return string HTML component with button */ function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'button bordertransp', $backtopagejsfields = '') { if (strpos($url, '?') > 0) { $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name); } else { $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name); } $out = ''; $backtopagejsfieldsid = ''; $backtopagejsfieldslabel = ''; if ($backtopagejsfields) { $tmpbacktopagejsfields = explode(':', $backtopagejsfields); if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it. $backtopagejsfields = $name.":".$backtopagejsfields; $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]); } else { $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]); } $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0]; $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1]; $url .= '&backtopagejsfields='.urlencode($backtopagejsfields); } //print ''; $out .= ''."\n"; $out .= ''.$buttonstring.''; $out .= ''; $out .= ''; $out .= ''; $out .= ''; $out .= ''; return $out; } /** * Show tab header of a card * * @param array $links Array of tabs. Currently initialized by calling a function xxx_admin_prepare_head * @param string $active Active tab name (document', 'info', 'ldap', ....) * @param string $title Title * @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) * @param string $picto Add a picto on tab title * @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. * @param string $morehtmlright Add more html content on right of tabs title * @param string $morecss More Css * @param int $limittoshow Limit number of tabs to show. Use 0 to use automatic default value. * @param string $moretabssuffix A suffix to use when you have several dol_get_fiche_head() in same page * @return void * @deprecated Use print dol_get_fiche_head() instead */ function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '') { print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix); } /** * Show tabs of a record * * @param array $links Array of tabs. Note that label into $links[$i][1] must be already HTML escaped. * @param string $active Active tab name * @param string $title Title * @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) * @param string $picto Add a picto on tab title * @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. * @param string $morehtmlright Add more html content on right of tabs title * @param string $morecss More CSS on the link * @param int $limittoshow Limit number of tabs to show. Use 0 to use automatic default value. * @param string $moretabssuffix A suffix to use when you have several dol_get_fiche_head() in same page * @return string */ function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '') { global $conf, $langs, $hookmanager; // Show title $showtitle = 1; if (!empty($conf->dol_optimize_smallscreen)) { $showtitle = 0; } $out = "\n".''; if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) { $out .= '
'."\n"; } // Show right part if ($morehtmlright) { $out .= '
'.$morehtmlright.'
'; // Output right area first so when space is missing, text is in front of tabs and not under. } // Show title if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { $limittitle = 30; $out .= '
'; if ($picto) { $noprefix = $pictoisfullpath; if (strpos($picto, 'fontawesome_') !== false) { $noprefix = 1; } $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' '; } $out .= ''.dol_escape_htmltag(dol_trunc($title, $limittitle)).''; $out .= ''; } // Show tabs // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs). $maxkey = -1; if (is_array($links) && !empty($links)) { $keys = array_keys($links); if (count($keys)) { $maxkey = max($keys); } } // Show tabs // if =0 we don't use the feature if (empty($limittoshow)) { $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD); } if (!empty($conf->dol_optimize_smallscreen)) { $limittoshow = 2; } $displaytab = 0; $nbintab = 0; $popuptab = 0; $outmore = ''; for ($i = 0; $i <= $maxkey; $i++) { if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) { // If active tab is already present if ($i >= $limittoshow) { $limittoshow--; } } } for ($i = 0; $i <= $maxkey; $i++) { if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) { $isactive = true; } else { $isactive = false; } if ($i < $limittoshow || $isactive) { // Output entry with a visible tab $out .= '
'; if (isset($links[$i][2]) && $links[$i][2] == 'image') { if (!empty($links[$i][0])) { $out .= ''.$links[$i][1].''."\n"; } else { $out .= ''.$links[$i][1].''."\n"; } } elseif (!empty($links[$i][1])) { //print "x $i $active ".$links[$i][2]." z"; $out .= '
'; if (!empty($links[$i][0])) { $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]); $out .= ''; } $out .= $links[$i][1]; if (!empty($links[$i][0])) { $out .= ''."\n"; } $out .= empty($links[$i][4]) ? '' : $links[$i][4]; $out .= '
'; } $out .= '
'; } else { // Add entry into the combo popup with the other tabs if (!$popuptab) { $popuptab = 1; $outmore .= '
'; // The css used to hide/show popup } $outmore .= '
'; if (isset($links[$i][2]) && $links[$i][2] == 'image') { if (!empty($links[$i][0])) { $outmore .= ''.$links[$i][1].''."\n"; } else { $outmore .= ''.$links[$i][1].''."\n"; } } elseif (!empty($links[$i][1])) { $outmore .= ''; $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. $outmore .= ''."\n"; } $outmore .= '
'; $nbintab++; } $displaytab = $i; } if ($popuptab) { $outmore .= '
'; } if ($popuptab) { // If there is some tabs not shown $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left'); $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right'); $widthofpopup = 200; $tabsname = $moretabssuffix; if (empty($tabsname)) { $tabsname = str_replace("@", "", $picto); } $out .= '
'; $out .= ''; // Do not use "reposition" class in the "More". $out .= '
'; $out .= $outmore; $out .= '
'; $out .= '
'; $out .= "
\n"; $out .= ""; } if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) { $out .= "
\n"; } if (!$notab || $notab == -1 || $notab == -2) { $out .= "\n".'
'."\n"; } $parameters = array('tabname' => $active, 'out' => $out); $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead" if ($reshook > 0) { $out = $hookmanager->resPrint; } return $out; } /** * Show tab footer of a card * * @param int $notab -1 or 0=Add tab footer, 1=no tab footer * @return void * @deprecated Use print dol_get_fiche_end() instead */ function dol_fiche_end($notab = 0) { print dol_get_fiche_end($notab); } /** * Return tab footer of a card * * @param int $notab -1 or 0=Add tab footer, 1=no tab footer * @return string */ function dol_get_fiche_end($notab = 0) { if (!$notab || $notab == -1) { return "\n
\n"; } else { return ''; } } /** * Show tab footer of a card. * Note: $object->next_prev_filter can be set to restrict select to find next or previous record by $form->showrefnav. * * @param Object $object Object to show * @param string $paramid Name of parameter to use to name the id into the URL next/previous link * @param string $morehtml More html content to output just before the nav bar * @param int $shownav Show Condition (navigation is shown if value is 1) * @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. * @param string $fieldref Nom du champ objet ref (object->ref) a utiliser pour select next et previous * @param string $morehtmlref More html to show after the ref (see $morehtmlleft for before) * @param string $moreparam More param to add in nav link url. * @param int $nodbprefix Do not include DB prefix to forge table name * @param string $morehtmlleft More html code to show before the ref (see $morehtmlref for after) * @param string $morehtmlstatus More html code to show under navigation arrows * @param int $onlybanner Put this to 1, if the card will contains only a banner (this add css 'arearefnobottom' on div) * @param string $morehtmlright More html code to show before navigation arrows * @return void */ function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '') { global $conf, $form, $user, $langs, $hookmanager, $action; $error = 0; $maxvisiblephotos = 1; $showimage = 1; $entity = (empty($object->entity) ? $conf->entity : $object->entity); $showbarcode = empty($conf->barcode->enabled) ? 0 : (empty($object->barcode) ? 0 : 1); if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) { $showbarcode = 0; } $modulepart = 'unknown'; if ($object->element == 'societe' || $object->element == 'contact' || $object->element == 'product' || $object->element == 'ticket') { $modulepart = $object->element; } elseif ($object->element == 'member') { $modulepart = 'memberphoto'; } elseif ($object->element == 'user') { $modulepart = 'userphoto'; } if (class_exists("Imagick")) { if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') { $modulepart = $object->element; } elseif ($object->element == 'fichinter') { $modulepart = 'ficheinter'; } elseif ($object->element == 'contrat') { $modulepart = 'contract'; } elseif ($object->element == 'order_supplier') { $modulepart = 'supplier_order'; } elseif ($object->element == 'invoice_supplier') { $modulepart = 'supplier_invoice'; } } if ($object->element == 'product') { $width = 80; $cssclass = 'photowithmargin photoref'; $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]); $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5); if ($conf->browser->layout == 'phone') { $maxvisiblephotos = 1; } if ($showimage) { $morehtmlleft .= '
'.$object->show_photos('product', $conf->product->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0).'
'; } else { if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) { $nophoto = ''; $morehtmlleft .= '
'; } else { // Show no photo link $nophoto = '/public/theme/common/nophoto.png'; $morehtmlleft .= '
No photo
'; } } } elseif ($object->element == 'ticket') { $width = 80; $cssclass = 'photoref'; $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref); $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2); if ($conf->browser->layout == 'phone') { $maxvisiblephotos = 1; } if ($showimage) { $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0); if ($object->nbphoto > 0) { $morehtmlleft .= '
'.$showphoto.'
'; } else { $showimage = 0; } } if (!$showimage) { if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) { $nophoto = ''; $morehtmlleft .= '
'; } else { // Show no photo link $nophoto = img_picto('No photo', 'object_ticket'); $morehtmlleft .= ''; $morehtmlleft .= '
'; $morehtmlleft .= $nophoto; $morehtmlleft .= '
'; } } } else { if ($showimage) { if ($modulepart != 'unknown') { $phototoshow = ''; // Check if a preview file is available if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) { $objectref = dol_sanitizeFileName($object->ref); $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/"; if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) { $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart); $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 } else { $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart); } if (empty($subdir)) { $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path } $filepath = $dir_output.$subdir."/"; $filepdf = $filepath.$objectref.".pdf"; $relativepath = $subdir.'/'.$objectref.'.pdf'; // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png") $fileimage = $filepdf.'_preview.png'; $relativepathimage = $relativepath.'_preview.png'; $pdfexists = file_exists($filepdf); // If PDF file exists if ($pdfexists) { // Conversion du PDF en image png si fichier png non existant if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) { if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) { // If you experience trouble with pdf thumb generation and imagick, you can disable here. include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png if ($ret < 0) { $error++; } } } } if ($pdfexists && !$error) { $heightforphotref = 80; if (!empty($conf->dol_optimize_smallscreen)) { $heightforphotref = 60; } // If the preview file is found if (file_exists($fileimage)) { $phototoshow = '
'; $phototoshow .= ''; $phototoshow .= '
'; } } } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos); } if ($phototoshow) { $morehtmlleft .= '
'; $morehtmlleft .= $phototoshow; $morehtmlleft .= '
'; } } if (empty($phototoshow)) { // Show No photo link (picto of object) if ($object->element == 'action') { $width = 80; $cssclass = 'photorefcenter'; $nophoto = img_picto('No photo', 'title_agenda'); } else { $width = 14; $cssclass = 'photorefcenter'; $picto = $object->picto; $prefix = 'object_'; if ($object->element == 'project' && !$object->public) { $picto = 'project'; // instead of projectpub } if (strpos($picto, 'fontawesome_') !== false) { $prefix = ''; } $nophoto = img_picto('No photo', $prefix.$picto); } $morehtmlleft .= ''; $morehtmlleft .= '
'; $morehtmlleft .= $nophoto; $morehtmlleft .= '
'; } } } if ($showbarcode) { $morehtmlleft .= '
'.$form->showbarcode($object, 100, 'photoref valignmiddle').'
'; } if ($object->element == 'societe') { if (!empty($conf->use_javascript_ajax) && $user->rights->societe->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) { $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased'); } else { $morehtmlstatus .= $object->getLibStatut(6); } } elseif ($object->element == 'product') { //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') '; if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) { $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell'); } else { $morehtmlstatus .= ''.$object->getLibStatut(6, 0).''; } $morehtmlstatus .= '   '; //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') '; if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) { $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy'); } else { $morehtmlstatus .= ''.$object->getLibStatut(6, 1).''; } } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva', 'salary'))) { $tmptxt = $object->getLibStatut(6, $object->totalpaid); if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) { $tmptxt = $object->getLibStatut(5, $object->totalpaid); } $morehtmlstatus .= $tmptxt; } elseif ($object->element == 'contrat' || $object->element == 'contract') { if ($object->statut == 0) { $morehtmlstatus .= $object->getLibStatut(5); } else { $morehtmlstatus .= $object->getLibStatut(4); } } elseif ($object->element == 'facturerec') { if ($object->frequency == 0) { $morehtmlstatus .= $object->getLibStatut(2); } else { $morehtmlstatus .= $object->getLibStatut(5); } } elseif ($object->element == 'project_task') { $object->fk_statut = 1; if ($object->progress > 0) { $object->fk_statut = 2; } if ($object->progress >= 100) { $object->fk_statut = 3; } $tmptxt = $object->getLibStatut(5); $morehtmlstatus .= $tmptxt; // No status on task } else { // Generic case $tmptxt = $object->getLibStatut(6); if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) { $tmptxt = $object->getLibStatut(5); } $morehtmlstatus .= $tmptxt; } // Add if object was dispatched "into accountancy" if (!empty($conf->accounting->enabled) && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) { // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank') if (method_exists($object, 'getVentilExportCompta')) { $accounted = $object->getVentilExportCompta(); $langs->load("accountancy"); $morehtmlstatus .= '
'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).''; } } // Add alias for thirdparty if (!empty($object->name_alias)) { $morehtmlref .= '
'.$object->name_alias.'
'; } // Add label if (in_array($object->element, array('product', 'bank_account', 'project_task'))) { if (!empty($object->label)) { $morehtmlref .= '
'.$object->label.'
'; } } // Show address and email if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) { $moreaddress = $object->getBannerAddress('refaddress', $object); if ($moreaddress) { $morehtmlref .= '
'; $morehtmlref .= $moreaddress; $morehtmlref .= '
'; } } 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)) { $morehtmlref .= '
'; $morehtmlref .= '
'; $morehtmlref .= $langs->trans("TechnicalID").': '.$object->id; $morehtmlref .= '
'; } $parameters=array('morehtmlref'=>$morehtmlref); $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action); if ($reshook < 0) { setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); } elseif (empty($reshook)) { $morehtmlref .= $hookmanager->resPrint; } elseif ($reshook > 0) { $morehtmlref = $hookmanager->resPrint; } print '
'; print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright); print '
'; print '
'; } /** * Show a string with the label tag dedicated to the HTML edit field. * * @param string $langkey Translation key * @param string $fieldkey Key of the html select field the text refers to * @param int $fieldrequired 1=Field is mandatory * @return string * @deprecated Form::editfieldkey */ function fieldLabel($langkey, $fieldkey, $fieldrequired = 0) { global $langs; $ret = ''; if ($fieldrequired) { $ret .= ''; } $ret .= ''; if ($fieldrequired) { $ret .= ''; } return $ret; } /** * Return string to add class property on html element with pair/impair. * * @param string $var 0 or 1 * @param string $moreclass More class to add * @return string String to add class onto HTML element */ function dol_bc($var, $moreclass = '') { global $bc; $ret = ' '.$bc[$var]; if ($moreclass) { $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret); } return $ret; } /** * Return a formated address (part address/zip/town/state) according to country rules. * See https://en.wikipedia.org/wiki/Address * * @param Object $object A company or contact object * @param int $withcountry 1=Add country into address string * @param string $sep Separator to use to build string * @param Translate $outputlangs Object lang that contains language for text translation. * @param int $mode 0=Standard output, 1=Remove address * @param string $extralangcode User extralanguage $langcode as values for address, town * @return string Formated string * @see dol_print_address() */ function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '') { global $conf, $langs, $hookmanager; $ret = ''; $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS // See format of addresses on https://en.wikipedia.org/wiki/Address // Address if (empty($mode)) { $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : $object->address)); } // Zip/Town/State if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) { // US: title firstname name \n address lines \n town, state, zip \n country $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); $ret .= (($ret && $town) ? $sep : '').$town; if (!empty($object->state)) { $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state; } if (!empty($object->zip)) { $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip; } } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) { // UK: title firstname name \n address lines \n town state \n zip \n country $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); $ret .= ($ret ? $sep : '').$town; if (!empty($object->state)) { $ret .= ($ret ? ", " : '').$object->state; } if (!empty($object->zip)) { $ret .= ($ret ? $sep : '').$object->zip; } } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) { // ES: title firstname name \n address lines \n zip town \n state \n country $ret .= ($ret ? $sep : '').$object->zip; $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); $ret .= ($town ? (($object->zip ? ' ' : '').$town) : ''); if (!empty($object->state)) { $ret .= "\n".$object->state; } } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) { // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country // See https://www.sljfaq.org/afaq/addresses.html $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip; } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) { // IT: title firstname name\n address lines \n zip town state_code \n country $ret .= ($ret ? $sep : '').$object->zip; $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); $ret .= ($town ? (($object->zip ? ' ' : '').$town) : ''); $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code)); } else { // Other: title firstname name \n address lines \n zip town[, state] \n country $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town)); $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : ''; $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : ''); if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) { $ret .= ($ret ? ", " : '').$object->state; } } if (!is_object($outputlangs)) { $outputlangs = $langs; } if ($withcountry) { $langs->load("dict"); $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code))); } if ($hookmanager) { $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode); $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object); if ($reshook > 0) { $ret = ''; } $ret .= $hookmanager->resPrint; } return $ret; } /** * Format a string. * * @param string $fmt Format of strftime function (http://php.net/manual/fr/function.strftime.php) * @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) * @param int $is_gmt See comment of timestamp parameter * @return string A formatted string */ function dol_strftime($fmt, $ts = false, $is_gmt = false) { if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts); } else { return 'Error date into a not supported range'; } } /** * Output date in a string format according to outputlangs (or langs if not defined). * Return charset is always UTF-8, except if encodetoouput is defined. In this case charset is output charset * * @param int $time GM Timestamps date * @param string $format Output date format (tag of strftime function) * "%d %b %Y", * "%d/%m/%Y %H:%M", * "%d/%m/%Y %H:%M:%S", * "%B"=Long text of month, "%A"=Long text of day, "%b"=Short text of month, "%a"=Short text of day * "day", "daytext", "dayhour", "dayhourldap", "dayhourtext", "dayrfc", "dayhourrfc", "...inputnoreduce", "...reduceformat" * @param string $tzoutput true or 'gmt' => string is for Greenwich location * false or 'tzserver' => output string is for local PHP server TZ usage * 'tzuser' => output string is for user TZ (current browser TZ with current dst) => In a future, we should have same behaviour than 'tzuserrel' * 'tzuserrel' => output string is for user TZ (current browser TZ with dst or not, depending on date position) * @param Translate $outputlangs Object lang that contains language for text translation. * @param boolean $encodetooutput false=no convert into output pagecode * @return string Formated date or '' if time is null * * @see dol_mktime(), dol_stringtotime(), dol_getdate() */ function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false) { global $conf, $langs; // If date undefined or "", we return "" if (dol_strlen($time) == 0) { return ''; // $time=0 allowed (it means 01/01/1970 00:00:00) } if ($tzoutput === 'auto') { $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver')); } // Clean parameters $to_gmt = false; $offsettz = $offsetdst = 0; if ($tzoutput) { $to_gmt = true; // For backward compatibility if (is_string($tzoutput)) { if ($tzoutput == 'tzserver') { $to_gmt = false; $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion' $offsettz = 0; // Timezone offset with server timezone, so 0 $offsetdst = 0; // Dst offset with server timezone, so 0 } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') { $to_gmt = true; $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion' if (class_exists('DateTimeZone')) { $user_date_tz = new DateTimeZone($offsettzstring); $user_dt = new DateTime(); $user_dt->setTimezone($user_date_tz); $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time); $offsettz = $user_dt->getOffset(); } else { // old method (The 'tzuser' was processed like the 'tzuserrel') $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore } } } } if (!is_object($outputlangs)) { $outputlangs = $langs; } if (!$format) { $format = 'daytextshort'; } // Do we have to reduce the length of date (year on 2 chars) to save space. // Note: dayinputnoreduce is same than day but no reduction of year length will be done $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param. $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day $formatwithoutreduce = preg_replace('/reduceformat/', '', $format); if ($formatwithoutreduce != $format) { $format = $formatwithoutreduce; $reduceformat = 1; } // so format 'dayreduceformat' is processed like day // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default. // TODO Add format daysmallyear and dayhoursmallyear if ($format == 'day') { $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short); } elseif ($format == 'hour') { $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short); } elseif ($format == 'hourduration') { $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration); } elseif ($format == 'daytext') { $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text); } elseif ($format == 'daytextshort') { $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short); } elseif ($format == 'dayhour') { $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short); } elseif ($format == 'dayhoursec') { $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short); } elseif ($format == 'dayhourtext') { $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text); } elseif ($format == 'dayhourtextshort') { $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short); } elseif ($format == 'dayhourlog') { // Format not sensitive to language $format = '%Y%m%d%H%M%S'; } elseif ($format == 'dayhourlogsmall') { // Format not sensitive to language $format = '%Y%m%d%H%M'; } elseif ($format == 'dayhourldap') { $format = '%Y%m%d%H%M%SZ'; } elseif ($format == 'dayhourxcard') { $format = '%Y%m%dT%H%M%SZ'; } elseif ($format == 'dayxcard') { $format = '%Y%m%d'; } elseif ($format == 'dayrfc') { $format = '%Y-%m-%d'; // DATE_RFC3339 } elseif ($format == 'dayhourrfc') { $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339 } elseif ($format == 'standard') { $format = '%Y-%m-%d %H:%M:%S'; } if ($reduceformat) { $format = str_replace('%Y', '%y', $format); $format = str_replace('yyyy', 'yy', $format); } // Clean format if (preg_match('/%b/i', $format)) { // There is some text to translate // We inhibate translation to text made by strftime functions. We will use trans instead later. $format = str_replace('%b', '__b__', $format); $format = str_replace('%B', '__B__', $format); } if (preg_match('/%a/i', $format)) { // There is some text to translate // We inhibate translation to text made by strftime functions. We will use trans instead later. $format = str_replace('%a', '__a__', $format); $format = str_replace('%A', '__A__', $format); } // Analyze date $reg = array(); 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 dol_print_error('', "Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]); return ''; } 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 // This part of code should not be used anymore. dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING); //if (function_exists('debug_print_backtrace')) debug_print_backtrace(); // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' $syear = (!empty($reg[1]) ? $reg[1] : ''); $smonth = (!empty($reg[2]) ? $reg[2] : ''); $sday = (!empty($reg[3]) ? $reg[3] : ''); $shour = (!empty($reg[4]) ? $reg[4] : ''); $smin = (!empty($reg[5]) ? $reg[5] : ''); $ssec = (!empty($reg[6]) ? $reg[6] : ''); $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true); $ret = adodb_strftime($format, $time + $offsettz + $offsetdst, $to_gmt); } else { // Date is a timestamps if ($time < 100000000000) { // Protection against bad date values $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring. $ret = adodb_strftime($format, $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server } else { $ret = 'Bad value '.$time.' for date'; } } if (preg_match('/__b__/i', $format)) { $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring. // Here ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs. $month = adodb_strftime('%m', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'. if ($encodetooutput) { $monthtext = $outputlangs->transnoentities('Month'.$month); $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month); } else { $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month); $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month); } //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort; $ret = str_replace('__b__', $monthtextshort, $ret); $ret = str_replace('__B__', $monthtext, $ret); //print 'x'.$outputlangs->charset_output.'-'.$ret.'x'; //return $ret; } if (preg_match('/__a__/i', $format)) { //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring"; $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring. $w = adodb_strftime('%w', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w); $ret = str_replace('__A__', $dayweek, $ret); $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret); } return $ret; } /** * Return an array with locale date info. * WARNING: This function use PHP server timezone by default to return locale informations. * Be aware to add the third parameter to "UTC" if you need to work on UTC. * * @param int $timestamp Timestamp * @param boolean $fast Fast mode. deprecated. * @param string $forcetimezone '' to use the PHP server timezone. Or use a form like 'gmt', 'Europe/Paris' or '+0200' to force timezone. * @return array Array of informations * 'seconds' => $secs, * 'minutes' => $min, * 'hours' => $hour, * 'mday' => $day, * 'wday' => $dow, 0=sunday, 6=saturday * 'mon' => $month, * 'year' => $year, * 'yday' => floor($secsInYear/$_day_power) * '0' => original timestamp * @see dol_print_date(), dol_stringtotime(), dol_mktime() */ function dol_getdate($timestamp, $fast = false, $forcetimezone = '') { //$datetimeobj = new DateTime('@'.$timestamp); $datetimeobj = new DateTime(); $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone if ($forcetimezone) { $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered) } $arrayinfo = array( 'year'=>((int) date_format($datetimeobj, 'Y')), 'mon'=>((int) date_format($datetimeobj, 'm')), 'mday'=>((int) date_format($datetimeobj, 'd')), 'wday'=>((int) date_format($datetimeobj, 'w')), 'yday'=>((int) date_format($datetimeobj, 'z')), 'hours'=>((int) date_format($datetimeobj, 'H')), 'minutes'=>((int) date_format($datetimeobj, 'i')), 'seconds'=>((int) date_format($datetimeobj, 's')), '0'=>$timestamp ); return $arrayinfo; } /** * Return a timestamp date built from detailed informations (by default a local PHP server timestamp) * Replace function mktime not available under Windows if year < 1970 * PHP mktime is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows * * @param int $hour Hour (can be -1 for undefined) * @param int $minute Minute (can be -1 for undefined) * @param int $second Second (can be -1 for undefined) * @param int $month Month (1 to 12) * @param int $day Day (1 to 31) * @param int $year Year * @param mixed $gm True or 1 or 'gmt'=Input informations are GMT values * False or 0 or 'tzserver' = local to server TZ * 'auto' * 'tzuser' = local to user TZ taking dst into account at the current date. Not yet implemented. * '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. * 'tz,TimeZone' = use specified timezone * @param int $check 0=No check on parameters (Can use day 32, etc...) * @return int|string Date as a timestamp, '' or false if error * @see dol_print_date(), dol_stringtotime(), dol_getdate() */ function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1) { global $conf; //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -"; if ($gm === 'auto') { $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey); } //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'
';exit; // Clean parameters if ($hour == -1 || empty($hour)) { $hour = 0; } if ($minute == -1 || empty($minute)) { $minute = 0; } if ($second == -1 || empty($second)) { $second = 0; } // Check parameters if ($check) { if (!$month || !$day) { return ''; } if ($day > 31) { return ''; } if ($month > 12) { return ''; } if ($hour < 0 || $hour > 24) { return ''; } if ($minute < 0 || $minute > 60) { return ''; } if ($second < 0 || $second > 60) { return ''; } } if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) { $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin' $localtz = new DateTimeZone($default_timezone); } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') { // We use dol_tz_string first because it is more reliable. $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin' try { $localtz = new DateTimeZone($default_timezone); } catch (Exception $e) { dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING); $default_timezone = @date_default_timezone_get(); } } elseif (strrpos($gm, "tz,") !== false) { $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin' try { $localtz = new DateTimeZone($timezone); } catch (Exception $e) { dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING); } } if (empty($localtz)) { $localtz = new DateTimeZone('UTC'); } //var_dump($localtz); //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute); $dt = new DateTime('now', $localtz); $dt->setDate((int) $year, (int) $month, (int) $day); $dt->setTime((int) $hour, (int) $minute, (int) $second); $date = $dt->getTimestamp(); // should include daylight saving time //var_dump($date); return $date; } /** * Return date for now. In most cases, we use this function without parameters (that means GMT time). * * @param string $mode 'auto' => for backward compatibility (avoid this), * 'gmt' => we return GMT timestamp, * 'tzserver' => we add the PHP server timezone * 'tzref' => we add the company timezone. Not implemented. * 'tzuser' or 'tzuserrel' => we add the user timezone * @return int $date Timestamp */ function dol_now($mode = 'auto') { $ret = 0; if ($mode === 'auto') { $mode = 'gmt'; } if ($mode == 'gmt') { $ret = time(); // Time for now at greenwich. } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time $ret = (int) (dol_now('gmt') + ($tzsecond * 3600)); //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time // $ret=dol_now('gmt')+($tzsecond*3600); //} } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') { // Time for now with user timezone added //print 'time: '.time(); $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst)); } return $ret; } /** * Return string with formated size * * @param int $size Size to print * @param int $shortvalue Tell if we want long value to use another unit (Ex: 1.5Kb instead of 1500b) * @param int $shortunit Use short label of size unit (for example 'b' instead of 'bytes') * @return string Link */ function dol_print_size($size, $shortvalue = 0, $shortunit = 0) { global $conf, $langs; $level = 1024; if (!empty($conf->dol_optimize_smallscreen)) { $shortunit = 1; } // Set value text if (empty($shortvalue) || $size < ($level * 10)) { $ret = $size; $textunitshort = $langs->trans("b"); $textunitlong = $langs->trans("Bytes"); } else { $ret = round($size / $level, 0); $textunitshort = $langs->trans("Kb"); $textunitlong = $langs->trans("KiloBytes"); } // Use long or short text unit if (empty($shortunit)) { $ret .= ' '.$textunitlong; } else { $ret .= ' '.$textunitshort; } return $ret; } /** * Show Url link * * @param string $url Url to show * @param string $target Target for link * @param int $max Max number of characters to show * @param int $withpicto With picto * @return string HTML Link */ function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0) { global $langs; if (empty($url)) { return ''; } $link = ''.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'
'; } /** * Show EMail link formatted for HTML output. * * @param string $email EMail to show (only email, without 'Name of recipient' before) * @param int $cid Id of contact if known * @param int $socid Id of third party if known * @param int $addlink 0=no link, 1=email has a html email link (+ link to create action if constant AGENDA_ADDACTIONFOREMAIL is on) * @param int $max Max number of characters to show * @param int $showinvalid 1=Show warning if syntax email is wrong * @param int|string $withpicto Show picto * @return string HTML Link */ function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0) { global $conf, $user, $langs, $hookmanager; $newemail = dol_escape_htmltag($email); if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) { $withpicto = 0; } if (empty($email)) { return ' '; } if (!empty($addlink)) { $newemail = ''; $newemail .= dol_trunc($email, $max); $newemail .= ''; if ($showinvalid && !isValidEmail($email)) { $langs->load("errors"); $newemail .= img_warning($langs->trans("ErrorBadEMail", $email)); } if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) { $type = 'AC_EMAIL'; $link = ''; if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) { $link = ''.img_object($langs->trans("AddAction"), "calendar").''; } if ($link) { $newemail = '
'.$newemail.' '.$link.'
'; } } } else { if ($showinvalid && !isValidEmail($email)) { $langs->load("errors"); $newemail .= img_warning($langs->trans("ErrorBadEMail", $email)); } } //$rep = '
'; $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto)).' ' : '').$newemail; //$rep .= '
'; if ($hookmanager) { $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto); $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email); if ($reshook > 0) { $rep = ''; } $rep .= $hookmanager->resPrint; } return $rep; } /** * Get array of social network dictionary * * @return array Array of Social Networks Dictionary */ function getArrayOfSocialNetworks() { global $conf, $db; $socialnetworks = array(); // Enable caching of array require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php'; $cachekey = 'socialnetworks_' . $conf->entity; $dataretrieved = dol_getcache($cachekey); if (!is_null($dataretrieved)) { $socialnetworks = $dataretrieved; } else { $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks"; $sql .= " WHERE entity=".$conf->entity; $resql = $db->query($sql); if ($resql) { while ($obj = $db->fetch_object($resql)) { $socialnetworks[$obj->code] = array( 'rowid' => $obj->rowid, 'label' => $obj->label, 'url' => $obj->url, 'icon' => $obj->icon, 'active' => $obj->active, ); } } dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result. } return $socialnetworks; } /** * Show social network link * * @param string $value Skype to show (only skype, without 'Name of recipient' before) * @param int $cid Id of contact if known * @param int $socid Id of third party if known * @param string $type 'skype','facebook',... * @param array $dictsocialnetworks socialnetworks availables * @return string HTML Link */ function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array()) { global $conf, $user, $langs; $htmllink = $value; if (empty($value)) { return ' '; } if (!empty($type)) { $htmllink = '
'; // Use dictionary definition for picto $dictsocialnetworks[$type]['icon'] $htmllink .= ''; if ($type == 'skype') { $htmllink .= dol_escape_htmltag($value); $htmllink .= ' '; $htmllink .= ''; $htmllink .= ''; $htmllink .= ''; $htmllink .= ''; $htmllink .= ''; if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) { $addlink = 'AC_SKYPE'; $link = ''; if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) { $link = ''.img_object($langs->trans("AddAction"), "calendar").''; } $htmllink .= ($link ? ' '.$link : ''); } } else { if (!empty($dictsocialnetworks[$type]['url'])) { $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']); if ($tmpvirginurl) { $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value); $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value); $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl); if ($tmpvirginurl3) { $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value); $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value); } $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl); if ($tmpvirginurl2) { $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value); $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value); } } $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']); if (preg_match('/^https?:\/\//i', $link)) { $htmllink .= ' '.dol_escape_htmltag($value).''; } else { $htmllink .= ' '.dol_escape_htmltag($value).''; } } else { $htmllink .= dol_escape_htmltag($value); } } $htmllink .= '
'; } else { $langs->load("errors"); $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value)); } return $htmllink; } /** * Format profIDs according to country * * @param string $profID Value of profID to format * @param string $profIDtype Type of profID to format ('1', '2', '3', '4', '5', '6' or 'VAT') * @param string $countrycode Country code to use for formatting * @param int $addcpButton Add button to copy to clipboard (1 => show only on hoover ; 2 => always display ) * @param string $separ Separation between numbers for a better visibility example : xxx xxx xxx xxxxx * @return string Formated profID */ function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1, $separ = ' ') { global $mysoc; if (empty($profID) || empty($profIDtype)) { return ''; } if (empty($countrycode)) $countrycode = $mysoc->country_code; $newProfID = $profID; $id = substr($profIDtype, -1); $ret = ''; if (strtoupper($countrycode) == 'FR') { // France if ($id == 1 && dol_strlen($newProfID) == 9) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3); if ($id == 2 && dol_strlen($newProfID) == 14) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3).$separ.substr($newProfID, 9, 5); if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) $newProfID = substr($newProfID, 0, 4).$separ.substr($newProfID, 4, 3).$separ.substr($newProfID, 7, 3).$separ.substr($newProfID, 10, 3); } if (!empty($addcpButton)) $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID); else $ret = $newProfID; return $ret; } /** * Format phone numbers according to country * * @param string $phone Phone number to format * @param string $countrycode Country code to use for formatting * @param int $cid Id of contact if known * @param int $socid Id of third party if known * @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) * @param string $separ Separation between numbers for a better visibility example : xx.xx.xx.xx.xx * @param string $withpicto Show picto ('fax', 'phone', 'mobile') * @param string $titlealt Text to show on alt * @param int $adddivfloat Add div float around phone. * @return string Formated phone number */ function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = " ", $withpicto = '', $titlealt = '', $adddivfloat = 0) { global $conf, $user, $langs, $mysoc, $hookmanager; // Clean phone parameter $phone = preg_replace("/[\s.-]/", "", trim($phone)); if (empty($phone)) { return ''; } if (!empty($conf->global->MAIN_PHONE_SEPAR)) { $separ = $conf->global->MAIN_PHONE_SEPAR; } if (empty($countrycode) && is_object($mysoc)) { $countrycode = $mysoc->country_code; } // Short format for small screens if ($conf->dol_optimize_smallscreen) { $separ = ''; } $newphone = $phone; if (strtoupper($countrycode) == "FR") { // France if (dol_strlen($phone) == 10) { $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); } elseif (dol_strlen($phone) == 7) { $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2); } elseif (dol_strlen($phone) == 9) { $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2); } elseif (dol_strlen($phone) == 11) { $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); } elseif (dol_strlen($phone) == 12) { $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); } elseif (dol_strlen($phone) == 13) { $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3).$separ.substr($newphone, 11, 2); } } elseif (strtoupper($countrycode) == "CA") { if (dol_strlen($phone) == 10) { $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4); } } elseif (strtoupper($countrycode) == "PT") {//Portugal if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); } } elseif (strtoupper($countrycode) == "SR") {//Suriname if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3); } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4); } } elseif (strtoupper($countrycode) == "DE") {//Allemagne if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3); } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3); } } elseif (strtoupper($countrycode) == "ES") {//Espagne if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); } } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI $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); } } elseif (strtoupper($countrycode) == "RO") {// Roumanie if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI $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); } } elseif (strtoupper($countrycode) == "TR") {//Turquie if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4); } } elseif (strtoupper($countrycode) == "US") {//Etat-Unis if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4); } } elseif (strtoupper($countrycode) == "MX") {//Mexique if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2); } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH $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); } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4); } } elseif (strtoupper($countrycode) == "ML") {//Mali if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH $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); } } elseif (strtoupper($countrycode) == "TH") {//Thaïlande if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3); } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI $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); } } elseif (strtoupper($countrycode) == "MU") { //Maurice if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2); } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2); } } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI $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); } } elseif (strtoupper($countrycode) == "SY") {//Syrie if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH $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); } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI $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); } } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2); } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4); } } elseif (strtoupper($countrycode) == "DZ") {//Algérie if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); } } elseif (strtoupper($countrycode) == "BE") {//Belgique if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3); } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); } } elseif (strtoupper($countrycode) == "PF") {//Polynésie française if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH $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); } } elseif (strtoupper($countrycode) == "CO") {//Colombie if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ $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); } } elseif (strtoupper($countrycode) == "JO") {//Jordanie if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH $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); } } elseif (strtoupper($countrycode) == "JM") {//Jamaïque if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4); } } elseif (strtoupper($countrycode) == "MG") {//Madagascar if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3).$separ.substr($newphone, 11, 2); } } elseif (strtoupper($countrycode) == "GB") {//Royaume uni if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3); } } elseif (strtoupper($countrycode) == "CH") {//Suisse if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI $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); } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL $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); } } elseif (strtoupper($countrycode) == "TN") {//Tunisie if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); } } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau) $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); } } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau) $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); } } elseif (strtoupper($countrycode) == "MQ") {//Martinique if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau) $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); } } elseif (strtoupper($countrycode) == "IT") {//Italie if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3); } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ $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); } } elseif (strtoupper($countrycode) == "AU") { //Australie if (dol_strlen($phone) == 12) { //ex: +61_A_BCDE_FGHI $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4); } } if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set) 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 $newphoneform = $newphone; $newphone = ''; } elseif (!empty($conf->clicktodial->enabled) && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url if (empty($user->clicktodial_loaded)) { $user->fetch_clicktodial(); } // Define urlmask $urlmask = 'ErrorClickToDialModuleNotConfigured'; if (!empty($conf->global->CLICKTODIAL_URL)) { $urlmask = $conf->global->CLICKTODIAL_URL; } if (!empty($user->clicktodial_url)) { $urlmask = $user->clicktodial_url; } $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : ''); $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : ''); $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : ''); // This line is for backward compatibility $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password); // Thoose lines are for substitution $substitarray = array('__PHONEFROM__'=>$clicktodial_poste, '__PHONETO__'=>urlencode($phone), '__LOGIN__'=>$clicktodial_login, '__PASS__'=>$clicktodial_password); $url = make_substitutions($url, $substitarray); $newphonesav = $newphone; if (empty($conf->global->CLICKTODIAL_DO_NOT_USE_AJAX_CALL)) { // Default and recommended: New method using ajax without submiting a page making a javascript history.go(-1) back $newphone = ''; } else { // Old method $newphone = 'global->CLICKTODIAL_FORCENEWTARGET)) { $newphone .= ' target="_blank" rel="noopener noreferrer"'; } $newphone .= '>'.$newphonesav.''; } } //if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) if (isModEnabled('agenda') && $user->rights->agenda->myactions->create) { $type = 'AC_TEL'; $link = ''; if ($addlink == 'AC_FAX') { $type = 'AC_FAX'; } if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) { $link = ''.img_object($langs->trans("AddAction"), "calendar").''; } if ($link) { $newphone = '
'.$newphone.' '.$link.'
'; } } } if (empty($titlealt)) { $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone")); } $rep = ''; if ($hookmanager) { $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto); $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone); $rep .= $hookmanager->resPrint; } if (empty($reshook)) { $picto = ''; if ($withpicto) { if ($withpicto == 'fax') { $picto = 'phoning_fax'; } elseif ($withpicto == 'phone') { $picto = 'phoning'; } elseif ($withpicto == 'mobile') { $picto = 'phoning_mobile'; } else { $picto = ''; } } if ($adddivfloat) { $rep .= '
'; } else { $rep .= ''; } $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone; if ($adddivfloat) { $rep .= '
'; } else { $rep .= ''; } } return $rep; } /** * Return an IP formated to be shown on screen * * @param string $ip IP * @param int $mode 0=return IP + country/flag, 1=return only country/flag, 2=return only IP * @return string Formated IP, with country if GeoIP module is enabled */ function dol_print_ip($ip, $mode = 0) { global $conf, $langs; $ret = ''; if (empty($mode)) { $ret .= $ip; } if ($mode != 2) { $countrycode = dolGetCountryCodeFromIp($ip); if ($countrycode) { // If success, countrycode is us, fr, ... if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) { $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1); } else { $ret .= ' ('.$countrycode.')'; } } else { // Nothing } } return $ret; } /** * Return the IP of remote user. * Take HTTP_X_FORWARDED_FOR (defined when using proxy) * Then HTTP_CLIENT_IP if defined (rare) * Then REMOTE_ADDR (no way to be modified by user but may be wrong if user is using a proxy) * * @return string Ip of remote user. */ function getUserRemoteIP() { if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) { if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) { if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) { $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client } else { $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client } } else { $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy } } else { $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy } return $ip; } /** * Return if we are using a HTTPS connexion * Check HTTPS (no way to be modified by user but may be empty or wrong if user is using a proxy) * Take HTTP_X_FORWARDED_PROTO (defined when using proxy) * Then HTTP_X_FORWARDED_SSL * * @return boolean True if user is using HTTPS */ function isHTTPS() { $isSecure = false; if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { $isSecure = true; } 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') { $isSecure = true; } return $isSecure; } /** * Return a country code from IP. Empty string if not found. * * @param string $ip IP * @return string Country code ('us', 'fr', ...) */ function dolGetCountryCodeFromIp($ip) { global $conf; $countrycode = ''; if (!empty($conf->geoipmaxmind->enabled)) { $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE'); //$ip='24.24.24.24'; //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages) include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php'; $geoip = new DolGeoIP('country', $datafile); //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n"; $countrycode = $geoip->getCountryCodeFromIP($ip); } return $countrycode; } /** * Return country code for current user. * If software is used inside a local network, detection may fails (we need a public ip) * * @return string Country code (fr, es, it, us, ...) */ function dol_user_country() { global $conf, $langs, $user; //$ret=$user->xxx; $ret = ''; if (!empty($conf->geoipmaxmind->enabled)) { $ip = getUserRemoteIP(); $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE'); //$ip='24.24.24.24'; //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat'; include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php'; $geoip = new DolGeoIP('country', $datafile); $countrycode = $geoip->getCountryCodeFromIP($ip); $ret = $countrycode; } return $ret; } /** * Format address string * * @param string $address Address string, already formatted with dol_format_address() * @param int $htmlid Html ID (for example 'gmap') * @param int $element 'thirdparty'|'contact'|'member'|'other' * @param int $id Id of object * @param int $noprint No output. Result is the function return * @param string $charfornl Char to use instead of nl2br. '' means we use a standad nl2br. * @return string|void Nothing if noprint is 0, formatted address if noprint is 1 * @see dol_format_address() */ function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '') { global $conf, $user, $langs, $hookmanager; $out = ''; if ($address) { if ($hookmanager) { $parameters = array('element' => $element, 'id' => $id); $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address); $out .= $hookmanager->resPrint; } if (empty($reshook)) { if (empty($charfornl)) { $out .= nl2br($address); } else { $out .= preg_replace('/[\r\n]+/', $charfornl, $address); } // TODO Remove this block, we can add this using the hook now $showgmap = $showomap = 0; if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) { $showgmap = 1; } if ($element == 'contact' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) { $showgmap = 1; } if ($element == 'member' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) { $showgmap = 1; } if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) { $showomap = 1; } if ($element == 'contact' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) { $showomap = 1; } if ($element == 'member' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) { $showomap = 1; } if ($showgmap) { $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1); $out .= ' '; } if ($showomap) { $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1); $out .= ' '; } } } if ($noprint) { return $out; } else { print $out; } } /** * Return true if email syntax is ok. * * @param string $address email (Ex: "toto@examle.com". Long form "John Do " will be false) * @param int $acceptsupervisorkey If 1, the special string '__SUPERVISOREMAIL__' is also accepted as valid * @param int $acceptuserkey If 1, the special string '__USER_EMAIL__' is also accepted as valid * @return boolean true if email syntax is OK, false if KO or empty string * @see isValidMXRecord() */ function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0) { if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') { return true; } if ($acceptuserkey && $address == '__USER_EMAIL__') { return true; } if (filter_var($address, FILTER_VALIDATE_EMAIL)) { return true; } return false; } /** * Return if the domain name has a valid MX record. * WARNING: This need function idn_to_ascii, checkdnsrr and getmxrr * * @param string $domain Domain name (Ex: "yahoo.com", "yhaoo.com", "dolibarr.fr") * @return int -1 if error (function not available), 0=Not valid, 1=Valid * @see isValidEmail() */ function isValidMXRecord($domain) { if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) { if (!checkdnsrr(idn_to_ascii($domain), 'MX')) { return 0; } if (function_exists('getmxrr')) { $mxhosts = array(); $weight = array(); getmxrr(idn_to_ascii($domain), $mxhosts, $weight); if (count($mxhosts) > 1) { return 1; } if (count($mxhosts) == 1 && !empty($mxhosts[0])) { return 1; } return 0; } } return -1; } /** * Return true if phone number syntax is ok * TODO Decide what to do with this * * @param string $phone phone (Ex: "0601010101") * @return boolean true if phone syntax is OK, false if KO or empty string */ function isValidPhone($phone) { return true; } /** * Make a strlen call. Works even if mbstring module not enabled * * @param string $string String to calculate length * @param string $stringencoding Encoding of string * @return int Length of string */ function dol_strlen($string, $stringencoding = 'UTF-8') { if (function_exists('mb_strlen')) { return mb_strlen($string, $stringencoding); } else { return strlen($string); } } /** * Make a substring. Works even if mbstring module is not enabled for better compatibility. * * @param string $string String to scan * @param string $start Start position * @param int $length Length (in nb of characters or nb of bytes depending on trunconbytes param) * @param string $stringencoding Page code used for input string encoding * @param int $trunconbytes 1=Length is max of bytes instead of max of characters * @return string substring */ function dol_substr($string, $start, $length, $stringencoding = '', $trunconbytes = 0) { global $langs; if (empty($stringencoding)) { $stringencoding = $langs->charset_output; } $ret = ''; if (empty($trunconbytes)) { if (function_exists('mb_substr')) { $ret = mb_substr($string, $start, $length, $stringencoding); } else { $ret = substr($string, $start, $length); } } else { if (function_exists('mb_strcut')) { $ret = mb_strcut($string, $start, $length, $stringencoding); } else { $ret = substr($string, $start, $length); } } return $ret; } /** * Truncate a string to a particular length adding '…' if string larger than length. * If length = max length+1, we do no truncate to avoid having just 1 char replaced with '…'. * MAIN_DISABLE_TRUNC=1 can disable all truncings * * @param string $string String to truncate * @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 ...) * @param string $trunc Where to trunc: 'right', 'left', 'middle' (size must be a 2 power), 'wrap' * @param string $stringencoding Tell what is source string encoding * @param int $nodot Truncation do not add … after truncation. So it's an exact truncation. * @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) * @return string Truncated string. WARNING: length is never higher than $size if $nodot is set, but can be 3 chars higher otherwise. */ function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0) { global $conf; if (empty($size) || !empty($conf->global->MAIN_DISABLE_TRUNC)) { return $string; } if (empty($stringencoding)) { $stringencoding = 'UTF-8'; } // reduce for small screen if ($conf->dol_optimize_smallscreen == 1 && $display == 1) { $size = round($size / 3); } // We go always here if ($trunc == 'right') { $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string; if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) { // If nodot is 0 and size is 1 chars more, we don't trunc and don't add … return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…'); } else { //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string; return $string; } } elseif ($trunc == 'middle') { $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string; if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) { $size1 = round($size / 2); $size2 = round($size / 2); return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding); } else { return $string; } } elseif ($trunc == 'left') { $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string; if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) { // If nodot is 0 and size is 1 chars more, we don't trunc and don't add … return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding); } else { return $string; } } elseif ($trunc == 'wrap') { $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string; if (dol_strlen($newstring, $stringencoding) > ($size + 1)) { return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc); } else { return $string; } } else { return 'BadParam3CallingDolTrunc'; } } /** * Show picto whatever it's its name (generic function) * * @param string $titlealt Text on title tag for tooltip. Not used if param notitle is set to 1. * @param string $picto Name of image file to show ('filenew', ...) * If no extension provided, we use '.png'. Image must be stored into theme/xxx/img directory. * Example: picto.png if picto.png is stored into htdocs/theme/mytheme/img * Example: picto.png@mymodule if picto.png is stored into htdocs/mymodule/img * Example: /mydir/mysubdir/picto.png if picto.png is stored into htdocs/mydir/mysubdir (pictoisfullpath must be set to 1) * Example: fontawesome_envelope-open-text_fas_red_1em if you want to use fontaweseome icons: fontawesome__