diff --git a/htdocs/core/modules/export/export_csv.modules.php b/htdocs/core/modules/export/export_csviso.modules.php similarity index 94% rename from htdocs/core/modules/export/export_csv.modules.php rename to htdocs/core/modules/export/export_csviso.modules.php index 26f20378b23..00759d3de55 100644 --- a/htdocs/core/modules/export/export_csv.modules.php +++ b/htdocs/core/modules/export/export_csviso.modules.php @@ -74,9 +74,8 @@ class ExportCsv extends ModeleExports } $this->escape = '"'; $this->enclosure = '"'; - - $this->id = 'csv'; // Same value then xxx in file name export_xxx.modules.php - $this->label = 'CSV'; // Label of driver + $this->id = 'csviso'; // Same value then xxx in file name export_xxx.modules.php + $this->label = 'CSV ISO-8859-1'; // Label of driver $this->desc = $langs->trans("CSVFormatDesc", $this->separator, $this->enclosure, $this->escape); $this->extension = 'csv'; // Extension for generated file by this driver $this->picto = 'mime/other'; // Picto @@ -215,11 +214,9 @@ class ExportCsv extends ModeleExports // phpcs:enable global $conf; - if (!empty($conf->global->EXPORT_CSV_FORCE_CHARSET)) { - $outputlangs->charset_output = $conf->global->EXPORT_CSV_FORCE_CHARSET; - } else { - $outputlangs->charset_output = 'ISO-8859-1'; - } + $outputlangs->charset_output = 'ISO-8859-1'; + $conf->global->EXPORT_CSV_FORCE_CHARSET = 'ISO-8859-1'; + $selectlabel = array(); foreach ($array_selected_sorted as $code => $value) { @@ -256,11 +253,8 @@ class ExportCsv extends ModeleExports // phpcs:enable global $conf; - if (!empty($conf->global->EXPORT_CSV_FORCE_CHARSET)) { - $outputlangs->charset_output = $conf->global->EXPORT_CSV_FORCE_CHARSET; - } else { - $outputlangs->charset_output = 'ISO-8859-1'; - } + $outputlangs->charset_output = 'ISO-8859-1'; + $conf->global->EXPORT_CSV_FORCE_CHARSET = 'ISO-8859-1'; $this->col = 0; diff --git a/htdocs/core/modules/export/export_csvutf8.modules.php b/htdocs/core/modules/export/export_csvutf8.modules.php new file mode 100644 index 00000000000..75958fb7b35 --- /dev/null +++ b/htdocs/core/modules/export/export_csvutf8.modules.php @@ -0,0 +1,379 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/core/modules/export/export_csv.modules.php + * \ingroup export + * \brief File of class to build exports with CSV format + */ + +require_once DOL_DOCUMENT_ROOT.'/core/modules/export/modules_export.php'; + +// avoid timeout for big export +set_time_limit(0); + +/** + * Class to build export files with format CSV + */ +class ExportCsvUtf8 extends ModeleExports +{ + /** + * @var string ID ex: csv, tsv, excel... + */ + public $id; + + /** + * @var string export files label + */ + public $label; + + public $extension; + + /** + * Dolibarr version of the loaded document + * @var string + */ + public $version = 'dolibarr'; + + public $label_lib; + + public $version_lib; + + public $separator; + + public $handle; // Handle fichier + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + global $conf, $langs; + $this->db = $db; + + $this->separator = ','; + if (!empty($conf->global->EXPORT_CSV_SEPARATOR_TO_USE)) { + $this->separator = $conf->global->EXPORT_CSV_SEPARATOR_TO_USE; + } + $this->escape = '"'; + $this->enclosure = '"'; + + $this->id = 'csvutf8'; // Same value then xxx in file name export_xxx.modules.php + $this->label = 'CSV UTF-8'; // Label of driver + $this->desc = $langs->trans("CSVFormatDesc", $this->separator, $this->enclosure, $this->escape); + $this->extension = 'csv'; // Extension for generated file by this driver + $this->picto = 'mime/other'; // Picto + $this->version = '1.32'; // Driver version + + // If driver use an external library, put its name here + $this->label_lib = 'Dolibarr'; + $this->version_lib = DOL_VERSION; + } + + /** + * getDriverId + * + * @return string + */ + public function getDriverId() + { + return $this->id; + } + + /** + * getDriverLabel + * + * @return string Return driver label + */ + public function getDriverLabel() + { + return $this->label; + } + + /** + * getDriverDesc + * + * @return string + */ + public function getDriverDesc() + { + return $this->desc; + } + + /** + * getDriverExtension + * + * @return string + */ + public function getDriverExtension() + { + return $this->extension; + } + + /** + * getDriverVersion + * + * @return string + */ + public function getDriverVersion() + { + return $this->version; + } + + /** + * getLabelLabel + * + * @return string + */ + public function getLibLabel() + { + return $this->label_lib; + } + + /** + * getLibVersion + * + * @return string + */ + public function getLibVersion() + { + return $this->version_lib; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Open output file + * + * @param string $file Path of filename to generate + * @param Translate $outputlangs Output language object + * @return int <0 if KO, >=0 if OK + */ + public function open_file($file, $outputlangs) + { + // phpcs:enable + global $langs; + + dol_syslog("ExportCsv::open_file file=".$file); + + $ret = 1; + + $outputlangs->load("exports"); + $this->handle = fopen($file, "wt"); + if (!$this->handle) { + $langs->load("errors"); + $this->error = $langs->trans("ErrorFailToCreateFile", $file); + $ret = -1; + } + + return $ret; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output header into file + * + * @param Translate $outputlangs Output language object + * @return int <0 if KO, >0 if OK + */ + public function write_header($outputlangs) + { + // phpcs:enable + return 0; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output title line into file + * + * @param array $array_export_fields_label Array with list of label of fields + * @param array $array_selected_sorted Array with list of field to export + * @param Translate $outputlangs Object lang to translate values + * @param array $array_types Array with types of fields + * @return int <0 if KO, >0 if OK + */ + public function write_title($array_export_fields_label, $array_selected_sorted, $outputlangs, $array_types) + { + // phpcs:enable + global $conf; + + $outputlangs->charset_output = 'UTF-8'; + // var_dump($outputlangs->charset_output);exit; + // var_dump($conf->global->EXPORT_CSV_FORCE_CHARSET, "here");exit; + $selectlabel = array(); + + foreach ($array_selected_sorted as $code => $value) { + $newvalue = $outputlangs->transnoentities($array_export_fields_label[$code]); // newvalue is now $outputlangs->charset_output encoded + $newvalue = $this->csvClean($newvalue, $outputlangs->charset_output); + + fwrite($this->handle, $newvalue.$this->separator); + $typefield = isset($array_types[$code]) ? $array_types[$code] : ''; + + if (preg_match('/^Select:/i', $typefield) && $typefield = substr($typefield, 7)) { + $selectlabel[$code."_label"] = $newvalue."_label"; + } + } + foreach ($selectlabel as $key => $value) { + fwrite($this->handle, $value.$this->separator); + } + fwrite($this->handle, "\n"); + return 0; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output record line into file + * + * @param array $array_selected_sorted Array with list of field to export + * @param resource $objp A record from a fetch with all fields from select + * @param Translate $outputlangs Object lang to translate values + * @param array $array_types Array with types of fields + * @return int <0 if KO, >0 if OK + */ + public function write_record($array_selected_sorted, $objp, $outputlangs, $array_types) + { + // phpcs:enable + global $conf; + + $outputlangs->charset_output = 'UTF-8'; + // var_dump($conf->global->EXPORT_CSV_FORCE_CHARSET, "here");exit; + $this->col = 0; + + $reg = array(); + $selectlabelvalues = array(); + foreach ($array_selected_sorted as $code => $value) { + if (strpos($code, ' as ') == 0) { + $alias = str_replace(array('.', '-', '(', ')'), '_', $code); + } else { + $alias = substr($code, strpos($code, ' as ') + 4); + } + if (empty($alias)) { + dol_print_error('', 'Bad value for field with key='.$code.'. Try to redefine export.'); + } + + $newvalue = $outputlangs->convToOutputCharset($objp->$alias); // objp->$alias must be utf8 encoded as any var in memory // newvalue is now $outputlangs->charset_output encoded + $typefield = isset($array_types[$code]) ? $array_types[$code] : ''; + + // Translation newvalue + if (preg_match('/^\((.*)\)$/i', $newvalue, $reg)) { + $newvalue = $outputlangs->transnoentities($reg[1]); + } + + // Clean data and add encloser if required (depending on value of USE_STRICT_CSV_RULES) + $newvalue = $this->csvClean($newvalue, $outputlangs->charset_output); + + if (preg_match('/^Select:/i', $typefield) && $typefield = substr($typefield, 7)) { + $array = jsonOrUnserialize($typefield); + if (is_array($array) && !empty($newvalue)) { + $array = $array['options']; + $selectlabelvalues[$code."_label"] = $array[$newvalue]; + } else { + $selectlabelvalues[$code."_label"] = ""; + } + } + + fwrite($this->handle, $newvalue.$this->separator); + $this->col++; + } + foreach ($selectlabelvalues as $key => $value) { + fwrite($this->handle, $value.$this->separator); + $this->col++; + } + + fwrite($this->handle, "\n"); + return 0; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output footer into file + * + * @param Translate $outputlangs Output language object + * @return int <0 if KO, >0 if OK + */ + public function write_footer($outputlangs) + { + // phpcs:enable + return 0; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Close file handle + * + * @return int <0 if KO, >0 if OK + */ + public function close_file() + { + // phpcs:enable + 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) + { + global $conf; + $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 (!empty($conf->global->USE_STRICT_CSV_RULES) && $oldvalue != $newvalue) { + // If we must use enclusure on text with CR/LF) + if ($conf->global->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 ? '"' : ''); + } +}