| Server IP : 202.61.199.114 / Your IP : 216.73.217.139 Web Server : nginx/1.22.1 System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64 User : web20 ( 1018) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/seaside.pacim.de/web/ |
Upload File : |
<?php
/**
* worktime-pdf.php – PDF-Ansicht der Zeitnachweise (/worktime/create/, Tab „PDF-Ansicht").
* Hinter dem PaCIM-Login. Nimmt mehrere hochgeladene Excel-Listen (je Mitarbeiter eine
* Datei, unabhängig von den im System erfassten Daten) entgegen, lässt den gewünschten
* Tabellenblatt-Namen wählen und erzeugt je Datei ein Querformat-PDF -> ZIP-Download.
*
* Aktionen (?action=):
* POST upload files[] -> { ok, job, files[], sheets[] }
* POST build job, sheet -> { ok, job } (startet Build im Hintergrund)
* GET status job -> { status, progress, ... }
* Download des fertigen ZIP über file.php?token=…&type=worktimepdf&job=<job>
*/
require_once( __DIR__ . '/includes/config.inc.php' );
@ini_set( 'display_errors', '0' );
error_reporting( 0 );
header( 'Content-Type: application/json; charset=utf-8' );
header( 'Cache-Control: no-store, no-cache, must-revalidate, max-age=0' );
if ( ! isset( $_SESSION['user'] ) || $_SESSION['user'] != LOGIN_USER ) {
http_response_code( 403 );
echo json_encode( array( 'ok' => false, 'error' => 'unauthorized' ) );
exit();
}
function wp_json( $a ) { echo json_encode( $a ); exit(); }
function wp_valid_job( $j ) { return (bool) preg_match( '/^[a-f0-9]{32}$/', (string) $j ); }
function wp_rrmdir( $d ) {
foreach ( (array) @glob( rtrim( $d, '/' ) . '/*' ) as $f ) { is_dir( $f ) ? wp_rrmdir( $f ) : @unlink( $f ); }
@rmdir( $d );
}
$action = $_GET['action'] ?? ( $_POST['action'] ?? '' );
$root = PATH_SYSTEM . '/time/pdf';
if ( ! is_dir( $root ) ) @mkdir( $root, 0775, true );
// Alte Jobs (> 24 h) aufräumen
foreach ( (array) @glob( $root . '/*' ) as $d ) {
if ( is_dir( $d ) && @filemtime( $d ) < time() - 86400 ) wp_rrmdir( $d );
}
/* ------------------------------------------------------------------ UPLOAD */
if ( $action === 'upload' ) {
if ( empty( $_FILES['files'] ) || ! isset( $_FILES['files']['name'] ) ) wp_json( array( 'ok' => false, 'error' => 'Keine Dateien empfangen.' ) );
$job = bin2hex( random_bytes( 16 ) );
$inDir = $root . '/' . $job . '/in';
@mkdir( $inDir, 0775, true );
$names = (array) $_FILES['files']['name'];
$tmps = (array) $_FILES['files']['tmp_name'];
$errs = (array) $_FILES['files']['error'];
$saved = array(); $skipped = array();
for ( $i = 0; $i < count( $names ); $i++ ) {
$fn = basename( (string) $names[$i] );
if ( (int) $errs[$i] !== UPLOAD_ERR_OK ) { $skipped[] = $fn; continue; }
if ( strtolower( pathinfo( $fn, PATHINFO_EXTENSION ) ) !== 'xlsx' ) { $skipped[] = $fn; continue; }
if ( ! is_uploaded_file( (string) $tmps[$i] ) ) { $skipped[] = $fn; continue; }
$safe = preg_replace( '/[^A-Za-z0-9._ äöüÄÖÜß-]/u', '_', $fn );
if ( $safe === '' || strtolower( pathinfo( $safe, PATHINFO_EXTENSION ) ) !== 'xlsx' ) $safe = 'Datei_' . ( $i + 1 ) . '.xlsx';
if ( move_uploaded_file( (string) $tmps[$i], $inDir . '/' . $safe ) ) $saved[] = $safe; else $skipped[] = $fn;
}
if ( ! $saved ) { wp_rrmdir( $root . '/' . $job ); wp_json( array( 'ok' => false, 'error' => 'Keine gültigen .xlsx-Dateien empfangen.' ) ); }
// Tabellenblatt-Namen aus der ersten Datei lesen (bei allen gleich benannt).
$sheets = array();
try {
$reader = PHPExcel_IOFactory::createReader( 'Excel2007' );
$sheets = (array) $reader->listWorksheetNames( $inDir . '/' . $saved[0] );
} catch ( Exception $e ) { $sheets = array(); }
wp_json( array( 'ok' => true, 'job' => $job, 'files' => $saved, 'skipped' => $skipped, 'sheets' => array_values( $sheets ) ) );
}
/* ------------------------------------------------------------------ BUILD */
if ( $action === 'build' ) {
$job = (string) ( $_POST['job'] ?? '' );
$sheet = trim( (string) ( $_POST['sheet'] ?? '' ) );
if ( ! wp_valid_job( $job ) ) wp_json( array( 'ok' => false, 'error' => 'Ungültiger Job.' ) );
$jobDir = $root . '/' . $job;
$inDir = $jobDir . '/in';
if ( ! is_dir( $inDir ) ) wp_json( array( 'ok' => false, 'error' => 'Job nicht gefunden – bitte erneut hochladen.' ) );
if ( $sheet === '' ) wp_json( array( 'ok' => false, 'error' => 'Bitte ein Tabellenblatt wählen.' ) );
$outDir = $jobDir . '/out'; @mkdir( $outDir, 0775, true );
$zipPath = $jobDir . '/timesheets_pdf.zip';
$statusPath = $jobDir . '/status.json';
@file_put_contents( $statusPath, json_encode( array(
'status' => 'starting', 'total' => 0, 'done' => 0, 'progress' => 0, 'current' => '', 'log' => array(), 'message' => ''
) ) );
$script = PATH_SYSTEM . '/time/upload/build_pdfs.py';
$cmd = 'HOME=/tmp PYTHONPATH=/usr/local/lib/python3.9/dist-packages nohup python3 '
. escapeshellarg( $script ) . ' '
. escapeshellarg( $inDir ) . ' '
. escapeshellarg( $sheet ) . ' '
. escapeshellarg( $outDir ) . ' '
. escapeshellarg( $zipPath ) . ' '
. escapeshellarg( $statusPath )
. ' >> ' . escapeshellarg( $jobDir . '/build.log' ) . ' 2>&1 &';
shell_exec( $cmd );
wp_json( array( 'ok' => true, 'job' => $job ) );
}
/* ------------------------------------------------------------------ STATUS */
if ( $action === 'status' ) {
$job = (string) ( $_GET['job'] ?? '' );
if ( ! wp_valid_job( $job ) ) wp_json( array( 'status' => 'error', 'message' => 'job' ) );
$sp = $root . '/' . $job . '/status.json';
if ( ! is_file( $sp ) ) wp_json( array( 'status' => 'idle', 'progress' => 0, 'total' => 0, 'done' => 0, 'current' => '', 'log' => array() ) );
$d = json_decode( (string) file_get_contents( $sp ), true );
wp_json( is_array( $d ) ? $d : array( 'status' => 'processing', 'progress' => 0 ) );
}
wp_json( array( 'ok' => false, 'error' => 'unknown_action' ) );