diff --git a/ChangeLog b/ChangeLog index 555258f3901..1a0a95aab9e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,6 +27,8 @@ NEW: A lot of addition of hooks. Following changes may create regressions for some external modules, but were necessary to make Dolibarr better: +* There is a new specific permission to be allowed to enter timesheets. If you use timesheet, don't forget to give the new permission (disable and + enable the module project if it is not visible). * The default value for MAIN_SECURITY_CSRF_WITH_TOKEN has been set to 2. It means any POST and any GET request that contains the "action" or "massaction" with a value of a sensitive action must also a valid token parameter (With previous value 1, only POST was concerned). Note: With value 3, any URL with parameter "action" or "massaction" need the token, whatever is the value of the action. diff --git a/README.md b/README.md index 43682368ba0..5559613eaa2 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,7 @@ Follow Dolibarr project on: - [Facebook](https://www.facebook.com/dolibarr) - [Twitter](https://www.twitter.com/dolibarr) - [LinkedIn](https://www.linkedin.com/company/association-dolibarr) -- [Reddit](https://www.reddit.com/r/Dolibarr/) +- [Reddit](https://www.reddit.com/r/Dolibarr_ERP_CRM/) - [YouTube](https://www.youtube.com/user/DolibarrERPCRM) - [GitHub](https://github.com/Dolibarr/dolibarr) diff --git a/htdocs/accountancy/admin/card.php b/htdocs/accountancy/admin/card.php index 91d8257ea7f..f2137d84af9 100644 --- a/htdocs/accountancy/admin/card.php +++ b/htdocs/accountancy/admin/card.php @@ -300,7 +300,7 @@ if ($action == 'create') { // Edit mode if ($action == 'update') { - print dol_get_fiche_head($head, 'card', $langs->trans('AccountAccounting'), 0, 'billr'); + print dol_get_fiche_head($head, 'card', $langs->trans('AccountAccounting'), 0, 'accounting_account'); print '
'; } else { if ($selected) { $this->load_cache_conditions_paiements(); if (isset($this->cache_conditions_paiements[$selected])) { - print $this->cache_conditions_paiements[$selected]['label']; + $label = $this->cache_conditions_paiements[$selected]['label']; + + if (! empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) { + $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label); + } + + print $label; } else { $langs->load('errors'); print $langs->trans('ErrorNotInDictionaryPaymentConditions'); @@ -7226,6 +7282,8 @@ class Form unset($adherenttmpselect); } + $urloption = ''; + $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/adherents/ajax/adherents.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions); if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel").' : '; @@ -7240,7 +7298,9 @@ class Form $out .= img_picto($langs->trans("Search"), 'search'); } } else { - $out .= $this->selectMembersList($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss); + $filterkey = ''; + + $out .= $this->selectMembersList($selected, $htmlname, $filtertype, $limit, $filterkey, $status, 0, $showempty, $forcecombo, $morecss); } if (empty($nooutput)) print $out; @@ -7255,8 +7315,8 @@ class Form * @param string $htmlname Name of select html * @param string $filtertype Filter on adherent type * @param int $limit Limit on number of returned lines - * @param string $filterkey Filter on adherent ref or subject - * @param int $status Ticket status + * @param string $filterkey Filter on member status + * @param int $status Member status * @param int $outputmode 0=HTML select string, 1=Array * @param string $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text. * @param int $forcecombo Force to use combo box @@ -7270,7 +7330,7 @@ class Form $out = ''; $outarray = array(); - $selectFields = " p.rowid, p.ref"; + $selectFields = " p.rowid, p.ref, p.firstname, p.lastname"; $sql = "SELECT "; $sql .= $selectFields; @@ -7280,21 +7340,23 @@ class Form // Add criteria on ref/label if ($filterkey != '') { $sql .= ' AND ('; - $prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on + $prefix = empty($conf->global->MEMBER_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on // For natural search $scrit = explode(' ', $filterkey); $i = 0; if (count($scrit) > 1) $sql .= "("; foreach ($scrit as $crit) { if ($i > 0) $sql .= " AND "; - $sql .= "p.ref LIKE '".$this->db->escape($prefix.$crit)."%'"; - $sql .= ""; + $sql .= "(p.firstname LIKE '".$this->db->escape($prefix.$crit)."%'"; + $sql .= " OR p.lastname LIKE '".$this->db->escape($prefix.$crit)."%')"; $i++; } if (count($scrit) > 1) $sql .= ")"; $sql .= ')'; } - + if ($status != -1) { + $sql .= ' AND statut = '.((int) $status); + } $sql .= $this->db->plimit($limit, 0); // Build output string @@ -7324,7 +7386,9 @@ class Form } else { if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty); } - if ($showempty) $out .= ''; + if ($showempty) { + $out .= ''; + } $i = 0; while ($num && $i < $num) { @@ -7333,6 +7397,7 @@ class Form $objp = $this->db->fetch_object($result); $this->constructMemberListOption($objp, $opt, $optJson, $selected, $filterkey); + // Add new entry // "key" value of json key array is used by jQuery automatically as selected value // "label" value of json key array is used by jQuery automatically as text for combo box @@ -7367,28 +7432,23 @@ class Form protected function constructMemberListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '') { $outkey = ''; - $outval = ''; - $outref = ''; $outlabel = ''; $outtype = ''; - $label = $objp->label; - $outkey = $objp->rowid; - $outref = $objp->ref; - $outlabel = $objp->label; - $outtype = $objp->fk_product_type; + $outlabel = dolGetFirstLastname($objp->firstname, $objp->lastname); + $outtype = $objp->fk_adherent_type; $opt = '\n"; - $optJson = array('key'=>$outkey, 'value'=>$outref, 'type'=>$outtype); + + $optJson = array('key'=>$outkey, 'value'=>$outlabel, 'type'=>$outtype); } /** diff --git a/htdocs/core/class/ldap.class.php b/htdocs/core/class/ldap.class.php index 9e2c45fd634..6f54229c993 100644 --- a/htdocs/core/class/ldap.class.php +++ b/htdocs/core/class/ldap.class.php @@ -234,7 +234,7 @@ class Ldap } } - if (is_resource($this->connection)) { + if (is_resource($this->connection) || is_object($this->connection)) { // Upgrade connexion to TLS, if requested by the configuration if (!empty($conf->global->LDAP_SERVER_USE_TLS)) { // For test/debug diff --git a/htdocs/core/class/translate.class.php b/htdocs/core/class/translate.class.php index 27f3ca1c836..94bd36799fc 100644 --- a/htdocs/core/class/translate.class.php +++ b/htdocs/core/class/translate.class.php @@ -1089,11 +1089,12 @@ class Translate $i = 0; while ($i < $num) { $obj = $db->fetch_object($resql); - - // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut - $this->cache_currencies[$obj->code_iso]['label'] = ($obj->code_iso && $this->trans("Currency".$obj->code_iso) != "Currency".$obj->code_iso ? $this->trans("Currency".$obj->code_iso) : ($obj->label != '-' ? $obj->label : '')); - $this->cache_currencies[$obj->code_iso]['unicode'] = (array) json_decode($obj->unicode, true); - $label[$obj->code_iso] = $this->cache_currencies[$obj->code_iso]['label']; + if ($obj) { + // If a translation exists, we use it lese we use the default label + $this->cache_currencies[$obj->code_iso]['label'] = ($obj->code_iso && $this->trans("Currency".$obj->code_iso) != "Currency".$obj->code_iso ? $this->trans("Currency".$obj->code_iso) : ($obj->label != '-' ? $obj->label : '')); + $this->cache_currencies[$obj->code_iso]['unicode'] = (array) json_decode($obj->unicode, true); + $label[$obj->code_iso] = $this->cache_currencies[$obj->code_iso]['label']; + } $i++; } if (empty($currency_code)) { diff --git a/htdocs/core/db/Database.interface.php b/htdocs/core/db/Database.interface.php index e349072ddec..1d24b058614 100644 --- a/htdocs/core/db/Database.interface.php +++ b/htdocs/core/db/Database.interface.php @@ -502,8 +502,8 @@ interface Database /** * Returns the current line (as an object) for the resultset cursor * - * @param resource $resultset Cursor of the desired request - * @return Object Object result line or false if KO or end of cursor + * @param resource|Connection $resultset Handler of the desired request + * @return Object Object result line or false if KO or end of cursor */ public function fetch_object($resultset); // phpcs:enable diff --git a/htdocs/core/db/pgsql.class.php b/htdocs/core/db/pgsql.class.php index 74abf7a1e36..5a2a1c1b753 100644 --- a/htdocs/core/db/pgsql.class.php +++ b/htdocs/core/db/pgsql.class.php @@ -116,7 +116,7 @@ class DoliDBPgsql extends DoliDB $this->connected = false; $this->ok = false; $this->error = 'Host, login or password incorrect'; - dol_syslog(get_class($this)."::DoliDBPgsql : Erreur Connect ".$this->error, LOG_ERR); + dol_syslog(get_class($this)."::DoliDBPgsql : Erreur Connect ".$this->error.'. Failed to connect to host='.$host.' port='.$port.' user='.$user, LOG_ERR); } // Si connexion serveur ok et si connexion base demandee, on essaie connexion base @@ -423,7 +423,7 @@ class DoliDBPgsql extends DoliDB } // if local connection failed or not requested, use TCP/IP - if (!$this->db) { + if (empty($this->db)) { if (!$host) { $host = "localhost"; } @@ -432,7 +432,11 @@ class DoliDBPgsql extends DoliDB } $con_string = "host='".$host."' port='".$port."' dbname='".$name."' user='".$login."' password='".$passwd."'"; - $this->db = @pg_connect($con_string); + try { + $this->db = @pg_connect($con_string); + } catch (Exception $e) { + print $e->getMessage(); + } } // now we test if at least one connect method was a success @@ -580,7 +584,7 @@ class DoliDBPgsql extends DoliDB { // phpcs:enable // If resultset not provided, we take the last used by connexion - if (!is_resource($resultset)) { + if (!is_resource($resultset) && !is_object($resultset)) { $resultset = $this->_results; } return pg_fetch_object($resultset); @@ -597,7 +601,7 @@ class DoliDBPgsql extends DoliDB { // phpcs:enable // If resultset not provided, we take the last used by connexion - if (!is_resource($resultset)) { + if (!is_resource($resultset) && !is_object($resultset)) { $resultset = $this->_results; } return pg_fetch_array($resultset); @@ -614,7 +618,7 @@ class DoliDBPgsql extends DoliDB { // phpcs:enable // Si le resultset n'est pas fourni, on prend le dernier utilise sur cette connexion - if (!is_resource($resultset)) { + if (!is_resource($resultset) && !is_object($resultset)) { $resultset = $this->_results; } return pg_fetch_row($resultset); @@ -632,7 +636,7 @@ class DoliDBPgsql extends DoliDB { // phpcs:enable // If resultset not provided, we take the last used by connexion - if (!is_resource($resultset)) { + if (!is_resource($resultset) && !is_object($resultset)) { $resultset = $this->_results; } return pg_num_rows($resultset); @@ -650,7 +654,7 @@ class DoliDBPgsql extends DoliDB { // phpcs:enable // If resultset not provided, we take the last used by connexion - if (!is_resource($resultset)) { + if (!is_resource($resultset) && !is_object($resultset)) { $resultset = $this->_results; } // pgsql necessite un resultset pour cette fonction contrairement @@ -668,11 +672,11 @@ class DoliDBPgsql extends DoliDB public function free($resultset = null) { // If resultset not provided, we take the last used by connexion - if (!is_resource($resultset)) { + if (!is_resource($resultset) && !is_object($resultset)) { $resultset = $this->_results; } // Si resultset en est un, on libere la memoire - if (is_resource($resultset)) { + if (is_resource($resultset) || is_object($resultset)) { pg_free_result($resultset); } } @@ -916,7 +920,8 @@ class DoliDBPgsql extends DoliDB // Test charset match LC_TYPE (pgsql error otherwise) //print $charset.' '.setlocale(LC_CTYPE,'0'); exit; - $sql = "CREATE DATABASE '".$this->escape($database)."' OWNER '".$this->escape($owner)."' ENCODING '".$this->escape($charset)."'"; + // NOTE: Do not use ' around the database name + $sql = "CREATE DATABASE ".$this->escape($database)." OWNER '".$this->escape($owner)."' ENCODING '".$this->escape($charset)."'"; dol_syslog($sql, LOG_DEBUG); $ret = $this->query($sql); return $ret; diff --git a/htdocs/core/js/lib_head.js.php b/htdocs/core/js/lib_head.js.php index 2bf03129210..8c4d958abac 100644 --- a/htdocs/core/js/lib_head.js.php +++ b/htdocs/core/js/lib_head.js.php @@ -127,6 +127,19 @@ $langs->trans("FridayMin"), $langs->trans("SaturdayMin") ); + +$dec = ','; +$thousand = ' '; +if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") { + $dec = $langs->transnoentitiesnoconv("SeparatorDecimal"); +} +if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") { + $thousand = $langs->transnoentitiesnoconv("SeparatorThousand"); +} +if ($thousand == 'Space') { + $thousand = ' '; +} + ?> // Javascript libraries for Dolibarr ERP CRM (https://www.dolibarr.org) @@ -136,6 +149,7 @@ var tradMonthsShort = ; var tradDays = ; var tradDaysShort = ; var tradDaysMin = ; +var currencyCache = cache_currencies) ?>; // For JQuery date picker $(document).ready(function() { @@ -623,6 +637,18 @@ function setConstant(url, code, input, entity, strict, forcereload, userid, toke url = url + "?dol_resetcache=1"; } } + var page_y = $(document).scrollTop(); + url = url.replace(/page_y=\d+/g, ''); + if (page_y > 0) { + if (url.indexOf('?') > -1) { + url = url + "&page_y="+page_y; + } else { + url = url + "?page_y="+page_y; + } + } + url = url.replace(/&&+/, '&'); + console.log("url ro redirect = "+url); + window.location.href = url; //location.reload(); return false; @@ -704,6 +730,18 @@ function delConstant(url, code, input, entity, strict, forcereload, userid, toke url = url + "?dol_resetcache=1"; } } + var page_y = $(document).scrollTop(); + url = url.replace(/page_y=\d+/g, ''); + if (page_y > 0) { + if (url.indexOf('?') > -1) { + url = url + "&page_y="+page_y; + } else { + url = url + "?page_y="+page_y; + } + } + url = url.replace(/&&+/, '&'); + console.log("url ro redirect = "+url); + window.location.href = url; //location.reload(); return false; @@ -1091,22 +1129,87 @@ function getParameterByName(name, valueifnotfound) // Another solution, easier, to build a javascript rounding function function dolroundjs(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); } - /** * Function similar to PHP price() * + * Example use: + * pricejs(13312.448, 'MT', 'EUR', 'fr_FR') + * // (depending on conf for 'MT'): '13 312.45 €' + * + * pricejs(343000.121, 'MT') + * // assuming conf for 'MT' is 2 and $langs->defaultlang is 'en_US': '343,000.12' + * * @param {number|string} amount The amount to show * @param {string} mode 'MT' or 'MU' + * @param {string} currency_code ISO code of currency (empty by default) + * @param {string} force_locale ISO code locale to use (if empty, will use Dolibarr's current locale code) * @return {string} The amount with digits + * */ -function pricejs(amount, mode) { +function pricejs(amount, mode = 'MT', currency_code = '', force_locale = '') { var main_max_dec_shown = global->MAIN_MAX_DECIMALS_SHOWN); ?>; var main_rounding_unit = global->MAIN_MAX_DECIMALS_UNIT; ?>; var main_rounding_tot = global->MAIN_MAX_DECIMALS_TOT; ?>; + var main_decimal_separator = ; + var main_thousand_separator = ; + var locale_code = force_locale || defaultlang) ?>; + var amountAsLocalizedString; + var useIntl = Boolean(Intl && Intl.NumberFormat); + var nDigits; + if (currency_code === 'auto') currency_code = currency) ?>; - if (mode == 'MU') return amount.toFixed(main_rounding_unit); - if (mode == 'MT') return amount.toFixed(main_rounding_tot); - return 'Bad value for parameter mode'; + if (mode === 'MU') nDigits = main_rounding_unit; + else if (mode === 'MT') nDigits = main_rounding_tot; + else return 'Bad value for parameter mode'; + + if (useIntl) { + // simple version: let the browser decide how to format the number using the provided language / currency + // parameters + var formattingOptions = { + minimumFractionDigits: nDigits, + maximumFractionDigits: nDigits + }; + if (currency_code) { + formattingOptions['style'] = 'currency'; + formattingOptions['currency'] = currency_code; + } + return Intl.NumberFormat(locale_code.replace('_', '-'), formattingOptions).format(amount); + } + + // No Intl -> attempt to format the number in a way similar to Dolibarr PHP's `price()` function + amountAsLocalizedString = amount.toFixed(nDigits).replace( + /((?!^)(?:\d{3})*)(?:\.(\d+))?$/, + (fullMatch, digitsByThree, decimals) => + digitsByThree.replace( + /\d{3}/g, + (groupOfThree) => main_thousand_separator + groupOfThree + ) + (decimals !== undefined ? main_decimal_separator + decimals : '') + ).replace(/ /, ' '); + if (!currency_code) return amountAsLocalizedString; + + // print with currency + var currency_symbol = currency_code; + + // codes of languages / currencies where the symbol must be placed before the amount + var currencyBeforeAmountCodes = { + currency: ['AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD'], + language: ['nl_NL'] + }; + + if (currencyCache[currency_code] + && currencyCache[currency_code]['unicode'] + && currencyCache[currency_code]['unicode'].length) { + currency_symbol = currencyCache[currency_code]['unicode'].reduce(function (res, cur) {return res + cur}, ''); + } + + if (currencyBeforeAmountCodes.currency.indexOf(currency_code) >= 0 + || currencyBeforeAmountCodes.language.indexOf(locale_code)) { + // if we use a language or a currency where the symbol is placed before the amount + return currency_symbol + amountAsLocalizedString; + } + + // by default: currency symbol after the amount + return amountAsLocalizedString + ' ' + currency_symbol; } /** @@ -1119,20 +1222,8 @@ function pricejs(amount, mode) { function price2numjs(amount) { if (amount == '') return ''; - transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") { - $dec = $langs->transnoentitiesnoconv("SeparatorDecimal"); - } - if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") { - $thousand = $langs->transnoentitiesnoconv("SeparatorThousand"); - } - if ($thousand == 'Space') { - $thousand = ' '; - } - print "var dec='".dol_escape_js($dec)."'; var thousand='".dol_escape_js($thousand)."';\n"; // Set var in javascript - ?> + var dec = ; + var thousand = ; var main_max_dec_shown = global->MAIN_MAX_DECIMALS_SHOWN); ?>; var main_rounding_unit = global->MAIN_MAX_DECIMALS_UNIT; ?>; diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 28774c0698a..5025347dcb2 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -9878,7 +9878,7 @@ function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowi if (is_null($dictvalues)) { $dictvalues = array(); - $sql = "SELECT * FROM ".$tablename." WHERE 1 = 1"; // Here select * is allowed as it is generic code and we don't have list of fields + $sql = "SELECT * FROM ".MAIN_DB_PREFIX.$tablename." WHERE 1 = 1"; // Here select * is allowed as it is generic code and we don't have list of fields if ($checkentity) { $sql .= ' AND entity IN (0,'.getEntity($tablename).')'; } diff --git a/htdocs/core/lib/usergroups.lib.php b/htdocs/core/lib/usergroups.lib.php index 9bc0a59cf86..e14302da340 100644 --- a/htdocs/core/lib/usergroups.lib.php +++ b/htdocs/core/lib/usergroups.lib.php @@ -29,10 +29,10 @@ /** * Prepare array with list of tabs * - * @param Object $object Object related to tabs + * @param User $object Object related to tabs * @return array Array of tabs to show */ -function user_prepare_head($object) +function user_prepare_head(User $object) { global $langs, $conf, $user, $db; diff --git a/htdocs/core/login/functions_ldap.php b/htdocs/core/login/functions_ldap.php index 1d86bd19549..022186b8de1 100644 --- a/htdocs/core/login/functions_ldap.php +++ b/htdocs/core/login/functions_ldap.php @@ -260,7 +260,7 @@ function check_user_password_ldap($usertotest, $passwordtotest, $entitytotest) ** 53 - Account inactive (manually locked out by administrator) */ dol_syslog("functions_ldap::check_user_password_ldap Authentication KO failed to connect to LDAP for '".$usertotest."'", LOG_NOTICE); - if (is_resource($ldap->connection)) { // If connection ok but bind ko + if (is_resource($ldap->connection) || is_object($ldap->connection)) { // If connection ok but bind ko $ldap->ldapErrorCode = ldap_errno($ldap->connection); $ldap->ldapErrorText = ldap_error($ldap->connection); dol_syslog("functions_ldap::check_user_password_ldap ".$ldap->ldapErrorCode." ".$ldap->ldapErrorText); diff --git a/htdocs/core/modules/DolibarrModules.class.php b/htdocs/core/modules/DolibarrModules.class.php index 71abeda1f03..b579c9b0059 100644 --- a/htdocs/core/modules/DolibarrModules.class.php +++ b/htdocs/core/modules/DolibarrModules.class.php @@ -956,11 +956,11 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it $err++; } else { $obj = $this->db->fetch_object($resql); - $tmp = array(); - if ($obj->note) { - $tmp = json_decode($obj->note, true); - } if ($obj) { + $tmp = array(); + if ($obj->note) { + $tmp = json_decode($obj->note, true); + } return array( 'authorid' => $tmp['authorid'], 'ip' => $tmp['ip'], @@ -1054,16 +1054,16 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps,PEAR.NamingConventions.ValidFunctionName.PublicUnderscore /** * Create tables and keys required by module: - * - Files module.sql with create table instructions - * - Then modules.key.sql with create keys instructions + * - Files table.sql or table-module.sql with create table instructions + * - Then table.key.sql or table-module.key.sql with create keys instructions * - Then data_xxx.sql (usualy provided by external modules only) * - Then update_xxx.sql (usualy provided by external modules only) - * Files must be stored in directory defined by reldir (Example: '/install/mysql/tables' or '/module/sql/') + * Files must be stored in subdirectory 'tables' or 'data' into directory $reldir (Example: '/install/mysql/' or '/module/sql/') * This function may also be called by : - * - _load_tables('/install/mysql/tables/', 'modulename') into the this->init() of core module descriptors. + * - _load_tables('/install/mysql/', 'modulename') into the this->init() of core module descriptors. * - _load_tables('/mymodule/sql/') into the this->init() of external module descriptors. * - * @param string $reldir Relative directory where to scan files. Example: '/install/mysql/tables' or '/module/sql/' + * @param string $reldir Relative directory where to scan files. Example: '/install/mysql/' or '/module/sql/' * @param string $onlywithsuffix Only with the defined suffix * @return int <=0 if KO, >0 if OK */ @@ -1084,112 +1084,147 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it $ok = 1; foreach ($conf->file->dol_document_root as $dirroot) { if ($ok) { - $dir = $dirroot.$reldir; + $dirsql = $dirroot.$reldir; $ok = 0; - $handle = @opendir($dir); // Dir may not exists - if (is_resource($handle)) { - $dirfound++; + // We will loop on xxx/, xxx/tables/, xxx/data/ + $listofsubdir = array('', 'tables/', 'data/'); + if ($this->db->type == 'pgsql') { + $listofsubdir[] = '../pgsql/functions/'; + } - // Run llx_mytable.sql files, then llx_mytable_*.sql - $files = array(); - while (($file = readdir($handle)) !== false) { - $files[] = $file; - } - sort($files); - foreach ($files as $file) { - if ($onlywithsuffix) { - if (!preg_match('/\-'.preg_quote($onlywithsuffix, '/').'\./i', $file)) { - //print 'File '.$file.' does not match suffix '.$onlywithsuffix.' so it is discarded
'.$langs->trans("ErrorPHPDoesNotSupport", "MBString")."
'.$langs->trans("PHPSupport", "MBString")."
'.$langs->trans("ErrorPHPDoesNotSupport", "JSON")."
'.$langs->trans("PHPSupport", "JSON")."
'.$langs->trans("ErrorPHPDoesNotSupportGD")."
'.$langs->trans("ErrorPHPDoesNotSupport", "GD")."
'.$langs->trans("PHPSupport", "GD")."
'.$langs->trans("ErrorPHPDoesNotSupportCurl")."
'.$langs->trans("ErrorPHPDoesNotSupport", "Curl")."
'.$langs->trans("PHPSupport", "Curl")."
'.$langs->trans("ErrorPHPDoesNotSupportCalendar")."
'.$langs->trans("ErrorPHPDoesNotSupport", "Calendar")."
'.$langs->trans("PHPSupport", "Calendar")."
'.$langs->trans("ErrorPHPDoesNotSupport", "Xml")."
'.$langs->trans("PHPSupport", "Xml")."
'.$langs->trans("ErrorPHPDoesNotSupportUTF8")."
'.$langs->trans("ErrorPHPDoesNotSupport", "UTF8")."
'.$langs->trans("PHPSupport", "UTF8")."
'.$langs->trans("ErrorPHPDoesNotSupportMbstring")."
'.$langs->trans("PHPSupport", "mbstring")."
'.$langs->trans("ErrorPHPDoesNotSupportIntl")."
'.$langs->trans("ErrorPHPDoesNotSupport", "Intl")."
'.$langs->trans("PHPSupport", "Intl")."
'.$langs->trans("ErrorPHPDoesNotSupport", "IMAP")."
'.$langs->trans("PHPSupport", "IMAP")."
'.$langs->trans("ErrorPHPDoesNotSupport", "ZIP")."| '.$langs->trans("AppliedPricesFrom").' | '; @@ -1659,6 +1696,9 @@ if ((empty($conf->global->PRODUIT_CUSTOMER_PRICES) || $action == 'showlog_defaul } print ''.$langs->trans("HT").' | '; print ''.$langs->trans("TTC").' | '; + if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") { + print ''.$langs->trans("INCT").' | '; + } if (!empty($conf->dynamicprices->enabled)) { print ''.$langs->trans("PriceExpressionSelected").' | '; } @@ -1725,6 +1765,24 @@ if ((empty($conf->global->PRODUIT_CUSTOMER_PRICES) || $action == 'showlog_defaul print ""; } + // Line for default price + if ($objp->price_base_type == 'HT') { + $pu = $objp->price; + } else { + $pu = $objp->price_ttc; + } + + // Local tax was not saved into table llx_product on old version. So we will use value linked to VAT code. + $localtaxarray = getLocalTaxesFromRate($objp->tva_tx.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), 0, $mysoc, $mysoc); + // Define part of HT, VAT, TTC + $resultarray = calcul_price_total(1, $pu, 0, $objp->tva_tx, 1, 1, 0, $objp->price_base_type, $objp->recuperableonly, $object->type, $mysoc, $localtaxarray); + // Calcul du total ht sans remise + $total_ht = $resultarray[0]; + $total_vat = $resultarray[1]; + $total_localtax1 = $resultarray[9]; + $total_localtax2 = $resultarray[10]; + $total_ttc = $resultarray[2]; + // Price if (!empty($objp->fk_price_expression) && !empty($conf->dynamicprices->enabled)) { $price_expression = new PriceExpression($db); @@ -1732,32 +1790,46 @@ if ((empty($conf->global->PRODUIT_CUSTOMER_PRICES) || $action == 'showlog_defaul $title = $price_expression->title; print ''; print ' | '; + if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") { + print ' | '; + } print ' | '.$title." | "; } else { + // Price HT print ''; if (empty($objp->price_by_qty)) { print price($objp->price); } print " | "; + // Price TTC print ''; if (empty($objp->price_by_qty)) { - print price($objp->price_ttc); + $price_ttc = $objp->price_ttc; + print price($price_ttc); } print " | "; + if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") { + print ''; + print $resultarray[2]; + print ' | '; + } if (!empty($conf->dynamicprices->enabled)) { //Only if module is enabled print ''; } } + // Price min print ' | '; if (empty($objp->price_by_qty)) { print price($objp->price_min); } print ' | '; + // Price min inc tax print ''; if (empty($objp->price_by_qty)) { - print price($objp->price_min_ttc); + $price_min_ttc = $objp->price_min_ttc; + print price($price_min_ttc); } print ' | '; @@ -2042,11 +2114,9 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print ''.$langs->trans("PriceBase").' | '; print ''.$langs->trans("DefaultTaxRate").' | '; print ''.$langs->trans("HT").' | '; + print ''.$langs->trans("TTC").' | '; if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") { - //print '' . $langs->trans("INCVATONLY") . ' | '; print ''.$langs->trans("INCT").' | '; - } else { - print ''.$langs->trans("TTC").' | '; } print ''.$langs->trans("MinPrice").' '.$langs->trans("HT").' | '; print ''.$langs->trans("MinPrice").' '.$langs->trans("TTC").' | '; @@ -2107,11 +2177,9 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print ""; print ''.price($line->price)." | "; + print ''.price($line->price_ttc)." | "; if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") { - //print '' . price($line->price_ttc) . " | "; print ''.price($resultarray[2]).' | '; - } else { - print ''.price($line->price_ttc)." | "; } print ''.price($line->price_min).' | '; @@ -2154,12 +2222,15 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print ''; print ''; - print '
| '; @@ -2179,13 +2250,10 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print ' | '.$langs->trans("PriceBase").' | '; print ''.$langs->trans("DefaultTaxRate").' | '; print ''.$langs->trans("HT").' | '; + print ''.$langs->trans("TTC").' | '; if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") { - //print '' . $langs->trans("INCVATONLY") . ' | '; print ''.$langs->trans("INCT").' | '; - } else { - print ''.$langs->trans("TTC").' | '; } - print ''.$langs->trans("MinPrice").' '.$langs->trans("HT").' | '; print ''.$langs->trans("MinPrice").' '.$langs->trans("TTC").' | '; print ''.$langs->trans("ChangedBy").' | '; @@ -2199,7 +2267,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { $pu = $object->price_ttc; } - // Local tax is not saved into table of product. We use value linked to VAT code. + // Local tax was not saved into table llx_product on old version. So we will use value linked to VAT code. $localtaxarray = getLocalTaxesFromRate($object->tva_tx.($object->default_vat_code ? ' ('.$object->default_vat_code.')' : ''), 0, $mysoc, $mysoc); // Define part of HT, VAT, TTC $resultarray = calcul_price_total(1, $pu, 0, $object->tva_tx, 1, 1, 0, $object->price_base_type, $object->recuperableonly, $object->type, $mysoc, $localtaxarray); @@ -2237,14 +2305,12 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print ''.price($object->price)." | "; + print ''.price($object->price_ttc)." | "; if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") { //print '' . price($object->price_ttc) . " | "; print ''.price($resultarray[2]).' | '; - } else { - print ''.price($object->price_ttc)." | "; } - print ''.price($object->price_min).' | '; print ''.price($object->price_min_ttc).' | '; print ''; @@ -2315,11 +2381,10 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print " | "; print ''.price($line->price)." | "; + print ''.price($line->price_ttc)." | "; if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") { //print '' . price($line->price_ttc) . " | "; print ''.price($resultarray[2]).' | '; - } else { - print ''.price($line->price_ttc)." | "; } print ''.price($line->price_min).' | '; diff --git a/htdocs/product/stock/movement_card.php b/htdocs/product/stock/movement_card.php index abcc5f7da42..e31420b7f21 100644 --- a/htdocs/product/stock/movement_card.php +++ b/htdocs/product/stock/movement_card.php @@ -161,7 +161,7 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x' $search_batch = ""; $search_qty = ''; $sall = ""; - $toselect = ''; + $toselect = array(); $search_array_options = array(); } diff --git a/htdocs/product/stock/movement_list.php b/htdocs/product/stock/movement_list.php index 73c6751262a..2ad77fca49e 100644 --- a/htdocs/product/stock/movement_list.php +++ b/htdocs/product/stock/movement_list.php @@ -223,7 +223,7 @@ if (empty($reshook)) { $search_qty = ''; $search_fk_projet=0; $sall = ""; - $toselect = ''; + $toselect = array(); $search_array_options = array(); } if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha') diff --git a/htdocs/product/stock/productlot_list.php b/htdocs/product/stock/productlot_list.php index 74abbad9140..565b309b113 100644 --- a/htdocs/product/stock/productlot_list.php +++ b/htdocs/product/stock/productlot_list.php @@ -168,7 +168,7 @@ if (empty($reshook)) { foreach ($object->fields as $key => $val) { $search[$key] = ''; } - $toselect = ''; + $toselect = array(); $search_array_options = array(); } if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha') diff --git a/htdocs/projet/class/task.class.php b/htdocs/projet/class/task.class.php index 9decf78f55a..10d4afcd9a7 100644 --- a/htdocs/projet/class/task.class.php +++ b/htdocs/projet/class/task.class.php @@ -920,6 +920,7 @@ class Task extends CommonObjectLine // Add where from extra fields $extrafieldsobjectkey = 'projet_task'; $extrafieldsobjectprefix = 'efpt.'; + global $db; // needed for extrafields_list_search_sql.tpl include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php'; // Add where from hooks $parameters = array(); diff --git a/htdocs/projet/list.php b/htdocs/projet/list.php index 6ec329c41f4..0d5c4964003 100644 --- a/htdocs/projet/list.php +++ b/htdocs/projet/list.php @@ -285,7 +285,7 @@ if (empty($reshook)) { $search_accept_booth_suggestions = ''; $search_price_registration = ''; $search_price_booth = ''; - $toselect = ''; + $toselect = array(); $search_array_options = array(); $search_category_array = array(); } diff --git a/htdocs/projet/tasks.php b/htdocs/projet/tasks.php index 364fcfe49f1..aabca026f20 100644 --- a/htdocs/projet/tasks.php +++ b/htdocs/projet/tasks.php @@ -209,7 +209,7 @@ if (empty($reshook)) { $search_progresscalc = ''; $search_progressdeclare = ''; $search_task_budget_amount = ''; - $toselect = ''; + $toselect = array(); $search_array_options = array(); $search_date_start_startmonth = ""; $search_date_start_startyear = ""; diff --git a/htdocs/projet/tasks/time.php b/htdocs/projet/tasks/time.php index 4374ffb2d37..53d0e6c7f91 100644 --- a/htdocs/projet/tasks/time.php +++ b/htdocs/projet/tasks/time.php @@ -164,12 +164,12 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x' $search_task_label = ''; $search_user = 0; $search_valuebilled = ''; - $toselect = ''; + $toselect = array(); $search_array_options = array(); $action = ''; } -if ($action == 'addtimespent' && $user->rights->projet->lire) { +if ($action == 'addtimespent' && $user->rights->projet->time) { $error = 0; $timespent_durationhour = GETPOST('timespent_durationhour', 'int'); @@ -313,7 +313,7 @@ if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $us } } -if ($action == 'confirm_deleteline' && $confirm == "yes" && $user->rights->projet->lire) { +if ($action == 'confirm_deleteline' && $confirm == "yes" && $user->rights->projet->supprimer) { $object->fetchTimeSpent(GETPOST('lineid', 'int')); // load properties like $object->timespent_id if (in_array($object->timespent_fk_user, $childids) || $user->rights->projet->all->creer) { @@ -888,7 +888,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser $linktocreatetimeBtnStatus = 0; $linktocreatetimeUrl = ''; $linktocreatetimeHelpText = ''; - if ($user->rights->projet->all->lire || $user->rights->projet->lire) { // To enter time, read permission is enough + if ($user->rights->projet->all->lire || $user->rights->projet->time) { if ($projectstatic->public || $userRead > 0) { $linktocreatetimeBtnStatus = 1; @@ -1032,6 +1032,10 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser if ($projectstatic->id > 0 || $allprojectforuser > 0) { + if ($action == 'deleteline' && !empty($projectidforalltimes)) { + print $form->formconfirm($_SERVER["PHP_SELF"]."?".($object->id > 0 ? "id=".$object->id : 'projectid='.$projectstatic->id).'&lineid='.GETPOST('lineid', 'int').($withproject ? '&withproject=1' : ''), $langs->trans("DeleteATimeSpent"), $langs->trans("ConfirmDeleteATimeSpent"), "confirm_deleteline", '', '', 1); + } + // Initialize technical object to manage hooks. Note that conf->hooks_modules contains array $hookmanager->initHooks(array('tasktimelist')); @@ -1129,7 +1133,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; } elseif ($action == 'splitline') { print ''; - } elseif ($action == 'createtime' && $user->rights->projet->lire) { + } elseif ($action == 'createtime' && $user->rights->projet->time) { print ''; } elseif ($massaction == 'generateinvoice' && $user->rights->facture->lire) { print ''; @@ -1403,7 +1407,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser /* * Form to add a new line of time spent */ - if ($action == 'createtime' && $user->rights->projet->lire) { + if ($action == 'createtime' && $user->rights->projet->time) { print ''."\n"; if (!empty($id)) { print ''; @@ -1863,8 +1867,8 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0 || $allprojectforuser print ''; print ''; $constforvar = 'RECRUITMENT_'.strtoupper($myTmpObjectKey).'_ADDON'; - if ($conf->global->$constforvar == $file) { + if (getDolGlobalString($constforvar) == $file) { print img_picto($langs->trans("Activated"), 'switch_on'); } else { print ''; @@ -473,7 +473,7 @@ foreach ($myTmpObjects as $myTmpObjectKey => $myTmpObjectArray) { // Default print ' | '; $constforvar = 'RECRUITMENT_'.strtoupper($myTmpObjectKey).'_ADDON_PDF'; - if ($conf->global->$constforvar == $name) { + if (getDolGlobalString($constforvar) == $name) { //print img_picto($langs->trans("Default"), 'on'); // Even if choice is the default value, we allow to disable it. Replace this with previous line if you need to disable unset print 'scandir).'&label='.urlencode($module->name).'&type='.urlencode($type).'" alt="'.$langs->trans("Disable").'">'.img_picto($langs->trans("Enabled"), 'on').''; diff --git a/htdocs/recruitment/admin/setup_candidatures.php b/htdocs/recruitment/admin/setup_candidatures.php index 726e24e89b1..58172c2e767 100644 --- a/htdocs/recruitment/admin/setup_candidatures.php +++ b/htdocs/recruitment/admin/setup_candidatures.php @@ -328,7 +328,7 @@ foreach ($myTmpObjects as $myTmpObjectKey => $myTmpObjectArray) { print ' | '; $constforvar = 'RECRUITMENT_'.strtoupper($myTmpObjectKey).'_ADDON'; - if ($conf->global->$constforvar == $file) { + if (getDolGlobalString($constforvar) == $file) { print img_picto($langs->trans("Activated"), 'switch_on'); } else { print ''; @@ -471,7 +471,7 @@ foreach ($myTmpObjects as $myTmpObjectKey => $myTmpObjectArray) { // Default print ' | '; $constforvar = 'RECRUITMENT_'.strtoupper($myTmpObjectKey).'_ADDON'; - if ($conf->global->$constforvar == $name) { + if (getDolGlobalString($constforvar) == $name) { //print img_picto($langs->trans("Default"), 'on'); // Even if choice is the default value, we allow to disable it. Replace this with previous line if you need to disable unset print 'scandir).'&label='.urlencode($module->name).'&type='.urlencode($type).'" alt="'.$langs->trans("Disable").'">'.img_picto($langs->trans("Enabled"), 'on').''; diff --git a/htdocs/recruitment/core/modules/recruitment/doc/doc_generic_recruitmentjobposition_odt.modules.php b/htdocs/recruitment/core/modules/recruitment/doc/doc_generic_recruitmentjobposition_odt.modules.php index e2b7c545aa9..c28bd4a9589 100644 --- a/htdocs/recruitment/core/modules/recruitment/doc/doc_generic_recruitmentjobposition_odt.modules.php +++ b/htdocs/recruitment/core/modules/recruitment/doc/doc_generic_recruitmentjobposition_odt.modules.php @@ -130,7 +130,7 @@ class doc_generic_recruitmentjobposition_odt extends ModelePDFRecruitmentJobPosi // List of directories area $texte .= ' |
| ';
$texttitle = $langs->trans("ListOfDirectories");
- $listofdir = explode(',', preg_replace('/[\r\n]+/', ',', trim($conf->global->RECRUITMENT_RECRUITMENTJOBPOSITION_ADDON_PDF_ODT_PATH)));
+ $listofdir = explode(',', preg_replace('/[\r\n]+/', ',', trim(getDolGlobalString('RECRUITMENT_RECRUITMENTJOBPOSITION_ADDON_PDF_ODT_PATH'))));
$listoffiles = array();
foreach ($listofdir as $key => $tmpdir) {
$tmpdir = trim($tmpdir);
@@ -155,7 +155,7 @@ class doc_generic_recruitmentjobposition_odt extends ModelePDFRecruitmentJobPosi
$texte .= $form->textwithpicto($texttitle, $texthelp, 1, 'help', '', 1);
$texte .= ' ';
$texte .= '';
$texte .= ' ';
$texte .= '';
diff --git a/htdocs/recruitment/core/modules/recruitment/doc/pdf_standard_recruitmentjobposition.modules.php b/htdocs/recruitment/core/modules/recruitment/doc/pdf_standard_recruitmentjobposition.modules.php
index cc215a855ff..ba1d0a8d9f5 100644
--- a/htdocs/recruitment/core/modules/recruitment/doc/pdf_standard_recruitmentjobposition.modules.php
+++ b/htdocs/recruitment/core/modules/recruitment/doc/pdf_standard_recruitmentjobposition.modules.php
@@ -849,8 +849,8 @@ class pdf_standard_recruitmentjobposition extends ModelePDFRecruitmentJobPositio
pdf_pagehead($pdf, $outputlangs, $this->page_hauteur);
// Show Draft Watermark
- if ($object->statut == $object::STATUS_DRAFT && (!empty($conf->global->FACTURE_DRAFT_WATERMARK))) {
- pdf_watermark($pdf, $outputlangs, $this->page_hauteur, $this->page_largeur, 'mm', $conf->global->FACTURE_DRAFT_WATERMARK);
+ if ($object->statut == $object::STATUS_DRAFT && (!empty($conf->global->RECRUITMENT_RECRUITMENTJOBPOSITION_DRAFT_WATERMARK))) {
+ pdf_watermark($pdf, $outputlangs, $this->page_hauteur, $this->page_largeur, 'mm', $conf->global->RECRUITMENT_RECRUITMENTJOBPOSITION_DRAFT_WATERMARK);
}
$pdf->SetTextColor(0, 0, 60);
diff --git a/htdocs/recruitment/core/modules/recruitment/mod_recruitmentjobposition_advanced.php b/htdocs/recruitment/core/modules/recruitment/mod_recruitmentjobposition_advanced.php
index fcc476abacf..e4a07c63633 100644
--- a/htdocs/recruitment/core/modules/recruitment/mod_recruitmentjobposition_advanced.php
+++ b/htdocs/recruitment/core/modules/recruitment/mod_recruitmentjobposition_advanced.php
@@ -79,7 +79,7 @@ class mod_recruitmentjobposition_advanced extends ModeleNumRefRecruitmentJobPosi
// Parametrage du prefix
$texte .= ' '.$langs->trans("Mask").': | ';
- $texte .= ''.$form->textwithpicto('', $tooltip, 1, 1).' | ';
+ $texte .= ''.$form->textwithpicto('', $tooltip, 1, 1).' | ';
$texte .= ' | ';
@@ -132,7 +132,7 @@ class mod_recruitmentjobposition_advanced extends ModeleNumRefRecruitmentJobPosi
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
// We get cursor rule
- $mask = $conf->global->RECRUITMENT_RECRUITMENTJOBPOSITION_ADVANCED_MASK;
+ $mask = getDolGlobalString('RECRUITMENT_RECRUITMENTJOBPOSITION_ADVANCED_MASK');
if (!$mask) {
$this->error = 'NotConfigured';
diff --git a/htdocs/salaries/card.php b/htdocs/salaries/card.php
index 3deab1e2ed8..3f077255a1c 100644
--- a/htdocs/salaries/card.php
+++ b/htdocs/salaries/card.php
@@ -554,7 +554,7 @@ if ($action == 'create') {
print '';
print $form->editfieldkey('Amount', 'amount', '', $object, 0, 'string', '', 1).' | ';
print ' ';
- print ' | ';
print ' | ';
+
+// complete header by hook
+$parameters=array();
+$reshook=$hookmanager->executeHooks('completeTakePosInvoiceHeader', $parameters, $invoice, $action); // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+print $hookmanager->resPrint;
+
if (empty($_SESSION["basiclayout"]) || $_SESSION["basiclayout"] != 1) {
print ''.$langs->trans('ReductionShort').' | '; print ''.$langs->trans('Qty').' | '; @@ -1445,6 +1452,13 @@ if ($placeid > 0) { } } $htmlsupplements[$line->fk_parent_line] .= ''; + + // complete line by hook + $parameters=array('line' => $line); + $reshook=$hookmanager->executeHooks('completeTakePosInvoiceParentLine', $parameters, $invoice, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + $htmlsupplements[$line->fk_parent_line] .= $hookmanager->resPrint; + if (empty($_SESSION["basiclayout"]) || $_SESSION["basiclayout"] != 1) { $htmlsupplements[$line->fk_parent_line] .= ''.vatrate($line->remise_percent, true).' | '; $htmlsupplements[$line->fk_parent_line] .= ''.$line->qty.' | '; @@ -1525,6 +1539,13 @@ if ($placeid > 0) { $htmlforlines .= ''.vatrate($line->remise_percent, true).' | '; $htmlforlines .= ''; if (!empty($conf->stock->enabled) && !empty($user->rights->stock->mouvement->lire)) { diff --git a/htdocs/theme/eldy/btn.inc.php b/htdocs/theme/eldy/btn.inc.php index 45ed3889a13..b29c55bd02d 100644 --- a/htdocs/theme/eldy/btn.inc.php +++ b/htdocs/theme/eldy/btn.inc.php @@ -147,9 +147,9 @@ span.butActionNewRefused>span.fa, span.butActionNewRefused>span.fa:hover } .butActionDelete, .butActionDelete:link, .butActionDelete:visited, .butActionDelete:hover, .butActionDelete:active, .buttonDelete { - background: var(--butactiondeletebg); + background: var(--butactiondeletebg) !important; /* border: 1px solid #633; */ - color: #633; + color: #633 !important; } .butActionDelete:hover { diff --git a/htdocs/theme/eldy/dropdown.inc.php b/htdocs/theme/eldy/dropdown.inc.php index 6ca9ad67b1f..338b4cf2759 100644 --- a/htdocs/theme/eldy/dropdown.inc.php +++ b/htdocs/theme/eldy/dropdown.inc.php @@ -121,8 +121,17 @@ button.dropdown-item.global-search-item { text-decoration: none !important; } + +/* CSS to hide the arrow to show open/close */ +div#topmenu-quickadd-dropdown, div#topmenu-bookmark-dropdown { + padding-right: 5px; +} +div#topmenu-quickadd-dropdown a::after, div#topmenu-bookmark-dropdown a::after { + display: none; +} + + .dropdown-toggle::after { - /* font part */ font-family: "Font Awesome 5 Free"; font-size: 0.7em; font-weight: 900; @@ -141,6 +150,7 @@ button.dropdown-item.global-search-item { transition: transform .2s ease-in-out; } + .open>.dropdown-toggle::after { transform: rotate(180deg); } diff --git a/htdocs/theme/eldy/global.inc.php b/htdocs/theme/eldy/global.inc.php index ff6fe3c4fb8..ecb98d59734 100644 --- a/htdocs/theme/eldy/global.inc.php +++ b/htdocs/theme/eldy/global.inc.php @@ -240,7 +240,7 @@ input, select { } #mainbody input.button:not(.buttongen):not(.bordertransp), #mainbody a.button:not(.buttongen):not(.bordertransp) { background: var(--butactionbg); - color: var(--textbutaction)!important; + color: var(--textbutaction); border-radius: 3px; border-collapse: collapse; border: none; @@ -2471,12 +2471,13 @@ a.tmenudisabled:link, a.tmenudisabled:visited, a.tmenudisabled:hover, a.tmenudis a.tmenu:link, a.tmenu:visited, a.tmenu:hover, a.tmenu:active { padding: 0px 2px 0px 2px; + margin: 0px 0px 0px 0px; white-space: nowrap; color: var(--colortextbackhmenu); text-decoration: none; } a.tmenusel:link, a.tmenusel:visited, a.tmenusel:hover, a.tmenusel:active { - padding: 0px 4px 0px 4px; + padding: 0px 2px 0px 2px; margin: 0px 0px 0px 0px; white-space: nowrap; color: var(--colortextbackhmenu); @@ -2605,27 +2606,45 @@ a.tmenuimage:hover{ /* To show text of top menu on hover only (THEME_TOPMENU_DISABLE_IMAGE == 2) */ -.tmenulabel:not(.menuhider), .tmenulabel:not(.menuhider):before { - display: none; +.tmenulabel:not(.menuhider), .tmenulabel:not(.menuhider)::before { + display: none; + /* opacity: 0; To show text after transition */ } -a.tmenuimage:not(.menuhider), a.tmenuimage:not(.menuhider):before, -div.tmenuimage:not(.menuhider), div.tmenuimage:not(.menuhider):before, -span.tmenuimage:not(.menuhider), span.tmenuimage:not(.menuhider):before { +a.tmenuimage:not(.menuhider), a.tmenuimage:not(.menuhider)::before, +div.tmenuimage:not(.menuhider), div.tmenuimage:not(.menuhider)::before, +span.tmenuimage:not(.menuhider), span.tmenuimage:not(.menuhider)::before { font-size: 1.3em; - margin-top: 10px !important; + margin-top: 8px !important; } + -.tmenudiv:hover .tmenulabel:not(.menuhider), .tmenudiv:hover .tmenulabel:not(.menuhider):before { +.tmenudiv:hover .tmenulabel:not(.menuhider), .tmenudiv:hover .tmenulabel:not(.menuhider)::before { + display: block; + position: relative; + overflow: hidden; + text-overflow: ellipsis; + /* For transition transition-delay: 1000ms; + transition-property: all; */ + opacity: 1; display: initial !important; + line-height: 0.6em !important; + height: 1em !important; + overflow: hidden; + text-overflow: ellipsis; + color: var(--colortextbackhmenu); + top: 0px; } -.tmenudiv:hover .tmenuimage:not(.menuhider), .tmenudiv:hover .tmenuimage:not(.menuhider):before { - font-size: 1.1em !important; + +.tmenudiv:hover .tmenuimage:not(.menuhider), .tmenudiv:hover .tmenuimage:not(.menuhider)::before { + /* For transition transition-delay: 1000ms; + transition-property: all; */ margin-top: 0px !important; } + -li.tmenu:hover .tmenulabel:not(.menuhider), li.tmenu:hover .tmenulabel:not(.menuhider):before { +li.tmenu:hover .tmenulabel:not(.menuhider), li.tmenu:hover .tmenulabel:not(.menuhider)::before { display: initial !important; } li.tmenu:hover .tmenuimage:not(.menuhider), li.tmenu:hover .tmenuimage:not(.menuhider):before { @@ -2694,12 +2713,14 @@ li.tmenu:hover .tmenuimage:not(.menuhider), li.tmenu:hover .tmenuimage:not(.menu $url = dol_buildpath($path.'/theme/'.$theme.'/img/menus/generic'.(min($generic, 4))."_over.png", 1); print "div.mainmenu.".$val." {\n"; print " background-image: url(".$url.");\n"; + print " background-position-y: 3px;\n"; print "}\n"; } $generic++; } else { print "div.mainmenu.".$val." {\n"; print " background-image: url(".$url.");\n"; + print " background-position-y: 3px;\n"; print "}\n"; } } @@ -3811,12 +3832,6 @@ div.pagination { div.pagination a { font-weight: normal; } -/*div.pagination a.butAction, div.fichehalfright a.butAction { - margin-right: 0px !important; -} -div.tabsAction a.butActionDelete:last-child, div.tabsAction a.butAction:last-child { - margin-right: 0px !important; -}*/ div.pagination ul { list-style: none; @@ -7327,11 +7342,18 @@ div.clipboardCPValue.hidewithsize { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + color: var(--colortextbackhmenu); + /* color: var(--colorbackhmenu1); */ } + .tmenuimage { + color: var(--colortextbackhmenu); + } + .mainmenuaspan { font-size: 0.9em; padding-right: 0; + padding-left: 0; } .topmenuimage { background-size: 22px auto; @@ -7386,6 +7408,15 @@ div.clipboardCPValue.hidewithsize { /* rule to reduce top menu - 3rd reduction: The menu for user is on left */ @media only screen and (max-width: global->THEME_ELDY_WITDHOFFSET_FOR_REDUC3) ? round($nbtopmenuentries * 47, 0) + 130 : $conf->global->THEME_ELDY_WITDHOFFSET_FOR_REDUC3; ?>px) /* reduction 3 */ { + + .tmenudiv .tmenulabel span.mainmenuaspan { + display: none !important; + } + .tmenudiv:hover .tmenuimage:not(.menuhider), .tmenudiv:hover .tmenuimage:not(.menuhider):before { + margin-top: 8px !important; + } + + .side-nav { z-index: 200; background: var(--colorbackvmenu1); diff --git a/htdocs/theme/eldy/main_menu_fa_icons.inc.php b/htdocs/theme/eldy/main_menu_fa_icons.inc.php index 60c3746fe66..ada1ae23a12 100644 --- a/htdocs/theme/eldy/main_menu_fa_icons.inc.php +++ b/htdocs/theme/eldy/main_menu_fa_icons.inc.php @@ -10,12 +10,12 @@ font-style: normal; font-variant: normal; text-rendering: auto; - line-height: 23px; - font-size: ; + line-height: 28px; -webkit-font-smoothing: antialiased; text-align:center; text-decoration:none; color: var(--colortextbackhmenu); + /* font-size: ; */ } .fa-15x { diff --git a/htdocs/theme/md/dropdown.inc.php b/htdocs/theme/md/dropdown.inc.php index c56590d7bfa..615951bbbd9 100644 --- a/htdocs/theme/md/dropdown.inc.php +++ b/htdocs/theme/md/dropdown.inc.php @@ -26,7 +26,7 @@ button.dropdown-item.global-search-item { } div#topmenu-quickadd-dropdown { position: fixed; - : 65px; + : 55px; top: 0px; } div#topmenu-bookmark-dropdown { @@ -124,6 +124,15 @@ button.dropdown-item.global-search-item { } +/* CSS to hide the arrow to show open/close */ +div#topmenu-quickadd-dropdown, div#topmenu-bookmark-dropdown { + padding-right: 2px; +} +div#topmenu-quickadd-dropdown a::after, div#topmenu-bookmark-dropdown a::after { + display: none; +} + + .dropdown-toggle{ text-decoration: none !important; } diff --git a/htdocs/user/class/user.class.php b/htdocs/user/class/user.class.php index 9e50dd2c47d..33ca6d0ec9c 100644 --- a/htdocs/user/class/user.class.php +++ b/htdocs/user/class/user.class.php @@ -297,7 +297,7 @@ class User extends CommonObject public $all_permissions_are_loaded; /** - * @var int Number of rights granted to the user + * @var int Number of rights granted to the user. Value loaded after a getrights(). */ public $nb_rights; @@ -883,15 +883,18 @@ class User extends CommonObject $i = 0; while ($i < $num) { $obj = $this->db->fetch_object($result); - $nid = $obj->id; - $sql = "DELETE FROM ".$this->db->prefix()."user_rights WHERE fk_user = ".((int) $this->id)." AND fk_id = ".((int) $nid)." AND entity = ".((int) $entity); - if (!$this->db->query($sql)) { - $error++; - } - $sql = "INSERT INTO ".$this->db->prefix()."user_rights (entity, fk_user, fk_id) VALUES (".((int) $entity).", ".((int) $this->id).", ".((int) $nid).")"; - if (!$this->db->query($sql)) { - $error++; + if ($obj) { + $nid = $obj->id; + + $sql = "DELETE FROM ".$this->db->prefix()."user_rights WHERE fk_user = ".((int) $this->id)." AND fk_id = ".((int) $nid)." AND entity = ".((int) $entity); + if (!$this->db->query($sql)) { + $error++; + } + $sql = "INSERT INTO ".$this->db->prefix()."user_rights (entity, fk_user, fk_id) VALUES (".((int) $entity).", ".((int) $this->id).", ".((int) $nid).")"; + if (!$this->db->query($sql)) { + $error++; + } } $i++; @@ -1096,6 +1099,14 @@ class User extends CommonObject } } + // For avoid error + if (!isset($this->rights) || !is_object($this->rights)) { + $this->rights = new stdClass(); // For avoid error + } + if (!isset($this->rights->user) || !is_object($this->rights->user)) { + $this->rights->user = new stdClass(); // For avoid error + } + // Get permission of users + Get permissions of groups // First user permissions @@ -1121,7 +1132,6 @@ class User extends CommonObject if ($resql) { $num = $this->db->num_rows($resql); $i = 0; - while ($i < $num) { $obj = $this->db->fetch_object($resql); @@ -1131,9 +1141,6 @@ class User extends CommonObject $subperms = $obj->subperms; if (!empty($perms)) { - if (!isset($this->rights) || !is_object($this->rights)) { - $this->rights = new stdClass(); // For avoid error - } if (!empty($module)) { if (!isset($this->rights->$module) || !is_object($this->rights->$module)) { $this->rights->$module = new stdClass(); @@ -1200,9 +1207,6 @@ class User extends CommonObject $subperms = $obj->subperms; if (!empty($perms)) { - if (!isset($this->rights) || !is_object($this->rights)) { - $this->rights = new stdClass(); // For avoid error - } if (!empty($module)) { if (!isset($this->rights->$module) || !is_object($this->rights->$module)) { $this->rights->$module = new stdClass(); @@ -1232,6 +1236,63 @@ class User extends CommonObject $this->db->free($resql); } + // Force permission on user for admin + if (!empty($this->admin)) { + if (empty($this->rights->user->user)) { + $this->rights->user->user = new stdClass(); + } + $listofpermtotest = array('lire', 'creer', 'password', 'supprimer', 'export'); + foreach ($listofpermtotest as $permtotest) { + if (empty($this->rights->user->user->$permtotest)) { + $this->rights->user->user->$permtotest = 1; + $this->nb_rights++; + } + } + if (empty($this->rights->user->self)) { + $this->rights->user->self = new stdClass(); + } + $listofpermtotest = array('creer', 'password'); + foreach ($listofpermtotest as $permtotest) { + if (empty($this->rights->user->self->$permtotest)) { + $this->rights->user->self->$permtotest = 1; + $this->nb_rights++; + } + } + // Add test on advanced permissions + if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS)) { + if (empty($this->rights->user->user_advance)) { + $this->rights->user->user_advance = new stdClass(); + } + $listofpermtotest = array('readperms', 'write'); + foreach ($listofpermtotest as $permtotest) { + if (empty($this->rights->user->user_advance->$permtotest)) { + $this->rights->user->user_advance->$permtotest = 1; + $this->nb_rights++; + } + } + if (empty($this->rights->user->self_advance)) { + $this->rights->user->self_advance = new stdClass(); + } + $listofpermtotest = array('readperms', 'writeperms'); + foreach ($listofpermtotest as $permtotest) { + if (empty($this->rights->user->self_advance->$permtotest)) { + $this->rights->user->self_advance->$permtotest = 1; + $this->nb_rights++; + } + } + if (empty($this->rights->user->group_advance)) { + $this->rights->user->group_advance = new stdClass(); + } + $listofpermtotest = array('read', 'readperms', 'write', 'delete'); + foreach ($listofpermtotest as $permtotest) { + if (empty($this->rights->user) || empty($this->rights->user->group_advance->$permtotest)) { + $this->rights->user->group_advance->$permtotest = 1; + $this->nb_rights++; + } + } + } + } + // For backward compatibility if (isset($this->rights->propale) && !isset($this->rights->propal)) { $this->rights->propal = $this->rights->propale; diff --git a/htdocs/user/group/card.php b/htdocs/user/group/card.php index f698d1b5a07..b185ea4ce42 100644 --- a/htdocs/user/group/card.php +++ b/htdocs/user/group/card.php @@ -454,7 +454,7 @@ if ($action == 'create') { print ' | '.$useringroup->getLibStatut(5).' | '; print ''; if (!empty($user->admin)) { - print ''; + print ''; print img_picto($langs->trans("RemoveFromGroup"), 'unlink'); print ''; } else { @@ -483,7 +483,7 @@ if ($action == 'create') { $genallowed = $user->rights->user->user->creer; $delallowed = $user->rights->user->user->supprimer; - $somethingshown = $formfile->showdocuments('usergroup', $filename, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', 0, '', $soc->default_lang); + $somethingshown = $formfile->showdocuments('usergroup', $filename, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', 0, '', $mysoc->default_lang); // Show links to link elements $linktoelem = $form->showLinkToObjectBlock($object, null, null); diff --git a/htdocs/user/group/perms.php b/htdocs/user/group/perms.php index 478e345f2e3..34e181469cc 100644 --- a/htdocs/user/group/perms.php +++ b/htdocs/user/group/perms.php @@ -328,7 +328,7 @@ if ($object->id > 0) { //print img_object('', $picto, 'class="inline-block pictoobjectwidth"').' '.$objMod->getName(); print ' | '; - if (is_array($permsgroupbyentity[$entity])) { + if (!empty($permsgroupbyentity[$entity]) && is_array($permsgroupbyentity[$entity])) { if (in_array($obj->id, $permsgroupbyentity[$entity])) { // Own permission by group if ($caneditperms) {