dolibarr  19.0.0-dev
export_excel2007.modules.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2006-2015 Laurent Destailleur <eldy@users.sourceforge.net>
3  * Copyright (C) 2012 Marcos GarcĂ­a <marcosgdf@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 
25 require_once DOL_DOCUMENT_ROOT.'/core/modules/export/modules_export.php';
26 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
27 
28 use PhpOffice\PhpSpreadsheet\Spreadsheet;
29 use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
30 use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
31 
36 {
40  public $id;
41 
45  public $label;
46 
47  public $extension;
48 
53  public $version = 'dolibarr';
54 
55  public $label_lib;
56 
57  public $version_lib;
58 
59  public $workbook; // Handle file
60 
61  public $worksheet; // Handle sheet
62 
63  public $styleArray;
64 
65  public $row;
66 
67  public $col;
68 
69  public $file; // To save filename
70 
71 
77  public function __construct($db)
78  {
79  global $conf, $langs;
80  $this->db = $db;
81 
82  $this->id = 'excel2007'; // Same value then xxx in file name export_xxx.modules.php
83  $this->label = 'Excel 2007'; // Label of driver
84  $this->desc = $langs->trans('Excel2007FormatDesc');
85  $this->extension = 'xlsx'; // Extension for generated file by this driver
86  $this->picto = 'mime/xls'; // Picto
87  $this->version = '1.30'; // Driver version
88  $this->phpmin = array(7, 0); // Minimum version of PHP required by module
89 
90  $this->disabled = 0;
91 
92  if (empty($this->disabled)) {
93  require_once PHPEXCELNEW_PATH.'Spreadsheet.php';
94  $this->label_lib = 'PhpSpreadSheet';
95  $this->version_lib = '1.12.0'; // No way to get info from library
96  }
97 
98  $this->row = 0;
99  }
100 
106  public function getDriverId()
107  {
108  return $this->id;
109  }
110 
116  public function getDriverLabel()
117  {
118  return $this->label;
119  }
120 
126  public function getDriverDesc()
127  {
128  return $this->desc;
129  }
130 
136  public function getDriverExtension()
137  {
138  return $this->extension;
139  }
140 
146  public function getDriverVersion()
147  {
148  return $this->version;
149  }
150 
156  public function getLibLabel()
157  {
158  return $this->label_lib;
159  }
160 
166  public function getLibVersion()
167  {
168  return $this->version_lib;
169  }
170 
171 
172  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
180  public function open_file($file, $outputlangs)
181  {
182  // phpcs:enable
183  global $user, $conf, $langs;
184 
185  if (!empty($conf->global->MAIN_USE_PHP_WRITEEXCEL)) {
186  $outputlangs->charset_output = 'ISO-8859-1'; // Because Excel 5 format is ISO
187  }
188 
189  dol_syslog(get_class($this)."::open_file file=".$file);
190  $this->file = $file;
191 
192  $ret = 1;
193 
194  $outputlangs->load("exports");
195 
196  require_once DOL_DOCUMENT_ROOT.'/includes/phpoffice/phpspreadsheet/src/autoloader.php';
197  require_once DOL_DOCUMENT_ROOT.'/includes/Psr/autoloader.php';
198  require_once PHPEXCELNEW_PATH.'Spreadsheet.php';
199 
200  if ($this->id == 'excel2007') {
201  if (!class_exists('ZipArchive')) { // For Excel2007, PHPSpreadSheet may need ZipArchive
202  $langs->load("errors");
203  $this->error = $langs->trans('ErrorPHPNeedModule', 'zip');
204  return -1;
205  }
206  }
207 
208  $this->workbook = new Spreadsheet();
209  $this->workbook->getProperties()->setCreator($user->getFullName($outputlangs).' - '.DOL_APPLICATION_TITLE.' '.DOL_VERSION);
210  //$this->workbook->getProperties()->setLastModifiedBy('Dolibarr '.DOL_VERSION);
211  $this->workbook->getProperties()->setTitle(basename($file));
212  $this->workbook->getProperties()->setSubject(basename($file));
213  $this->workbook->getProperties()->setDescription(DOL_APPLICATION_TITLE.' '.DOL_VERSION);
214 
215  $this->workbook->setActiveSheetIndex(0);
216  $this->workbook->getActiveSheet()->setTitle($outputlangs->trans("Sheet"));
217  $this->workbook->getActiveSheet()->getDefaultRowDimension()->setRowHeight(16);
218 
219  return $ret;
220  }
221 
222  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
229  public function write_header($outputlangs)
230  {
231  // phpcs:enable
232  //$outputlangs->charset_output='ISO-8859-1'; // Because Excel 5 format is ISO
233 
234  return 0;
235  }
236 
237 
238  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
248  public function write_title($array_export_fields_label, $array_selected_sorted, $outputlangs, $array_types)
249  {
250  // phpcs:enable
251  global $conf;
252 
253  // Create a format for the column headings
254  $this->workbook->getActiveSheet()->getStyle('1')->getFont()->setBold(true);
255  $this->workbook->getActiveSheet()->getStyle('1')->getAlignment()->setHorizontal(PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
256  $selectlabel = array();
257 
258  $this->col = 1;
259  if (!empty($conf->global->MAIN_USE_PHP_WRITEEXCEL)) {
260  $this->col = 0;
261  }
262  foreach ($array_selected_sorted as $code => $value) {
263  $alias = $array_export_fields_label[$code];
264  //print "dd".$alias;
265  if (empty($alias)) {
266  dol_print_error('', 'Bad value for field with code='.$code.'. Try to redefine export.');
267  }
268  $typefield = isset($array_types[$code]) ? $array_types[$code] : '';
269 
270  if (preg_match('/^Select:/i', $typefield) && $typefield = substr($typefield, 7)) {
271  $selectlabel[$code."_label"] = $alias."_label";
272  }
273  if (!empty($conf->global->MAIN_USE_PHP_WRITEEXCEL)) {
274  $this->worksheet->write($this->row, $this->col, $outputlangs->transnoentities($alias), $formatheader);
275  } else {
276  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, $outputlangs->transnoentities($alias));
277  if (!empty($array_types[$code]) && in_array($array_types[$code], array('Date', 'Numeric', 'TextAuto'))) { // Set autowidth for some types
278  $this->workbook->getActiveSheet()->getColumnDimension($this->column2Letter($this->col + 1))->setAutoSize(true);
279  }
280  }
281  $this->col++;
282  }
283  foreach ($selectlabel as $key => $value) {
284  if (!empty($conf->global->MAIN_USE_PHP_WRITEEXCEL)) {
285  $this->worksheet->write($this->row, $this->col, $outputlangs->transnoentities($value), $formatheader);
286  } else {
287  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, $outputlangs->transnoentities($value));
288  if (!empty($array_types[$code]) && in_array($array_types[$code], array('Date', 'Numeric', 'TextAuto'))) { // Set autowidth for some types
289  $this->workbook->getActiveSheet()->getColumnDimension($this->column2Letter($this->col + 1))->setAutoSize(true);
290  }
291  }
292  $this->col++;
293  }
294  $this->row++;
295  return 0;
296  }
297 
298  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
308  public function write_record($array_selected_sorted, $objp, $outputlangs, $array_types)
309  {
310  // phpcs:enable
311  global $conf;
312 
313  // Define first row
314  $this->col = 1;
315  if (!empty($conf->global->MAIN_USE_PHP_WRITEEXCEL)) {
316  $this->col = 0;
317  }
318 
319  $reg = array();
320  $selectlabelvalues = array();
321  foreach ($array_selected_sorted as $code => $value) {
322  if (strpos($code, ' as ') == 0) {
323  $alias = str_replace(array('.', '-', '(', ')'), '_', $code);
324  } else {
325  $alias = substr($code, strpos($code, ' as ') + 4);
326  }
327  if (empty($alias)) {
328  dol_print_error('', 'Bad value for field with code='.$code.'. Try to redefine export.');
329  }
330  $newvalue = $objp->$alias;
331 
332  $newvalue = $this->excel_clean($newvalue);
333  $typefield = isset($array_types[$code]) ? $array_types[$code] : '';
334 
335  if (preg_match('/^Select:/i', $typefield) && $typefield = substr($typefield, 7)) {
336  $array = jsonOrUnserialize($typefield);
337  if (is_array($array) && !empty($newvalue)) {
338  $array = $array['options'];
339  $selectlabelvalues[$code."_label"] = $array[$newvalue];
340  } else {
341  $selectlabelvalues[$code."_label"] = "";
342  }
343  }
344 
345  // Traduction newvalue
346  if (preg_match('/^\‍((.*)\‍)$/i', $newvalue, $reg)) {
347  $newvalue = $outputlangs->transnoentities($reg[1]);
348  } else {
349  $newvalue = $outputlangs->convToOutputCharset($newvalue);
350  }
351 
352  if (preg_match('/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/i', $newvalue)) {
353  $newvalue = dol_stringtotime($newvalue);
354  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($newvalue));
355  $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
356  $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('yyyy-mm-dd');
357  } elseif (preg_match('/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]$/i', $newvalue)) {
358  $newvalue = dol_stringtotime($newvalue);
359  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($newvalue));
360  $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
361  $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('yyyy-mm-dd h:mm:ss');
362  } else {
363  if ($typefield == 'Text' || $typefield == 'TextAuto') {
364  // If $newvalue start with an equal sign we don't want it to be interpreted as a formula, so we add a '. Such transformation should be
365  // done by SetCellValueByColumnAndRow but it is not, so we do it ourself.
366  $newvalue = (dol_substr($newvalue, 0, 1) === '=' ? '\'' : '') . $newvalue;
367  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, $newvalue);
368  $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
369  $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('@');
370  $this->workbook->getActiveSheet()->getStyle($coord)->getAlignment()->setHorizontal(PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
371  } else {
372  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, $newvalue);
373  }
374  }
375  $this->col++;
376  }
377  foreach ($selectlabelvalues as $key => $newvalue) {
378  if (preg_match('/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/i', $newvalue)) {
379  $newvalue = dol_stringtotime($newvalue);
380  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($newvalue));
381  $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
382  $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('yyyy-mm-dd');
383  } elseif (preg_match('/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]$/i', $newvalue)) {
384  $newvalue = dol_stringtotime($newvalue);
385  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($newvalue));
386  $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
387  $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('yyyy-mm-dd h:mm:ss');
388  } else {
389  if ($typefield == 'Text' || $typefield == 'TextAuto') {
390  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, (string) $newvalue);
391  $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
392  $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('@');
393  $this->workbook->getActiveSheet()->getStyle($coord)->getAlignment()->setHorizontal(PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
394  } else {
395  $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, $newvalue);
396  }
397  }
398  $this->col++;
399  }
400  $this->row++;
401  return 0;
402  }
403 
404 
405  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
412  public function write_footer($outputlangs)
413  {
414  // phpcs:enable
415  return 0;
416  }
417 
418 
419  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
425  public function close_file()
426  {
427  // phpcs:enable
428  global $conf;
429 
430  $objWriter = new Xlsx($this->workbook);
431  $objWriter->save($this->file);
432  $this->workbook->disconnectWorksheets();
433  unset($this->workbook);
434 
435  return 1;
436  }
437 
438 
439  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
446  public function excel_clean($newvalue)
447  {
448  // phpcs:enable
449  // Rule Dolibarr: No HTML
450  $newvalue = dol_string_nohtmltag($newvalue);
451 
452  return $newvalue;
453  }
454 
455 
462  public function column2Letter($c)
463  {
464 
465  $c = intval($c);
466  if ($c <= 0) {
467  return '';
468  }
469 
470  $letter = '';
471  while ($c != 0) {
472  $p = ($c - 1) % 26;
473  $c = intval(($c - $p) / 26);
474  $letter = chr(65 + $p).$letter;
475  }
476 
477  return $letter;
478  }
479 
488  public function setCellValue($val, $startCell, $endCell = '')
489  {
490  try {
491  $this->workbook->getActiveSheet()->setCellValue($startCell, $val);
492 
493  if (!empty($endCell)) {
494  $cellRange = $startCell.':'.$endCell;
495  $this->workbook->getActiveSheet()->mergeCells($startCell.':'.$endCell);
496  } else {
497  $cellRange = $startCell;
498  }
499  if (!empty($this->styleArray)) {
500  $this->workbook->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->styleArray);
501  }
502  } catch (Exception $e) {
503  $this->error = $e->getMessage();
504  return -1;
505  }
506  return 1;
507  }
508 
516  public function setBorderStyle($thickness, $color)
517  {
518  $this->styleArray['borders'] = array(
519  'outline' => array(
520  'borderStyle' => $thickness,
521  'color' => array('argb' => $color)
522  )
523  );
524  return 1;
525  }
526 
534  public function setFontStyle($bold, $color)
535  {
536  $this->styleArray['font'] = array(
537  'color' => array('argb' => $color),
538  'bold' => $bold
539  );
540  return 1;
541  }
542 
549  public function setAlignmentStyle($horizontal)
550  {
551  $this->styleArray['alignment'] = array('horizontal' => $horizontal);
552  return 1;
553  }
554 
559  public function resetStyle()
560  {
561  $this->styleArray = array();
562  return 1;
563  }
564 
573  public function setBlock($startCell, $TDatas = array(), $boldTitle = false)
574  {
575  try {
576  if (!empty($TDatas)) {
577  $startCell = $this->workbook->getActiveSheet()->getCell($startCell);
578  $startColumn = Coordinate::columnIndexFromString($startCell->getColumn());
579  $startRow = $startCell->getRow();
580  foreach ($TDatas as $column => $TRows) {
581  if ($boldTitle) {
582  $this->setFontStyle(true, $this->styleArray['font']['color']['argb']);
583  }
584  $cell = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn, $startRow);
585  $this->setCellValue($column, $cell->getCoordinate());
586  $rowPos = $startRow;
587  if ($boldTitle) {
588  $this->setFontStyle(false, $this->styleArray['font']['color']['argb']);
589  }
590  foreach ($TRows as $row) {
591  $rowPos++;
592  $cell = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn, $rowPos);
593  $this->setCellValue($row, $cell->getCoordinate());
594  }
595  $startColumn++;
596  }
597  }
598  } catch (Exception $e) {
599  $this->error = $e->getMessage();
600  return -1;
601  }
602  return 1;
603  }
604 
613  public function setBlock2Columns($startCell, $TDatas = array(), $boldTitle = false)
614  {
615  try {
616  if (!empty($TDatas)) {
617  $startCell = $this->workbook->getActiveSheet()->getCell($startCell);
618  $startColumn = Coordinate::columnIndexFromString($startCell->getColumn());
619  $startRow = $startCell->getRow();
620  foreach ($TDatas as $title => $val) {
621  $cell = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn, $startRow);
622  if ($boldTitle) {
623  $this->setFontStyle(true, $this->styleArray['font']['color']['argb']);
624  }
625  $this->setCellValue($title, $cell->getCoordinate());
626  if ($boldTitle) {
627  $this->setFontStyle(false, $this->styleArray['font']['color']['argb']);
628  }
629  $cell2 = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn + 1, $startRow);
630  $this->setCellValue($val, $cell2->getCoordinate());
631  $startRow++;
632  }
633  }
634  } catch (Exception $e) {
635  $this->error = $e->getMessage();
636  return -1;
637  }
638  return 1;
639  }
640 
648  public function enableAutosize($firstColumn, $lastColumn)
649  {
650  foreach (range($firstColumn, $lastColumn) as $columnID) {
651  $this->workbook->getActiveSheet()->getColumnDimension($columnID)->setAutoSize(true);
652  }
653  return 1;
654  }
655 
665  public function setMergeCellValueByLength($val, $startCell, $length, $offset = 0)
666  {
667  try {
668  $startCell = $this->workbook->getActiveSheet()->getCell($startCell);
669  $startColumn = Coordinate::columnIndexFromString($startCell->getColumn());
670  if (!empty($offset)) {
671  $startColumn += $offset;
672  }
673 
674  $startRow = $startCell->getRow();
675  $startCell = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn, $startRow);
676  $startCoordinate = $startCell->getCoordinate();
677  $this->setCellValue($val, $startCell->getCoordinate());
678 
679  $endCell = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn + ($length - 1), $startRow);
680  $endCoordinate = $endCell->getCoordinate();
681  $this->workbook->getActiveSheet()->mergeCells($startCoordinate.':'.$endCoordinate);
682  } catch (Exception $e) {
683  $this->error = $e->getMessage();
684  return -1;
685  }
686  return $endCoordinate;
687  }
688 }
Class to build export files with Excel format.
write_record($array_selected_sorted, $objp, $outputlangs, $array_types)
Output record line into file.
setBlock2Columns($startCell, $TDatas=array(), $boldTitle=false)
Make a 2xN Tab in Sheet.
getDriverVersion()
getDriverVersion
setCellValue($val, $startCell, $endCell='')
Set cell value and automatically merge if we give an endcell.
setMergeCellValueByLength($val, $startCell, $length, $offset=0)
Set a value cell and merging it by giving a starting cell and a length.
close_file()
Close Excel file.
write_footer($outputlangs)
Write footer.
column2Letter($c)
Convert a column to letter (1->A, 0->B, 27->AA, ...)
getDriverExtension()
getDriverExtension
__construct($db)
Constructor.
setFontStyle($bold, $color)
Set font style.
setBlock($startCell, $TDatas=array(), $boldTitle=false)
Make a NxN Block in sheet.
open_file($file, $outputlangs)
Open output file.
write_title($array_export_fields_label, $array_selected_sorted, $outputlangs, $array_types)
Output title line into file.
enableAutosize($firstColumn, $lastColumn)
Enable auto sizing for column range.
setAlignmentStyle($horizontal)
Set alignment style (horizontal, left, right, ...)
write_header($outputlangs)
Write header.
getDriverLabel()
getDriverLabel
excel_clean($newvalue)
Clean a cell to respect rules of Excel file cells.
setBorderStyle($thickness, $color)
Set border style.
Parent class for export modules.
dol_stringtotime($string, $gm=1)
Convert a string date into a GM Timestamps date Warning: YYYY-MM-DDTHH:MM:SS+02:00 (RFC3339) is not s...
Definition: date.lib.php:408
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
jsonOrUnserialize($stringtodecode)
Decode an encode string.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.