Merge branch '16.0' of git@github.com:Dolibarr/dolibarr.git into develop

This commit is contained in:
Laurent Destailleur 2022-09-12 10:32:50 +02:00
commit 8771dc197b
16 changed files with 107 additions and 62 deletions

View File

@ -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

View File

@ -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();

View File

@ -2385,7 +2385,7 @@ if ($resql) {
// Note public
if (!empty($arrayfields['f.note_public']['checked'])) {
print '<td class="center">';
print dol_escape_htmltag($obj->note_public);
print dol_string_nohtmltag($obj->note_public);
print '</td>';
if (!$i) {
$totalarray['nbfield']++;
@ -2394,7 +2394,7 @@ if ($resql) {
// Note private
if (!empty($arrayfields['f.note_private']['checked'])) {
print '<td class="center">';
print dol_escape_htmltag($obj->note_private);
print dol_string_nohtmltag($obj->note_private);
print '</td>';
if (!$i) {
$totalarray['nbfield']++;

View File

@ -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';

View File

@ -838,6 +838,7 @@ class FormFile
// Show file name with link to download
$out .= '<td class="minwidth200 tdoverflowmax300">';
$out .= $this->showPreview($file, $modulepart, $relativepath, 0, $param, 'paddingright')."\n";
$out .= '<a class="documentdownload paddingright" href="'.$documenturl.'?modulepart='.$modulepart.'&amp;file='.urlencode($relativepath).($param ? '&'.$param : '').'"';
$mime = dol_mimetype($relativepath, '', 0);
@ -846,9 +847,8 @@ class FormFile
}
$out .= '>';
$out .= img_mime($file["name"], $langs->trans("File").': '.$file["name"]);
$out .= dol_trunc($file["name"], 40);
$out .= '</a>'."\n";
$out .= $this->showPreview($file, $modulepart, $relativepath, 0, $param);
$out .= dol_trunc($file["name"], 150);
$out .= '</a>';
$out .= '</td>';
// Show file size
@ -1300,6 +1300,11 @@ class FormFile
// File name
print '<td class="minwith200 tdoverflowmax500">';
// 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 '<a class="paddingright valignmiddle" href="'.DOL_URL_ROOT.'/document.php?modulepart='.$modulepart;
@ -1331,10 +1336,6 @@ class FormFile
print dol_escape_htmltag(dol_trunc($filenametoshow, 200));
print '</a>';
}
// Preview link
if (!$editline) {
print $this->showPreview($file, $modulepart, $filepath, 0, 'entity='.(!empty($object->entity) ? $object->entity : $conf->entity));
}
print "</td>\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 .= '<a class="pictopreview '.$urladvancedpreview['css'].'" href="'.$urladvancedpreview['url'].'"'.(empty($urladvancedpreview['mime']) ? '' : ' mime="'.$urladvancedpreview['mime'].'"').' '.(empty($urladvancedpreview['target']) ? '' : ' target="'.$urladvancedpreview['target'].'"').'>';
$out .= '<a class="pictopreview '.$urladvancedpreview['css'].(!empty($moreclass)?' '.$moreclass:'').'" href="'.$urladvancedpreview['url'].'"'.(empty($urladvancedpreview['mime']) ? '' : ' mime="'.$urladvancedpreview['mime'].'"').' '.(empty($urladvancedpreview['target']) ? '' : ' target="'.$urladvancedpreview['target'].'"').'>';
//$out.= '<a class="pictopreview">';
if (empty($ruleforpicto)) {
//$out.= img_picto($langs->trans('Preview').' '.$file['name'], 'detail');

View File

@ -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;

View File

@ -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("<b>"=>'', '</b>'=>''));

View File

@ -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';
}
/**

View File

@ -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)."<br>\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];

View File

@ -802,7 +802,7 @@ while ($i < $imaxinloop) {
// Note public
if (!empty($arrayfields['f.note_public']['checked'])) {
print '<td class="center">';
print dol_escape_htmltag($obj->note_public);
print dol_string_nohtmltag($obj->note_public);
print '</td>';
if (!$i) {
$totalarray['nbfield']++;
@ -811,7 +811,7 @@ while ($i < $imaxinloop) {
// Note private
if (!empty($arrayfields['f.note_private']['checked'])) {
print '<td class="center">';
print dol_escape_htmltag($obj->note_private);
print dol_string_nohtmltag($obj->note_private);
print '</td>';
if (!$i) {
$totalarray['nbfield']++;

View File

@ -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"];

View File

@ -448,7 +448,10 @@ if (!empty($force_install_noedit)) {
<input type="checkbox"
id="db_create_database"
name="db_create_database"
<?php if ($force_install_createdatabase) {
<?php
$checked = 0;
if ($force_install_createdatabase) {
$checked = 1;
print ' checked';
} ?>
<?php if ($force_install_noedit == 2 && $force_install_createdatabase !== null) {
@ -456,7 +459,8 @@ if (!empty($force_install_noedit)) {
} ?>
>
</td>
<td class="comment"><?php echo $langs->trans("CheckToCreateDatabase"); ?>
<td class="comment">
<?php echo $langs->trans("CheckToCreateDatabase"); ?>
</td>
</tr>
@ -503,7 +507,10 @@ if (!empty($force_install_noedit)) {
<input type="checkbox"
id="db_create_user"
name="db_create_user"
<?php if (!empty($force_install_createuser)) {
<?php
$checked = 0;
if (!empty($force_install_createuser)) {
$checked = 1;
print ' checked';
} ?>
<?php if ($force_install_noedit == 2 && $force_install_createuser !== null) {
@ -511,7 +518,8 @@ if (!empty($force_install_noedit)) {
} ?>
>
</td>
<td class="comment"><?php echo $langs->trans("CheckToCreateUser"); ?>
<td class="comment">
<?php echo $langs->trans("CheckToCreateUser"); ?>
</td>
</tr>
@ -613,30 +621,13 @@ jQuery(document).ready(function() {
});
function init_needroot()
{
console.log("init_needroot force_install_noedit=<?php echo $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();
<?php
if (empty($force_install_noedit)) { ?>
jQuery(".needroot").removeAttr('disabled');
<?php } ?>
}
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();
});
<?php if ($force_install_noedit == 2 && empty($force_install_databasepass)) { ?>
@ -644,6 +635,27 @@ jQuery(document).ready(function() {
<?php } ?>
});
function init_needroot()
{
console.log("init_needroot force_install_noedit=<?php echo $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();
<?php
if (empty($force_install_noedit)) { ?>
jQuery(".needroot").removeAttr('disabled');
<?php } ?>
}
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('<?php echo dol_escape_js($langs->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('<?php echo dol_escape_js($langs->transnoentities("YouAskToCreateDatabaseUserSoRootRequired")); ?>');
init_needroot();
}
return ok;

View File

@ -530,7 +530,7 @@ function pFooter($nonext = 0, $setuplang = '', $jscheckfunction = '', $withpleas
print '<input type="hidden" name="selectlang" value="'.dol_escape_htmltag($setuplang).'">';
}
print '</form>'."\n";
print '</form><br>'."\n";
// If there is some logs in buffer to show
if (isset($conf->logbuffer) && count($conf->logbuffer)) {

View File

@ -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
NodoUpgradeAfterFiles=No action requested by external modules after upgrade of files or directories

View File

@ -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"));

View File

@ -449,7 +449,7 @@ if ($action == 'create') {
print '<tr class="oddeven">';
print '<td class="tdoverflowmax150">';
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');