diff --git a/htdocs/core/class/translate.class.php b/htdocs/core/class/translate.class.php index e6c36e61e51..cef98ab273d 100644 --- a/htdocs/core/class/translate.class.php +++ b/htdocs/core/class/translate.class.php @@ -871,8 +871,6 @@ class Translate public function get_available_languages($langdir = DOL_DOCUMENT_ROOT, $maxlength = 0, $usecode = 0, $mainlangonly = 0) { // phpcs:enable - global $conf; - $this->load("languages"); // We scan directory langs to detect available languages diff --git a/htdocs/core/lib/functions2.lib.php b/htdocs/core/lib/functions2.lib.php index a21389385a7..aeb6086e29c 100644 --- a/htdocs/core/lib/functions2.lib.php +++ b/htdocs/core/lib/functions2.lib.php @@ -3075,3 +3075,53 @@ function removeEmoji($text, $allowedemoji = 1) return $text; } + + +/** + * Clean a cell to respect rules of CSV file cells + * + * @param string $newvalue String to clean + * @param string $charset Input AND Output character set + * @param string $separator CSV char separator (often ',' or ';'). Default '' will use the value into EXPORT_CSV_SEPARATOR_TO_USE. + * @return string Value cleaned + */ +function csvClean($newvalue, $charset, $separator = '') +{ + $addquote = 0; + + if (empty($separator)) { + $separator = getDolGlobalString('EXPORT_CSV_SEPARATOR_TO_USE'); + } + + // Rule Dolibarr: No HTML + //print $charset.' '.$newvalue."\n"; + //$newvalue=dol_string_nohtmltag($newvalue,0,$charset); + $newvalue = dol_htmlcleanlastbr($newvalue); + //print $charset.' '.$newvalue."\n"; + + // Rule 1 CSV: No CR, LF in cells (except if USE_STRICT_CSV_RULES is 1, we can keep record as it is but we must add quotes) + $oldvalue = $newvalue; + $newvalue = str_replace("\r", '', $newvalue); + $newvalue = str_replace("\n", '\n', $newvalue); + if (getDolGlobalString('USE_STRICT_CSV_RULES') && $oldvalue != $newvalue) { + // If we must use enclusure on text with CR/LF) + if (getDolGlobalInt('USE_STRICT_CSV_RULES') == 1) { + // If we use strict CSV rules (original value must remain but we add quote) + $newvalue = $oldvalue; + } + $addquote = 1; + } + + // Rule 2 CSV: If value contains ", we must escape with ", and add " + if (preg_match('/"/', $newvalue)) { + $addquote = 1; + $newvalue = str_replace('"', '""', $newvalue); + } + + // Rule 3 CSV: If value contains separator, we must add " + if (preg_match('/'.$separator.'/', $newvalue)) { + $addquote = 1; + } + + return ($addquote ? '"' : '').$newvalue.($addquote ? '"' : ''); +} diff --git a/htdocs/core/modules/export/exportcsv.class.php b/htdocs/core/modules/export/exportcsv.class.php index 4be654b38eb..91b7f63a161 100644 --- a/htdocs/core/modules/export/exportcsv.class.php +++ b/htdocs/core/modules/export/exportcsv.class.php @@ -209,7 +209,10 @@ class ExportCsv extends ModeleExports } $newvalue = $outputlangs->transnoentities($array_export_fields_label[$code]); // newvalue is now $outputlangs->charset_output encoded - $newvalue = $this->csvClean($newvalue, $outputlangs->charset_output); + + // 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); fwrite($this->handle, $newvalue.$this->separator); $typefield = isset($array_types[$code]) ? $array_types[$code] : ''; @@ -267,7 +270,8 @@ class ExportCsv extends ModeleExports } // Clean data and add encloser if required (depending on value of USE_STRICT_CSV_RULES) - $newvalue = $this->csvClean($newvalue, $outputlangs->charset_output); + include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; + $newvalue = csvClean($newvalue, $outputlangs->charset_output, $this->separator); if (preg_match('/^Select:/i', $typefield) && $typefield = substr($typefield, 7)) { $array = jsonOrUnserialize($typefield); @@ -316,51 +320,4 @@ class ExportCsv extends ModeleExports fclose($this->handle); return 0; } - - - /** - * Clean a cell to respect rules of CSV file cells - * Note: It uses $this->separator - * Note: We keep this function public to be able to test - * - * @param string $newvalue String to clean - * @param string $charset Input AND Output character set - * @return string Value cleaned - */ - public function csvClean($newvalue, $charset) - { - $addquote = 0; - - // Rule Dolibarr: No HTML - //print $charset.' '.$newvalue."\n"; - //$newvalue=dol_string_nohtmltag($newvalue,0,$charset); - $newvalue = dol_htmlcleanlastbr($newvalue); - //print $charset.' '.$newvalue."\n"; - - // Rule 1 CSV: No CR, LF in cells (except if USE_STRICT_CSV_RULES is 1, we can keep record as it is but we must add quotes) - $oldvalue = $newvalue; - $newvalue = str_replace("\r", '', $newvalue); - $newvalue = str_replace("\n", '\n', $newvalue); - if (getDolGlobalString('USE_STRICT_CSV_RULES') && $oldvalue != $newvalue) { - // If we must use enclusure on text with CR/LF) - if (getDolGlobalInt('USE_STRICT_CSV_RULES') == 1) { - // If we use strict CSV rules (original value must remain but we add quote) - $newvalue = $oldvalue; - } - $addquote = 1; - } - - // Rule 2 CSV: If value contains ", we must escape with ", and add " - if (preg_match('/"/', $newvalue)) { - $addquote = 1; - $newvalue = str_replace('"', '""', $newvalue); - } - - // Rule 3 CSV: If value contains separator, we must add " - if (preg_match('/'.$this->separator.'/', $newvalue)) { - $addquote = 1; - } - - return ($addquote ? '"' : '').$newvalue.($addquote ? '"' : ''); - } } diff --git a/test/phpunit/ExportTest.php b/test/phpunit/ExportTest.php index 4364eb8bb28..89d23a431ae 100644 --- a/test/phpunit/ExportTest.php +++ b/test/phpunit/ExportTest.php @@ -30,6 +30,7 @@ global $conf,$user,$langs,$db; require_once dirname(__FILE__).'/../../htdocs/master.inc.php'; require_once dirname(__FILE__).'/../../htdocs/exports/class/export.class.php'; require_once dirname(__FILE__).'/../../htdocs/core/lib/files.lib.php'; +require_once dirname(__FILE__).'/../../htdocs/core/lib/functions2.lib.php'; require_once dirname(__FILE__).'/CommonClassTest.class.php'; if (! defined('NOREQUIREUSER')) { @@ -99,31 +100,31 @@ class ExportTest extends CommonClassTest $valtotest = 'A simple string'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, 'A simple string'); $valtotest = 'A string with , and ; inside'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with , and ; inside"', 'Error in csvClean for '.$file); $valtotest = 'A string with " inside'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with "" inside"'); $valtotest = 'A string with " inside and '."\r\n".' carriage returns'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with "" inside and \n carriage returns"'); $valtotest = 'A string with html
content
inside
'."\n"; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with html
content
inside"'); @@ -132,31 +133,31 @@ class ExportTest extends CommonClassTest $valtotest = 'A simple string'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, 'A simple string'); $valtotest = 'A string with , and ; inside'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with , and ; inside"'); $valtotest = 'A string with " inside'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with "" inside"'); $valtotest = 'A string with " inside and '."\r\n".' carriage returns'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, "\"A string with \"\" inside and \r\n carriage returns\""); $valtotest = 'A string with html
content
inside
'."\n"; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with html
content
inside"'); } @@ -188,31 +189,31 @@ class ExportTest extends CommonClassTest $valtotest = 'A simple string'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, 'A simple string'); $valtotest = 'A string with , and ; inside'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with , and ; inside"', 'Error in csvClean for '.$file); $valtotest = 'A string with " inside'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with "" inside"'); $valtotest = 'A string with " inside and '."\r\n".' carriage returns'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with "" inside and \n carriage returns"'); $valtotest = 'A string with html
content
inside
'."\n"; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with html
content
inside"'); @@ -221,31 +222,31 @@ class ExportTest extends CommonClassTest $valtotest = 'A simple string'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, 'A simple string'); $valtotest = 'A string with , and ; inside'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with , and ; inside"'); $valtotest = 'A string with " inside'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with "" inside"'); $valtotest = 'A string with " inside and '."\r\n".' carriage returns'; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, "\"A string with \"\" inside and \r\n carriage returns\""); $valtotest = 'A string with html
content
inside
'."\n"; print __METHOD__." valtotest=".$valtotest."\n"; - $result = $objmodel->csvClean($valtotest, $langs->charset_output); + $result = csvClean($valtotest, $langs->charset_output); print __METHOD__." result=".$result."\n"; $this->assertEquals($result, '"A string with html
content
inside"'); }