Automated merge from 22.0 to 23.0 by tool pullmerge.sh

This commit is contained in:
Laurent Destailleur 2026-06-10 12:33:21 +02:00
commit c1291a2e31
18 changed files with 96 additions and 25 deletions

View file

@ -63,9 +63,8 @@ exit_code=0
grep --no-filename -r -oP -- '^([^#=]+?)(?=\s*=.*)' "${LANG_DIR}" \
| grep -x -v -F -f "${EXCLUDE_KEYS_SRC_FILE}" \
| sort > "${AVAILABLE_FILE_NODEDUP}"
sort -u \
< "${AVAILABLE_FILE_NODEDUP}" \
> "${AVAILABLE_FILE}"
# sort -u is buggued in ubutnu 26.04. The line StatusProspect-1 and StatusProspect1 are treated as similar lines Replaced with "uniq".
uniq < "${AVAILABLE_FILE_NODEDUP}" > "${AVAILABLE_FILE}"
# Combine strings found in sources with pre-determined dynamic string values.

View file

@ -375,7 +375,19 @@ if (empty($reshook)) {
foreach ($toselect as $id) {
$res = $tmpmember->fetch($id);
if ($res > 0) {
$result = $tmpmember->subscription($now, (float) $amount, 0, '', '', '', '', '', $datesubend);
// Mirror the single subscription form (adherents/subscription.php): if no
// global EOM/EOY override was set, compute the end date from the member's
// AdherentType duration_value/duration_unit so a 6-month type does not
// silently fall back to the 1-year default in Adherent::subscription()
$datesubendthismember = $datesubend;
if (!$datesubendthismember && $tmpmember->typeid > 0) {
if ($adht->fetch($tmpmember->typeid) > 0) {
$delay = !empty($adht->duration_value) ? $adht->duration_value : 1;
$delayunit = !empty($adht->duration_unit) ? $adht->duration_unit : 'y';
$datesubendthismember = dol_time_plus_duree(dol_time_plus_duree($now, $delay, $delayunit), -1, 'd');
}
}
$result = $tmpmember->subscription($now, (float) $amount, 0, '', '', '', '', '', $datesubendthismember);
if ($result < 0) {
$error++;
} else {

View file

@ -30,6 +30,7 @@ require_once DOL_DOCUMENT_ROOT.'/product/class/api_products.class.php';
require_once DOL_DOCUMENT_ROOT.'/societe/class/api_contacts.class.php';
require_once DOL_DOCUMENT_ROOT.'/societe/class/api_thirdparties.class.php';
require_once DOL_DOCUMENT_ROOT.'/projet/class/api_projects.class.php';
require_once DOL_DOCUMENT_ROOT.'/ticket/class/api_tickets.class.php';
/**
* API class for categories
@ -842,6 +843,8 @@ class Categories extends DolibarrApi
$objects_api = new Contacts();
} elseif ($type == 'project') {
$objects_api = new Projects();
} elseif ($type == 'ticket') {
$objects_api = new Tickets();
}
if (is_object($objects_api)) {

View file

@ -1283,6 +1283,12 @@ class Facture extends CommonInvoice
$this->db->begin();
$object->fetch($fromid);
// fetch() clears $object->thirdparty, but the subsequent $object->create() path
// reaches addLine() -> getLocalTaxesFromRate() -> get_localtax() and uses the
// buyer thirdparty to decide whether IRPF/localtax2 applies. Without this fetch,
// get_localtax() sees $thirdparty_buyer = null, returns 0 and the cloned lines
// lose their localtax2_tx (see issue #29052).
$object->fetch_thirdparty();
// Load source object
$objFrom = clone $object;

View file

@ -2254,8 +2254,8 @@ class BonPrelevement extends CommonObject
$pre = substr(dol_string_nospecial(dol_string_unaccent($langs->transnoentitiesnoconv('RUM'))), 0, 3); // Must always be on 3 char ('RUM' or 'UMR'. This is a protection against bad translation)
// 3 char + '-' + 12 + '-' + id + '-' + code Must be lower than 32.
return $pre . '-' . dol_print_date($row_datec, 'dayhourlogsmall') . '-' . dol_trunc($row_drum . ($row_code_client ? '-' . $row_code_client : ''), 13, 'right', 'UTF-8', 1);
// 3 char + '-' + 10 (yymmddHHMM) + '-' + id + '-' + code. Must be under 32 (SEPA char limit for MndtId is however 35).
return $pre . '-' . dol_print_date($row_datec, 'dayhourlogsmall') . '-' . dol_trunc($row_drum . ($row_code_client ? '-' . $row_code_client : ''), 17, 'right', 'UTF-8', 1);
}

View file

@ -324,19 +324,23 @@ if (!$error && $massaction == 'confirm_presend') {
foreach ($listofobjectref[$thirdpartyid] as $objectid => $objectobj) {
//var_dump($thirdpartyid.' - '.$objectid.' - '.$objectobj->statut);
if ($objectclass == 'Propal' && $objectobj->status == Propal::STATUS_DRAFT) {
// Honour the same per-object hidden constants used by the "Send by email"
// button on the card pages (comm/propal/card.php, commande/card.php,
// compta/facture/card.php) so the behaviour is consistent between the
// single-object action and the mass action.
if ($objectclass == 'Propal' && $objectobj->status == Propal::STATUS_DRAFT && !getDolGlobalString('PROPOSAL_SENDBYEMAIL_FOR_ALL_STATUS')) {
$langs->load("errors");
$nbignored++;
$resaction .= '<div class="error">'.$langs->trans('ErrorOnlyProposalNotDraftCanBeSentInMassAction', $objectobj->ref).'</div><br>';
continue; // Payment done or started or canceled
}
if ($objectclass == 'Commande' && $objectobj->status == Commande::STATUS_DRAFT) {
if ($objectclass == 'Commande' && $objectobj->status == Commande::STATUS_DRAFT && !getDolGlobalString('COMMANDE_SENDBYEMAIL_FOR_ALL_STATUS')) {
$langs->load("errors");
$nbignored++;
$resaction .= '<div class="error">'.$langs->trans('ErrorOnlyOrderNotDraftCanBeSentInMassAction', $objectobj->ref).'</div><br>';
continue;
}
if ($objectclass == 'Facture' && $objectobj->status == Facture::STATUS_DRAFT) {
if ($objectclass == 'Facture' && $objectobj->status == Facture::STATUS_DRAFT && !getDolGlobalString('FACTURE_SENDBYEMAIL_FOR_ALL_STATUS')) {
$langs->load("errors");
$nbignored++;
$resaction .= '<div class="error">'.$langs->trans('ErrorOnlyInvoiceValidatedCanBeSentInMassAction', $objectobj->ref).'</div><br>';

View file

@ -1797,9 +1797,9 @@ class ExtraFields
$InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
}
// Fix better compatibility with some old extrafield syntax filter "(field=123)"
// Fix better compatibility with some old extrafield syntax filter "(field_name=123)"
$reg = array();
if (preg_match('/^\(?([a-z0-9]+)([=<>]+)(\d+)\)?$/i', $InfoFieldList[4], $reg)) {
if (preg_match('/^\(?([a-z0-9_]+)([=<>]+)(\d+)\)?$/i', $InfoFieldList[4], $reg)) {
$InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
}

View file

@ -44,7 +44,7 @@ function holiday_prepare_head($object)
// Attachments
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php';
$upload_dir = $conf->holiday->multidir_output[$object->entity ?? $conf->entity].'/'.dol_sanitizeFileName($object->ref);
$upload_dir = $conf->holiday->multidir_output[$object->entity ?? $conf->entity].'/'.get_exdir(0, 0, 0, 1, $object, 'holiday');
$nbFiles = count(dol_dir_list($upload_dir, 'files', 0, '', '(\.meta|_preview.*\.png)$'));
$nbLinks = Link::count($db, $object->element, $object->id);
$head[$h][0] = dolBuildUrl(DOL_URL_ROOT.'/holiday/document.php', ['id' => $object->id]);

View file

@ -287,7 +287,7 @@ function getOnlinePaymentUrl($mode, $type, $ref = '', $amount = 0, $freetag = 'y
}
if ($type == 'free') {
$out = $urltouse.'/public/payment/newpayment.php?amount='.($mode ? '<span style="color: #666666">' : '').price2num($amount, 'MT').($mode ? '</span>' : '').'&tag='.($mode ? '<span style="color: #666666">' : '').$freetag.($mode ? '</span>' : '');
$out = $urltouse.'/public/payment/newpayment.php?amount='.($mode ? '<span style="color: #666666">' : '').price2num($amount, 'MT').($mode ? '</span>' : '').'&tag='.($mode ? '<span style="color: #666666">' : '').urlencode($freetag).($mode ? '</span>' : '');
if (getDolGlobalString('PAYMENT_SECURITY_TOKEN')) {
if (!getDolGlobalString('PAYMENT_SECURITY_TOKEN_UNIQUE')) {
$out .= '&securekey='.urlencode(getDolGlobalString('PAYMENT_SECURITY_TOKEN'));

View file

@ -195,14 +195,14 @@ class pdf_standard_evaluation extends ModelePDFEvaluation
if ($conf->hrm->dir_output) {
// Definition of $dir and $file
$entity = isset($object->entity) ? $object->entity : 1;
$basedir = empty($conf->hrm->multidir_output[$entity]) ? $conf->hrm->dir_output : $conf->hrm->multidir_output[$entity];
if ($object->specimen) {
//$dir = $conf->hrm->dir_output;
$dir = $conf->hrm->multidir_output[isset($object->entity) ? $object->entity : 1].'/evaluation';
$dir = $basedir.'/evaluation';
$file = $dir."/SPECIMEN.pdf";
} else {
$objectref = dol_sanitizeFileName($object->ref);
//$dir = $conf->hrm->dir_output."/".$objectref;
$dir = $conf->hrm->multidir_output[isset($object->entity) ? $object->entity : 1].'/evaluation'."/".$objectref;
$dir = $basedir.'/evaluation'."/".$objectref;
$file = $dir."/".$objectref.".pdf";
}

View file

@ -351,7 +351,7 @@ if ($massaction == 'presend') {
// Tableau des parameters complementaires du post
$formmail->param['action'] = $action;
$formmail->param['models'] = $modelmail; // the filter to know which kind of template emails to show. 'none' means no template suggested.
$formmail->param['models_id'] = GETPOSTINT('modelmailselected') ? GETPOSTINT('modelmailselected') : '-1';
$formmail->param['models_id'] = GETPOSTINT('modelmailselected') ? GETPOSTINT('modelmailselected') : '';
$formmail->param['id'] = implode(',', $arrayofselected);
// $formmail->param['returnurl']=$_SERVER["PHP_SELF"].'?id='.$object->id;
if (getDolGlobalString('MAILING_LIMIT_SENDBYWEB') && count($listofselectedrecipientobjid) > $conf->global->MAILING_LIMIT_SENDBYWEB) {

View file

@ -537,6 +537,7 @@ if (empty($reshook)) {
}
}
// option to notify the validator if the balance is less than the request
// (only for leave types that actually use the balance counter)
if (!getDolGlobalString('HOLIDAY_HIDE_APPROVER_ABOUT_NEGATIVE_BALANCE')) {

View file

@ -377,6 +377,7 @@ ErrorMaxDecimalsShownTooLowComparedToUnitOrTotal=Value for 'Max. decimals for pr
ErrorOnlyDraftStatusCanBeDeletedInMassAction=Only elements in draft status can be deleted in mass action
ErrorLDAPFunctionsAreDisabledOnThisPHP=LDAP functions are disabled on this PHP.
ErrorFailedToGetTokenFromClientIdAndSecret=Failed to get a %s token from client ID and client secret
ErrorTooManyLinksIntoHTMLString=The HTML content contains too many links or images. Reduce the number or raise MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT.
# Warnings
WarningParamUploadMaxFileSizeHigherThanPostMaxSize=Your PHP parameter upload_max_filesize (%s) is higher than PHP parameter post_max_size (%s). This is not a consistent setup.

View file

@ -53,6 +53,7 @@ require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT . '/core/class/html.formother.class.php';
require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
require_once DOL_DOCUMENT_ROOT . '/core/class/html.formintervention.class.php';
require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
// Load translation files required by the page
$langsLoad = array('projects', 'bills', 'orders', 'companies');
@ -575,8 +576,11 @@ if ($action == 'confirm_generateinvoice' && $user->hasRight('facture', 'creer'))
$idprodline = $fk_product;
}
// Add lines
$lineid = $tmpinvoice->addline($langs->trans("TimeSpentForInvoice").' - '.$username . ' : ' . $qtyhourtext, $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), (float) $remiseproject);
// Add lines. Pass type=1 (service) explicitly so the invoice line is tagged
// as a service even when no product is bound to the time entry. Otherwise
// the default $type=0 leaks through and the PDF labels the operation as
// "Delivery of goods" instead of "Provision of services" (issue #34571).
$lineid = $tmpinvoice->addline($langs->trans("TimeSpentForInvoice").' - '.$username . ' : ' . $qtyhourtext, $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), (float) $remiseproject, '', '', 0, 0, '', 'HT', 0, Product::TYPE_SERVICE);
if ($lineid < 0) {
$error++;
setEventMessages(null, $tmpinvoice->errors, 'errors');
@ -675,7 +679,11 @@ if ($action == 'confirm_generateinvoice' && $user->hasRight('facture', 'creer'))
}
$idprodline = $value['fk_product'];
}
$lineid = $tmpinvoice->addline($value['note'], $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), (float) $remiseproject);
// Pass type=1 (service) explicitly so the invoice line is tagged as a service
// even when no product is bound to the time entry. Otherwise the default
// $type=0 leaks through and the PDF labels the operation as
// "Delivery of goods" instead of "Provision of services" (issue #34571).
$lineid = $tmpinvoice->addline($value['note'], $pu_htline, round($qtyhour / $prodDurationHours, 2), $txtvaline, $localtax1line, $localtax2line, ($idprodline > 0 ? $idprodline : 0), (float) $remiseproject, '', '', 0, 0, '', 'HT', 0, Product::TYPE_SERVICE);
if ($lineid < 0) {
$error++;
setEventMessages(null, $tmpinvoice->errors, 'errors');

View file

@ -413,7 +413,9 @@ class Ticket extends CommonObject
if (isset($this->message)) {
$this->message = trim($this->message);
if (dol_strlen($this->message) > 65000) {
$this->errors[] = 'ErrorFieldTooLong';
global $langs;
$langs->loadLangs(array('errors', 'ticket'));
$this->errors[] = $langs->trans('ErrorFieldTooLong', $langs->transnoentitiesnoconv('InitialMessage'));
dol_syslog(get_class($this).'::create error -1 message too long', LOG_ERR);
$result = -1;
}
@ -1048,7 +1050,9 @@ class Ticket extends CommonObject
if (isset($this->message)) {
$this->message = trim($this->message);
if (dol_strlen($this->message) > 65000) {
$this->errors[] = 'ErrorFieldTooLong';
global $langs;
$langs->loadLangs(array('errors', 'ticket'));
$this->errors[] = $langs->trans('ErrorFieldTooLong', $langs->transnoentitiesnoconv('InitialMessage'));
dol_syslog(get_class($this).'::update error -1 message too long', LOG_ERR);
return -1;
}

View file

@ -318,6 +318,16 @@ if (empty($reshook)) {
$object->email = preg_replace('/\s+/', '', GETPOST("email", 'alphanohtml'));
$object->job = GETPOST("job", 'alphanohtml');
$object->signature = GETPOST("signature", 'restricthtml');
// restricthtml may swap the value with the literal 'ErrorTooManyLinksIntoHTMLString'
// when the html exceeds MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT (see issue #27987).
// Refuse the save so the literal does not end up persisted and later sent as an
// email body to customers.
if ($object->signature === 'ErrorTooManyLinksIntoHTMLString') {
$error++;
$langs->load("errors");
setEventMessages($langs->trans('ErrorTooManyLinksIntoHTMLString'), null, 'errors');
$action = 'create';
}
$object->accountancy_code = GETPOST("accountancy_code", 'alphanohtml');
$object->note_public = GETPOST("note_public", 'restricthtml');
$object->note_private = GETPOST("note_private", 'restricthtml');
@ -472,7 +482,7 @@ if (empty($reshook)) {
$object->pass = GETPOST("password", 'password');
}
if ($permissiontoeditpasswordandsee || $user->hasRight("api", "apikey", "generate")) {
$object->api_key = (GETPOSTISSET("api_key", 'alphanohtml')) ? GETPOST("api_key", 'alphanohtml') : $object->api_key;
$object->api_key = (GETPOSTISSET("api_key") ? GETPOST("api_key", 'alphanohtml') : $object->api_key);
}
if (!empty($user->admin) && $user->id != $id) {
// admin flag can only be set/unset by an admin user and not four ourself
@ -503,6 +513,16 @@ if (empty($reshook)) {
$object->email = preg_replace('/\s+/', '', GETPOST("email", 'alphanohtml'));
$object->job = GETPOST("job", 'alphanohtml');
$object->signature = GETPOST("signature", 'restricthtml');
// restricthtml may swap the value with the literal 'ErrorTooManyLinksIntoHTMLString'
// when the html exceeds MAIN_SECURITY_MAX_IMG_IN_HTML_CONTENT (see issue #27987).
// Refuse the save so the literal does not end up persisted and later sent as an
// email body to customers.
if ($object->signature === 'ErrorTooManyLinksIntoHTMLString') {
$error++;
$langs->load("errors");
setEventMessages($langs->trans('ErrorTooManyLinksIntoHTMLString'), null, 'errors');
$action = 'edit';
}
$object->accountancy_code = GETPOST("accountancy_code", 'alphanohtml');
$object->openid = GETPOST("openid", 'alphanohtml');
$object->fk_user = GETPOSTINT("fk_user") > 0 ? GETPOSTINT("fk_user") : 0;

View file

@ -884,6 +884,12 @@ class Users extends DolibarrApi
throw new RestException(404, 'Usergroup not found');
}
if ($load_members > 0 && is_array($group_static->members) && count($group_static->members) > 0) {
foreach ($group_static->members as &$member) {
$member = $this->_cleanObjectDatas($member);
}
}
return $this->_cleanUserGroup($group_static);
}

View file

@ -430,7 +430,14 @@ class ProductCombination
$parent = new Product($this->db);
$parent->fetch($this->fk_product_parent);
$this->updateProperties($parent, $user);
// Propagate failure of updateProperties (otherwise an update where the variant
// percentage drives the new price below the parent product's price_min returns
// success but leaves the variant product at the price=0 it had right after
// createProductCombination, see issue #32372).
$result = $this->updateProperties($parent, $user);
if ($result < 0) {
return $result;
}
return 1;
}