Menü und PDF Überarbeitung

This commit is contained in:
2026-03-22 11:10:58 +01:00
parent 8a68e6477f
commit 43d611035b
8 changed files with 28210 additions and 16 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

File diff suppressed because one or more lines are too long
+241
View File
@@ -0,0 +1,241 @@
<?php
//============================================================+
// File name : tcpdf_autoconfig.php
// Version : 1.1.1
// Begin : 2013-05-16
// Last Update : 2014-12-18
// Authors : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
// License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
// -------------------------------------------------------------------
// Copyright (C) 2011-2014 Nicola Asuni - Tecnick.com LTD
//
// This file is part of TCPDF software library.
//
// TCPDF is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// TCPDF 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 Lesser General Public License for more details.
//
// You should have received a copy of the License
// along with TCPDF. If not, see
// <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
//
// See LICENSE.TXT file for more information.
// -------------------------------------------------------------------
//
// Description : Try to automatically configure some TCPDF
// constants if not defined.
//
//============================================================+
/**
* @file
* Try to automatically configure some TCPDF constants if not defined.
* @package com.tecnick.tcpdf
* @version 1.1.1
*/
// DOCUMENT_ROOT fix for IIS Webserver
if ((!isset($_SERVER['DOCUMENT_ROOT'])) OR (empty($_SERVER['DOCUMENT_ROOT']))) {
if(isset($_SERVER['SCRIPT_FILENAME'])) {
$_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr($_SERVER['SCRIPT_FILENAME'], 0, 0-strlen($_SERVER['PHP_SELF'])));
} elseif(isset($_SERVER['PATH_TRANSLATED'])) {
$_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr(str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']), 0, 0-strlen($_SERVER['PHP_SELF'])));
} else {
// define here your DOCUMENT_ROOT path if the previous fails (e.g. '/var/www')
$_SERVER['DOCUMENT_ROOT'] = '/';
}
}
$_SERVER['DOCUMENT_ROOT'] = str_replace('//', '/', $_SERVER['DOCUMENT_ROOT']);
if (substr($_SERVER['DOCUMENT_ROOT'], -1) != '/') {
$_SERVER['DOCUMENT_ROOT'] .= '/';
}
// Load main configuration file only if the K_TCPDF_EXTERNAL_CONFIG constant is set to false.
if (!defined('K_TCPDF_EXTERNAL_CONFIG') OR !K_TCPDF_EXTERNAL_CONFIG) {
// define a list of default config files in order of priority
$tcpdf_config_files = array(dirname(__FILE__).'/config/tcpdf_config.php', '/etc/php-tcpdf/tcpdf_config.php', '/etc/tcpdf/tcpdf_config.php', '/etc/tcpdf_config.php');
foreach ($tcpdf_config_files as $tcpdf_config) {
if (@file_exists($tcpdf_config) AND is_readable($tcpdf_config)) {
require_once($tcpdf_config);
break;
}
}
}
if (!defined('K_PATH_MAIN')) {
define ('K_PATH_MAIN', dirname(__FILE__).'/');
}
if (!defined('K_PATH_FONTS')) {
define ('K_PATH_FONTS', K_PATH_MAIN.'fonts/');
}
if (!defined('K_PATH_URL')) {
$k_path_url = K_PATH_MAIN; // default value for console mode
if (isset($_SERVER['HTTP_HOST']) AND (!empty($_SERVER['HTTP_HOST']))) {
if(isset($_SERVER['HTTPS']) AND (!empty($_SERVER['HTTPS'])) AND (strtolower($_SERVER['HTTPS']) != 'off')) {
$k_path_url = 'https://';
} else {
$k_path_url = 'http://';
}
$k_path_url .= $_SERVER['HTTP_HOST'];
$k_path_url .= str_replace( '\\', '/', substr(K_PATH_MAIN, (strlen($_SERVER['DOCUMENT_ROOT']) - 1)));
}
define ('K_PATH_URL', $k_path_url);
}
if (!defined('K_PATH_IMAGES')) {
$tcpdf_images_dirs = array(K_PATH_MAIN.'examples/images/', K_PATH_MAIN.'images/', '/usr/share/doc/php-tcpdf/examples/images/', '/usr/share/doc/tcpdf/examples/images/', '/usr/share/doc/php/tcpdf/examples/images/', '/var/www/tcpdf/images/', '/var/www/html/tcpdf/images/', '/usr/local/apache2/htdocs/tcpdf/images/', K_PATH_MAIN);
foreach ($tcpdf_images_dirs as $tcpdf_images_path) {
if (@file_exists($tcpdf_images_path)) {
define ('K_PATH_IMAGES', $tcpdf_images_path);
break;
}
}
}
if (!defined('PDF_HEADER_LOGO')) {
$tcpdf_header_logo = '';
if (@file_exists(K_PATH_IMAGES.'tcpdf_logo.jpg')) {
$tcpdf_header_logo = 'tcpdf_logo.jpg';
}
define ('PDF_HEADER_LOGO', $tcpdf_header_logo);
}
if (!defined('PDF_HEADER_LOGO_WIDTH')) {
if (!empty($tcpdf_header_logo)) {
define ('PDF_HEADER_LOGO_WIDTH', 30);
} else {
define ('PDF_HEADER_LOGO_WIDTH', 0);
}
}
if (!defined('K_PATH_CACHE')) {
$K_PATH_CACHE = ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir();
if (substr($K_PATH_CACHE, -1) != '/') {
$K_PATH_CACHE .= '/';
}
define ('K_PATH_CACHE', $K_PATH_CACHE);
}
if (!defined('K_BLANK_IMAGE')) {
define ('K_BLANK_IMAGE', '_blank.png');
}
if (!defined('PDF_PAGE_FORMAT')) {
define ('PDF_PAGE_FORMAT', 'A4');
}
if (!defined('PDF_PAGE_ORIENTATION')) {
define ('PDF_PAGE_ORIENTATION', 'P');
}
if (!defined('PDF_CREATOR')) {
define ('PDF_CREATOR', 'TCPDF');
}
if (!defined('PDF_AUTHOR')) {
define ('PDF_AUTHOR', 'TCPDF');
}
if (!defined('PDF_HEADER_TITLE')) {
define ('PDF_HEADER_TITLE', 'TCPDF Example');
}
if (!defined('PDF_HEADER_STRING')) {
define ('PDF_HEADER_STRING', "by Nicola Asuni - Tecnick.com\nwww.tcpdf.org");
}
if (!defined('PDF_UNIT')) {
define ('PDF_UNIT', 'mm');
}
if (!defined('PDF_MARGIN_HEADER')) {
define ('PDF_MARGIN_HEADER', 5);
}
if (!defined('PDF_MARGIN_FOOTER')) {
define ('PDF_MARGIN_FOOTER', 10);
}
if (!defined('PDF_MARGIN_TOP')) {
define ('PDF_MARGIN_TOP', 27);
}
if (!defined('PDF_MARGIN_BOTTOM')) {
define ('PDF_MARGIN_BOTTOM', 25);
}
if (!defined('PDF_MARGIN_LEFT')) {
define ('PDF_MARGIN_LEFT', 15);
}
if (!defined('PDF_MARGIN_RIGHT')) {
define ('PDF_MARGIN_RIGHT', 15);
}
if (!defined('PDF_FONT_NAME_MAIN')) {
define ('PDF_FONT_NAME_MAIN', 'helvetica');
}
if (!defined('PDF_FONT_SIZE_MAIN')) {
define ('PDF_FONT_SIZE_MAIN', 10);
}
if (!defined('PDF_FONT_NAME_DATA')) {
define ('PDF_FONT_NAME_DATA', 'helvetica');
}
if (!defined('PDF_FONT_SIZE_DATA')) {
define ('PDF_FONT_SIZE_DATA', 8);
}
if (!defined('PDF_FONT_MONOSPACED')) {
define ('PDF_FONT_MONOSPACED', 'courier');
}
if (!defined('PDF_IMAGE_SCALE_RATIO')) {
define ('PDF_IMAGE_SCALE_RATIO', 1.25);
}
if (!defined('HEAD_MAGNIFICATION')) {
define('HEAD_MAGNIFICATION', 1.1);
}
if (!defined('K_CELL_HEIGHT_RATIO')) {
define('K_CELL_HEIGHT_RATIO', 1.25);
}
if (!defined('K_TITLE_MAGNIFICATION')) {
define('K_TITLE_MAGNIFICATION', 1.3);
}
if (!defined('K_SMALL_RATIO')) {
define('K_SMALL_RATIO', 2/3);
}
if (!defined('K_THAI_TOPCHARS')) {
define('K_THAI_TOPCHARS', true);
}
if (!defined('K_TCPDF_CALLS_IN_HTML')) {
define('K_TCPDF_CALLS_IN_HTML', false);
}
if (!defined('K_TCPDF_THROW_EXCEPTION_ERROR')) {
define('K_TCPDF_THROW_EXCEPTION_ERROR', false);
}
if (!defined('K_TIMEZONE')) {
define('K_TIMEZONE', @date_default_timezone_get());
}
//============================================================+
// END OF FILE
//============================================================+
File diff suppressed because it is too large Load Diff
+349
View File
@@ -0,0 +1,349 @@
<?php
//============================================================+
// File name : tcpdf_barcodes_2d.php
// Version : 1.0.015
// Begin : 2009-04-07
// Last Update : 2014-05-20
// Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
// License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
// -------------------------------------------------------------------
// Copyright (C) 2009-2014 Nicola Asuni - Tecnick.com LTD
//
// This file is part of TCPDF software library.
//
// TCPDF is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// TCPDF 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with TCPDF. If not, see <http://www.gnu.org/licenses/>.
//
// See LICENSE.TXT file for more information.
// -------------------------------------------------------------------
//
// Description : PHP class to creates array representations for
// 2D barcodes to be used with TCPDF.
//
//============================================================+
/**
* @file
* PHP class to creates array representations for 2D barcodes to be used with TCPDF.
* @package com.tecnick.tcpdf
* @author Nicola Asuni
* @version 1.0.015
*/
/**
* @class TCPDF2DBarcode
* PHP class to creates array representations for 2D barcodes to be used with TCPDF (http://www.tcpdf.org).
* @package com.tecnick.tcpdf
* @version 1.0.015
* @author Nicola Asuni
*/
class TCPDF2DBarcode {
/**
* Array representation of barcode.
* @protected
*/
protected $barcode_array = array();
/**
* This is the class constructor.
* Return an array representations for 2D barcodes:<ul>
* <li>$arrcode['code'] code to be printed on text label</li>
* <li>$arrcode['num_rows'] required number of rows</li>
* <li>$arrcode['num_cols'] required number of columns</li>
* <li>$arrcode['bcode'][$r][$c] value of the cell is $r row and $c column (0 = transparent, 1 = black)</li></ul>
* @param string $code code to print
* @param string $type type of barcode: <ul><li>DATAMATRIX : Datamatrix (ISO/IEC 16022)</li><li>PDF417 : PDF417 (ISO/IEC 15438:2006)</li><li>PDF417,a,e,t,s,f,o0,o1,o2,o3,o4,o5,o6 : PDF417 with parameters: a = aspect ratio (width/height); e = error correction level (0-8); t = total number of macro segments; s = macro segment index (0-99998); f = file ID; o0 = File Name (text); o1 = Segment Count (numeric); o2 = Time Stamp (numeric); o3 = Sender (text); o4 = Addressee (text); o5 = File Size (numeric); o6 = Checksum (numeric). NOTES: Parameters t, s and f are required for a Macro Control Block, all other parameters are optional. To use a comma character ',' on text options, replace it with the character 255: "\xff".</li><li>QRCODE : QRcode Low error correction</li><li>QRCODE,L : QRcode Low error correction</li><li>QRCODE,M : QRcode Medium error correction</li><li>QRCODE,Q : QRcode Better error correction</li><li>QRCODE,H : QR-CODE Best error correction</li><li>RAW: raw mode - comma-separad list of array rows</li><li>RAW2: raw mode - array rows are surrounded by square parenthesis.</li><li>TEST : Test matrix</li></ul>
*/
public function __construct($code, $type) {
$this->setBarcode($code, $type);
}
/**
* Return an array representations of barcode.
* @return array
*/
public function getBarcodeArray() {
return $this->barcode_array;
}
/**
* Send barcode as SVG image object to the standard output.
* @param int $w Width of a single rectangle element in user units.
* @param int $h Height of a single rectangle element in user units.
* @param string $color Foreground color (in SVG format) for bar elements (background is transparent).
* @public
*/
public function getBarcodeSVG($w=3, $h=3, $color='black') {
// send headers
$code = $this->getBarcodeSVGcode($w, $h, $color);
header('Content-Type: application/svg+xml');
header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
header('Pragma: public');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Content-Disposition: inline; filename="'.md5($code).'.svg";');
//header('Content-Length: '.strlen($code));
echo $code;
}
/**
* Return a SVG string representation of barcode.
* @param int $w Width of a single rectangle element in user units.
* @param int $h Height of a single rectangle element in user units.
* @param string $color Foreground color (in SVG format) for bar elements (background is transparent).
* @return string SVG code.
* @public
*/
public function getBarcodeSVGcode($w=3, $h=3, $color='black') {
// replace table for special characters
$repstr = array("\0" => '', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;');
$svg = '<'.'?'.'xml version="1.0" standalone="no"'.'?'.'>'."\n";
$svg .= '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'."\n";
$svg .= '<svg width="'.round(($this->barcode_array['num_cols'] * $w), 3).'" height="'.round(($this->barcode_array['num_rows'] * $h), 3).'" version="1.1" xmlns="http://www.w3.org/2000/svg">'."\n";
$svg .= "\t".'<desc>'.strtr($this->barcode_array['code'], $repstr).'</desc>'."\n";
$svg .= "\t".'<g id="elements" fill="'.$color.'" stroke="none">'."\n";
// print barcode elements
$y = 0;
// for each row
for ($r = 0; $r < $this->barcode_array['num_rows']; ++$r) {
$x = 0;
// for each column
for ($c = 0; $c < $this->barcode_array['num_cols']; ++$c) {
if ($this->barcode_array['bcode'][$r][$c] == 1) {
// draw a single barcode cell
$svg .= "\t\t".'<rect x="'.$x.'" y="'.$y.'" width="'.$w.'" height="'.$h.'" />'."\n";
}
$x += $w;
}
$y += $h;
}
$svg .= "\t".'</g>'."\n";
$svg .= '</svg>'."\n";
return $svg;
}
/**
* Return an HTML representation of barcode.
* @param int $w Width of a single rectangle element in pixels.
* @param int $h Height of a single rectangle element in pixels.
* @param string $color Foreground color for bar elements (background is transparent).
* @return string HTML code.
* @public
*/
public function getBarcodeHTML($w=10, $h=10, $color='black') {
$html = '<div style="font-size:0;position:relative;width:'.($w * $this->barcode_array['num_cols']).'px;height:'.($h * $this->barcode_array['num_rows']).'px;">'."\n";
// print barcode elements
$y = 0;
// for each row
for ($r = 0; $r < $this->barcode_array['num_rows']; ++$r) {
$x = 0;
// for each column
for ($c = 0; $c < $this->barcode_array['num_cols']; ++$c) {
if ($this->barcode_array['bcode'][$r][$c] == 1) {
// draw a single barcode cell
$html .= '<div style="background-color:'.$color.';width:'.$w.'px;height:'.$h.'px;position:absolute;left:'.$x.'px;top:'.$y.'px;">&nbsp;</div>'."\n";
}
$x += $w;
}
$y += $h;
}
$html .= '</div>'."\n";
return $html;
}
/**
* Send a PNG image representation of barcode (requires GD or Imagick library).
* @param int $w Width of a single rectangle element in pixels.
* @param int $h Height of a single rectangle element in pixels.
* @param array $color RGB (0-255) foreground color for bar elements (background is transparent).
* @public
*/
public function getBarcodePNG($w=3, $h=3, $color=array(0,0,0)) {
$data = $this->getBarcodePngData($w, $h, $color);
// send headers
header('Content-Type: image/png');
header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
header('Pragma: public');
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
//header('Content-Length: '.strlen($data));
echo $data;
}
/**
* Return a PNG image representation of barcode (requires GD or Imagick library).
* @param int $w Width of a single rectangle element in pixels.
* @param int $h Height of a single rectangle element in pixels.
* @param array $color RGB (0-255) foreground color for bar elements (background is transparent).
* @return string|Imagick|false image or false in case of error.
* @public
*/
public function getBarcodePngData($w=3, $h=3, $color=array(0,0,0)) {
// calculate image size
$width = ($this->barcode_array['num_cols'] * $w);
$height = ($this->barcode_array['num_rows'] * $h);
if (function_exists('imagecreate')) {
// GD library
$imagick = false;
$png = imagecreate($width, $height);
$bgcol = imagecolorallocate($png, 255, 255, 255);
imagecolortransparent($png, $bgcol);
$fgcol = imagecolorallocate($png, $color[0], $color[1], $color[2]);
} elseif (extension_loaded('imagick')) {
$imagick = true;
$bgcol = new imagickpixel('rgb(255,255,255');
$fgcol = new imagickpixel('rgb('.$color[0].','.$color[1].','.$color[2].')');
$png = new Imagick();
$png->newImage($width, $height, 'none', 'png');
$bar = new imagickdraw();
$bar->setfillcolor($fgcol);
} else {
return false;
}
// print barcode elements
$y = 0;
// for each row
for ($r = 0; $r < $this->barcode_array['num_rows']; ++$r) {
$x = 0;
// for each column
for ($c = 0; $c < $this->barcode_array['num_cols']; ++$c) {
if ($this->barcode_array['bcode'][$r][$c] == 1) {
// draw a single barcode cell
if ($imagick) {
$bar->rectangle($x, $y, ($x + $w - 1), ($y + $h - 1));
} else {
imagefilledrectangle($png, $x, $y, ($x + $w - 1), ($y + $h - 1), $fgcol);
}
}
$x += $w;
}
$y += $h;
}
if ($imagick) {
$png->drawimage($bar);
return $png;
} else {
ob_start();
imagepng($png);
$imagedata = ob_get_clean();
imagedestroy($png);
return $imagedata;
}
}
/**
* Set the barcode.
* @param string $code code to print
* @param string $type type of barcode: <ul><li>DATAMATRIX : Datamatrix (ISO/IEC 16022)</li><li>PDF417 : PDF417 (ISO/IEC 15438:2006)</li><li>PDF417,a,e,t,s,f,o0,o1,o2,o3,o4,o5,o6 : PDF417 with parameters: a = aspect ratio (width/height); e = error correction level (0-8); t = total number of macro segments; s = macro segment index (0-99998); f = file ID; o0 = File Name (text); o1 = Segment Count (numeric); o2 = Time Stamp (numeric); o3 = Sender (text); o4 = Addressee (text); o5 = File Size (numeric); o6 = Checksum (numeric). NOTES: Parameters t, s and f are required for a Macro Control Block, all other parameters are optional. To use a comma character ',' on text options, replace it with the character 255: "\xff".</li><li>QRCODE : QRcode Low error correction</li><li>QRCODE,L : QRcode Low error correction</li><li>QRCODE,M : QRcode Medium error correction</li><li>QRCODE,Q : QRcode Better error correction</li><li>QRCODE,H : QR-CODE Best error correction</li><li>RAW: raw mode - comma-separad list of array rows</li><li>RAW2: raw mode - array rows are surrounded by square parenthesis.</li><li>TEST : Test matrix</li></ul>
* @return void
*/
public function setBarcode($code, $type) {
$mode = explode(',', $type);
$qrtype = strtoupper($mode[0]);
switch ($qrtype) {
case 'DATAMATRIX': { // DATAMATRIX (ISO/IEC 16022)
require_once(dirname(__FILE__).'/include/barcodes/datamatrix.php');
$qrcode = new Datamatrix($code);
$this->barcode_array = $qrcode->getBarcodeArray();
$this->barcode_array['code'] = $code;
break;
}
case 'PDF417': { // PDF417 (ISO/IEC 15438:2006)
require_once(dirname(__FILE__).'/include/barcodes/pdf417.php');
if (!isset($mode[1]) OR ($mode[1] === '')) {
$aspectratio = 2; // default aspect ratio (width / height)
} else {
$aspectratio = floatval($mode[1]);
}
if (!isset($mode[2]) OR ($mode[2] === '')) {
$ecl = -1; // default error correction level (auto)
} else {
$ecl = intval($mode[2]);
}
// set macro block
$macro = array();
if (isset($mode[3]) AND ($mode[3] !== '') AND isset($mode[4]) AND ($mode[4] !== '') AND isset($mode[5]) AND ($mode[5] !== '')) {
$macro['segment_total'] = intval($mode[3]);
$macro['segment_index'] = intval($mode[4]);
$macro['file_id'] = strtr($mode[5], "\xff", ',');
for ($i = 0; $i < 7; ++$i) {
$o = $i + 6;
if (isset($mode[$o]) AND ($mode[$o] !== '')) {
// add option
$macro['option_'.$i] = strtr($mode[$o], "\xff", ',');
}
}
}
$qrcode = new PDF417($code, $ecl, $aspectratio, $macro);
$this->barcode_array = $qrcode->getBarcodeArray();
$this->barcode_array['code'] = $code;
break;
}
case 'QRCODE': { // QR-CODE
require_once(dirname(__FILE__).'/include/barcodes/qrcode.php');
if (!isset($mode[1]) OR (!in_array($mode[1],array('L','M','Q','H')))) {
$mode[1] = 'L'; // Ddefault: Low error correction
}
$qrcode = new QRcode($code, strtoupper($mode[1]));
$this->barcode_array = $qrcode->getBarcodeArray();
$this->barcode_array['code'] = $code;
break;
}
case 'RAW':
case 'RAW2': { // RAW MODE
// remove spaces
$code = preg_replace('/[\s]*/si', '', $code);
if (strlen($code) < 3) {
break;
}
if ($qrtype == 'RAW') {
// comma-separated rows
$rows = explode(',', $code);
} else { // RAW2
// rows enclosed in square parentheses
$code = substr($code, 1, -1);
$rows = explode('][', $code);
}
$this->barcode_array['num_rows'] = count($rows);
$this->barcode_array['num_cols'] = strlen($rows[0]);
$this->barcode_array['bcode'] = array();
foreach ($rows as $r) {
$this->barcode_array['bcode'][] = str_split($r, 1);
}
$this->barcode_array['code'] = $code;
break;
}
case 'TEST': { // TEST MODE
$this->barcode_array['num_rows'] = 5;
$this->barcode_array['num_cols'] = 15;
$this->barcode_array['bcode'] = array(
array(1,1,1,0,1,1,1,0,1,1,1,0,1,1,1),
array(0,1,0,0,1,0,0,0,1,0,0,0,0,1,0),
array(0,1,0,0,1,1,0,0,1,1,1,0,0,1,0),
array(0,1,0,0,1,0,0,0,0,0,1,0,0,1,0),
array(0,1,0,0,1,1,1,0,1,1,1,0,0,1,0));
$this->barcode_array['code'] = $code;
break;
}
default: {
$this->barcode_array = array();
}
}
}
} // end of class
//============================================================+
// END OF FILE
//============================================================+
+104
View File
@@ -0,0 +1,104 @@
<?php
//============================================================+
// File name : tcpdf_import.php
// Version : 1.0.001
// Begin : 2011-05-23
// Last Update : 2013-09-17
// Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
// License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
// -------------------------------------------------------------------
// Copyright (C) 2011-2013 Nicola Asuni - Tecnick.com LTD
//
// This file is part of TCPDF software library.
//
// TCPDF is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// TCPDF 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 Lesser General Public License for more details.
//
// You should have received a copy of the License
// along with TCPDF. If not, see
// <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
//
// See LICENSE.TXT file for more information.
// -------------------------------------------------------------------
//
// Description : This is a PHP class extension of the TCPDF library to
// import existing PDF documents.
//
//============================================================+
/**
* @file
* !!! THIS CLASS IS UNDER DEVELOPMENT !!!
* This is a PHP class extension of the TCPDF (http://www.tcpdf.org) library to import existing PDF documents.<br>
* @package com.tecnick.tcpdf
* @author Nicola Asuni
* @version 1.0.001
*/
// include the TCPDF class
require_once(dirname(__FILE__).'/tcpdf.php');
// include PDF parser class
require_once(dirname(__FILE__).'/tcpdf_parser.php');
/**
* @class TCPDF_IMPORT
* !!! THIS CLASS IS UNDER DEVELOPMENT !!!
* PHP class extension of the TCPDF (http://www.tcpdf.org) library to import existing PDF documents.<br>
* @package com.tecnick.tcpdf
* @brief PHP class extension of the TCPDF library to import existing PDF documents.
* @version 1.0.001
* @author Nicola Asuni - info@tecnick.com
*/
class TCPDF_IMPORT extends TCPDF {
/**
* Import an existing PDF document
* @param string $filename Filename of the PDF document to import.
* @return void
* @public
* @since 1.0.000 (2011-05-24)
*/
public function importPDF($filename) {
// load document
$rawdata = file_get_contents($filename);
if ($rawdata === false) {
$this->Error('Unable to get the content of the file: '.$filename);
}
// configuration parameters for parser
$cfg = array(
'die_for_errors' => false,
'ignore_filter_decoding_errors' => true,
'ignore_missing_filter_decoders' => true,
);
try {
// parse PDF data
$pdf = new TCPDF_PARSER($rawdata, $cfg);
} catch (Exception $e) {
die($e->getMessage());
}
// get the parsed data
$data = $pdf->getParsedData();
// release some memory
unset($rawdata);
// ...
print_r($data); // DEBUG
unset($pdf);
}
} // END OF CLASS
//============================================================+
// END OF FILE
//============================================================+
+369 -5
View File
@@ -956,16 +956,16 @@ function app_handle_export_download(PDO $pdo, array $auth): void
$tenantId = (string) ($auth['tenant_id'] ?? '');
if (!app_tenant_has_feature($pdo, $tenantId, 'basic_exports')) {
app_flash('Die Basis-Exporte sind in deiner Lizenz nicht freigeschaltet.', 'warning');
app_redirect('/exports/');
}
$tenant = app_tenant_by_id($pdo, $tenantId);
$tenantKey = (string) ($tenant['tenant_key'] ?? 'tenant');
$dateSuffix = date('Y-m-d');
if ($download === 'members-csv') {
if (!app_tenant_has_feature($pdo, $tenantId, 'basic_exports')) {
app_flash('Die Basis-Exporte sind in deiner Lizenz nicht freigeschaltet.', 'warning');
app_redirect('/exports/');
}
$rows = [];
foreach (app_members_for_tenant($pdo, $tenantId) as $member) {
@@ -985,6 +985,11 @@ function app_handle_export_download(PDO $pdo, array $auth): void
}
if ($download === 'ledger-csv') {
if (!app_tenant_has_feature($pdo, $tenantId, 'basic_exports')) {
app_flash('Die Basis-Exporte sind in deiner Lizenz nicht freigeschaltet.', 'warning');
app_redirect('/exports/');
}
$rows = [];
foreach (app_ledger_for_tenant($pdo, $tenantId) as $entry) {
@@ -1004,10 +1009,369 @@ function app_handle_export_download(PDO $pdo, array $auth): void
);
}
if ($download === 'print-list-pdf') {
if (!app_tenant_has_feature($pdo, $tenantId, 'pdf_export')) {
app_flash('Der PDF-Export ist in deiner Lizenz nicht freigeschaltet.', 'warning');
app_redirect('/exports/');
}
$listId = trim((string) ($_GET['list'] ?? ''));
if ($listId === '') {
app_flash('Bitte wähle zuerst eine Druckliste aus.', 'warning');
app_redirect('/exports/');
}
app_send_print_list_pdf($pdo, $tenantId, $listId);
}
app_flash('Der angeforderte Export ist nicht verfügbar.', 'warning');
app_redirect('/exports/');
}
function app_print_list_export_rows(PDO $pdo, string $tenantId, string $listId): array
{
return app_query_all(
$pdo,
<<<'SQL'
SELECT
pli.id,
pli.member_id,
pli.member_name,
pli.list_position,
COALESCE((
SELECT SUM(le.amount)
FROM ledger_entries le
WHERE le.tenant_id = :tenant_id
AND le.member_id = pli.member_id
), 0) AS balance,
COALESCE((
SELECT SUM(ce.strokes)
FROM coffee_entries ce
WHERE ce.tenant_id = :tenant_id
AND ce.member_id = pli.member_id
AND ce.booked_at >= DATE_SUB(CURRENT_DATE(), INTERVAL 100 DAY)
), 0) AS recent_strokes
FROM print_list_items pli
INNER JOIN print_lists pl ON pl.id = pli.print_list_id
WHERE pl.tenant_id = :tenant_id
AND pl.id = :print_list_id
ORDER BY pli.member_name ASC
SQL,
[
'tenant_id' => $tenantId,
'print_list_id' => $listId,
]
);
}
function app_print_list_pdf_groups(PDO $pdo, string $tenantId, string $listId): array
{
$heavyRows = [];
$lightRows = [];
foreach (app_print_list_export_rows($pdo, $tenantId, $listId) as $row) {
if ((int) ($row['recent_strokes'] ?? 0) >= 10) {
$heavyRows[] = $row;
continue;
}
$lightRows[] = $row;
}
return [
'front' => $heavyRows,
'back' => $lightRows,
];
}
function app_pdf_money(float $value): string
{
return number_format($value, 2, ',', '.') . ' €';
}
function app_print_list_pdf_table(string $headline, string $headerColor, string $headerTextColor, string $metaText, array $rows, int $targetRows, ?string $sideNote = null): string
{
$html = '
<table border="1" cellpadding="3" style="white-space:nowrap;">
<tr style="height: 25px; background-color: ' . $headerColor . '; color: ' . $headerTextColor . ';" valign="bottom">
<th style="width:30%;font-size:13px;" colspan="2"><b>' . app_h($headline) . '</b></th>
<th style="width:70%;" valign="bottom">' . app_h($metaText) . '</th>
</tr>
<tr>
<td style="width:20%"><b>Name</b></td>
<td style="width:10%"><b>Guthaben</b></td>
<td><b>Striche</b></td>
</tr>';
$rowCount = 1;
foreach ($rows as $row) {
$rowCount++;
$balance = (float) ($row['balance'] ?? 0);
$balanceStyle = '';
if ($balance < -10.0) {
$balanceStyle = 'background-color: rgb(204, 0, 0); color: rgb(255,255,255);';
} elseif ($balance > 5.0) {
$balanceStyle = 'background-color: rgb(0, 102, 0); color: rgb(255,255,255);';
}
$html .= '<tr style="height: 16px;">';
$html .= '<td>' . app_h((string) ($row['member_name'] ?? '')) . '</td>';
$html .= '<td style="' . $balanceStyle . '">' . app_h(app_pdf_money($balance)) . '</td>';
$html .= '<td></td>';
$html .= '</tr>';
}
for ($i = $rowCount; $i < $targetRows; $i++) {
$html .= '<tr><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr>';
}
if ($sideNote !== null && $sideNote !== '') {
$html .= '<tr><td>&nbsp;</td><td>&nbsp;</td><td style="background-color: rgb(255,255,0); text-align: right;"><b>'
. app_h($sideNote)
. '</b></td></tr>';
}
$html .= '</table>';
return $html;
}
function app_pdf_mm_to_pt(float $value): float
{
return $value * 72 / 25.4;
}
function app_pdf_rgb(array $rgb): string
{
return sprintf('%.3F %.3F %.3F', ($rgb[0] ?? 0) / 255, ($rgb[1] ?? 0) / 255, ($rgb[2] ?? 0) / 255);
}
function app_pdf_escape_text(string $text): string
{
$encoding = function_exists('iconv') ? @iconv('UTF-8', 'Windows-1252//TRANSLIT', $text) : $text;
$encoded = is_string($encoding) ? $encoding : $text;
return str_replace(['\\', '(', ')'], ['\\\\', '\\(', '\\)'], $encoded);
}
function app_pdf_rect(float $xMm, float $yMm, float $wMm, float $hMm, ?array $fillRgb = null, ?array $strokeRgb = [0, 0, 0]): string
{
$pageHeightPt = app_pdf_mm_to_pt(297);
$x = app_pdf_mm_to_pt($xMm);
$y = $pageHeightPt - app_pdf_mm_to_pt($yMm + $hMm);
$w = app_pdf_mm_to_pt($wMm);
$h = app_pdf_mm_to_pt($hMm);
$commands = '';
if ($strokeRgb !== null) {
$commands .= app_pdf_rgb($strokeRgb) . " RG\n";
}
if ($fillRgb !== null) {
$commands .= app_pdf_rgb($fillRgb) . " rg\n";
}
$operator = ($fillRgb !== null && $strokeRgb !== null) ? 'B' : (($fillRgb !== null) ? 'f' : 'S');
return $commands . sprintf("%.2F %.2F %.2F %.2F re %s\n", $x, $y, $w, $h, $operator);
}
function app_pdf_text(float $xMm, float $yMm, string $text, string $font = 'F1', float $size = 10, array $rgb = [0, 0, 0]): string
{
$pageHeightPt = app_pdf_mm_to_pt(297);
$x = app_pdf_mm_to_pt($xMm);
$y = $pageHeightPt - app_pdf_mm_to_pt($yMm);
return "BT\n"
. app_pdf_rgb($rgb) . " rg\n"
. '/' . $font . ' ' . number_format($size, 2, '.', '') . " Tf\n"
. sprintf("1 0 0 1 %.2F %.2F Tm\n", $x, $y)
. '(' . app_pdf_escape_text($text) . ") Tj\nET\n";
}
function app_pdf_truncate(string $text, int $maxLength): string
{
if (function_exists('mb_strlen') && function_exists('mb_substr')) {
if (mb_strlen($text, 'UTF-8') <= $maxLength) {
return $text;
}
return mb_substr($text, 0, $maxLength - 1, 'UTF-8') . '…';
}
if (strlen($text) <= $maxLength) {
return $text;
}
return substr($text, 0, $maxLength - 3) . '...';
}
function app_render_print_list_pdf_page(string $headline, array $headerFill, array $headerText, string $metaText, array $rows, int $targetRows, ?string $sideNote = null): string
{
$commands = "0.4 w\n";
$x = 5.0;
$y = 5.0;
$nameWidth = 40.0;
$balanceWidth = 20.0;
$strikeWidth = 140.0;
$headerHeight = 9.0;
$subHeaderHeight = 6.5;
$rowHeight = 4.0;
$commands .= app_pdf_rect($x, $y, $nameWidth + $balanceWidth, $headerHeight, $headerFill);
$commands .= app_pdf_rect($x + $nameWidth + $balanceWidth, $y, $strikeWidth, $headerHeight, $headerFill);
$commands .= app_pdf_text($x + 2, $y + 5.8, $headline, 'F2', 13, $headerText);
$commands .= app_pdf_text($x + $nameWidth + $balanceWidth + 2, $y + 5.8, app_pdf_truncate($metaText, 86), 'F1', 9, $headerText);
$y += $headerHeight;
$commands .= app_pdf_rect($x, $y, $nameWidth, $subHeaderHeight);
$commands .= app_pdf_rect($x + $nameWidth, $y, $balanceWidth, $subHeaderHeight);
$commands .= app_pdf_rect($x + $nameWidth + $balanceWidth, $y, $strikeWidth, $subHeaderHeight);
$commands .= app_pdf_text($x + 2, $y + 4.4, 'Name', 'F2', 9.5);
$commands .= app_pdf_text($x + $nameWidth + 2, $y + 4.4, 'Guthaben', 'F2', 9.5);
$commands .= app_pdf_text($x + $nameWidth + $balanceWidth + 2, $y + 4.4, 'Striche', 'F2', 9.5);
$y += $subHeaderHeight;
$rowCount = 1;
foreach ($rows as $row) {
$rowCount++;
$balance = (float) ($row['balance'] ?? 0);
$balanceFill = null;
$balanceTextColor = [0, 0, 0];
if ($balance < -10.0) {
$balanceFill = [204, 0, 0];
$balanceTextColor = [255, 255, 255];
} elseif ($balance > 5.0) {
$balanceFill = [0, 102, 0];
$balanceTextColor = [255, 255, 255];
}
$commands .= app_pdf_rect($x, $y, $nameWidth, $rowHeight);
$commands .= app_pdf_rect($x + $nameWidth, $y, $balanceWidth, $rowHeight, $balanceFill);
$commands .= app_pdf_rect($x + $nameWidth + $balanceWidth, $y, $strikeWidth, $rowHeight);
$commands .= app_pdf_text($x + 1.5, $y + 2.9, app_pdf_truncate((string) ($row['member_name'] ?? ''), 28), 'F1', 9);
$commands .= app_pdf_text($x + $nameWidth + 1.5, $y + 2.9, app_pdf_money($balance), 'F1', 8.5, $balanceTextColor);
$y += $rowHeight;
}
for ($i = $rowCount; $i < $targetRows; $i++) {
$commands .= app_pdf_rect($x, $y, $nameWidth, $rowHeight);
$commands .= app_pdf_rect($x + $nameWidth, $y, $balanceWidth, $rowHeight);
$commands .= app_pdf_rect($x + $nameWidth + $balanceWidth, $y, $strikeWidth, $rowHeight);
$y += $rowHeight;
}
if ($sideNote !== null && $sideNote !== '') {
$commands .= app_pdf_rect($x, $y, $nameWidth, $rowHeight);
$commands .= app_pdf_rect($x + $nameWidth, $y, $balanceWidth, $rowHeight);
$commands .= app_pdf_rect($x + $nameWidth + $balanceWidth, $y, $strikeWidth, $rowHeight, [255, 255, 0]);
$commands .= app_pdf_text($x + $nameWidth + $balanceWidth + 88, $y + 2.9, $sideNote, 'F2', 8.5);
}
return $commands;
}
function app_build_simple_pdf(array $pageStreams): string
{
$objects = [];
$objects[1] = "<< /Type /Catalog /Pages 2 0 R >>";
$objects[3] = "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>";
$objects[4] = "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >>";
$kids = [];
$nextObject = 5;
foreach ($pageStreams as $stream) {
$pageObject = $nextObject++;
$contentObject = $nextObject++;
$kids[] = $pageObject . ' 0 R';
$objects[$pageObject] = '<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595.28 841.89] /Resources << /Font << /F1 3 0 R /F2 4 0 R >> >> /Contents ' . $contentObject . ' 0 R >>';
$objects[$contentObject] = "<< /Length " . strlen($stream) . " >>\nstream\n" . $stream . "\nendstream";
}
$objects[2] = '<< /Type /Pages /Kids [' . implode(' ', $kids) . '] /Count ' . count($kids) . ' >>';
ksort($objects);
$pdf = "%PDF-1.4\n%\xE2\xE3\xCF\xD3\n";
$offsets = [];
foreach ($objects as $number => $object) {
$offsets[$number] = strlen($pdf);
$pdf .= $number . " 0 obj\n" . $object . "\nendobj\n";
}
$xrefOffset = strlen($pdf);
$pdf .= "xref\n0 " . (count($objects) + 1) . "\n";
$pdf .= "0000000000 65535 f \n";
foreach (range(1, count($objects)) as $number) {
$pdf .= sprintf("%010d 00000 n \n", $offsets[$number] ?? 0);
}
$pdf .= "trailer << /Size " . (count($objects) + 1) . " /Root 1 0 R >>\n";
$pdf .= "startxref\n" . $xrefOffset . "\n%%EOF";
return $pdf;
}
function app_send_print_list_pdf(PDO $pdo, string $tenantId, string $listId): void
{
$list = app_print_list_detail($pdo, $tenantId, $listId);
if ($list === null) {
throw new RuntimeException('Die ausgewählte Druckliste wurde nicht gefunden.');
}
$tenant = app_tenant_by_id($pdo, $tenantId) ?? ['name' => 'Kaffeeliste'];
$groups = app_print_list_pdf_groups($pdo, $tenantId, $listId);
$heavyRows = $groups['front'];
$lightRows = $groups['back'];
$unitPrice = (float) ($list['unit_price'] ?? 0.50);
$meta = '1 Strich = ' . app_pdf_money($unitPrice) . '. Bitte spätestens bei 10,00 € einzahlen. '
. date('d.m.Y H:i') . ' | ' . (string) ($tenant['name'] ?? 'Kaffeeliste');
$pageStreams = [];
if ($heavyRows !== []) {
$pageStreams[] = app_render_print_list_pdf_page(
'Kaffeeliste - Vieltrinker',
[0, 94, 63],
[255, 255, 255],
$meta,
$heavyRows,
64,
$lightRows !== [] ? 'Rückseite beachten!' : null
);
}
if ($lightRows !== [] || $heavyRows === []) {
$pageStreams[] = app_render_print_list_pdf_page(
'Kaffeeliste - Wenigtrinker',
[91, 209, 215],
[0, 0, 0],
$meta,
$lightRows,
65,
$heavyRows !== [] ? 'Vorderseite beachten!' : null
);
}
$filename = preg_replace('/[^A-Za-z0-9._-]+/', '-', (string) ($list['title'] ?? 'Kaffeestrichliste'));
$filename = trim((string) $filename, '-');
$filename = $filename !== '' ? $filename : 'Kaffeestrichliste';
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . $filename . '.pdf"');
header('Cache-Control: no-store, no-cache, must-revalidate');
echo app_build_simple_pdf($pageStreams);
exit;
}
function app_memberships_by_email(PDO $pdo, string $email): array
{
$sql = <<<'SQL'
+37 -11
View File
@@ -88,6 +88,8 @@ $tenantLicense = ['plan_key' => 'starter', 'plan_name' => 'Starter', 'features'
$tenantSettings = app_tenant_settings_defaults();
$printLists = [];
$activePrintList = null;
$printListGroups = ['front' => [], 'back' => []];
$printListItemMap = [];
$hasTenantSettingsFeature = false;
$hasPdfExportFeature = false;
$hasPaperStrikeEntryFeature = false;
@@ -175,6 +177,12 @@ if ($auth !== null && $pdo instanceof PDO) {
if (isset($_GET['list']) && $_GET['list'] !== '') {
$activePrintList = app_print_list_detail($pdo, (string) $auth['tenant_id'], (string) $_GET['list']);
if (is_array($activePrintList)) {
$printListGroups = app_print_list_pdf_groups($pdo, (string) $auth['tenant_id'], (string) $_GET['list']);
foreach (($activePrintList['items'] ?? []) as $item) {
$printListItemMap[(string) ($item['id'] ?? '')] = $item;
}
}
}
}
@@ -218,13 +226,11 @@ $canManageTenant = app_can_manage_tenant($auth);
<span>Zentrale Anmeldung, Tenant-Verwaltung und alle Kernfunktionen der Kaffeeliste in einer Oberfläche.</span>
</div>
<nav class="links" aria-label="Navigation">
<a href="/" class="<?= $page === 'home' ? 'active' : '' ?>">Start</a>
<a href="/login/" class="<?= $page === 'login' ? 'active' : '' ?>">Anmeldung</a>
<a href="<?= $auth !== null && app_is_platform_admin($auth) ? '/admin/' : '/admin/login/' ?>" class="<?= $page === 'tenants' ? 'active' : '' ?>"><?= $auth === null ? 'Für Betreiber' : 'Betreiber-Übersicht' ?></a>
<?php if ($auth !== null): ?>
<?php if (app_is_platform_admin($auth) && app_admin_user() !== null): ?>
<a href="/admin/">Zentrale Verwaltung</a>
<?php endif; ?>
<?php if ($auth === null): ?>
<a href="/" class="<?= $page === 'home' ? 'active' : '' ?>">Start</a>
<a href="/login/" class="<?= $page === 'login' ? 'active' : '' ?>">Anmeldung</a>
<a href="/admin/login/" class="<?= $page === 'tenants' ? 'active' : '' ?>">Für Betreiber</a>
<?php else: ?>
<a href="/dashboard/" class="<?= $page === 'dashboard' ? 'active' : '' ?>">Dashboard</a>
<a href="/content/" class="<?= $page === 'content' ? 'active' : '' ?>">Hinweise</a>
<?php if ($canManageTenant): ?>
@@ -619,7 +625,7 @@ $canManageTenant = app_can_manage_tenant($auth);
</section>
<?php elseif ($page === 'exports'): ?>
<?php if (isset($_GET['view']) && $_GET['view'] === 'print' && is_array($activePrintList)): ?>
<section class="hero"><div class="eyebrow">PDF-Ansicht</div><h1><?= h((string) $activePrintList['title']) ?></h1><p>Diese Ansicht ist druckfertig vorbereitet. Du kannst sie im Browser direkt als PDF speichern.</p><div class="actions" style="margin-top:18px"><a class="button secondary" href="/exports/?list=<?= h((string) $activePrintList['id']) ?>">Zurück</a><button type="button" onclick="window.print()">Jetzt drucken / als PDF speichern</button></div></section>
<section class="hero"><div class="eyebrow">PDF-Vorschau</div><h1><?= h((string) $activePrintList['title']) ?></h1><p>Die echte Druckliste steht jetzt als generiertes PDF zur Verfügung. Diese Web-Ansicht bleibt nur als schnelle Vorschau.</p><div class="actions" style="margin-top:18px"><a class="button secondary" href="/exports/?list=<?= h((string) $activePrintList['id']) ?>">Zurück</a><a class="button" href="/exports/?download=print-list-pdf&list=<?= h((string) $activePrintList['id']) ?>">PDF herunterladen</a></div></section>
<section class="card"><div class="grid grid-2"><article><h2><?= h((string) ($tenantSettings['front_page_label'] ?? 'Vorderseite')) ?></h2><div class="table"><table><thead><tr><th>#</th><th>Person</th><th>Striche</th></tr></thead><tbody><?php foreach (($activePrintList['items'] ?? []) as $item): ?><tr><td><?= h((string) $item['list_position']) ?></td><td><?= h((string) $item['member_name']) ?></td><td>________</td></tr><?php endforeach; ?></tbody></table></div></article><article><h2><?= h((string) ($tenantSettings['back_page_label'] ?? 'Rückseite')) ?></h2><div class="table"><table><thead><tr><th>#</th><th>Person</th><th>Striche</th></tr></thead><tbody><?php foreach (($activePrintList['items'] ?? []) as $item): ?><tr><td><?= h((string) $item['list_position']) ?></td><td><?= h((string) $item['member_name']) ?></td><td>________</td></tr><?php endforeach; ?></tbody></table></div></article></div></section>
<?php else: ?>
<section class="hero"><div class="eyebrow">Exporte und Drucklisten</div><h1>Drucklisten als PDF vorbereiten und später aus Papier nacherfassen</h1><p>Hier erzeugst du druckfertige Listen, speicherst sie als PDF und buchst anschließend die Striche aus Vorder- und Rückseite zurück ins System.</p></section>
@@ -659,7 +665,7 @@ $canManageTenant = app_can_manage_tenant($auth);
<div class="table">
<table>
<thead><tr><th>Titel</th><th>Preis</th><th>Status</th><th>Erstellt</th><th>Aktionen</th></tr></thead>
<tbody><?php foreach ($printLists as $list): ?><tr><td><strong><?= h((string) $list['title']) ?></strong></td><td><?= money($list['unit_price'] ?? 0) ?></td><td><?= ((string) ($list['status'] ?? 'draft')) === 'imported' ? badge('Verbucht', 'success') : badge('Offen') ?></td><td><?= dt((string) ($list['created_at'] ?? '')) ?></td><td><div class="actions"><a class="button secondary" href="/exports/?list=<?= h((string) $list['id']) ?>">Bearbeiten</a><a class="button secondary" href="/exports/?list=<?= h((string) $list['id']) ?>&view=print">PDF-Ansicht</a></div></td></tr><?php endforeach; ?></tbody>
<tbody><?php foreach ($printLists as $list): ?><tr><td><strong><?= h((string) $list['title']) ?></strong></td><td><?= money($list['unit_price'] ?? 0) ?></td><td><?= ((string) ($list['status'] ?? 'draft')) === 'imported' ? badge('Verbucht', 'success') : badge('Offen') ?></td><td><?= dt((string) ($list['created_at'] ?? '')) ?></td><td><div class="actions"><a class="button secondary" href="/exports/?list=<?= h((string) $list['id']) ?>">Bearbeiten</a><a class="button secondary" href="/exports/?download=print-list-pdf&list=<?= h((string) $list['id']) ?>">PDF herunterladen</a><a class="button secondary" href="/exports/?list=<?= h((string) $list['id']) ?>&view=print">Vorschau</a></div></td></tr><?php endforeach; ?></tbody>
</table>
</div>
</section>
@@ -674,12 +680,32 @@ $canManageTenant = app_can_manage_tenant($auth);
<label>Buchungszeit<input type="datetime-local" name="booked_at" value="<?= date('Y-m-d\TH:i') ?>"></label>
<div class="metric"><h3>Status</h3><p><?= h((string) ($activePrintList['status'] ?? 'draft')) ?></p></div>
</div>
<div class="table" style="margin-top:18px">
<div class="grid grid-2" style="margin-top:18px">
<article>
<h3><?= h((string) ($tenantSettings['front_page_label'] ?? 'Vorderseite')) ?> / Vieltrinker</h3>
<div class="table">
<table>
<thead><tr><th>Person</th><th>Striche</th><th>Bereits erfasst</th></tr></thead>
<tbody><?php foreach (($printListGroups['front'] ?? []) as $item): ?><tr><td><?= h((string) $item['member_name']) ?></td><td><input type="number" min="0" step="1" name="strokes[<?= h((string) $item['id']) ?>][front]" value="<?= h((string) (($printListItemMap[(string) $item['id']]['front_strokes'] ?? 0))) ?>"></td><td><?= h((string) (($printListItemMap[(string) $item['id']]['front_strokes'] ?? 0))) ?></td></tr><?php endforeach; ?></tbody>
</table>
</div>
</article>
<article>
<h3><?= h((string) ($tenantSettings['back_page_label'] ?? 'Rückseite')) ?> / Wenigtrinker</h3>
<div class="table">
<table>
<thead><tr><th>Person</th><th>Striche</th><th>Bereits erfasst</th></tr></thead>
<tbody><?php foreach (($printListGroups['back'] ?? []) as $item): ?><tr><td><?= h((string) $item['member_name']) ?></td><td><input type="number" min="0" step="1" name="strokes[<?= h((string) $item['id']) ?>][back]" value="<?= h((string) (($printListItemMap[(string) $item['id']]['back_strokes'] ?? 0))) ?>"></td><td><?= h((string) (($printListItemMap[(string) $item['id']]['back_strokes'] ?? 0))) ?></td></tr><?php endforeach; ?></tbody>
</table>
</div>
</article>
</div>
<?php if (false): ?><div class="table" style="display:none;margin-top:18px">
<table>
<thead><tr><th>#</th><th>Person</th><th>Vorderseite</th><th>Rückseite</th><th>Gesamt</th></tr></thead>
<tbody><?php foreach (($activePrintList['items'] ?? []) as $item): ?><tr><td><?= h((string) $item['list_position']) ?></td><td><?= h((string) $item['member_name']) ?></td><td><input type="number" min="0" step="1" name="strokes[<?= h((string) $item['id']) ?>][front]" value="<?= h((string) ($item['front_strokes'] ?? 0)) ?>"></td><td><input type="number" min="0" step="1" name="strokes[<?= h((string) $item['id']) ?>][back]" value="<?= h((string) ($item['back_strokes'] ?? 0)) ?>"></td><td><?= h((string) ($item['total_strokes'] ?? 0)) ?></td></tr><?php endforeach; ?></tbody>
</table>
</div>
</div><?php endif; ?>
<div class="actions" style="margin-top:18px"><button type="submit">Striche verbuchen</button><a class="button secondary" href="/exports/?list=<?= h((string) $activePrintList['id']) ?>&view=print">Zur PDF-Ansicht</a></div>
</form>
</section>