dolibarr 19.0.3
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
25require_once DOL_DOCUMENT_ROOT.'/core/modules/export/modules_export.php';
26require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
27
28use PhpOffice\PhpSpreadsheet\Spreadsheet;
29use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
30use 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
60 public $workbook; // Handle file
61
62 public $worksheet; // Handle sheet
63
64 public $styleArray;
65
66 public $row;
67
68 public $col;
69
70 public $file; // To save filename
71
72
78 public function __construct($db)
79 {
80 global $langs;
81 $this->db = $db;
82
83 $this->id = 'excel2007'; // Same value then xxx in file name export_xxx.modules.php
84 $this->label = 'Excel 2007'; // Label of driver
85 $this->desc = $langs->trans('Excel2007FormatDesc');
86 $this->extension = 'xlsx'; // Extension for generated file by this driver
87 $this->picto = 'mime/xls'; // Picto
88 $this->version = '1.30'; // Driver version
89 $this->phpmin = array(7, 0); // Minimum version of PHP required by module
90
91 $this->disabled = 0;
92
93 if (empty($this->disabled)) {
94 require_once PHPEXCELNEW_PATH.'Spreadsheet.php';
95 $this->label_lib = 'PhpSpreadSheet';
96 $this->version_lib = '1.12.0'; // No way to get info from library
97 }
98
99 $this->row = 0;
100 }
101
107 public function getDriverId()
108 {
109 return $this->id;
110 }
111
117 public function getDriverLabel()
118 {
119 return $this->label;
120 }
121
127 public function getDriverLabelBis()
128 {
129 global $langs;
130 $langs->load("errors");
131 return $langs->trans("NumberOfLinesLimited");
132 }
133
139 public function getDriverDesc()
140 {
141 return $this->desc;
142 }
143
149 public function getDriverExtension()
150 {
151 return $this->extension;
152 }
153
159 public function getDriverVersion()
160 {
161 return $this->version;
162 }
163
169 public function getLibLabel()
170 {
171 return $this->label_lib;
172 }
173
179 public function getLibVersion()
180 {
181 return $this->version_lib;
182 }
183
184
185 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
193 public function open_file($file, $outputlangs)
194 {
195 // phpcs:enable
196 global $user, $conf, $langs;
197
198 dol_syslog(get_class($this)."::open_file file=".$file);
199 $this->file = $file;
200
201 $ret = 1;
202
203 $outputlangs->load("exports");
204
205 require_once DOL_DOCUMENT_ROOT.'/includes/phpoffice/phpspreadsheet/src/autoloader.php';
206 require_once DOL_DOCUMENT_ROOT.'/includes/Psr/autoloader.php';
207 require_once PHPEXCELNEW_PATH.'Spreadsheet.php';
208
209 if ($this->id == 'excel2007') {
210 if (!class_exists('ZipArchive')) { // For Excel2007, PHPSpreadSheet may need ZipArchive
211 $langs->load("errors");
212 $this->error = $langs->trans('ErrorPHPNeedModule', 'zip');
213 return -1;
214 }
215 }
216
217 $this->workbook = new Spreadsheet();
218 $this->workbook->getProperties()->setCreator($user->getFullName($outputlangs).' - '.DOL_APPLICATION_TITLE.' '.DOL_VERSION);
219 //$this->workbook->getProperties()->setLastModifiedBy('Dolibarr '.DOL_VERSION);
220 $this->workbook->getProperties()->setTitle(basename($file));
221 $this->workbook->getProperties()->setSubject(basename($file));
222 $this->workbook->getProperties()->setDescription(DOL_APPLICATION_TITLE.' '.DOL_VERSION);
223
224 $this->workbook->setActiveSheetIndex(0);
225 $this->workbook->getActiveSheet()->setTitle($outputlangs->trans("Sheet"));
226 $this->workbook->getActiveSheet()->getDefaultRowDimension()->setRowHeight(16);
227
228 return $ret;
229 }
230
231 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
238 public function write_header($outputlangs)
239 {
240 // phpcs:enable
241 //$outputlangs->charset_output='ISO-8859-1'; // Because Excel 5 format is ISO
242
243 return 0;
244 }
245
246
247 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
257 public function write_title($array_export_fields_label, $array_selected_sorted, $outputlangs, $array_types)
258 {
259 // phpcs:enable
260 global $conf;
261
262 // Create a format for the column headings
263 $this->workbook->getActiveSheet()->getStyle('1')->getFont()->setBold(true);
264 $this->workbook->getActiveSheet()->getStyle('1')->getAlignment()->setHorizontal(PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
265 $selectlabel = array();
266
267 $this->col = 1;
268
269 foreach ($array_selected_sorted as $code => $value) {
270 $alias = $array_export_fields_label[$code];
271 //print "dd".$alias;
272 if (empty($alias)) {
273 dol_print_error('', 'Bad value for field with code='.$code.'. Try to redefine export.');
274 }
275 $typefield = isset($array_types[$code]) ? $array_types[$code] : '';
276
277 if (preg_match('/^Select:/i', $typefield) && $typefield = substr($typefield, 7)) {
278 $selectlabel[$code."_label"] = $alias."_label";
279 }
280 $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, $outputlangs->transnoentities($alias));
281 if (!empty($array_types[$code]) && in_array($array_types[$code], array('Date', 'Numeric', 'TextAuto'))) { // Set autowidth for some types
282 $this->workbook->getActiveSheet()->getColumnDimension($this->column2Letter($this->col + 1))->setAutoSize(true);
283 }
284 $this->col++;
285 }
286 foreach ($selectlabel as $key => $value) {
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 $this->col++;
292 }
293 $this->row++;
294 return 0;
295 }
296
297 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
307 public function write_record($array_selected_sorted, $objp, $outputlangs, $array_types)
308 {
309 // phpcs:enable
310 global $conf;
311
312 // Define first row
313 $this->col = 1;
314
315 $reg = array();
316 $selectlabelvalues = array();
317 foreach ($array_selected_sorted as $code => $value) {
318 if (strpos($code, ' as ') == 0) {
319 $alias = str_replace(array('.', '-', '(', ')'), '_', $code);
320 } else {
321 $alias = substr($code, strpos($code, ' as ') + 4);
322 }
323 if (empty($alias)) {
324 dol_print_error('', 'Bad value for field with code='.$code.'. Try to redefine export.');
325 }
326 $newvalue = $objp->$alias;
327
328 $newvalue = $this->excel_clean($newvalue);
329 $typefield = isset($array_types[$code]) ? $array_types[$code] : '';
330
331 if (preg_match('/^Select:/i', $typefield) && $typefield = substr($typefield, 7)) {
332 $array = jsonOrUnserialize($typefield);
333 if (is_array($array) && !empty($newvalue)) {
334 $array = $array['options'];
335 $selectlabelvalues[$code."_label"] = $array[$newvalue];
336 } else {
337 $selectlabelvalues[$code."_label"] = "";
338 }
339 }
340
341 // Traduction newvalue
342 if (preg_match('/^\‍((.*)\‍)$/i', $newvalue, $reg)) {
343 $newvalue = $outputlangs->transnoentities($reg[1]);
344 } else {
345 $newvalue = $outputlangs->convToOutputCharset($newvalue);
346 }
347
348 if (preg_match('/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/i', $newvalue)) {
349 $newvalue = dol_stringtotime($newvalue);
350 $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($newvalue));
351 $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
352 $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('yyyy-mm-dd');
353 } 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)) {
354 $newvalue = dol_stringtotime($newvalue);
355 $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($newvalue));
356 $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
357 $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('yyyy-mm-dd h:mm:ss');
358 } else {
359 if ($typefield == 'Text' || $typefield == 'TextAuto') {
360 // 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
361 // done by SetCellValueByColumnAndRow but it is not, so we do it ourself.
362 $newvalue = (dol_substr($newvalue, 0, 1) === '=' ? '\'' : '') . $newvalue;
363 $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, $newvalue);
364 $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
365 $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('@');
366 $this->workbook->getActiveSheet()->getStyle($coord)->getAlignment()->setHorizontal(PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
367 } else {
368 $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, $newvalue);
369 }
370 }
371 $this->col++;
372 }
373 foreach ($selectlabelvalues as $key => $newvalue) {
374 if (preg_match('/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/i', $newvalue)) {
375 $newvalue = dol_stringtotime($newvalue);
376 $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($newvalue));
377 $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
378 $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('yyyy-mm-dd');
379 } 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)) {
380 $newvalue = dol_stringtotime($newvalue);
381 $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, \PhpOffice\PhpSpreadsheet\Shared\Date::PHPToExcel($newvalue));
382 $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
383 $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('yyyy-mm-dd h:mm:ss');
384 } else {
385 if ($typefield == 'Text' || $typefield == 'TextAuto') {
386 $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, (string) $newvalue);
387 $coord = $this->workbook->getActiveSheet()->getCellByColumnAndRow($this->col, $this->row + 1)->getCoordinate();
388 $this->workbook->getActiveSheet()->getStyle($coord)->getNumberFormat()->setFormatCode('@');
389 $this->workbook->getActiveSheet()->getStyle($coord)->getAlignment()->setHorizontal(PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_LEFT);
390 } else {
391 $this->workbook->getActiveSheet()->SetCellValueByColumnAndRow($this->col, $this->row + 1, $newvalue);
392 }
393 }
394 $this->col++;
395 }
396 $this->row++;
397 return 0;
398 }
399
400
401 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
408 public function write_footer($outputlangs)
409 {
410 // phpcs:enable
411 return 0;
412 }
413
414
415 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
421 public function close_file()
422 {
423 // phpcs:enable
424 global $conf;
425
426 $objWriter = new Xlsx($this->workbook);
427 $objWriter->save($this->file);
428 $this->workbook->disconnectWorksheets();
429 unset($this->workbook);
430
431 return 1;
432 }
433
434
435 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
442 public function excel_clean($newvalue)
443 {
444 // phpcs:enable
445 // Rule Dolibarr: No HTML
446 $newvalue = dol_string_nohtmltag($newvalue);
447
448 return $newvalue;
449 }
450
451
458 public function column2Letter($c)
459 {
460 $c = intval($c);
461 if ($c <= 0) {
462 return '';
463 }
464
465 $letter = '';
466 while ($c != 0) {
467 $p = ($c - 1) % 26;
468 $c = intval(($c - $p) / 26);
469 $letter = chr(65 + $p).$letter;
470 }
471
472 return $letter;
473 }
474
483 public function setCellValue($val, $startCell, $endCell = '')
484 {
485 try {
486 $this->workbook->getActiveSheet()->setCellValue($startCell, $val);
487
488 if (!empty($endCell)) {
489 $cellRange = $startCell.':'.$endCell;
490 $this->workbook->getActiveSheet()->mergeCells($startCell.':'.$endCell);
491 } else {
492 $cellRange = $startCell;
493 }
494 if (!empty($this->styleArray)) {
495 $this->workbook->getActiveSheet()->getStyle($cellRange)->applyFromArray($this->styleArray);
496 }
497 } catch (Exception $e) {
498 $this->error = $e->getMessage();
499 return -1;
500 }
501 return 1;
502 }
503
511 public function setBorderStyle($thickness, $color)
512 {
513 $this->styleArray['borders'] = array(
514 'outline' => array(
515 'borderStyle' => $thickness,
516 'color' => array('argb' => $color)
517 )
518 );
519 return 1;
520 }
521
529 public function setFontStyle($bold, $color)
530 {
531 $this->styleArray['font'] = array(
532 'color' => array('argb' => $color),
533 'bold' => $bold
534 );
535 return 1;
536 }
537
544 public function setAlignmentStyle($horizontal)
545 {
546 $this->styleArray['alignment'] = array('horizontal' => $horizontal);
547 return 1;
548 }
549
554 public function resetStyle()
555 {
556 $this->styleArray = array();
557 return 1;
558 }
559
568 public function setBlock($startCell, $TDatas = array(), $boldTitle = false)
569 {
570 try {
571 if (!empty($TDatas)) {
572 $startCell = $this->workbook->getActiveSheet()->getCell($startCell);
573 $startColumn = Coordinate::columnIndexFromString($startCell->getColumn());
574 $startRow = $startCell->getRow();
575 foreach ($TDatas as $column => $TRows) {
576 if ($boldTitle) {
577 $this->setFontStyle(true, $this->styleArray['font']['color']['argb']);
578 }
579 $cell = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn, $startRow);
580 $this->setCellValue($column, $cell->getCoordinate());
581 $rowPos = $startRow;
582 if ($boldTitle) {
583 $this->setFontStyle(false, $this->styleArray['font']['color']['argb']);
584 }
585 foreach ($TRows as $row) {
586 $rowPos++;
587 $cell = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn, $rowPos);
588 $this->setCellValue($row, $cell->getCoordinate());
589 }
590 $startColumn++;
591 }
592 }
593 } catch (Exception $e) {
594 $this->error = $e->getMessage();
595 return -1;
596 }
597 return 1;
598 }
599
608 public function setBlock2Columns($startCell, $TDatas = array(), $boldTitle = false)
609 {
610 try {
611 if (!empty($TDatas)) {
612 $startCell = $this->workbook->getActiveSheet()->getCell($startCell);
613 $startColumn = Coordinate::columnIndexFromString($startCell->getColumn());
614 $startRow = $startCell->getRow();
615 foreach ($TDatas as $title => $val) {
616 $cell = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn, $startRow);
617 if ($boldTitle) {
618 $this->setFontStyle(true, $this->styleArray['font']['color']['argb']);
619 }
620 $this->setCellValue($title, $cell->getCoordinate());
621 if ($boldTitle) {
622 $this->setFontStyle(false, $this->styleArray['font']['color']['argb']);
623 }
624 $cell2 = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn + 1, $startRow);
625 $this->setCellValue($val, $cell2->getCoordinate());
626 $startRow++;
627 }
628 }
629 } catch (Exception $e) {
630 $this->error = $e->getMessage();
631 return -1;
632 }
633 return 1;
634 }
635
643 public function enableAutosize($firstColumn, $lastColumn)
644 {
645 foreach (range($firstColumn, $lastColumn) as $columnID) {
646 $this->workbook->getActiveSheet()->getColumnDimension($columnID)->setAutoSize(true);
647 }
648 return 1;
649 }
650
660 public function setMergeCellValueByLength($val, $startCell, $length, $offset = 0)
661 {
662 try {
663 $startCell = $this->workbook->getActiveSheet()->getCell($startCell);
664 $startColumn = Coordinate::columnIndexFromString($startCell->getColumn());
665 if (!empty($offset)) {
666 $startColumn += $offset;
667 }
668
669 $startRow = $startCell->getRow();
670 $startCell = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn, $startRow);
671 $startCoordinate = $startCell->getCoordinate();
672 $this->setCellValue($val, $startCell->getCoordinate());
673
674 $endCell = $this->workbook->getActiveSheet()->getCellByColumnAndRow($startColumn + ($length - 1), $startRow);
675 $endCoordinate = $endCell->getCoordinate();
676 $this->workbook->getActiveSheet()->mergeCells($startCoordinate.':'.$endCoordinate);
677 } catch (Exception $e) {
678 $this->error = $e->getMessage();
679 return -1;
680 }
681 return $endCoordinate;
682 }
683}
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
getDriverLabelBis()
getDriverLabel
__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.
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:425
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.