| 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
/**
* zufriedenheit-api.php – Admin-Auswertung der Kundenzufriedenheits-Umfrage (pacim_survey).
* Nur Admins (is_admin).
*
* action=list GET [rating=all|pos|neg, q, limit, offset]
* -> { ok, surveys:[...], total, stats:{...} }
* action=get GET id -> { ok, survey:{... + booking/customer} } (markiert gelesen)
* action=delete POST id -> { ok }
* action=bookings GET q -> { ok, bookings:[{...,link,hasSurvey}] } (Link erzeugen)
*/
require_once( __DIR__ . '/includes/config.inc.php' );
require_once( __DIR__ . '/includes/survey.php' );
@ini_set( 'display_errors', '0' );
error_reporting( 0 );
ob_start();
function sv_json( $a ) { while ( ob_get_level() > 0 ) ob_end_clean(); header( 'Content-Type: application/json; charset=utf-8' ); echo json_encode( $a ); exit(); }
function sv_fail( $m ) { sv_json( array( 'ok' => false, 'error' => $m ) ); }
if ( ! function_exists( 'is_admin' ) || ! is_admin() ) { http_response_code( 403 ); sv_fail( 'forbidden' ); }
$action = isset( $_GET['action'] ) ? $_GET['action'] : ( isset( $_POST['action'] ) ? $_POST['action'] : '' );
$db = new MysqliDb( MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB );
$db->rawQuery( "SET NAMES utf8mb4" );
pacim_survey_ensure( $db );
pacim_survey_aspects_ensure( $db );
$ASPECTS = pacim_survey_aspects( $db );
/* --------------------------------------------------------------- LISTE + STATISTIK */
if ( $action === 'list' ) {
$scope = (string) ( $_GET['scope'] ?? 'all' );
if ( ! in_array( $scope, array( 'all', 'customer', 'general' ), true ) ) $scope = 'all';
$rating = (string) ( $_GET['rating'] ?? 'all' );
$q = trim( (string) ( $_GET['q'] ?? '' ) );
$limit = min( 200, max( 1, (int) ( $_GET['limit'] ?? 100 ) ) );
$offset = max( 0, (int) ( $_GET['offset'] ?? 0 ) );
pacim_survey_general_ensure( $db );
$wantCust = ( $scope !== 'general' );
$wantGen = ( $scope !== 'customer' );
$parkLbl = array( 'halle' => 'Halle', 'aussen' => 'Außen' );
// ---- Kundenbezogen: Rohkomponenten (über ALLE) + gefilterte Tabellenzeilen ----
$custRaw = null; $custRows = array(); $custTot = 0;
if ( $wantCust ) {
$st = $db->rawQueryOne(
"SELECT COUNT(*) n, SUM(survey_overall) ovs, SUM(survey_overall>0) ovc,
SUM(survey_recommend=1) ry, SUM(survey_recommend IS NOT NULL) rt,
SUM(survey_overall=1) d1, SUM(survey_overall=2) d2, SUM(survey_overall=3) d3,
SUM(survey_overall=4) d4, SUM(survey_overall=5) d5, SUM(survey_seen=0) unseen,
SUM(survey_a_booking) sb, SUM(survey_a_booking>0) cb,
SUM(survey_a_parking) sp, SUM(survey_a_parking>0) cp,
SUM(survey_a_service) ss, SUM(survey_a_service>0) cs,
SUM(survey_a_staff) sf, SUM(survey_a_staff>0) cf,
SUM(survey_a_price) sr, SUM(survey_a_price>0) cr
FROM pacim_survey" );
$asp_sum = array(); $asp_cnt = array();
$bm = array( 'booking' => array( 'sb', 'cb' ), 'parking' => array( 'sp', 'cp' ), 'service' => array( 'ss', 'cs' ), 'staff' => array( 'sf', 'cf' ), 'price' => array( 'sr', 'cr' ) );
foreach ( $bm as $k => $m ) { $asp_sum[ $k ] = (float) ( $st[ $m[0] ] ?? 0 ); $asp_cnt[ $k ] = (int) ( $st[ $m[1] ] ?? 0 ); }
foreach ( (array) $db->rawQuery( "SELECT ans_key, SUM(ans_rating) s, SUM(ans_rating>0) c FROM pacim_survey_answers GROUP BY ans_key" ) as $rr ) {
$asp_sum[ $rr['ans_key'] ] = ( $asp_sum[ $rr['ans_key'] ] ?? 0 ) + (float) $rr['s'];
$asp_cnt[ $rr['ans_key'] ] = ( $asp_cnt[ $rr['ans_key'] ] ?? 0 ) + (int) $rr['c'];
}
$custRaw = array(
'n' => (int) $st['n'], 'ovs' => (float) $st['ovs'], 'ovc' => (int) $st['ovc'],
'ry' => (int) $st['ry'], 'rt' => (int) $st['rt'], 'unseen' => (int) $st['unseen'],
'dist' => array( 1 => (int) $st['d1'], 2 => (int) $st['d2'], 3 => (int) $st['d3'], 4 => (int) $st['d4'], 5 => (int) $st['d5'] ),
'asp_sum' => $asp_sum, 'asp_cnt' => $asp_cnt,
);
$where = array(); $params = array();
if ( $rating === 'pos' ) { $where[] = 's.survey_overall >= 4'; }
elseif ( $rating === 'neg' ) { $where[] = 's.survey_overall <= 2'; }
if ( $q !== '' ) {
$like = '%' . $q . '%';
$where[] = '( b.booking_no LIKE ? OR c.customer_name LIKE ? OR CONCAT_WS(" ",c.customer_firstname,c.customer_lastname) LIKE ? OR b.booking_event LIKE ? OR s.survey_comment LIKE ? )';
array_push( $params, $like, $like, $like, $like, $like );
}
$wsql = $where ? ( 'WHERE ' . implode( ' AND ', $where ) ) : '';
$rows = (array) $db->rawQuery(
"SELECT s.survey_id, s.survey_booking, s.survey_overall, s.survey_recommend, s.survey_comment,
s.survey_seen, s.survey_created, s.survey_updated,
b.booking_no, b.booking_event, b.booking_begin, b.booking_end,
c.customer_firstname, c.customer_lastname, c.customer_name
FROM pacim_survey s
LEFT JOIN pacim_booking b ON b.booking_id = s.survey_booking
LEFT JOIN pacim_customer c ON c.customer_id = s.survey_customer
$wsql
ORDER BY COALESCE(s.survey_updated, s.survey_created) DESC
LIMIT $limit OFFSET $offset", $params );
foreach ( $rows as $r ) { $r['_type'] = 'customer'; $r['_ts'] = $r['survey_updated'] ?: $r['survey_created']; $custRows[] = $r; }
$ct = $db->rawQueryOne( "SELECT COUNT(*) n FROM pacim_survey s LEFT JOIN pacim_booking b ON b.booking_id=s.survey_booking LEFT JOIN pacim_customer c ON c.customer_id=s.survey_customer $wsql", $params );
$custTot = (int) ( $ct['n'] ?? 0 );
}
// ---- Terminbezogen: Rohkomponenten (über ALLE) + gefilterte Tabellenzeilen ----
$genRaw = null; $genRows = array(); $genTot = 0;
if ( $wantGen ) {
$gst = $db->rawQueryOne(
"SELECT COUNT(*) n, SUM(sg_overall) ovs, SUM(sg_overall>0) ovc,
SUM(sg_recommend=1) ry, SUM(sg_recommend IS NOT NULL) rt,
SUM(sg_overall=1) d1, SUM(sg_overall=2) d2, SUM(sg_overall=3) d3,
SUM(sg_overall=4) d4, SUM(sg_overall=5) d5
FROM pacim_survey_general" );
$gsum = array(); $gcnt = array();
foreach ( (array) $db->rawQuery( "SELECT sg_aspects FROM pacim_survey_general" ) as $ar ) {
$j = json_decode( (string) $ar['sg_aspects'], true );
if ( is_array( $j ) ) foreach ( $j as $k => $v ) { $v = (int) $v; if ( $v >= 1 && $v <= 5 ) { $gsum[ $k ] = ( $gsum[ $k ] ?? 0 ) + $v; $gcnt[ $k ] = ( $gcnt[ $k ] ?? 0 ) + 1; } }
}
$genRaw = array(
'n' => (int) $gst['n'], 'ovs' => (float) $gst['ovs'], 'ovc' => (int) $gst['ovc'],
'ry' => (int) $gst['ry'], 'rt' => (int) $gst['rt'], 'unseen' => 0,
'dist' => array( 1 => (int) $gst['d1'], 2 => (int) $gst['d2'], 3 => (int) $gst['d3'], 4 => (int) $gst['d4'], 5 => (int) $gst['d5'] ),
'asp_sum' => $gsum, 'asp_cnt' => $gcnt,
);
$gwhere = array(); $gparams = array();
if ( $rating === 'pos' ) { $gwhere[] = 'sg_overall >= 4'; }
elseif ( $rating === 'neg' ) { $gwhere[] = 'sg_overall <= 2'; }
if ( $q !== '' ) { $like = '%' . $q . '%'; $gwhere[] = '( sg_ship LIKE ? OR sg_comment LIKE ? )'; array_push( $gparams, $like, $like ); }
$gwsql = $gwhere ? ( 'WHERE ' . implode( ' AND ', $gwhere ) ) : '';
$grows = (array) $db->rawQuery(
"SELECT sg_id, sg_session, sg_ship, sg_park, sg_overall, sg_recommend, sg_aspects, sg_comment, sg_created
FROM pacim_survey_general $gwsql ORDER BY sg_created DESC LIMIT $limit OFFSET $offset", $gparams );
foreach ( $grows as $r ) {
$genRows[] = array(
'_type' => 'general', '_ts' => $r['sg_created'],
'sg_id' => (int) $r['sg_id'], 'session_short' => substr( (string) $r['sg_session'], 0, 8 ),
'ship' => (string) $r['sg_ship'], 'park' => $parkLbl[ $r['sg_park'] ] ?? (string) $r['sg_park'],
'overall' => $r['sg_overall'] !== null ? (int) $r['sg_overall'] : null,
'recommend' => $r['sg_recommend'] !== null ? (int) $r['sg_recommend'] : null,
'aspects' => json_decode( (string) $r['sg_aspects'], true ) ?: array(),
'comment' => (string) $r['sg_comment'], 'created' => $r['sg_created'],
);
}
$gt = $db->rawQueryOne( "SELECT COUNT(*) n FROM pacim_survey_general $gwsql", $gparams );
$genTot = (int) ( $gt['n'] ?? 0 );
}
// ---- Rohkomponenten je Scope zusammenführen -> Ø-Statistik ----
$agg = array( 'n' => 0, 'ovs' => 0, 'ovc' => 0, 'ry' => 0, 'rt' => 0, 'unseen' => 0, 'dist' => array( 1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0 ), 'asp_sum' => array(), 'asp_cnt' => array() );
foreach ( array( $custRaw, $genRaw ) as $p ) {
if ( ! $p ) continue;
$agg['n'] += $p['n']; $agg['ovs'] += $p['ovs']; $agg['ovc'] += $p['ovc'];
$agg['ry'] += $p['ry']; $agg['rt'] += $p['rt']; $agg['unseen'] += $p['unseen'];
foreach ( array( 1, 2, 3, 4, 5 ) as $d ) { $agg['dist'][ $d ] += $p['dist'][ $d ]; }
foreach ( $p['asp_sum'] as $k => $v ) { $agg['asp_sum'][ $k ] = ( $agg['asp_sum'][ $k ] ?? 0 ) + $v; }
foreach ( $p['asp_cnt'] as $k => $v ) { $agg['asp_cnt'][ $k ] = ( $agg['asp_cnt'][ $k ] ?? 0 ) + $v; }
}
$aspAvg = array();
foreach ( array_keys( $ASPECTS ) as $k ) { $aspAvg[ $k ] = ! empty( $agg['asp_cnt'][ $k ] ) ? round( $agg['asp_sum'][ $k ] / $agg['asp_cnt'][ $k ], 2 ) : 0; }
// ---- Tabellenzeilen je Scope (bei „Alle" mischen + nach Datum sortieren) ----
if ( $scope === 'customer' ) { $tableRows = $custRows; $total = $custTot; }
elseif ( $scope === 'general' ) { $tableRows = $genRows; $total = $genTot; }
else {
$tableRows = array_merge( $custRows, $genRows );
usort( $tableRows, function ( $a, $b ) { return strcmp( (string) $b['_ts'], (string) $a['_ts'] ); } );
if ( count( $tableRows ) > $limit ) $tableRows = array_slice( $tableRows, 0, $limit );
$total = $custTot + $genTot;
}
sv_json( array(
'ok' => true,
'scope' => $scope,
'surveys' => $tableRows,
'total' => $total,
'aspects' => $ASPECTS,
'stats' => array(
'count' => $agg['n'],
'unseen' => $agg['unseen'],
'overall' => $agg['ovc'] ? round( $agg['ovs'] / $agg['ovc'], 2 ) : 0,
'aspects' => $aspAvg,
'rec_yes' => $agg['ry'],
'rec_tot' => $agg['rt'],
'dist' => $agg['dist'],
),
) );
}
/* --------------------------------------------------------------- EINZELNE BEWERTUNG */
if ( $action === 'get' ) {
$id = (int) ( $_GET['id'] ?? 0 );
$r = $db->rawQueryOne(
"SELECT s.*, b.booking_no, b.booking_event, b.booking_begin, b.booking_end, b.booking_invoice,
c.customer_firstname, c.customer_lastname, c.customer_name, c.customer_email
FROM pacim_survey s
LEFT JOIN pacim_booking b ON b.booking_id = s.survey_booking
LEFT JOIN pacim_customer c ON c.customer_id = s.survey_customer
WHERE s.survey_id = ?", array( $id ) );
if ( ! $r ) sv_fail( 'not_found' );
if ( (int) $r['survey_seen'] === 0 ) { $db->where( 'survey_id', $id ); $db->update( 'pacim_survey', array( 'survey_seen' => 1 ) ); $r['survey_seen'] = 1; }
$r['aspects'] = $ASPECTS;
// Custom-Bereich-Bewertungen unter survey_a_<key> spiegeln, damit das Frontend sie einheitlich liest.
foreach ( (array) $db->rawQuery( "SELECT ans_key, ans_rating FROM pacim_survey_answers WHERE ans_survey = ?", array( $id ) ) as $a ) {
$r[ 'survey_a_' . $a['ans_key'] ] = $a['ans_rating'];
}
sv_json( array( 'ok' => true, 'survey' => $r ) );
}
/* --------------------------------------------------------------- LÖSCHEN */
if ( $action === 'delete' ) {
$id = (int) ( $_POST['id'] ?? 0 );
if ( $id <= 0 ) sv_fail( 'bad_id' );
$db->where( 'survey_id', $id );
$db->delete( 'pacim_survey' );
$db->rawQuery( "DELETE FROM pacim_survey_answers WHERE ans_survey = ?", array( $id ) );
sv_json( array( 'ok' => true ) );
}
/* --------------------------------------------------------------- EINSTELLUNGEN: BEWERTUNGSBEREICHE */
if ( $action === 'aspects' ) {
sv_json( array( 'ok' => true, 'aspects' => array_values( pacim_survey_aspects_all( $db ) ) ) );
}
if ( $action === 'aspect_add' ) {
$label = mb_substr( trim( (string) ( $_POST['label'] ?? '' ) ), 0, 191 );
if ( $label === '' ) sv_fail( 'label' );
pacim_survey_aspects_ensure( $db );
// Eindeutigen Key (Slug) erzeugen – keine Kollision mit eingebauten/vorhandenen Keys.
$base = trim( preg_replace( '/[^a-z0-9]+/', '_', strtolower( $label ) ), '_' );
if ( $base === '' ) $base = 'bereich';
$base = substr( $base, 0, 30 );
$key = $base; $n = 1;
while ( pacim_survey_aspect_is_builtin( $key ) || $db->rawQueryOne( "SELECT aspect_id FROM pacim_survey_aspects WHERE aspect_key = ?", array( $key ) ) ) {
$key = substr( $base, 0, 28 ) . '_' . ( ++$n );
}
$mx = $db->rawQueryOne( "SELECT COALESCE(MAX(aspect_sort),0) AS mx FROM pacim_survey_aspects" );
$id = $db->insert( 'pacim_survey_aspects', array(
'aspect_key' => $key, 'aspect_label' => $label, 'aspect_sort' => (int) ( $mx['mx'] ?? 0 ) + 10,
'aspect_active' => 1, 'aspect_builtin' => 0, 'aspect_created' => date( 'Y-m-d H:i:s' ),
) );
sv_json( array( 'ok' => (bool) $id, 'id' => (int) $id, 'key' => $key ) );
}
if ( $action === 'aspect_update' ) {
$id = (int) ( $_POST['id'] ?? 0 );
if ( $id <= 0 ) sv_fail( 'bad_id' );
if ( ! $db->rawQueryOne( "SELECT aspect_id FROM pacim_survey_aspects WHERE aspect_id = ?", array( $id ) ) ) sv_fail( 'not_found' );
$upd = array();
if ( isset( $_POST['label'] ) ) { $lab = trim( (string) $_POST['label'] ); if ( $lab === '' ) sv_fail( 'label' ); $upd['aspect_label'] = mb_substr( $lab, 0, 191 ); }
if ( isset( $_POST['active'] ) ) $upd['aspect_active'] = ( (int) $_POST['active'] ? 1 : 0 );
if ( $upd ) { $db->where( 'aspect_id', $id ); $db->update( 'pacim_survey_aspects', $upd ); }
sv_json( array( 'ok' => true ) );
}
if ( $action === 'aspect_delete' ) {
$id = (int) ( $_POST['id'] ?? 0 );
if ( $id <= 0 ) sv_fail( 'bad_id' );
$row = $db->rawQueryOne( "SELECT aspect_key, aspect_builtin FROM pacim_survey_aspects WHERE aspect_id = ?", array( $id ) );
if ( ! $row ) sv_fail( 'not_found' );
if ( (int) $row['aspect_builtin'] === 1 ) sv_fail( 'builtin' ); // eingebaute Bereiche nur deaktivierbar, nicht löschbar
$db->where( 'aspect_id', $id ); $db->delete( 'pacim_survey_aspects' );
$db->rawQuery( "DELETE FROM pacim_survey_answers WHERE ans_key = ?", array( $row['aspect_key'] ) );
sv_json( array( 'ok' => true ) );
}
if ( $action === 'aspects_reorder' ) {
$ids = $_POST['order'] ?? '';
if ( is_string( $ids ) ) $ids = json_decode( $ids, true );
if ( ! is_array( $ids ) ) sv_fail( 'order' );
$sort = 0;
foreach ( $ids as $aid ) { $sort += 10; $db->where( 'aspect_id', (int) $aid ); $db->update( 'pacim_survey_aspects', array( 'aspect_sort' => $sort ) ); }
sv_json( array( 'ok' => true ) );
}
/* --------------------------------------------------------------- BUCHUNGEN SUCHEN (Link erzeugen) */
if ( $action === 'bookings' ) {
$q = trim( (string) ( $_GET['q'] ?? '' ) );
if ( mb_strlen( $q ) < 2 ) sv_json( array( 'ok' => true, 'bookings' => array() ) );
$like = '%' . $q . '%';
$rows = (array) $db->rawQuery(
"SELECT b.booking_id, b.booking_no, b.booking_event, b.booking_begin, b.booking_end,
c.customer_firstname, c.customer_lastname, c.customer_name,
i.invoice_no, license_plate(b.booking_serial) AS plate,
(SELECT sv.survey_id FROM pacim_survey sv WHERE sv.survey_booking = b.booking_id) AS survey_id
FROM pacim_booking b
LEFT JOIN pacim_customer c ON c.customer_id = b.booking_customer
LEFT JOIN pacim_invoice i ON i.invoice_id = b.booking_invoice
WHERE b.booking_no LIKE ? OR i.invoice_no LIKE ? OR c.customer_name LIKE ?
OR CONCAT_WS(' ', c.customer_firstname, c.customer_lastname) LIKE ?
OR license_plate(b.booking_serial) LIKE ?
ORDER BY b.booking_id DESC LIMIT 25",
array( $like, $like, $like, $like, $like ) );
$out = array();
foreach ( $rows as $r ) {
$out[] = array(
'booking_id' => (int) $r['booking_id'],
'booking_no' => (string) $r['booking_no'],
'invoice_no' => (string) $r['invoice_no'],
'ship' => (string) $r['booking_event'],
'plate' => (string) $r['plate'],
'name' => trim( (string) $r['customer_firstname'] . ' ' . (string) $r['customer_lastname'] ) ?: (string) $r['customer_name'],
'begin' => (string) $r['booking_begin'],
'end' => (string) $r['booking_end'],
'hasSurvey' => ! empty( $r['survey_id'] ),
'link' => pacim_survey_link( (int) $r['booking_id'] ),
);
}
sv_json( array( 'ok' => true, 'bookings' => $out ) );
}
/* --------------------------------------------------------------- ANALYSE-TAB (Filter-Optionen) */
if ( $action === 'analyse_meta' ) {
require_once( __DIR__ . '/includes/survey-analyse.php' );
sv_json( array( 'ok' => true, 'meta' => pacim_sa_meta( $db ) ) );
}
/* --------------------------------------------------------------- ANALYSE-TAB (PDF-Bericht) */
if ( $action === 'analyse_pdf' ) {
require_once( __DIR__ . '/includes/survey-analyse.php' );
$raw = file_get_contents( 'php://input' ); $in = json_decode( $raw, true ); if ( ! is_array( $in ) ) $in = array();
$f = ( isset( $in['filters'] ) && is_array( $in['filters'] ) ) ? $in['filters'] : array();
$images = ( isset( $in['images'] ) && is_array( $in['images'] ) ) ? $in['images'] : array();
$ftext = isset( $in['filters_text'] ) ? (string) $in['filters_text'] : '';
$bytes = pacim_survey_analyse_pdf( $db, $f, $images, $ftext );
while ( ob_get_level() > 0 ) ob_end_clean();
header( 'Content-Type: application/pdf' );
header( 'Content-Disposition: attachment; filename="kundenzufriedenheit-analyse.pdf"' );
header( 'Content-Length: ' . strlen( $bytes ) );
header( 'Cache-Control: no-store' );
echo $bytes; exit();
}
/* --------------------------------------------------------------- ANALYSE-TAB (alle Widget-Daten) */
if ( $action === 'analyse' ) {
require_once( __DIR__ . '/includes/survey-analyse.php' );
$keys = array( 'from','to','ship','trip','product','area','stars','recommend','land','bundesland','ci_from','ci_to','months','duration' );
$f = array(); foreach ( $keys as $k ) if ( isset( $_GET[ $k ] ) ) $f[ $k ] = (string) $_GET[ $k ];
// Optionaler 5-Minuten-Cache über pacim_dash_cache (dc_key ≤ 40 Zeichen).
$ck = 'svan:' . md5( json_encode( $f ) );
$cached = $db->rawQueryOne( "SELECT dc_data, dc_updated FROM pacim_dash_cache WHERE dc_key = ? AND dc_updated >= (NOW() - INTERVAL 5 MINUTE)", array( $ck ) );
if ( $cached && ! empty( $cached['dc_data'] ) ) {
$data = json_decode( $cached['dc_data'], true );
if ( is_array( $data ) ) sv_json( array( 'ok' => true, 'cached' => true, 'data' => $data ) );
}
$data = pacim_survey_analyse( $db, $f );
// Cache schreiben (ohne Abhängigkeit von einem Unique-Key: alten Eintrag ersetzen).
$db->rawQuery( "DELETE FROM pacim_dash_cache WHERE dc_key = ?", array( $ck ) );
$db->rawQuery( "INSERT INTO pacim_dash_cache (dc_key, dc_date, dc_data, dc_updated) VALUES (?, CURDATE(), ?, NOW())", array( $ck, json_encode( $data ) ) );
sv_json( array( 'ok' => true, 'cached' => false, 'data' => $data ) );
}
/* --------------------------------------------------------------- ALLGEMEINER LINK + QR */
if ( $action === 'general_link' ) {
$link = pacim_survey_general_link();
$png = pacim_survey_qr( $link, 'png', 8 );
$svg = pacim_survey_qr( $link, 'svg' );
sv_json( array(
'ok' => true,
'link' => $link,
'qr_png' => 'data:image/png;base64,' . base64_encode( (string) $png ),
'qr_svg' => (string) $svg,
) );
}
/* --------------------------------------------------------------- ALLGEMEINE BEWERTUNGEN (nach Session) */
if ( $action === 'general_list' ) {
pacim_survey_general_ensure( $db );
$rows = (array) $db->rawQuery(
"SELECT sg_id, sg_session, sg_ship, sg_park, sg_overall, sg_recommend, sg_aspects, sg_comment, sg_created
FROM pacim_survey_general ORDER BY sg_created DESC LIMIT 1000" );
$parkLbl = array( 'halle' => 'Halle', 'aussen' => 'Außen' );
$sessions = array();
foreach ( $rows as $r ) {
$sid = (string) $r['sg_session'];
if ( ! isset( $sessions[ $sid ] ) ) {
$sessions[ $sid ] = array( 'session' => $sid, 'short' => substr( $sid, 0, 8 ), 'count' => 0, 'osum' => 0, 'on' => 0, 'last' => $r['sg_created'], 'items' => array() );
}
$sessions[ $sid ]['count']++;
if ( $r['sg_overall'] !== null ) { $sessions[ $sid ]['osum'] += (int) $r['sg_overall']; $sessions[ $sid ]['on']++; }
if ( $r['sg_created'] > $sessions[ $sid ]['last'] ) $sessions[ $sid ]['last'] = $r['sg_created'];
$sessions[ $sid ]['items'][] = array(
'id' => (int) $r['sg_id'],
'ship' => (string) $r['sg_ship'],
'park' => $parkLbl[ $r['sg_park'] ] ?? (string) $r['sg_park'],
'overall' => $r['sg_overall'] !== null ? (int) $r['sg_overall'] : null,
'recommend' => $r['sg_recommend'] !== null ? (int) $r['sg_recommend'] : null,
'aspects' => json_decode( (string) $r['sg_aspects'], true ) ?: array(),
'comment' => (string) $r['sg_comment'],
'created' => $r['sg_created'],
);
}
$out = array();
foreach ( $sessions as $s ) { $s['avg'] = $s['on'] ? round( $s['osum'] / $s['on'], 2 ) : null; unset( $s['osum'], $s['on'] ); $out[] = $s; }
sv_json( array( 'ok' => true, 'aspects' => $ASPECTS, 'sessions' => $out, 'total' => count( $rows ) ) );
}
if ( $action === 'general_delete' ) {
$id = (int) ( $_POST['id'] ?? 0 );
if ( $id <= 0 ) sv_fail( 'bad_id' );
$db->where( 'sg_id', $id );
$db->delete( 'pacim_survey_general' );
sv_json( array( 'ok' => true ) );
}
/* --------------------------------------------------------------- QR-CODE (hochauflösend) */
if ( $action === 'general_qr' ) {
$px = (int) ( $_GET['px'] ?? 4000 );
$png = pacim_survey_qr_png_hires( pacim_survey_general_link(), $px );
while ( ob_get_level() > 0 ) ob_end_clean();
if ( $png === null ) { http_response_code( 500 ); header( 'Content-Type: text/plain; charset=utf-8' ); echo 'QR-Erzeugung fehlgeschlagen.'; exit(); }
header( 'Content-Type: image/png' );
header( 'Content-Disposition: attachment; filename="qr-bewertungslink-' . max( 100, min( 8000, $px ) ) . 'px.png"' );
header( 'Content-Length: ' . strlen( $png ) );
header( 'Cache-Control: no-store, no-cache, must-revalidate' );
echo $png;
exit();
}
sv_fail( 'unknown_action' );