diff --git a/REST/functions.php b/REST/functions.php
index c0cb292a..7138f836 100644
--- a/REST/functions.php
+++ b/REST/functions.php
@@ -49,10 +49,10 @@
$newPositionList = array();
$total_netto = 0; $total_brutto = 0; $total_discount = 0; $vats = array();
foreach ( $sale->position_list as $product ) {
- echo 'Produkt: '.$product->product_code.'';
- $p = getProduct($product->product_code);
+ echo 'Produkt: '.$product->product_code;
+ $p = getProduct(trim($product->product_code));
if (!$p) {
- echo ' Produkt nie istnieje w bazie E5
';
+ echo ' (produkt nie istnieje w bazie E5)
';
$gotAllProducts = false;
continue;
} else {
diff --git a/include/language/en_us.EcmDropdownEditor.php b/include/language/en_us.EcmDropdownEditor.php
index 9778bb8f..4432d9b2 100644
--- a/include/language/en_us.EcmDropdownEditor.php
+++ b/include/language/en_us.EcmDropdownEditor.php
@@ -1,5 +1,5 @@
array (
@@ -71,6 +71,7 @@ $customDoms = array (
'349b50d1-0a7b-b3bd-e72e-662b42ca98ae' => 'Foam 400ml',
'cb66a685-53e1-5f87-0a25-66967c7cb63b' => 'Doypack 210x285mm',
'75f4bd2a-51a8-0767-635e-66dde15af79e' => 'Foam 200ml',
+ '225115e4-1ce2-9a98-b7a0-672930e3318c' => 'Blister tabletki 10szt. 2 g',
),
'ecmproducts_brand_dom' =>
array (
@@ -149,6 +150,8 @@ $customDoms = array (
'd5f819ff-297b-c0d8-cfcd-66d0d0103806' => 'EcoExpert',
'4144e574-4870-fa26-25a0-66d85b094f7b' => 'Office Discount',
'e00b0e4a-ba66-796d-3bcb-66d89240c2cd' => 'Printus',
+ '661bc56d-f77f-6d98-98be-672a06cf59ee' => 'AVCedukacja',
+ '8d09cc5f-2348-6d22-80dc-6736057e390d' => 'Seltino',
),
'ecmproducts_category_dom' =>
array (
diff --git a/include/language/pl_pl.EcmDropdownEditor.php b/include/language/pl_pl.EcmDropdownEditor.php
index 914e9018..8acd75e2 100644
--- a/include/language/pl_pl.EcmDropdownEditor.php
+++ b/include/language/pl_pl.EcmDropdownEditor.php
@@ -1,5 +1,5 @@
array (
@@ -71,6 +71,7 @@ $customDoms = array (
'349b50d1-0a7b-b3bd-e72e-662b42ca98ae' => 'Pianka 400ml',
'cb66a685-53e1-5f87-0a25-66967c7cb63b' => 'Doypack 210x285mm',
'75f4bd2a-51a8-0767-635e-66dde15af79e' => 'Pianka 200ml',
+ '225115e4-1ce2-9a98-b7a0-672930e3318c' => 'Blister tabletki 10szt. 2 g',
),
'ecmproducts_brand_dom' =>
array (
@@ -149,6 +150,8 @@ $customDoms = array (
'd5f819ff-297b-c0d8-cfcd-66d0d0103806' => 'EcoExpert',
'4144e574-4870-fa26-25a0-66d85b094f7b' => 'Office Discount',
'e00b0e4a-ba66-796d-3bcb-66d89240c2cd' => 'Printus',
+ '661bc56d-f77f-6d98-98be-672a06cf59ee' => 'AVCedukacja',
+ '8d09cc5f-2348-6d22-80dc-6736057e390d' => 'Seltino',
),
'ecmproducts_category_dom' =>
array (
diff --git a/modules/EcmActions/metadata/detailviewdefs.php b/modules/EcmActions/metadata/detailviewdefs.php
index 0e601b1c..1fb75db6 100644
--- a/modules/EcmActions/metadata/detailviewdefs.php
+++ b/modules/EcmActions/metadata/detailviewdefs.php
@@ -58,14 +58,22 @@ $viewdefs [$module_name] = array (
0 => array (
'name' => 'category',
'label' => 'LBL_CATEGORY'
- )
+ ),
+ 1 => array (
+ 'name' => 'shape',
+ 'label' => 'Forma'
+ )
),
2 => array (
0 => array (
'name' => 'time',
'customCode' => '{$fields.time.value}',
'label' => 'LBL_TIME'
- )
+ ),
+ 1 => array (
+ 'name' => 'performance',
+ 'label' => 'Wydajność'
+ )
),
3 => array (
0 => array (
diff --git a/modules/EcmActions/metadata/editviewdefs.php b/modules/EcmActions/metadata/editviewdefs.php
index 6276eaa3..d91a8e9d 100644
--- a/modules/EcmActions/metadata/editviewdefs.php
+++ b/modules/EcmActions/metadata/editviewdefs.php
@@ -1,31 +1,31 @@
array (
- 'templateMeta' => array (
- 'maxColumns' => '2',
- 'widths' => array (
- 0 => array (
- 'label' => '10',
- 'field' => '30'
- ),
- 1 => array (
- 'label' => '10',
- 'field' => '30'
- )
- ),
- 'useTabs' => false,
- 'tabDefs' => array (
- 'DEFAULT' => array (
- 'newTab' => false,
- 'panelDefault' => 'expanded'
- ),
- 'LBL_EDITVIEW_PANEL1' => array (
- 'newTab' => false,
- 'panelDefault' => 'expanded'
- )
- ),
- 'javascript' => '
+$viewdefs [$module_name] = array(
+ 'EditView' => array(
+ 'templateMeta' => array(
+ 'maxColumns' => '2',
+ 'widths' => array(
+ 0 => array(
+ 'label' => '10',
+ 'field' => '30'
+ ),
+ 1 => array(
+ 'label' => '10',
+ 'field' => '30'
+ )
+ ),
+ 'useTabs' => false,
+ 'tabDefs' => array(
+ 'DEFAULT' => array(
+ 'newTab' => false,
+ 'panelDefault' => 'expanded'
+ ),
+ 'LBL_EDITVIEW_PANEL1' => array(
+ 'newTab' => false,
+ 'panelDefault' => 'expanded'
+ )
+ ),
+ 'javascript' => '
@@ -33,63 +33,73 @@ $viewdefs [$module_name] = array (
',
- 'includes' => array (
- 0 => array (
- 'file' => 'include/JSON.js'
- )
- )
- ),
- 'panels' => array (
- 'default' => array (
- 0 => array (
- 0 => array (
- 'name' => 'name',
- 'label' => 'LBL_NAME',
- 'tabIndex' => '100',
- 'displayParams' => array (
- 'size' => 40
- )
- ),
- 1 => array (
- 'name' => 'indeks',
- 'label' => 'LBL_INDEKS',
- 'tabindex' => '120',
- )
- ),
- 1 => array (
- 0 => array (
- 'name' => 'category',
- 'label' => 'LBL_CATEGORY',
- 'tabIndex' => '101',
- )
- ),
- 2 => array (
- 0 => array (
- 'name' => 'time',
- 'customCode' => "{include file='modules/EcmActions/tpl/EditViewTime.tpl'}",
- 'label' => 'LBL_TIME',
- 'tabIndex' => '105',
- )
- ),
- 3 => array (
- 0 => array (
- 'name' => 'cost_action',
- 'label' => 'LBL_COST_ACTION',
- 'tabIndex' => '106',
- )
- ),
- 4 => array (
- 0 => array (
- 'name' => 'cost_hour',
- 'label' => 'LBL_COST_HOUR',
- 'tabIndex' => '107',
- )
- ),
- 6 => array (
- 0 => 'description'
- )
- )
- )
- )
-);
+ 'includes' => array(
+ 0 => array(
+ 'file' => 'include/JSON.js'
+ )
+ )
+ ),
+ 'panels' => array(
+ 'default' => array(
+ 0 => array(
+ 0 => array(
+ 'name' => 'name',
+ 'label' => 'LBL_NAME',
+ 'tabIndex' => '100',
+ 'displayParams' => array(
+ 'size' => 40
+ )
+ ),
+ 1 => array(
+ 'name' => 'indeks',
+ 'label' => 'LBL_INDEKS',
+ 'tabindex' => '120',
+ )
+ ),
+ 1 => array(
+ 0 => array(
+ 'name' => 'category',
+ 'label' => 'LBL_CATEGORY',
+ 'tabIndex' => '101',
+ ),
+ 1 => array(
+ 'name' => 'shape',
+ 'label' => 'Forma',
+ 'tabIndex' => '101',
+ )
+ ),
+ 2 => array(
+ 0 => array(
+ 'name' => 'time',
+ 'customCode' => "{include file='modules/EcmActions/tpl/EditViewTime.tpl'}",
+ 'label' => 'LBL_TIME',
+ 'tabIndex' => '105',
+ ),
+ 1 => array (
+ 'name' => 'performance',
+ 'label' => 'Wydajność',
+ 'tabIndex' => '101',
+ )
+ ),
+ 3 => array(
+ 0 => array(
+ 'name' => 'cost_action',
+ 'label' => 'LBL_COST_ACTION',
+ 'tabIndex' => '106',
+ )
+ ),
+ 4 => array(
+ 0 => array(
+ 'name' => 'cost_hour',
+ 'label' => 'LBL_COST_HOUR',
+ 'tabIndex' => '107',
+ )
+ ),
+ 6 => array(
+ 0 => 'description'
+ )
+ )
+ )
+ )
+)
?>
diff --git a/modules/EcmActions/vardefs.php b/modules/EcmActions/vardefs.php
index 85262184..24c6061b 100644
--- a/modules/EcmActions/vardefs.php
+++ b/modules/EcmActions/vardefs.php
@@ -129,7 +129,20 @@ $dictionary ['EcmAction'] = array (
'size' => '20',
'enable_range_search' => false,
'precision' => '2'
- )
+ ),
+ 'shape' => array(
+ 'name' => 'shape',
+ 'options' => 'ecmproducts_shape_dom',
+ 'vname' => 'Forma',
+ 'len' => 36,
+ 'type' => 'enum'
+ ),
+ 'performance' => array(
+ 'name' => 'performance',
+ 'vname' => 'Wydajność',
+ 'type' => 'decimal',
+ 'len' => '15,2',
+ ),
),
'relationships' => array (),
'optimistic_locking' => true,
diff --git a/modules/EcmProducts/javascript/stockAddress.js b/modules/EcmProducts/javascript/stockAddress.js
index 462488ab..991aef82 100644
--- a/modules/EcmProducts/javascript/stockAddress.js
+++ b/modules/EcmProducts/javascript/stockAddress.js
@@ -114,12 +114,12 @@ function isAddressValid(id) {
return false;
}
const segments = $(id).val().split(".");
- if (parseInt(segments[0]) > 17) {
- $(id + "Error").html("Błąd pierwszego segmentu [zakres 01-20]");
+ if (parseInt(segments[0]) > 99) {
+ $(id + "Error").html("Błąd pierwszego segmentu [zakres 01-99]");
return false;
}
- if (parseInt(segments[1]) > 15) {
- $(id + "Error").html("Błąd drugiego segmentu [zakres 01-15]");
+ if (parseInt(segments[1]) > 99) {
+ $(id + "Error").html("Błąd drugiego segmentu [zakres 01-99]");
return false;
}
return true;
@@ -138,24 +138,24 @@ $(document).ready(function () {
$("#address").mask("AB.CD.E.F", {
placeholder: "__.__._._",
translation: {
- A: { pattern: /[0-2]/ },
+ A: { pattern: /[0-9]/ },
B: { pattern: /[0-9]/ },
- C: { pattern: /[0-1]/ },
+ C: { pattern: /[0-9]/ },
D: { pattern: /[0-9]/ },
- E: { pattern: /[1-5]/ },
- F: { pattern: /[0-5]/ },
+ E: { pattern: /[1-9]/ },
+ F: { pattern: /[0-9]/ },
}
});
if ($("#newAddress")) {
$("#newAddress").mask("AB.CD.E.F", {
placeholder: "__.__._._",
translation: {
- A: { pattern: /[0-2]/ },
+ A: { pattern: /[0-9]/ },
B: { pattern: /[0-9]/ },
- C: { pattern: /[0-1]/ },
+ C: { pattern: /[0-9]/ },
D: { pattern: /[0-9]/ },
- E: { pattern: /[1-5]/ },
- F: { pattern: /[0-5]/ },
+ E: { pattern: /[1-9]/ },
+ F: { pattern: /[0-9]/ },
}
});
$("#newAddress").focus(() => $("#newAddressError").html(""));
diff --git a/modules/EcmProducts/stockAddress.php b/modules/EcmProducts/stockAddress.php
index d1c04c41..c4911210 100644
--- a/modules/EcmProducts/stockAddress.php
+++ b/modules/EcmProducts/stockAddress.php
@@ -35,7 +35,7 @@
break;
}
} else {
- $pRes = $db->query("SELECT DISTINCT id, code FROM ecmproducts WHERE deleted=0 AND active=1");
+ $pRes = $db->query("SELECT DISTINCT id, code FROM ecmproducts WHERE deleted=0 AND status != 'end_of_life';");
$codes = array();
while ($p = $db -> fetchByAssoc($pRes)) {
$index = str_replace("'", "'", $p['code']);
diff --git a/modules/EcmReports/BimIT-Reports/lib/xlsxGenerator.php b/modules/EcmReports/BimIT-Reports/lib/xlsxGenerator.php
new file mode 100644
index 00000000..9475ebe4
--- /dev/null
+++ b/modules/EcmReports/BimIT-Reports/lib/xlsxGenerator.php
@@ -0,0 +1,1200 @@
+subject = '';
+ $this->title = '';
+ $this->author = '';
+ $this->company = '';
+ $this->manager = '';
+ $this->description = '';
+ $this->keywords = '';
+ $this->category = '';
+ $this->language = 'en-US';
+ $this->lastModifiedBy = '';
+ $this->application = __CLASS__;
+
+ $this->curSheet = -1;
+ $this->defaultFont = 'Calibri';
+ $this->defaultFontSize = 10;
+ $this->rtl = false;
+ $this->sheets = [];
+ $this->extLinkId = 0;
+ $this->SI = []; // sharedStrings index
+ $this->SI_KEYS = []; // & keys
+
+ // https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_numFmts_topic_ID0E6KK6.html
+ $this->NF = [
+ self::N_RUB => '#,##0.00\ "₽"',
+ self::N_DOLLAR => '[$$-1]#,##0.00',
+ self::N_EURO => '#,##0.00\ [$€-1]'
+ ];
+ $this->NF_KEYS = array_flip($this->NF);
+
+ $this->BR_STYLE = [
+ self::B_NONE => 'none',
+ self::B_THIN => 'thin',
+ self::B_MEDIUM => 'medium',
+ self::B_DASHED => 'dashed',
+ self::B_DOTTED => 'dotted',
+ self::B_THICK => 'thick',
+ self::B_DOUBLE => 'double',
+ self::B_HAIR => 'hair',
+ self::B_MEDIUM_DASHED => 'mediumDashed',
+ self::B_DASH_DOT => 'dashDot',
+ self::B_MEDIUM_DASH_DOT => 'mediumDashDot',
+ self::B_DASH_DOT_DOT => 'dashDotDot',
+ self::B_MEDIUM_DASH_DOT_DOT => 'mediumDashDotDot',
+ self::B_SLANT_DASH_DOT => 'slantDashDot'
+ ];
+
+ $this->XF = [ // styles 0 - num fmt, 1 - align, 2 - font, 3 - fill, 4 - font color, 5 - bgcolor, 6 - border, 7 - font size
+ [self::N_NORMAL, self::A_DEFAULT, self::F_NORMAL, self::FL_NONE, 0, 0, '', 0],
+ [self::N_NORMAL, self::A_DEFAULT, self::F_NORMAL, self::FL_GRAY_125, 0, 0, '', 0], // hack
+ ];
+ $this->XF_KEYS[implode('-', $this->XF[0])] = 0; // & keys
+ $this->XF_KEYS[implode('-', $this->XF[1])] = 1;
+ $this->template = [
+ '_rels/.rels' => ''."\r\n"
+ .''."\r\n"
+ .''."\r\n"
+ .''."\r\n"
+ .''."\r\n"
+ .'',
+ 'docProps/app.xml' => ''."\r\n"
+ .''."\r\n"
+ .'0'."\r\n"
+ .'{APP}'."\r\n"
+ .'{COMPANY}'."\r\n"
+ .'{MANAGER}'."\r\n"
+ .'',
+ 'docProps/core.xml' => ''."\r\n"
+ .''."\r\n"
+ .'{DATE}'."\r\n"
+ .'{TITLE}'."\r\n"
+ .'{SUBJECT}'."\r\n"
+ .'{AUTHOR}'."\r\n"
+ .'{LAST_MODIFY_BY}'."\r\n"
+ .'{KEYWORD}'."\r\n"
+ .'{DESCRIPTION}'."\r\n"
+ .'{CATEGORY}'."\r\n"
+ .'{LANGUAGE}'."\r\n"
+ .'{DATE}'."\r\n"
+ .'1'."\r\n"
+ .'',
+ 'xl/_rels/workbook.xml.rels' => ''."\r\n"
+ .''
+ ."\r\n{RELS}\r\n",
+ 'xl/worksheets/sheet1.xml' => ''."\r\n"
+ .''."\r\n"
+ .''."\r\n"
+ ."{SHEETVIEWS}\r\n{COLS}\r\n{ROWS}\r\n{AUTOFILTER}{MERGECELLS}{HYPERLINKS}",
+ 'xl/worksheets/_rels/sheet1.xml.rels' => ''."\r\n"
+ .'{HYPERLINKS}',
+ 'xl/sharedStrings.xml' => ''."\r\n"
+ .'{STRINGS}',
+ 'xl/styles.xml' => ''."\r\n"
+ .''
+ ."\r\n{NUMFMTS}\r\n{FONTS}\r\n{FILLS}\r\n{BORDERS}\r\n"
+ .''
+ ."\r\n{XF}\r\n"
+ .'',
+ 'xl/workbook.xml' => ''."\r\n"
+ .''."\r\n"
+ .'{SHEETS}',
+ '[Content_Types].xml' => ''."\r\n"
+ .''."\r\n"
+ .''."\r\n"
+ .''."\r\n"
+ .''."\r\n"
+ .''."\r\n"
+ .''."\r\n"
+ .'
+'
+ ."\r\n{TYPES}",
+ ];
+ }
+ public static function create($title = null)
+ {
+ $xlsx = new static();
+ if ($title) {
+ $xlsx->setTitle($title);
+ }
+ return $xlsx;
+ }
+
+ public static function fromArray(array $rows, $sheetName = null)
+ {
+ $xlsx = new static();
+ $xlsx->addSheet($rows, $sheetName);
+ if ($sheetName) {
+ $xlsx->setTitle($sheetName);
+ }
+ return $xlsx;
+ }
+
+ public function addSheet(array $rows, $name = null)
+ {
+ $this->curSheet++;
+ if ($name === null) { // autogenerated sheet names
+ $name = ($this->title ? mb_substr($this->title, 0, 31) : 'Sheet') . ($this->curSheet + 1);
+ } else {
+ $name = mb_substr($name, 0, 31);
+ $names = [];
+ foreach ($this->sheets as $sh) {
+ $names[mb_strtoupper($sh['name'])] = 1;
+ }
+ for ($i = 0; $i < 100; $i++) {
+ $postfix = ' (' . $i . ')';
+ $new_name = ($i === 0) ? $name : $name . $postfix;
+ if (mb_strlen($new_name) > 31) {
+ $new_name = mb_substr($name, 0, 31 - mb_strlen($postfix)) . $postfix;
+ }
+ $NEW_NAME = mb_strtoupper($new_name);
+ if (!isset($names[$NEW_NAME])) {
+ $name = $new_name;
+ break;
+ }
+ }
+ }
+ $this->sheets[$this->curSheet] = ['name' => $name, 'hyperlinks' => [], 'mergecells' => [], 'colwidth' => [], 'autofilter' => '', 'frozen' => ''];
+ if (isset($rows[0]) && is_array($rows[0])) {
+ $this->sheets[$this->curSheet]['rows'] = $rows;
+ } else {
+ $this->sheets[$this->curSheet]['rows'] = [];
+ }
+ return $this;
+ }
+
+ public function __toString()
+ {
+ $fh = fopen('php://memory', 'wb');
+ if (!$fh) {
+ return '';
+ }
+ if (!$this->_write($fh)) {
+ fclose($fh);
+ return '';
+ }
+ $size = ftell($fh);
+ fseek($fh, 0);
+ return (string)fread($fh, $size);
+ }
+
+ public function save()
+ {
+ return $this->saveAs(($this->title ?: gmdate('YmdHi')) . '.xlsx');
+ }
+ public function saveAs($filename)
+ {
+ $fh = fopen(str_replace(["\0","\r","\n","\t",'"'], '', $filename), 'wb');
+ if (!$fh) {
+ return false;
+ }
+ if (!$this->_write($fh)) {
+ fclose($fh);
+ return false;
+ }
+ fclose($fh);
+ return true;
+ }
+
+ public function download()
+ {
+ return $this->downloadAs(($this->title ?: gmdate('YmdHi')) . '.xlsx');
+ }
+
+ public function downloadAs($filename)
+ {
+ $fh = fopen('php://memory', 'wb');
+ if (!$fh) {
+ return false;
+ }
+ if (!$this->_write($fh)) {
+ fclose($fh);
+ return false;
+ }
+ $size = ftell($fh);
+ header('Content-type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+ header('Content-Disposition: attachment; filename="' . str_replace(["\0","\r","\n","\t",'"'], '', $filename) . '"');
+ header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', time()));
+ header('Content-Length: ' . $size);
+ while (ob_get_level()) {
+ ob_end_clean();
+ }
+ fseek($fh, 0);
+ //Some servers disable fpassthru function. The alternative, stream_get_contents, use more memory
+ if (function_exists('fpassthru')) {
+ fpassthru($fh);
+ } else {
+ echo stream_get_contents($fh);
+ }
+ fclose($fh);
+ return true;
+ }
+
+ protected function _write($fh)
+ {
+ $dirSignatureE = "\x50\x4b\x05\x06"; // end of central dir signature
+ $zipComments = 'Generated by ' . __CLASS__ . ' PHP class, thanks sergey.shuchkin@gmail.com';
+ if (!$fh) {
+ return false;
+ }
+ $cdrec = ''; // central directory content
+ $entries = 0; // number of zipped files
+ $cnt_sheets = count($this->sheets);
+ if ($cnt_sheets === 0) {
+ $this->addSheet([], 'No data');
+ $cnt_sheets = 1;
+ }
+ foreach ($this->template as $cfilename => $template) {
+ if ($cfilename === 'xl/_rels/workbook.xml.rels') {
+ $s = '';
+ for ($i = 0; $i < $cnt_sheets; $i++) {
+ $s .= '\r\n";
+ }
+ $s .= '' . "\r\n";
+ $s .= '';
+
+ $template = str_replace('{RELS}', $s, $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } elseif ($cfilename === 'xl/workbook.xml') {
+ $s = '';
+ foreach ($this->sheets as $k => $v) {
+ $s .= '';
+ }
+ $search = ['{SHEETS}', '{APP}'];
+ $replace = [$s, self::esc($this->application)];
+ $template = str_replace($search, $replace, $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } elseif ($cfilename === 'docProps/app.xml') {
+ $search = ['{APP}', '{COMPANY}', '{MANAGER}'];
+ $replace = [self::esc($this->application), self::esc($this->company), self::esc($this->manager)];
+ $template = str_replace($search, $replace, $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } elseif ($cfilename === 'docProps/core.xml') {
+ $search = ['{DATE}', '{AUTHOR}', '{TITLE}', '{SUBJECT}', '{KEYWORD}', '{DESCRIPTION}', '{CATEGORY}', '{LANGUAGE}', '{LAST_MODIFY_BY}'];
+ $replace = [gmdate('Y-m-d\TH:i:s\Z'), self::esc($this->author), self::esc($this->title), self::esc($this->subject), self::esc($this->keywords), self::esc($this->description), self::esc($this->category), self::esc($this->language), self::esc($this->lastModifiedBy)];
+ $template = str_replace($search, $replace, $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } elseif ($cfilename === 'xl/sharedStrings.xml') {
+ $si_cnt = count($this->SI);
+ if ($si_cnt) {
+ $si = [];
+ foreach ($this->SI as $s) {
+ $si[] = '' . (preg_match('/^\s|\s$/', $s) ? '' . $s . '' : '' . $s . '') . '';
+ }
+ $template = str_replace(['{CNT}', '{STRINGS}'], [$si_cnt, implode("\r\n", $si)], $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ }
+ } elseif ($cfilename === 'xl/worksheets/sheet1.xml') {
+ foreach ($this->sheets as $k => $v) {
+ $filename = 'xl/worksheets/sheet' . ($k + 1) . '.xml';
+ $xml = $this->_sheetToXML($k, $template);
+ $this->_writeEntry($fh, $cdrec, $filename, $xml);
+ $entries++;
+ }
+ $xml = null;
+ } elseif ($cfilename === 'xl/worksheets/_rels/sheet1.xml.rels') {
+ foreach ($this->sheets as $k => $v) {
+ if ($this->extLinkId) {
+ $RH = [];
+ $filename = 'xl/worksheets/_rels/sheet' . ($k + 1) . '.xml.rels';
+ foreach ($v['hyperlinks'] as $h) {
+ if ($h['ID']) {
+ $RH[] = '';
+ }
+ }
+ $xml = str_replace('{HYPERLINKS}', implode("\r\n", $RH), $template);
+ $this->_writeEntry($fh, $cdrec, $filename, $xml);
+ $entries++;
+ }
+ }
+ $xml = null;
+ } elseif ($cfilename === '[Content_Types].xml') {
+ $TYPES = [''];
+ foreach ($this->sheets as $k => $v) {
+ $TYPES[] = '';
+ if ($this->extLinkId) {
+ $TYPES[] = '';
+ }
+ }
+ $template = str_replace('{TYPES}', implode("\r\n", $TYPES), $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } elseif ($cfilename === 'xl/styles.xml') {
+ $NF = $XF = $FONTS = $F_KEYS = $FILLS = $FL_KEYS = [];
+ $BR = [''];
+ $BR_KEYS = [0 => 0];
+ foreach ($this->NF as $k => $v) {
+ $NF[] = '';
+ }
+ foreach ($this->XF as $xf) {
+ // 0 - num fmt, 1 - align, 2 - font, 3 - fill, 4 - font color, 5 - bgcolor, 6 - border, 7 - font size
+ // fonts
+ $F_KEY = $xf[2] . '-' . $xf[4] . '-' . $xf[7];
+ if (isset($F_KEYS[$F_KEY])) {
+ $F_ID = $F_KEYS[$F_KEY];
+ } else {
+ $F_ID = $F_KEYS[$F_KEY] = count($FONTS);
+ $FONTS[] = ''
+ . ($xf[7] ? '' : '')
+ . ($xf[2] & self::F_BOLD ? '' : '')
+ . ($xf[2] & self::F_ITALIC ? '' : '')
+ . ($xf[2] & self::F_UNDERLINE ? '' : '')
+ . ($xf[2] & self::F_STRIKE ? '' : '')
+ . ($xf[2] & self::F_HYPERLINK ? '' : '')
+ . ($xf[2] & self::F_COLOR ? '' : '')
+ . '';
+ }
+ // fills
+ $FL_KEY = $xf[3] . '-' . $xf[5];
+ if (isset($FL_KEYS[$FL_KEY])) {
+ $FL_ID = $FL_KEYS[$FL_KEY];
+ } else {
+ $FL_ID = $FL_KEYS[$FL_KEY] = count($FILLS);
+ $FILLS[] = '' : ' />')
+ . '';
+ }
+ $align = '';
+ if ($xf[1] & self::A_LEFT) {
+ $align .= ' horizontal="left"';
+ } elseif ($xf[1] & self::A_RIGHT) {
+ $align .= ' horizontal="right"';
+ } elseif ($xf[1] & self::A_CENTER) {
+ $align .= ' horizontal="center"';
+ }
+ if ($xf[1] & self::A_TOP) {
+ $align .= ' vertical="top"';
+ } elseif ($xf[1] & self::A_MIDDLE) {
+ $align .= ' vertical="center"';
+ } elseif ($xf[1] & self::A_BOTTOM) {
+ $align .= ' vertical="bottom"';
+ }
+ if ($xf[1] & self::A_WRAPTEXT) {
+ $align .= ' wrapText="1"';
+ }
+
+ // border
+ $BR_ID = 0;
+ if ($xf[6] !== '') {
+ $b = $xf[6];
+ if (isset($BR_KEYS[$b])) {
+ $BR_ID = $BR_KEYS[$b];
+ } else {
+ $BR_ID = count($BR_KEYS);
+ $BR_KEYS[$b] = $BR_ID;
+ $border = '';
+ $ba = explode(' ', $b);
+ if (!isset($ba[1])) {
+ $ba[] = $ba[0];
+ $ba[] = $ba[0];
+ $ba[] = $ba[0];
+ }
+ if (!isset($ba[4])) { // diagonal
+ $ba[] = 'none';
+ }
+ $sides = ['left' => 3, 'right' => 1, 'top' => 0, 'bottom' => 2, 'diagonal' => 4];
+ foreach ($sides as $side => $idx) {
+ $s = 'thin';
+ $c = '';
+ $va = explode('#', $ba[$idx]);
+ if (isset($va[1])) {
+ $s = $va[0] === '' ? 'thin' : $va[0];
+ $c = $va[1];
+ } elseif (in_array($va[0], $this->BR_STYLE, true)) {
+ $s = $va[0];
+ } else {
+ $c = $va[0];
+ }
+ if (strlen($c) === 6) {
+ $c = 'FF' . $c;
+ }
+ if ($s && $s !== 'none') {
+ $border .= '<' . $side . ' style="' . $s . '">'
+ . ''
+ . '' . $side . '>';
+ } else {
+ $border .= '<' . $side . '/>';
+ }
+ }
+ $border .= '';
+ $BR[] = $border;
+ }
+ }
+ $XF[] = ' 0 ? ' applyNumberFormat="1"' : '')
+ . ($F_ID > 0 ? ' applyFont="1"' : '')
+ . ($FL_ID > 0 ? ' applyFill="1"' : '')
+ . ($BR_ID > 0 ? ' applyBorder="1"' : '')
+ . ($align ? ' applyAlignment="1">' : '/>');
+ }
+ // wrap collections
+ array_unshift($NF, '');
+ $NF[] = '';
+ array_unshift($XF, '');
+ $XF[] = '';
+ array_unshift($FONTS, '');
+ $FONTS[] = '';
+ array_unshift($FILLS, '');
+ $FILLS[] = '';
+ array_unshift($BR, '');
+ $BR[] = '';
+
+ $template = str_replace(
+ ['{NUMFMTS}', '{FONTS}', '{XF}', '{FILLS}', '{BORDERS}'],
+ [implode("\r\n", $NF), implode("\r\n", $FONTS), implode("\r\n", $XF), implode("\r\n", $FILLS), implode("\r\n", $BR)],
+ $template
+ );
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } else {
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ }
+ }
+ $before_cd = ftell($fh);
+ fwrite($fh, $cdrec);
+ // end of central dir
+ fwrite($fh, $dirSignatureE);
+ fwrite($fh, pack('v', 0)); // number of this disk
+ fwrite($fh, pack('v', 0)); // number of the disk with the start of the central directory
+ fwrite($fh, pack('v', $entries)); // total # of entries "on this disk"
+ fwrite($fh, pack('v', $entries)); // total # of entries overall
+ fwrite($fh, pack('V', mb_strlen($cdrec, '8bit'))); // size of central dir
+ fwrite($fh, pack('V', $before_cd)); // offset to start of central dir
+ fwrite($fh, pack('v', mb_strlen($zipComments, '8bit'))); // .zip file comment length
+ fwrite($fh, $zipComments);
+
+ return true;
+ }
+
+ protected function _writeEntry($fh, &$cdrec, $cfilename, $data)
+ {
+ $zipSignature = "\x50\x4b\x03\x04"; // local file header signature
+ $dirSignature = "\x50\x4b\x01\x02"; // central dir header signature
+
+ $e = [];
+ $e['uncsize'] = mb_strlen($data, '8bit');
+ // if data to compress is too small, just store it
+ if ($e['uncsize'] < 256) {
+ $e['comsize'] = $e['uncsize'];
+ $e['vneeded'] = 10;
+ $e['cmethod'] = 0;
+ $zdata = $data;
+ } else { // otherwise, compress it
+ $zdata = gzcompress($data);
+ $zdata = substr(substr($zdata, 0, -4), 2); // fix crc bug (thanks to Eric Mueller)
+ $e['comsize'] = mb_strlen($zdata, '8bit');
+ $e['vneeded'] = 10;
+ $e['cmethod'] = 8;
+ }
+ $e['bitflag'] = 0;
+ $e['crc_32'] = crc32($data);
+
+ // Convert date and time to DOS Format, and set then
+ $date = getdate();
+ $e['dostime'] = (
+ (($date['year'] - 1980) << 25)
+ | ($date['mon'] << 21)
+ | ($date['mday'] << 16)
+ | ($date['hours'] << 11)
+ | ($date['minutes'] << 5)
+ | ($date['seconds'] >> 1)
+ );
+
+ $e['offset'] = ftell($fh);
+
+ fwrite($fh, $zipSignature);
+ fwrite($fh, pack('v', $e['vneeded'])); // version_needed
+ fwrite($fh, pack('v', $e['bitflag'])); // general_bit_flag
+ fwrite($fh, pack('v', $e['cmethod'])); // compression_method
+ fwrite($fh, pack('V', $e['dostime'])); // lastmod datetime
+ fwrite($fh, pack('V', $e['crc_32'])); // crc-32
+ fwrite($fh, pack('V', $e['comsize'])); // compressed_size
+ fwrite($fh, pack('V', $e['uncsize'])); // uncompressed_size
+ fwrite($fh, pack('v', mb_strlen($cfilename, '8bit'))); // file_name_length
+ fwrite($fh, pack('v', 0)); // extra_field_length
+ fwrite($fh, $cfilename); // file_name
+ // ignoring extra_field
+ fwrite($fh, $zdata);
+
+ // Append it to central dir
+ $e['external_attributes'] = (substr($cfilename, -1) === '/' && !$zdata) ? 16 : 32; // Directory or file name
+ $e['comments'] = '';
+
+ $cdrec .= $dirSignature;
+ $cdrec .= "\x0\x0"; // version made by
+ $cdrec .= pack('v', $e['vneeded']); // version needed to extract
+ $cdrec .= "\x0\x0"; // general bit flag
+ $cdrec .= pack('v', $e['cmethod']); // compression method
+ $cdrec .= pack('V', $e['dostime']); // lastmod datetime
+ $cdrec .= pack('V', $e['crc_32']); // crc32
+ $cdrec .= pack('V', $e['comsize']); // compressed filesize
+ $cdrec .= pack('V', $e['uncsize']); // uncompressed filesize
+ $cdrec .= pack('v', mb_strlen($cfilename, '8bit')); // file name length
+ $cdrec .= pack('v', 0); // extra field length
+ $cdrec .= pack('v', mb_strlen($e['comments'], '8bit')); // file comment length
+ $cdrec .= pack('v', 0); // disk number start
+ $cdrec .= pack('v', 0); // internal file attributes
+ $cdrec .= pack('V', $e['external_attributes']); // internal file attributes
+ $cdrec .= pack('V', $e['offset']); // relative offset of local header
+ $cdrec .= $cfilename;
+ $cdrec .= $e['comments'];
+ }
+
+ protected function _sheetToXML($idx, $template)
+ {
+ // locale floats fr_FR 1.234,56 -> 1234.56
+ $_loc = setlocale(LC_NUMERIC, 0);
+ setlocale(LC_NUMERIC, 'C');
+ $COLS = [];
+ $ROWS = [];
+ // $SHEETVIEWS = 'rtl ? ' rightToLeft="1"' : '').'>';
+ $SHEETVIEWS = '';
+ $PANE = '';
+ if (count($this->sheets[$idx]['rows'])) {
+ $ROWS[] = '';
+ if ($this->sheets[$idx]['frozen'] !== '' || isset($this->sheets[$idx]['frozen'][0]) || isset($this->sheets[$idx]['frozen'][1])) {
+ // $AC = 'A1'; // Active Cell
+ $x = $y = 0;
+ if (is_string($this->sheets[$idx]['frozen'])) {
+ $AC = $this->sheets[$idx]['frozen'];
+ self::cell2coord($AC, $x, $y);
+ } else {
+ if (isset($this->sheets[$idx]['frozen'][0])) {
+ $x = $this->sheets[$idx]['frozen'][0];
+ }
+ if (isset($this->sheets[$idx]['frozen'][1])) {
+ $y = $this->sheets[$idx]['frozen'][1];
+ }
+ $AC = self::coord2cell($x, $y);
+ }
+ if ($x > 0 || $y > 0) {
+ $split = '';
+ if ($x > 0) {
+ $split .= ' xSplit="' . $x . '"';
+ }
+ if ($y > 0) {
+ $split .= ' ySplit="' . $y . '"';
+ }
+ $activepane = 'bottomRight';
+ if ($x > 0 && $y === 0) {
+ $activepane = 'topRight';
+ }
+ if ($x === 0 && $y > 0) {
+ $activepane = 'bottomLeft';
+ }
+ $PANE .= '';
+ $PANE .= '';
+ }
+ }
+ if ($this->rtl || $PANE) {
+ $SHEETVIEWS .= '
+rtl ? ' rightToLeft="1"' : '');
+ $SHEETVIEWS .= $PANE ? ">\r\n" . $PANE . "\r\n" : ' />';
+ $SHEETVIEWS .= "\r\n";
+ }
+ $COLS[] = '';
+ $CUR_ROW = 0;
+ $COL = [];
+ foreach ($this->sheets[$idx]['rows'] as $r) {
+ $CUR_ROW++;
+ $row = '';
+ $CUR_COL = 0;
+ $RH = 0; // row height
+ foreach ($r as $v) {
+ $CUR_COL++;
+ if (!isset($COL[$CUR_COL])) {
+ $COL[$CUR_COL] = 0;
+ }
+ $cname = self::coord2cell($CUR_COL-1) . $CUR_ROW;
+ if ($v === null || $v === '') {
+ $row .= '';
+ continue;
+ }
+ $ct = $cv = $cf = null;
+ $N = $A = $F = $FL = $C = $BG = $FS = $FR = 0;
+ $BR = '';
+ if (is_string($v)) {
+ if ($v[0] === "\0") { // RAW value as string
+ $v = substr($v, 1);
+ $vl = mb_strlen($v);
+ } else {
+ if (strpos($v, '<') !== false) { // tags?
+ if (strpos($v, '') !== false) {
+ $F += self::F_BOLD;
+ }
+ if (strpos($v, '') !== false) {
+ $F += self::F_ITALIC;
+ }
+ if (strpos($v, '') !== false) {
+ $F += self::F_UNDERLINE;
+ }
+ if (strpos($v, '') !== false) {
+ $F += self::F_STRIKE;
+ }
+ if (preg_match('/