diff --git a/ChangeLog b/ChangeLog index 7a87f3fbc2d..cbd76fe7c37 100644 --- a/ChangeLog +++ b/ChangeLog @@ -29,8 +29,8 @@ For users: --------------- NEW: PHP 8.1 compatibility: - Warning: Application works correctly with PHP8 and 8.1 but you may experience a lot of PHP warning into the PHP server log files (depending - on the PHP setup). Removal of all PHP warnings on server side is planned for v17. + Warning!! Application works correctly with PHP8 and 8.1 but you will experience a lot of PHP warnings into the PHP server + log files (depending on your PHP setup). Removal of all PHP warnings on server side is planned for v17. NEW: Support for recurring purchase invoices. NEW: #20292 Include German public holidays NEW: Can show ZATCA QR-Code on PDFs diff --git a/htdocs/comm/mailing/card.php b/htdocs/comm/mailing/card.php index 81e36c006ca..e59389a87b7 100644 --- a/htdocs/comm/mailing/card.php +++ b/htdocs/comm/mailing/card.php @@ -477,7 +477,7 @@ if (empty($reshook)) { } } - $trackid = 'emailingtest'; + $trackid = 'emailing-test'; $mailfile = new CMailFile($tmpsujet, $object->sendto, $object->email_from, $tmpbody, $arr_file, $arr_mime, $arr_name, '', '', 0, $msgishtml, $object->email_errorsto, $arr_css, $trackid, '', 'emailing'); $result = $mailfile->sendfile(); diff --git a/htdocs/compta/facture/list.php b/htdocs/compta/facture/list.php index a8d54909065..56b8943f5ad 100644 --- a/htdocs/compta/facture/list.php +++ b/htdocs/compta/facture/list.php @@ -2385,7 +2385,7 @@ if ($resql) { // Note public if (!empty($arrayfields['f.note_public']['checked'])) { print ''; - print dol_escape_htmltag($obj->note_public); + print dol_string_nohtmltag($obj->note_public); print ''; if (!$i) { $totalarray['nbfield']++; @@ -2394,7 +2394,7 @@ if ($resql) { // Note private if (!empty($arrayfields['f.note_private']['checked'])) { print ''; - print dol_escape_htmltag($obj->note_private); + print dol_string_nohtmltag($obj->note_private); print ''; if (!$i) { $totalarray['nbfield']++; diff --git a/htdocs/conf/conf.php.example b/htdocs/conf/conf.php.example index b58ac33707e..4090130bc36 100644 --- a/htdocs/conf/conf.php.example +++ b/htdocs/conf/conf.php.example @@ -162,8 +162,9 @@ $dolibarr_main_db_readonly=0; // dolibarr_main_instance_unique_id // An secret ID that is unique for each installation. -// This value is also visible and never propagated outside of Dolibarr, so it can be used as a salt / key for some encryption. -// To propagate a unique key, you propagate the value concatenated with a string with a hash function. Example: md5('dolibarr'+dolibarr_main_instance_unique_id) +// This value is also visible and never propagated outside of Dolibarr, so it can be used as a salt / key for some encryption (For example to get +// a unique hashed key, application will hash the value concatenated with a string. Example: md5('dolibarr'+dolibarr_main_instance_unique_id) +// WARNING: Changing this value will also make some sensitive values encrypted in database wrong. // Default value: randomly defined during installation // Examples: // $dolibarr_main_instance_unique_id='84b5bc91f83b56e458db71e0adac2b62'; diff --git a/htdocs/core/class/html.formfile.class.php b/htdocs/core/class/html.formfile.class.php index 05489c3dc50..ba9ebfd81c4 100644 --- a/htdocs/core/class/html.formfile.class.php +++ b/htdocs/core/class/html.formfile.class.php @@ -838,6 +838,7 @@ class FormFile // Show file name with link to download $out .= ''; + $out .= $this->showPreview($file, $modulepart, $relativepath, 0, $param, 'paddingright')."\n"; $out .= 'trans("File").': '.$file["name"]); - $out .= dol_trunc($file["name"], 40); - $out .= ''."\n"; - $out .= $this->showPreview($file, $modulepart, $relativepath, 0, $param); + $out .= dol_trunc($file["name"], 150); + $out .= ''; $out .= ''; // Show file size @@ -1300,6 +1300,11 @@ class FormFile // File name print ''; + // Preview link + if (!$editline) { + print $this->showPreview($file, $modulepart, $filepath, 0, '&entity='.(!empty($object->entity) ? $object->entity : $conf->entity), 'paddingright') . "\n"; + } + // Show file name with link to download //print "XX".$file['name']; //$file['name'] must be utf8 print '\n"; @@ -2072,9 +2073,10 @@ class FormFile * @param string $relativepath Relative path of docs * @param integer $ruleforpicto Rule for picto: 0=Use the generic preview picto, 1=Use the picto of mime type of file). Use a negative value to show a generic picto even if preview not available. * @param string $param More param on http links + * @param string $moreclass Add more class to class style * @return string $out Output string with HTML */ - public function showPreview($file, $modulepart, $relativepath, $ruleforpicto = 0, $param = '') + public function showPreview($file, $modulepart, $relativepath, $ruleforpicto = 0, $param = '', $moreclass = '') { global $langs, $conf; @@ -2082,7 +2084,7 @@ class FormFile if ($conf->browser->layout != 'phone' && !empty($conf->use_javascript_ajax)) { $urladvancedpreview = getAdvancedPreviewUrl($modulepart, $relativepath, 1, $param); // Return if a file is qualified for preview. if (count($urladvancedpreview)) { - $out .= ''; + $out .= ''; //$out.= ''; if (empty($ruleforpicto)) { //$out.= img_picto($langs->trans('Preview').' '.$file['name'], 'detail'); diff --git a/htdocs/core/class/notify.class.php b/htdocs/core/class/notify.class.php index 3f26a98c728..5e12a30ba0e 100644 --- a/htdocs/core/class/notify.class.php +++ b/htdocs/core/class/notify.class.php @@ -456,7 +456,7 @@ class Notify $notifcodedefid = $obj->adid; $trackid = ''; if ($obj->type_target == 'tocontactid') { - $trackid = 'con'.$obj->cid; + $trackid = 'ctc'.$obj->cid; } if ($obj->type_target == 'touserid') { $trackid = 'use'.$obj->cid; diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index a4ee4e5b802..7b3aa47af17 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -1456,9 +1456,9 @@ function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapeta // escape quotes and backslashes, newlines, etc. if ($escapeonlyhtmltags) { - $tmp = htmlspecialchars_decode($stringtoescape, ENT_COMPAT); + $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT); } else { - $tmp = html_entity_decode($stringtoescape, ENT_COMPAT, 'UTF-8'); + $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8'); } if (!$keepb) { $tmp = strtr($tmp, array(""=>'', ''=>'')); diff --git a/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php b/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php index 335024554fc..a8932934bd1 100644 --- a/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php +++ b/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php @@ -52,7 +52,7 @@ class InterfaceWebhookTriggers extends DolibarrTriggers $this->description = "Webhook triggers."; // 'development', 'experimental', 'dolibarr' or version $this->version = 'development'; - $this->picto = 'webhook@webhook'; + $this->picto = 'webhook'; } /** diff --git a/htdocs/emailcollector/class/emailcollector.class.php b/htdocs/emailcollector/class/emailcollector.class.php index 006376cfd55..47d62197a70 100644 --- a/htdocs/emailcollector/class/emailcollector.class.php +++ b/htdocs/emailcollector/class/emailcollector.class.php @@ -1478,22 +1478,38 @@ class EmailCollector extends CommonObject $headers['Subject'] = $this->decodeSMTPSubject($headers['Subject']); + $emailto = $this->decodeSMTPSubject($overview[0]->to); + dol_syslog("** Process email ".$iforemailloop." References: ".$headers['References']." Subject: ".$headers['Subject']); + $trackidfoundintorecipienttype = ''; + $trackidfoundintorecipientid = 0; + $reg = array(); + // See also later list of all supported tags... + if (preg_match('/\+(thi|ctc|use|mem|sub|proj|tas|con|tic|job|pro|ord|inv|spro|sor|sin|leav|stockinv|job|surv|salary)([0-9]+)@/', $emailto, $reg)) { + $trackidfoundintorecipienttype = $reg[1]; + $trackidfoundintorecipientid = $reg[2]; + } elseif (preg_match('/\+emailing-(\w+)@/', $emailto, $reg)) { // Can be 'emailing-test' or 'emailing-IdMailing-IdRecipient' + $trackidfoundintorecipienttype = 'emailing'; + $trackidfoundintorecipientid = $reg[1]; + } + // If there is a filter on trackid if ($searchfilterdoltrackid > 0) { - if (empty($headers['References']) || !preg_match('/@'.preg_quote($host, '/').'/', $headers['References'])) { - $nbemailprocessed++; - dol_syslog(" Discarded - No header References found"); - continue; // Exclude email + if (empty($trackidfoundintorecipienttype)) { + if (empty($headers['References']) || !preg_match('/@'.preg_quote($host, '/').'/', $headers['References'])) { + $nbemailprocessed++; + dol_syslog(" Discarded - No suffix in email recipient and no Header References found matching signature of application so with a trackid"); + continue; // Exclude email + } } } if ($searchfilternodoltrackid > 0) { - if (!empty($headers['References']) && preg_match('/@'.preg_quote($host, '/').'/', $headers['References'])) { + if (!empty($trackidfoundintorecipienttype) || (!empty($headers['References']) && preg_match('/@'.preg_quote($host, '/').'/', $headers['References']))) { $nbemailprocessed++; - dol_syslog(" Discarded - Header References found and matching signature of application"); + dol_syslog(" Discarded - Suffix found into email or Header References found and matching signature of application so with a trackid"); continue; // Exclude email } } @@ -1705,13 +1721,19 @@ class EmailCollector extends CommonObject foreach ($arrayofreferences as $reference) { //print "Process mail ".$iforemailloop." email_msgid ".$msgid.", date ".dol_print_date($date, 'dayhour').", subject ".$subject.", reference ".dol_escape_htmltag($reference)."
\n"; - $resultsearchtrackid = preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote($host, '/').'/', $reference, $reg); - if (empty($resultsearchtrackid) && getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE')) { - $resultsearchtrackid = preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote(getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE'), '/').'/', $reference, $reg); + if (!empty($trackidfoundintorecipienttype)) { + $resultsearchtrackid = -1; + $reg[1] = $trackidfoundintorecipienttype; + $reg[2] = $trackidfoundintorecipientid; + } else { + $resultsearchtrackid = preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote($host, '/').'/', $reference, $reg); + if (empty($resultsearchtrackid) && getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE')) { + $resultsearchtrackid = preg_match('/dolibarr-([a-z]+)([0-9]+)@'.preg_quote(getDolGlobalString('EMAIL_ALTERNATIVE_HOST_SIGNATURE'), '/').'/', $reference, $reg); + } } - if ($resultsearchtrackid) { - // This is a Dolibarr reference of the server + if (!empty($resultsearchtrackid)) { + // We found a tracker (in recipient email or into a Reference matching the Dolibarr server) $trackid = $reg[1].$reg[2]; $objectid = $reg[2]; diff --git a/htdocs/fichinter/list.php b/htdocs/fichinter/list.php index 858ea2d3a4b..731442695d2 100644 --- a/htdocs/fichinter/list.php +++ b/htdocs/fichinter/list.php @@ -802,7 +802,7 @@ while ($i < $imaxinloop) { // Note public if (!empty($arrayfields['f.note_public']['checked'])) { print ''; - print dol_escape_htmltag($obj->note_public); + print dol_string_nohtmltag($obj->note_public); print ''; if (!$i) { $totalarray['nbfield']++; @@ -811,7 +811,7 @@ while ($i < $imaxinloop) { // Note private if (!empty($arrayfields['f.note_private']['checked'])) { print ''; - print dol_escape_htmltag($obj->note_private); + print dol_string_nohtmltag($obj->note_private); print ''; if (!$i) { $totalarray['nbfield']++; diff --git a/htdocs/fourn/class/fournisseur.product.class.php b/htdocs/fourn/class/fournisseur.product.class.php index 1fdc1816544..5524783e275 100644 --- a/htdocs/fourn/class/fournisseur.product.class.php +++ b/htdocs/fourn/class/fournisseur.product.class.php @@ -578,7 +578,7 @@ class ProductFournisseur extends Product $sql .= " pfp.supplier_reputation, pfp.fk_user, pfp.datec,"; $sql .= " pfp.multicurrency_price, pfp.multicurrency_unitprice, pfp.multicurrency_tx, pfp.fk_multicurrency, pfp.multicurrency_code,"; $sql .= " pfp.barcode, pfp.fk_barcode_type, pfp.packaging,"; - $sql .= " p.ref as product_ref"; + $sql .= " p.ref as product_ref, p.tosell as status, p.tobuy as status_buy"; $sql .= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp, ".MAIN_DB_PREFIX."product as p"; $sql .= " WHERE pfp.rowid = ".(int) $rowid; $sql .= " AND pfp.fk_product = p.rowid"; @@ -594,7 +594,8 @@ class ProductFournisseur extends Product $this->fk_product = $obj->fk_product; $this->product_id = $obj->fk_product; $this->product_ref = $obj->product_ref; - + $this->status = $obj->status; + $this->status_buy = $obj->status_buy; $this->fourn_id = $obj->fk_soc; $this->fourn_ref = $obj->ref_fourn; // deprecated $this->ref_supplier = $obj->ref_fourn; @@ -670,7 +671,7 @@ class ProductFournisseur extends Product // phpcs:enable global $conf; - $sql = "SELECT s.nom as supplier_name, s.rowid as fourn_id, p.ref as product_ref,"; + $sql = "SELECT s.nom as supplier_name, s.rowid as fourn_id, p.ref as product_ref, p.tosell as status, p.tobuy as status_buy, "; $sql .= " pfp.rowid as product_fourn_pri_id, pfp.entity, pfp.ref_fourn, pfp.desc_fourn, pfp.fk_product as product_fourn_id, pfp.fk_supplier_price_expression,"; $sql .= " pfp.price, pfp.quantity, pfp.unitprice, pfp.remise_percent, pfp.remise, pfp.tva_tx, pfp.fk_availability, pfp.charges, pfp.info_bits, pfp.delivery_time_days, pfp.supplier_reputation,"; $sql .= " pfp.multicurrency_price, pfp.multicurrency_unitprice, pfp.multicurrency_tx, pfp.fk_multicurrency, pfp.multicurrency_code, pfp.datec, pfp.tms,"; @@ -699,6 +700,8 @@ class ProductFournisseur extends Product $prodfourn->product_ref = $record["product_ref"]; $prodfourn->product_fourn_price_id = $record["product_fourn_pri_id"]; + $prodfourn->status = $record["status"]; + $prodfourn->status_buy = $record["status_buy"]; $prodfourn->product_fourn_id = $record["product_fourn_id"]; $prodfourn->product_fourn_entity = $record["entity"]; $prodfourn->ref_supplier = $record["ref_fourn"]; diff --git a/htdocs/install/fileconf.php b/htdocs/install/fileconf.php index 14f1ee2a1cd..f8dcdfe230b 100644 --- a/htdocs/install/fileconf.php +++ b/htdocs/install/fileconf.php @@ -448,7 +448,10 @@ if (!empty($force_install_noedit)) { > - trans("CheckToCreateDatabase"); ?> + + trans("CheckToCreateDatabase"); ?> @@ -503,7 +507,10 @@ if (!empty($force_install_noedit)) { > - trans("CheckToCreateUser"); ?> + + trans("CheckToCreateUser"); ?> @@ -613,30 +621,13 @@ jQuery(document).ready(function() { }); - function init_needroot() - { - console.log("init_needroot force_install_noedit="); - /*alert(jQuery("#db_create_database").prop("checked")); */ - if (jQuery("#db_create_database").is(":checked") || jQuery("#db_create_user").is(":checked")) - { - jQuery(".hideroot").show(); - - jQuery(".needroot").removeAttr('disabled'); - - } - else - { - jQuery(".hideroot").hide(); - jQuery(".needroot").prop('disabled', true); - } - } - init_needroot(); jQuery("#db_create_database").click(function() { + console.log("click on db_create_database"); init_needroot(); }); jQuery("#db_create_user").click(function() { + console.log("click on db_create_user"); init_needroot(); }); @@ -644,6 +635,27 @@ jQuery(document).ready(function() { }); +function init_needroot() +{ + console.log("init_needroot force_install_noedit="); + /*alert(jQuery("#db_create_database").prop("checked")); */ + if (jQuery("#db_create_database").is(":checked") || jQuery("#db_create_user").is(":checked")) + { + console.log("init_needroot show root section"); + jQuery(".hideroot").show(); + + jQuery(".needroot").removeAttr('disabled'); + + } + else + { + console.log("init_needroot hide root section"); + jQuery(".hideroot").hide(); + jQuery(".needroot").prop('disabled', true); + } +} + function checkDatabaseName(databasename) { if (databasename.match(/[;\.]/)) { return false; } return true; @@ -651,6 +663,8 @@ function checkDatabaseName(databasename) { function jscheckparam() { + console.log("Click on jscheckparam"); + ok=true; if (document.forminstall.main_dir.value == '') @@ -688,12 +702,14 @@ function jscheckparam() { ok=false; alert('transnoentities("YouAskToCreateDatabaseSoRootRequired")); ?>'); + init_needroot(); } // If create user asked else if (document.forminstall.db_create_user.checked == true && (document.forminstall.db_user_root.value == '')) { ok=false; alert('transnoentities("YouAskToCreateDatabaseUserSoRootRequired")); ?>'); + init_needroot(); } return ok; diff --git a/htdocs/install/inc.php b/htdocs/install/inc.php index a20c3489866..657e9841100 100644 --- a/htdocs/install/inc.php +++ b/htdocs/install/inc.php @@ -530,7 +530,7 @@ function pFooter($nonext = 0, $setuplang = '', $jscheckfunction = '', $withpleas print ''; } - print ''."\n"; + print '
'."\n"; // If there is some logs in buffer to show if (isset($conf->logbuffer) && count($conf->logbuffer)) { diff --git a/htdocs/langs/en_US/install.lang b/htdocs/langs/en_US/install.lang index 1b9eb767416..ad8217153be 100644 --- a/htdocs/langs/en_US/install.lang +++ b/htdocs/langs/en_US/install.lang @@ -213,4 +213,4 @@ ClickOnLinkOrRemoveManualy=If an upgrade is in progress, please wait. If not, cl Loaded=Loaded FunctionTest=Function test NodoUpgradeAfterDB=No action requested by external modules after upgrade of database -NodoUpgradeAfterFiles=No action requested by external modules after upgrade of files or directories \ No newline at end of file +NodoUpgradeAfterFiles=No action requested by external modules after upgrade of files or directories diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index 6d89923f20e..9969ea061e9 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -1445,6 +1445,7 @@ function top_httphead($contenttype = 'text/html', $forcenocache = 0) $contentsecuritypolicy = getDolGlobalString('MAIN_SECURITY_FORCECSP'); if (!is_object($hookmanager)) { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; $hookmanager = new HookManager($db); } $hookmanager->initHooks(array("main")); diff --git a/htdocs/user/group/card.php b/htdocs/user/group/card.php index e5ffc83d6eb..c17893e7eb4 100644 --- a/htdocs/user/group/card.php +++ b/htdocs/user/group/card.php @@ -449,7 +449,7 @@ if ($action == 'create') { print ''; print ''; print $useringroup->getNomUrl(-1, '', 0, 0, 24, 0, 'login'); - if ($useringroup->admin && !$useringroup->entity) { + if (isModEnabled('multicompany') && $useringroup->admin && empty($useringroup->entity)) { print img_picto($langs->trans("SuperAdministrator"), 'redstar'); } elseif ($useringroup->admin) { print img_picto($langs->trans("Administrator"), 'star');