From 61899bb8cd9b0482ef24d0e61087f810ae2ce59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Aliot?= Date: Wed, 15 Jul 2026 00:44:37 +0200 Subject: [PATCH] FIX #39113 CSV export: decode HTML entities in header labels (#39151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extrafield labels are stored HTML encoded in llx_extrafields.label because they are saved through Translate::trans(), which runs the string through htmlentities(). write_title() wrote the label as-is, so the CSV header line showed raw entities (Données instead of DonnĂ©es) while the data cells were fine. With a ; separator the trailing ; of each entity also wrapped the label in double quotes. Decode entities and remove HTML with dol_string_nohtmltag() before csvClean(), consistent with the TSV and Excel export drivers. For the derived _label column of Select extrafields, append the suffix before csvClean() so the column stays valid CSV when the decoded label contains the separator or a quote. Signed-off-by: Gregory Aliot Co-authored-by: Gregory Aliot --- htdocs/core/modules/export/exportcsv.class.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/htdocs/core/modules/export/exportcsv.class.php b/htdocs/core/modules/export/exportcsv.class.php index 1e263a67284..b9e6c567c6a 100644 --- a/htdocs/core/modules/export/exportcsv.class.php +++ b/htdocs/core/modules/export/exportcsv.class.php @@ -210,6 +210,11 @@ class ExportCsv extends ModeleExports $newvalue = $array_export_fields_label[$code]; + // Label may be stored HTML encoded (for example extrafield labels saved through Translate::trans()), + // so decode entities and remove HTML to keep the header consistent with the data cells. + $newvalue = dol_string_nohtmltag($newvalue); + $decodedlabel = $newvalue; + // Clean data and add encloser if required (depending on value of USE_STRICT_CSV_RULES) include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; $newvalue = csvClean($newvalue, $outputlangs->charset_output, $this->separator); @@ -218,7 +223,9 @@ class ExportCsv extends ModeleExports $typefield = isset($array_types[$code]) ? $array_types[$code] : ''; if (preg_match('/^Select:/i', $typefield) && $typefield = substr($typefield, 7)) { - $selectlabel[$code."_label"] = $newvalue."_label"; + // Append the "_label" suffix before cleaning so the derived column stays valid CSV + // even when the decoded label contains the separator or a quote. + $selectlabel[$code."_label"] = csvClean($decodedlabel."_label", $outputlangs->charset_output, $this->separator); } }