| Server IP : 188.114.96.3 / Your IP : 216.73.216.143 Web Server : Apache System : Linux sv058.home.net.pl 6.8.2 husak_dirty_pages_fix #1 SMP PREEMPT_DYNAMIC Tue Apr 2 22:06:51 CEST 2024 x86_64 User : ( 3194769) PHP Version : 8.5.12 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /polana/includes/ |
Upload File : |
<?php
declare(strict_types=1);
/**
* TKLM XLSX minimalny: natywny Office Open XML, ZIP bez rozszerzenia ZipArchive
* i bez plików tymczasowych. Wykorzystywany do dziennego planu sali.
*/
function polana_xlsx_xml(string $value): string
{
// XML 1.0 nie obsługuje części znaków kontrolnych.
$value = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/u', ' ', $value) ?? '';
return htmlspecialchars($value, ENT_QUOTES | ENT_XML1 | ENT_SUBSTITUTE, 'UTF-8');
}
function polana_xlsx_column(int $index): string
{
$str = '';
for ($i = $index + 1; $i > 0; $i = intdiv($i - 1, 26)) {
$str = chr(65 + ($i - 1) % 26) . $str;
}
return $str;
}
function polana_xlsx_cell(int $col, int $row, mixed $value, int $style = 0, bool $number = false): string
{
$ref = polana_xlsx_column($col) . $row;
if ($number) {
return '<c r="' . $ref . '" s="' . $style . '"><v>' . (int)$value . '</v></c>';
}
// inlineStr zabezpiecza przed formułami Excel/CSV injection także dla =,+,-,@.
$value = (string)$value;
if (strlen($value) > 32000) $value = substr($value, 0, 32000);
return '<c r="' . $ref . '" s="' . $style . '" t="inlineStr"><is><t xml:space="preserve">'
. polana_xlsx_xml($value) . '</t></is></c>';
}
/** @param array<int,string> $files */
function polana_xlsx_zip(array $files): string
{
$fileEntries = '';
$central = '';
$offset = 0;
foreach ($files as $filename => $contents) {
$length = strlen($contents);
$nameLength = strlen($filename);
$crc = crc32($contents);
// ZIP STORED bez kompresji; brak zależności od PHP zip, zlib i /tmp.
$local = pack('VvvvvvVVVvv', 0x04034b50, 20, 0, 0, 0, 0, $crc, $length, $length, $nameLength, 0) . $filename;
$fileEntries .= $local . $contents;
$central .= pack('VvvvvvvVVVvvvvvVV', 0x02014b50, 20, 20, 0, 0, 0, 0, $crc, $length, $length, $nameLength, 0, 0, 0, 0, 0, $offset) . $filename;
$offset += strlen($local) + $length;
}
$num = count($files);
return $fileEntries . $central . pack('VvvvvVVv', 0x06054b50, 0, 0, $num, $num, strlen($central), $offset, 0);
}
/** @param array<int,array<string,mixed>> $rows @param array<int,array<string,int>> $hours */
function polana_reservation_xlsx(string $date, array $rows, array $hours, array $counts): string
{
$names = ['Godzina','Gość','Osoby','Status','Stolik','Kelner','Telefon','Uwagi gościa','Uwagi obsługi','Kod','Kontrola'];
$body = '';
$rowNum = 7;
foreach ($rows as $r) {
$line = [
substr((string)$r['reservation_time'], 0, 5), (string)$r['guest_name'], (int)$r['guests'],
(string)($r['_status_label'] ?? ''), (string)($r['table_label'] ?? ''), (string)($r['_waiter_name'] ?? ''),
(string)($r['phone'] ?? ''), (string)($r['notes'] ?? ''), (string)($r['internal_note'] ?? ''),
(string)($r['reservation_code'] ?? ''), !empty($r['_duplicate']) ? 'MOŻLIWY DUPLIKAT' : '',
];
$cells = '';
$style = !empty($r['_duplicate']) ? 5 : (((string)($r['status'] ?? '') === 'new') ? 4 : 0);
foreach ($line as $i => $val) {
$cells .= polana_xlsx_cell($i, $rowNum, $val, ($i === 2 ? 3 : $style), $i === 2);
}
$body .= '<row r="' . $rowNum . '" ht="28" customHeight="1">' . $cells . '</row>';
$rowNum++;
}
$cols = [10,26,10,19,14,24,17,45,45,26,22];
$columnXml = '';
foreach ($cols as $i => $width) {
$col = $i + 1;
$columnXml .= '<col min="' . $col . '" max="' . $col . '" width="' . $width . '" customWidth="1"/>';
}
$headerCells = '';
foreach ($names as $i => $name) $headerCells .= polana_xlsx_cell($i, 6, $name, 2);
$sum = 'Zgłoszenia: ' . $counts['requests'] . ' | Osoby (wszystkie): ' . $counts['guests']
. ' | Potwierdzone: ' . $counts['confirmed'] . ' | Nowe: ' . $counts['new'];
$warn = 'Możliwe powtórzenia: ' . $counts['duplicates'] . ' | Osoby bez możliwych powtórzeń (szacunek): ' . $counts['estimated_guests']
. ' | Uwaga: nie kasowano żadnych zgłoszeń; zweryfikuj z obsługą.';
$sheet1 = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
. '<sheetViews><sheetView workbookViewId="0"><pane ySplit="6" topLeftCell="A7" activePane="bottomLeft" state="frozen"/></sheetView></sheetViews>'
. '<cols>' . $columnXml . '</cols><sheetData>'
. '<row r="1">' . polana_xlsx_cell(0, 1, 'MOJA POLANA SMAKÓW – PLAN SALI', 1) . '</row>'
. '<row r="2">' . polana_xlsx_cell(0, 2, 'Dzień: ' . date('d.m.Y', strtotime($date)), 1) . '</row>'
. '<row r="3">' . polana_xlsx_cell(0, 3, $sum, 6) . '</row>'
. '<row r="4">' . polana_xlsx_cell(0, 4, $warn, 5) . '</row>'
. '<row r="6" ht="30" customHeight="1">' . $headerCells . '</row>'
. $body . '</sheetData><mergeCells count="4"><mergeCell ref="A1:K1"/><mergeCell ref="A2:K2"/><mergeCell ref="A3:K3"/><mergeCell ref="A4:K4"/></mergeCells>'
. '<autoFilter ref="A6:K' . max(6, $rowNum - 1) . '"/>'
. '<pageMargins left="0.3" right="0.3" top="0.45" bottom="0.45" header="0.2" footer="0.2"/>'
. '<pageSetup orientation="landscape" paperSize="9" fitToWidth="1"/>'
. '</worksheet>';
$rows2 = '<row r="1">' . polana_xlsx_cell(0,1,'MOJA POLANA SMAKÓW – REZERWACJE WG GODZIN',1) . '</row>'
. '<row r="2">' . polana_xlsx_cell(0,2,'Termin: '.date('d.m.Y',strtotime($date)),1) . '</row>'
. '<row r="4">' . polana_xlsx_cell(0,4,'Godzina',2) . polana_xlsx_cell(1,4,'Rezerwacje',2) . polana_xlsx_cell(2,4,'Osoby',2) . polana_xlsx_cell(3,4,'Podejrzane powtórzenia',2) . '</row>';
$idx = 5;
foreach ($hours as $hour => $h) {
$rows2 .= '<row r="' . $idx . '">' . polana_xlsx_cell(0,$idx,$hour)
. polana_xlsx_cell(1,$idx,$h['requests'],3,true)
. polana_xlsx_cell(2,$idx,$h['guests'],3,true)
. polana_xlsx_cell(3,$idx,$h['duplicates'],3,true) . '</row>';
$idx++;
}
$rows2 .= '<row r="' . ($idx+1) . '">' . polana_xlsx_cell(0,$idx+1,'RAZEM',2)
. polana_xlsx_cell(1,$idx+1,$counts['requests'],3,true)
. polana_xlsx_cell(2,$idx+1,$counts['guests'],3,true)
. polana_xlsx_cell(3,$idx+1,$counts['duplicates'],3,true) . '</row>';
$sheet2 = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
. '<cols><col min="1" max="1" width="16" customWidth="1"/><col min="2" max="3" width="20" customWidth="1"/><col min="4" max="4" width="27" customWidth="1"/></cols>'
. '<sheetData>' . $rows2 . '</sheetData><mergeCells count="2"><mergeCell ref="A1:D1"/><mergeCell ref="A2:D2"/></mergeCells></worksheet>';
$styles = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
. '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'
. '<fonts count="3"><font><sz val="11"/><name val="Aptos"/></font><font><b/><sz val="17"/><color rgb="FFFFFFFF"/><name val="Aptos Display"/></font><font><b/><sz val="11"/><color rgb="FF212B24"/><name val="Aptos"/></font></fonts>'
. '<fills count="6"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill><fill><patternFill patternType="solid"><fgColor rgb="FF31533D"/><bgColor indexed="64"/></patternFill></fill><fill><patternFill patternType="solid"><fgColor rgb="FFDAE7D6"/><bgColor indexed="64"/></patternFill></fill><fill><patternFill patternType="solid"><fgColor rgb="FFFFF1D7"/><bgColor indexed="64"/></patternFill></fill><fill><patternFill patternType="solid"><fgColor rgb="FFFFDEDC"/><bgColor indexed="64"/></patternFill></fill></fills>'
. '<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>'
. '<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>'
. '<cellXfs count="7">'
. '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>'
. '<xf numFmtId="0" fontId="1" fillId="2" borderId="0" xfId="0" applyFill="1" applyFont="1"/>'
. '<xf numFmtId="0" fontId="2" fillId="3" borderId="0" xfId="0" applyFill="1" applyFont="1"/>'
. '<xf numFmtId="1" fontId="2" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/>'
. '<xf numFmtId="0" fontId="0" fillId="4" borderId="0" xfId="0" applyFill="1"/>'
. '<xf numFmtId="0" fontId="0" fillId="5" borderId="0" xfId="0" applyFill="1"/>'
. '<xf numFmtId="0" fontId="2" fillId="0" borderId="0" xfId="0" applyFont="1"/>'
. '</cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>'
. '</styleSheet>';
return polana_xlsx_zip([
'[Content_Types].xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/worksheets/sheet2.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/></Types>',
'_rels/.rels' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>',
'xl/workbook.xml' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="Lista rezerwacji" sheetId="1" r:id="rId1"/><sheet name="Godziny" sheetId="2" r:id="rId2"/></sheets></workbook>',
'xl/_rels/workbook.xml.rels' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet2.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>',
'xl/styles.xml' => $styles,
'xl/worksheets/sheet1.xml' => $sheet1,
'xl/worksheets/sheet2.xml' => $sheet2,
]);
}