Files
WP-Multi-Formular/inc/classes/class-submissions-list.php
2026-04-13 18:52:46 +02:00

482 lines
25 KiB
PHP

<?php
if(!defined('ABSPATH')) exit;
class WMF_Submissions_List {
/* ================================================================
LISTE ALLER EINREICHUNGEN EINES FORMULARS
================================================================ */
public static function render($form_id) {
$meta = wmf_get_form_meta($form_id);
$fields = self::get_display_fields($meta);
$per_page = 25;
$current_page= max(1, intval($_GET['paged'] ?? 1));
$offset = ($current_page - 1) * $per_page;
$status_filter = sanitize_text_field($_GET['sub_status'] ?? '');
$search = sanitize_text_field($_GET['sub_search'] ?? '');
$submissions = wmf_get_submissions($form_id, array(
'limit' => $per_page,
'offset' => $offset,
'status' => $status_filter,
'search' => $search,
));
$total = wmf_count_submissions($form_id, $status_filter, $search);
$total_pages = ceil($total / $per_page);
$base_url = add_query_arg(array(
'page' => 'wp-multi-formular',
'view_submissions' => $form_id,
), admin_url('admin.php'));
?>
<div class="wmf-submissions-wrap">
<!-- Header -->
<div class="wmf-subs-header">
<h2 class="wmf-subs-title">
Einreichungen
<span class="wmf-subs-count"><?php echo intval($total); ?></span>
</h2>
<div class="wmf-subs-actions">
<?php if($submissions): ?>
<a href="<?php echo esc_url(self::export_url($form_id)); ?>"
class="button">&#11015; CSV exportieren</a>
<?php endif; ?>
</div>
</div>
<!-- Filter-Leiste -->
<div class="wmf-subs-filter-bar">
<form method="get" action="<?php echo esc_url(admin_url('admin.php')); ?>">
<input type="hidden" name="page" value="wp-multi-formular">
<input type="hidden" name="view_submissions" value="<?php echo intval($form_id); ?>">
<!-- Status-Tabs -->
<div class="wmf-status-tabs">
<?php
$statuses = array('' => 'Alle', 'neu' => 'Neu', 'gelesen' => 'Gelesen', 'archiviert' => 'Archiviert');
foreach($statuses as $s_key => $s_label):
$cnt = ($s_key === '') ? wmf_count_submissions($form_id) : wmf_count_submissions($form_id, $s_key);
$active = ($status_filter === $s_key) ? 'current' : '';
?>
<a href="<?php echo esc_url(add_query_arg(array('sub_status'=>$s_key,'paged'=>1), $base_url)); ?>"
class="wmf-status-tab <?php echo $active; ?>">
<?php echo esc_html($s_label); ?>
<span class="count">(<?php echo intval($cnt); ?>)</span>
</a>
<?php endforeach; ?>
</div>
<!-- Suche -->
<div class="wmf-subs-search">
<input type="search" name="sub_search" value="<?php echo esc_attr($search); ?>"
placeholder="Einreichungen durchsuchen …" class="wmf-search-input">
<button type="submit" class="button">Suchen</button>
<?php if($search): ?>
<a href="<?php echo esc_url($base_url); ?>" class="button">✕ Zurücksetzen</a>
<?php endif; ?>
</div>
</form>
</div>
<?php if(empty($submissions)): ?>
<div class="wmf-empty-submissions">
<span class="dashicons dashicons-email-alt" style="font-size:48px;width:48px;height:48px;color:#c3c4c7;margin-bottom:12px;display:block;"></span>
<?php if($search||$status_filter): ?>
<p>Keine Einreichungen gefunden.</p>
<a href="<?php echo esc_url($base_url); ?>" class="button">Filter zurücksetzen</a>
<?php else: ?>
<p>Noch keine Einreichungen vorhanden.</p>
<?php endif; ?>
</div>
<?php else: ?>
<!-- Tabelle -->
<div class="wmf-table-wrap">
<table class="wp-list-table widefat fixed striped wmf-submissions-table">
<thead>
<tr>
<th class="wmf-col-id">#</th>
<th class="wmf-col-date">Datum</th>
<?php foreach(array_slice($fields, 0, 4) as $f): ?>
<th><?php echo esc_html($f['label']); ?></th>
<?php endforeach; ?>
<th class="wmf-col-status">Status</th>
<th class="wmf-col-actions">Aktionen</th>
</tr>
</thead>
<tbody>
<?php foreach($submissions as $row):
$data = json_decode($row->data, true) ?? array();
$view_url = add_query_arg(array(
'page' => 'wp-multi-formular',
'view_submission' => $row->id,
'form_id' => $form_id,
), admin_url('admin.php'));
?>
<tr class="wmf-sub-row <?php echo $row->status === 'neu' ? 'wmf-sub-new' : ''; ?>">
<td class="wmf-col-id">
<a href="<?php echo esc_url($view_url); ?>" class="wmf-sub-id-link">
#<?php echo intval($row->id); ?>
</a>
</td>
<td class="wmf-col-date">
<a href="<?php echo esc_url($view_url); ?>">
<?php echo esc_html(date_i18n('d.m.Y', strtotime($row->created_at))); ?>
<span class="wmf-time"><?php echo esc_html(date_i18n('H:i', strtotime($row->created_at))); ?></span>
</a>
</td>
<?php foreach(array_slice($fields, 0, 4) as $f):
$v = $data[$f['id']] ?? '';
if(is_array($v)) $v = implode(', ', $v);
if(($f['type']??'') === 'signature') $v = '(Unterschrift)';
$v_short = mb_strlen($v) > 60 ? mb_substr($v, 0, 60).'…' : $v;
?>
<td>
<a href="<?php echo esc_url($view_url); ?>" title="<?php echo esc_attr($v); ?>">
<?php echo esc_html($v_short); ?>
</a>
</td>
<?php endforeach; ?>
<td class="wmf-col-status">
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="wmf-status-form">
<?php wp_nonce_field('wmf_update_status'); ?>
<input type="hidden" name="action" value="wmf_update_submission_status">
<input type="hidden" name="submission_id" value="<?php echo intval($row->id); ?>">
<input type="hidden" name="form_id" value="<?php echo intval($form_id); ?>">
<input type="hidden" name="redirect_url" value="<?php echo esc_attr(add_query_arg(array('page'=>'wp-multi-formular','view_submissions'=>$form_id,'paged'=>$current_page,'sub_status'=>$status_filter), admin_url('admin.php'))); ?>">
<select name="status" class="wmf-status-select wmf-status-<?php echo esc_attr($row->status); ?>" onchange="this.form.submit()">
<option value="neu" <?php selected($row->status,'neu'); ?>>Neu</option>
<option value="gelesen" <?php selected($row->status,'gelesen'); ?>>Gelesen</option>
<option value="archiviert" <?php selected($row->status,'archiviert'); ?>>Archiviert</option>
</select>
</form>
</td>
<td class="wmf-col-actions">
<a href="<?php echo esc_url($view_url); ?>" class="button button-small">
&#128065; Ansehen
</a>
<a href="<?php echo esc_url(wp_nonce_url(
add_query_arg(array('wmf_action'=>'delete_submission','submission_id'=>$row->id,'form_id'=>$form_id,'page'=>'wp-multi-formular'), admin_url('admin.php')),
'wmf_delete_submission'
)); ?>"
class="button button-small wmf-btn-delete"
onclick="return confirm('Einreichung #<?php echo intval($row->id); ?> wirklich löschen?')">
&#128465; Löschen
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Pagination -->
<?php if($total_pages > 1): ?>
<div class="wmf-pagination">
<?php if($current_page > 1): ?>
<a href="<?php echo esc_url(add_query_arg('paged', $current_page-1, $base_url)); ?>" class="button">&laquo; Zurück</a>
<?php endif; ?>
<span class="wmf-page-info">
Seite <?php echo $current_page; ?> von <?php echo $total_pages; ?>
</span>
<?php if($current_page < $total_pages): ?>
<a href="<?php echo esc_url(add_query_arg('paged', $current_page+1, $base_url)); ?>" class="button">Weiter &raquo;</a>
<?php endif; ?>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<?php
}
/* ================================================================
DETAILANSICHT EINER EINZELNEN EINREICHUNG
================================================================ */
public static function render_single($submission_id, $form_id) {
global $wpdb;
$row = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}wmf_submissions WHERE id = %d AND form_id = %d",
$submission_id, $form_id
));
if(!$row) {
echo '<div class="notice notice-error"><p>Einreichung nicht gefunden.</p></div>';
return;
}
// Als gelesen markieren
if($row->status === 'neu') {
WMF_Submission::update_status($submission_id, 'gelesen');
$row->status = 'gelesen';
}
$data = json_decode($row->data, true) ?? array();
$meta = wmf_get_form_meta($form_id);
$fields = self::get_display_fields($meta);
$form = get_post($form_id);
$back_url = add_query_arg(array(
'page' => 'wp-multi-formular',
'view_submissions' => $form_id,
), admin_url('admin.php'));
$prev_url = self::get_adjacent_url($submission_id, $form_id, 'prev');
$next_url = self::get_adjacent_url($submission_id, $form_id, 'next');
?>
<div class="wmf-submission-detail">
<!-- Navigation -->
<div class="wmf-detail-nav">
<a href="<?php echo esc_url($back_url); ?>" class="button">
&larr; Alle Einreichungen
</a>
<div class="wmf-detail-nav-arrows">
<?php if($prev_url): ?>
<a href="<?php echo esc_url($prev_url); ?>" class="button" title="Vorherige Einreichung">&laquo; Vorherige</a>
<?php else: ?>
<button class="button" disabled>&laquo; Vorherige</button>
<?php endif; ?>
<?php if($next_url): ?>
<a href="<?php echo esc_url($next_url); ?>" class="button" title="Nächste Einreichung">Nächste &raquo;</a>
<?php else: ?>
<button class="button" disabled>Nächste &raquo;</button>
<?php endif; ?>
</div>
</div>
<!-- Meta-Karte -->
<div class="wmf-detail-meta-card">
<div class="wmf-detail-meta-item">
<span class="wmf-detail-meta-label">Einreichung</span>
<span class="wmf-detail-meta-value">#<?php echo intval($row->id); ?></span>
</div>
<div class="wmf-detail-meta-item">
<span class="wmf-detail-meta-label">Formular</span>
<span class="wmf-detail-meta-value"><?php echo esc_html($form ? $form->post_title : 'Unbekannt'); ?></span>
</div>
<div class="wmf-detail-meta-item">
<span class="wmf-detail-meta-label">Datum</span>
<span class="wmf-detail-meta-value"><?php echo esc_html(date_i18n('d.m.Y H:i', strtotime($row->created_at))); ?></span>
</div>
<div class="wmf-detail-meta-item">
<span class="wmf-detail-meta-label">IP-Adresse</span>
<span class="wmf-detail-meta-value"><?php echo esc_html($row->ip ?: '—'); ?></span>
</div>
<div class="wmf-detail-meta-item">
<span class="wmf-detail-meta-label">Status</span>
<form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" style="display:inline;">
<?php wp_nonce_field('wmf_update_status'); ?>
<input type="hidden" name="action" value="wmf_update_submission_status">
<input type="hidden" name="submission_id" value="<?php echo intval($row->id); ?>">
<input type="hidden" name="form_id" value="<?php echo intval($form_id); ?>">
<input type="hidden" name="redirect_url" value="<?php echo esc_attr(add_query_arg(array('page'=>'wp-multi-formular','view_submission'=>$row->id,'form_id'=>$form_id), admin_url('admin.php'))); ?>">
<select name="status" class="wmf-status-select wmf-status-<?php echo esc_attr($row->status); ?>" onchange="this.form.submit()">
<option value="neu" <?php selected($row->status,'neu'); ?>>Neu</option>
<option value="gelesen" <?php selected($row->status,'gelesen'); ?>>Gelesen</option>
<option value="archiviert" <?php selected($row->status,'archiviert'); ?>>Archiviert</option>
</select>
</form>
</div>
<div class="wmf-detail-meta-item wmf-detail-meta-actions">
<a href="<?php echo esc_url(wp_nonce_url(
add_query_arg(array('wmf_action'=>'delete_submission','submission_id'=>$row->id,'form_id'=>$form_id,'page'=>'wp-multi-formular'), admin_url('admin.php')),
'wmf_delete_submission'
)); ?>"
class="button wmf-btn-delete"
onclick="return confirm('Einreichung #<?php echo intval($row->id); ?> wirklich löschen?')">
&#128465; Löschen
</a>
<button onclick="window.print()" class="button">&#128438; Drucken</button>
</div>
</div>
<!-- Felder-Inhalt -->
<div class="wmf-detail-fields">
<h3 class="wmf-detail-section-title">Eingereichte Daten</h3>
<div class="wmf-detail-fields-grid">
<?php foreach($fields as $field):
$val = $data[$field['id']] ?? '';
$type = $field['type'] ?? 'text';
?>
<div class="wmf-detail-field <?php echo 'wmf-detail-field-' . esc_attr($type); ?>">
<div class="wmf-detail-field-label">
<?php echo esc_html($field['label']); ?>
<span class="wmf-detail-field-type"><?php echo esc_html($type); ?></span>
</div>
<div class="wmf-detail-field-value">
<?php self::render_field_value($val, $field); ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- Technische Infos (aufklappbar) -->
<details class="wmf-detail-technical">
<summary>Technische Informationen</summary>
<table class="widefat" style="margin-top:12px;">
<tr><th>User-Agent</th><td><?php echo esc_html($row->user_agent ?: '—'); ?></td></tr>
<tr><th>Erstellt am</th><td><?php echo esc_html($row->created_at); ?></td></tr>
<tr><th>Datenbank-ID</th><td><?php echo intval($row->id); ?></td></tr>
</table>
</details>
</div>
<style>
@media print {
#adminmenumain,#wpadminbar,.wmf-detail-nav,.wmf-detail-meta-actions,
#screen-meta,#wpfooter,.notice { display:none!important; }
.wmf-submission-detail { margin:0; padding:0; }
.wmf-detail-fields { border:none; }
}
</style>
<?php
}
/* ================================================================
FELDINHALTE RENDERN (je nach Typ)
================================================================ */
private static function render_field_value($val, $field) {
$type = $field['type'] ?? 'text';
if($val === '' || $val === null || $val === array()) {
echo '<span class="wmf-empty-value">—</span>';
return;
}
switch($type) {
case 'checkbox':
$vals = is_array($val) ? $val : explode(', ', $val);
echo '<ul class="wmf-val-list">';
foreach($vals as $v) echo '<li>' . esc_html($v) . '</li>';
echo '</ul>';
break;
case 'gdpr':
$icon = $val === '1' ? '✅' : '❌';
echo $icon . ' ' . ($val === '1' ? 'Zugestimmt' : 'Nicht zugestimmt');
break;
case 'signature':
if(strpos($val, 'data:image') === 0) {
echo '<img src="' . esc_attr($val) . '" style="max-width:300px;border:1px solid #dcdcde;border-radius:4px;background:#fff;">';
} else {
echo '<span class="wmf-empty-value">Keine Unterschrift</span>';
}
break;
case 'file':
$urls = is_array($val) ? $val : explode(', ', $val);
echo '<div class="wmf-val-files">';
foreach($urls as $url) {
$url = trim($url);
if(!$url) continue;
$filename = basename(parse_url($url, PHP_URL_PATH));
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$is_image = in_array($ext, array('jpg','jpeg','png','gif','webp'));
if($is_image) {
echo '<div class="wmf-val-file-img"><a href="' . esc_url($url) . '" target="_blank">';
echo '<img src="' . esc_url($url) . '" style="max-width:200px;max-height:150px;border-radius:4px;"></a></div>';
} else {
echo '<a href="' . esc_url($url) . '" target="_blank" class="wmf-val-file-link">📎 ' . esc_html($filename) . '</a>';
}
}
echo '</div>';
break;
case 'rating':
$stars = intval($val);
$max = intval($field['max_stars'] ?? 5);
echo '<div class="wmf-val-stars">';
for($i = 1; $i <= $max; $i++) {
echo '<span style="color:' . ($i <= $stars ? '#f0b429' : '#ddd') . ';font-size:20px;">★</span>';
}
echo ' <span style="color:#646970;font-size:13px;">' . $stars . '/' . $max . '</span></div>';
break;
case 'url':
echo '<a href="' . esc_url($val) . '" target="_blank">' . esc_html($val) . '</a>';
break;
case 'email':
echo '<a href="mailto:' . esc_attr($val) . '">' . esc_html($val) . '</a>';
break;
case 'textarea':
echo '<div class="wmf-val-textarea">' . nl2br(esc_html($val)) . '</div>';
break;
default:
if(is_array($val)) {
echo '<ul class="wmf-val-list">';
foreach($val as $v) echo '<li>' . esc_html($v) . '</li>';
echo '</ul>';
} else {
echo '<span class="wmf-val-text">' . esc_html($val) . '</span>';
}
}
}
/* ================================================================
HILFSFUNKTIONEN
================================================================ */
private static function get_display_fields($meta) {
return array_values(array_filter($meta['fields'] ?? array(), function($f) {
return !in_array($f['type'] ?? '', array('html','divider','hidden'));
}));
}
private static function get_adjacent_url($submission_id, $form_id, $direction) {
global $wpdb;
$op = $direction === 'prev' ? '>' : '<';
$order = $direction === 'prev' ? 'ASC' : 'DESC';
$adj = $wpdb->get_var($wpdb->prepare(
"SELECT id FROM {$wpdb->prefix}wmf_submissions WHERE form_id = %d AND id {$op} %d ORDER BY id {$order} LIMIT 1",
$form_id, $submission_id
));
if(!$adj) return null;
return add_query_arg(array('page'=>'wp-multi-formular','view_submission'=>$adj,'form_id'=>$form_id), admin_url('admin.php'));
}
public static function status_label($s) {
return array('neu'=>'Neu','gelesen'=>'Gelesen','archiviert'=>'Archiviert')[$s] ?? $s;
}
public static function export_url($form_id) {
return wp_nonce_url(add_query_arg(array('wmf_action'=>'export_csv','form_id'=>$form_id,'page'=>'wp-multi-formular'), admin_url('admin.php')), 'wmf_export_csv');
}
public static function maybe_export($form_id) {
if(empty($_GET['wmf_action']) || $_GET['wmf_action'] !== 'export_csv') return;
if(!wp_verify_nonce($_GET['_wpnonce'] ?? '', 'wmf_export_csv')) return;
$meta = wmf_get_form_meta($form_id);
$fields = self::get_display_fields($meta);
$rows = wmf_get_submissions($form_id, array('limit'=>9999));
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="formular-' . $form_id . '-einreichungen.csv"');
$out = fopen('php://output', 'w');
fprintf($out, chr(0xEF).chr(0xBB).chr(0xBF));
$header = array('ID','Datum','Status','IP');
foreach($fields as $f) $header[] = $f['label'];
fputcsv($out, $header, ';');
foreach($rows as $row) {
$data = json_decode($row->data, true) ?? array();
$line = array($row->id, date_i18n('d.m.Y H:i', strtotime($row->created_at)), self::status_label($row->status), $row->ip);
foreach($fields as $f) {
$v = $data[$f['id']] ?? '';
if(is_array($v)) $v = implode(', ', $v);
if(($f['type']??'') === 'signature') $v = '(Unterschrift)';
$line[] = $v;
}
fputcsv($out, $line, ';');
}
fclose($out);
exit;
}
}