diff --git a/wp-multi-ticket.php b/wp-multi-ticket.php
new file mode 100644
index 0000000..d02e234
--- /dev/null
+++ b/wp-multi-ticket.php
@@ -0,0 +1,1331 @@
+table_tickets = $wpdb->prefix . 'wmt_tickets';
+ $this->table_messages = $wpdb->prefix . 'wmt_messages';
+
+ register_activation_hook( __FILE__, array( $this, 'create_tables' ) );
+ add_action( 'plugins_loaded', array( $this, 'check_db_update' ) );
+
+ add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
+ add_action( 'admin_init', array( $this, 'register_settings' ) );
+
+ add_action( 'wp_dashboard_setup', array( $this, 'add_dashboard_widget' ) );
+
+ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) );
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
+ add_action( 'admin_enqueue_scripts', array( $this, 'load_analytics_scripts' ) );
+
+ add_shortcode( 'wmt_form', array( $this, 'render_creation_form' ) );
+ add_shortcode( 'wmt_view', array( $this, 'render_ticket_view' ) );
+ add_shortcode( 'wmt_lookup', array( $this, 'render_lookup_form' ) );
+
+ add_action( 'init', array( $this, 'handle_guest_creation' ) );
+ add_action( 'init', array( $this, 'handle_guest_reply' ) );
+ add_action( 'init', array( $this, 'handle_guest_lookup' ) );
+
+ add_action( 'admin_post_wmt_admin_reply', array( $this, 'handle_admin_post' ) );
+ add_action( 'admin_init', array( $this, 'handle_delete_ticket' ) );
+ add_action( 'admin_init', array( $this, 'handle_csv_export' ) );
+ }
+
+ // *** HIER: korrigierter Hook-Name für die Analytics-Unterseite ***
+ public function load_analytics_scripts( $hook ) {
+ // Für Submenu: parent slug wmt_tickets -> Hook: wmt-tickets_page_wmt_analytics
+ if ( 'wmt-tickets_page_wmt_analytics' !== $hook ) return;
+
+ $local_js = plugin_dir_path( __FILE__ ) . 'chart.js';
+ $js_url = plugins_url( 'chart.js', __FILE__ );
+
+ if ( file_exists( $local_js ) ) {
+ wp_enqueue_script( 'chartjs', $js_url, array(), '4.4.0', true );
+ }
+ }
+
+ public function enqueue_styles() {
+ ?>
+
+ get_charset_collate();
+
+ $sql_tickets = "CREATE TABLE $this->table_tickets (
+ id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+ title varchar(255) NOT NULL,
+ category varchar(100) DEFAULT 'Allgemein',
+ department varchar(100) DEFAULT NULL,
+ status varchar(50) DEFAULT 'Offen',
+ priority varchar(50) DEFAULT 'Mittel',
+ guest_name varchar(100) NOT NULL,
+ guest_email varchar(100) NOT NULL,
+ ticket_hash varchar(64) NOT NULL,
+ assigned_to bigint(20) UNSIGNED DEFAULT NULL,
+ created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (id),
+ KEY assigned_to (assigned_to)
+ ) $charset_collate;";
+
+ $sql_messages = "CREATE TABLE $this->table_messages (
+ id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+ ticket_id bigint(20) UNSIGNED NOT NULL,
+ sender_name varchar(100) NOT NULL,
+ sender_type varchar(20) NOT NULL,
+ message longtext DEFAULT NULL,
+ internal_note text DEFAULT NULL,
+ file_url varchar(255) DEFAULT NULL,
+ created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ PRIMARY KEY (id)
+ ) $charset_collate;";
+
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
+ dbDelta( $sql_tickets );
+ dbDelta( $sql_messages );
+ }
+
+ public function check_db_update() {
+ global $wpdb;
+ $cols = $wpdb->get_col( "SHOW COLUMNS FROM $this->table_tickets" );
+ if( !in_array('ticket_hash', $cols) ) $wpdb->query( "ALTER TABLE $this->table_tickets ADD COLUMN ticket_hash varchar(64) NOT NULL AFTER guest_email" );
+ if( !in_array('guest_name', $cols) ) { $wpdb->query( "ALTER TABLE $this->table_tickets ADD COLUMN guest_name varchar(100) NOT NULL" ); $wpdb->query( "ALTER TABLE $this->table_tickets ADD COLUMN guest_email varchar(100) NOT NULL" ); }
+ if( !in_array('department', $cols) ) $wpdb->query( "ALTER TABLE $this->table_tickets ADD COLUMN department varchar(100) DEFAULT NULL AFTER category" );
+
+ $mcols = $wpdb->get_col( "SHOW COLUMNS FROM $this->table_messages" );
+ if( !in_array('sender_type', $mcols) ) { $wpdb->query( "ALTER TABLE $this->table_messages ADD COLUMN sender_name varchar(100) NOT NULL" ); $wpdb->query( "ALTER TABLE $this->table_messages ADD COLUMN sender_type varchar(20) NOT NULL" ); }
+ if( !in_array('internal_note', $mcols) ) $wpdb->query( "ALTER TABLE $this->table_messages ADD COLUMN internal_note text DEFAULT NULL AFTER message" );
+ if( !in_array('file_url', $mcols) ) $wpdb->query( "ALTER TABLE $this->table_messages ADD COLUMN file_url varchar(255) DEFAULT NULL AFTER internal_note" );
+
+ $wpdb->query( "ALTER TABLE $this->table_messages MODIFY COLUMN message longtext DEFAULT NULL" );
+ }
+
+ public function add_admin_menu() {
+ add_menu_page( 'Tickets', 'Tickets Pro', 'manage_options', 'wmt_tickets', array( $this, 'render_admin_page' ), 'dashicons-tickets-alt', 30 );
+ add_submenu_page( 'wmt_tickets', 'Übersicht', 'Übersicht', 'manage_options', 'wmt_tickets', array( $this, 'render_admin_page' ) );
+ add_submenu_page( 'wmt_tickets', 'Analytics', 'Analytics', 'manage_options', 'wmt_analytics', array( $this, 'render_analytics_page' ) );
+ add_submenu_page( 'wmt_tickets', 'Einstellungen', 'Einstellungen', 'manage_options', 'wmt_settings', array( $this, 'render_settings_page' ) );
+ add_submenu_page( 'wmt_tickets', 'Benachrichtigungen', 'Benachrichtigungen', 'manage_options', 'wmt_notifications', array( $this, 'render_notifications_page' ) );
+ }
+
+ public function register_settings() {
+ register_setting( 'wmt_settings_group', 'wmt_categories' );
+ register_setting( 'wmt_settings_group', 'wmt_departments' );
+ register_setting( 'wmt_settings_group', 'wmt_priorities' );
+ register_setting( 'wmt_settings_group', 'wmt_statuses' );
+ register_setting( 'wmt_settings_group', 'wmt_admin_email' );
+ register_setting( 'wmt_settings_group', 'wmt_templates' );
+ register_setting( 'wmt_settings_group', 'wmt_allowed_filetypes' );
+
+ register_setting( 'wmt_notifications_group', 'wmt_discord_webhook' );
+ register_setting( 'wmt_notifications_group', 'wmt_telegram_token' );
+ register_setting( 'wmt_notifications_group', 'wmt_telegram_chat_id' );
+ register_setting( 'wmt_notifications_group', 'wmt_new_ticket_notify_users' );
+ }
+
+ public function render_analytics_page() {
+ global $wpdb;
+
+ $total_tickets = $wpdb->get_var("SELECT COUNT(*) FROM $this->table_tickets");
+
+ if ( $total_tickets == 0 ) {
+ echo '
';
+ echo '
';
+ echo '
Noch keine Daten ';
+ echo '
Es wurden noch keine Tickets erstellt.
';
+ echo '
Zu den Tickets ';
+ echo '
';
+ return;
+ }
+
+ $open_tickets = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $this->table_tickets WHERE status NOT LIKE %s", '%Geschlossen%'));
+ $closed_tickets = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $this->table_tickets WHERE status LIKE %s", '%Geschlossen%'));
+ $new_tickets_30d = $wpdb->get_var("SELECT COUNT(*) FROM $this->table_tickets WHERE created_at >= DATE_SUB(DATE_ADD(NOW(), INTERVAL 2 YEAR), INTERVAL 30 DAY)");
+
+ $status_data = $wpdb->get_results("SELECT COALESCE(status, 'Unbekannt') as status, COUNT(*) as count FROM $this->table_tickets GROUP BY status");
+
+ $cat_data = $wpdb->get_results("SELECT COALESCE(category, 'Keine Kategorie') as category, COUNT(*) as count FROM $this->table_tickets GROUP BY category ORDER BY count DESC LIMIT 8");
+
+ $agent_data = $wpdb->get_results("
+ SELECT u.display_name, COUNT(t.id) as count
+ FROM {$wpdb->users} u
+ LEFT JOIN $this->table_tickets t ON u.ID = t.assigned_to
+ GROUP BY u.ID
+ HAVING count > 0
+ ORDER BY count DESC
+ ");
+
+ $timeline_data = $wpdb->get_results("
+ SELECT DATE(created_at) as date, COUNT(*) as count
+ FROM $this->table_tickets
+ GROUP BY DATE(created_at)
+ ORDER BY date ASC
+ ");
+
+ ?>
+
+
+
+
+
+
+
Aktiv / Offen
+
+
Benötigt Aufmerksamkeit
+
+
+
+
Letzte 30 Tage
+
+
Neue Eingänge
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'display_name' ) );
+ $selected_users = get_option( 'wmt_new_ticket_notify_users', array() ); // FIX: Corrected array()
+ if ( ! is_array( $selected_users ) ) $selected_users = array();
+ ?>
+
+
Benachrichtigungen
+
+
+
+
+
+
+ get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $this->table_tickets WHERE status NOT LIKE %s", '%Geschlossen%' ) );
+ $link = admin_url( 'admin.php?page=wmt_tickets' );
+
+ echo '';
+ if ( $count > 0 ) {
+ echo '
' . intval( $count ) . '
';
+ echo '
Offene Tickets
';
+ } else {
+ echo '
0
';
+ echo '
Keine offenen Tickets
';
+ }
+ echo '
Alle Tickets ansehen ';
+ echo '
';
+ }
+
+ public function render_admin_page() {
+ if ( isset( $_GET['wmt_print'] ) && $_GET['wmt_print'] == '1' && isset( $_GET['id'] ) ) {
+ $this->render_print_view( intval( $_GET['id'] ) );
+ return;
+ }
+
+ if ( isset( $_GET['action'] ) && $_GET['action'] === 'edit' && isset( $_GET['id'] ) ) {
+ $this->render_admin_detail( intval( $_GET['id'] ) );
+ return;
+ }
+
+ if( isset( $_GET['deleted'] ) && $_GET['deleted'] == '1' ) {
+ echo 'Ticket erfolgreich gelöscht.
';
+ }
+
+ $search = isset( $_GET['s'] ) ? sanitize_text_field( $_GET['s'] ) : '';
+ $status_filter = isset( $_GET['status_filter'] ) ? sanitize_text_field( $_GET['status_filter'] ) : '';
+
+ global $wpdb;
+ $sql = "SELECT * FROM $this->table_tickets WHERE 1=1";
+
+ if ( $search ) {
+ $sql .= $wpdb->prepare( " AND (title LIKE %s OR guest_name LIKE %s OR guest_email LIKE %s)", '%' . $wpdb->esc_like( $search ) . '%', '%' . $wpdb->esc_like( $search ) . '%', '%' . $wpdb->esc_like( $search ) . '%' );
+ }
+
+ if ( $status_filter ) {
+ $sql .= $wpdb->prepare( " AND status = %s", $status_filter );
+ }
+
+ $sql .= " ORDER BY updated_at DESC";
+ $tickets = $wpdb->get_results( $sql );
+
+ echo 'Ticket Übersicht ';
+
+ echo '
';
+
+ echo '
';
+ echo 'ID Betreff Kat. Abt. Prio Gast Info Status Zugewiesen Aktionen ';
+ echo '';
+ foreach ( $tickets as $t ) {
+ $prio_style = strtolower( $t->priority ) === 'hoch' ? 'color: #d63638; font-weight: bold; font-size: 1.1em;' : '';
+ $assigned_user = $t->assigned_to ? get_userdata( $t->assigned_to ) : null;
+ $assigned_name = $assigned_user ? $assigned_user->display_name : '-';
+ echo '';
+ echo '#' . $t->id . ' ';
+ echo '' . esc_html( $t->title ) . ' ';
+ echo '' . esc_html( $t->category ) . ' ';
+ echo '' . ( $t->department ? esc_html($t->department) : '-' ) . ' ';
+ echo '' . esc_html( $t->priority ) . ' ';
+ echo '' . esc_html( $t->guest_name ) . ' ' . esc_html( $t->guest_email ) . ' ';
+ echo '' . esc_html( $t->status ) . ' ';
+ echo '' . esc_html( $assigned_name ) . ' ';
+ echo '';
+ echo 'Bearbeiten ';
+ $delete_url = wp_nonce_url( admin_url( 'admin.php?page=wmt_tickets&action=wmt_delete&id=' . $t->id ), 'wmt_delete_ticket_' . $t->id );
+ echo 'Löschen ';
+ echo ' ';
+ echo ' ';
+ }
+ echo '
';
+ }
+
+ private function render_admin_detail( $id ) {
+ global $wpdb;
+ $ticket = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->table_tickets WHERE id = %d", $id ) );
+ if ( ! $ticket ) return;
+
+ if ( isset( $_GET['msg'] ) && $_GET['msg'] == 'sent' ) {
+ echo 'Aktualisierung erfolgreich!
';
+ }
+
+ $messages = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $this->table_messages WHERE ticket_id = %d ORDER BY created_at ASC", $id ) );
+
+ $all_users = get_users( array( 'orderby' => 'display_name' ) );
+
+ $status_opts = array_map( 'trim', explode( ',', get_option('wmt_statuses') ) );
+
+ $mapping_raw = get_option('wmt_departments', '');
+ $dept_opts = array();
+ if($mapping_raw) {
+ $pairs = array_map('trim', explode(',', $mapping_raw));
+ foreach($pairs as $pair) {
+ if(strpos($pair, ':') !== false) {
+ list($cat, $dept) = explode(':', $pair, 2);
+ $dept_opts[] = trim($dept);
+ }
+ }
+ $dept_opts = array_unique($dept_opts);
+ }
+
+ $raw_templates = get_option('wmt_templates', array());
+ $tpl_public = '';
+ $tpl_internal = '';
+
+ if( is_array($raw_templates) ) {
+ foreach($raw_templates as $tpl) {
+ if(isset($tpl['type']) && isset($tpl['content'])) {
+ $name = isset($tpl['name']) && !empty($tpl['name']) ? esc_html($tpl['name']) : substr(esc_html($tpl['content']), 0, 30) . '...';
+ $content = esc_attr($tpl['content']);
+ $opt = '' . $name . ' ';
+ if($tpl['type'] === 'internal') $tpl_internal .= $opt;
+ else $tpl_public .= $opt;
+ }
+ }
+ }
+
+ $colors = array('#e57373', '#f06292', '#ba68c8', '#9575cd', '#7986cb', '#64b5f6', '#4fc3f7', '#4dd0e1', '#4db6ac', '#81c784', '#aed581', '#ffca28', '#ffa726');
+
+ ?>
+
+
Ticket #id; ?> bearbeiten
+
+
+
+
+ sender_type === 'system' ): ?>
+
System: message); ?> created_at; ?>
+ sender_type === 'admin' );
+ $bg = $is_admin ? '#fff' : '#e3f2fd';
+ $align = $is_admin ? 'left' : 'right';
+ $initial = substr($msg->sender_name, 0, 1);
+ $char_code = ord(strtolower($initial));
+ $bg_color = $is_admin ? '#555' : $colors[$char_code % count($colors)];
+ ?>
+
+
+
+
sender_name ); ?> created_at; ?>
+ (Support)'; ?>
+ internal_note) ) : ?>
+
Interne Notiz: internal_note ) ); ?>
+
+ message) ): ?>
+
message); ?>
+
+
Keine öffentliche Nachricht.
+
+ file_url ) : ?>
+
Datei herunterladen
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ get_row( $wpdb->prepare( "SELECT * FROM $this->table_tickets WHERE id = %d", $id ) );
+ if ( ! $ticket ) return;
+
+ $messages = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $this->table_messages WHERE ticket_id = %d ORDER BY created_at ASC", $id ) );
+
+ echo 'Ticket #' . $id . ' ';
+ echo '';
+
+ echo '';
+
+ echo 'Kunde: ' . esc_html( $ticket->guest_name ) . ' (' . esc_html( $ticket->guest_email ) . ')
';
+ echo 'Kategorie: ' . esc_html( $ticket->category ) . '
';
+ echo 'Abteilung: ' . ($ticket->department ? esc_html($ticket->department) : '-') . '
';
+ echo 'Status: ' . esc_html( $ticket->status ) . '
';
+ echo 'Priorität: ' . esc_html( $ticket->priority ) . '
';
+ $assigned = $ticket->assigned_to ? get_userdata($ticket->assigned_to)->display_name : 'Niemand';
+ echo 'Zugewiesen an: ' . esc_html($assigned) . '
';
+
+ echo 'Verlauf ';
+
+ foreach ( $messages as $msg ) {
+ if ( $msg->sender_type === 'system' ) {
+ echo 'System: ' . esc_html($msg->message) . ' (' . $msg->created_at . ')
';
+ continue;
+ }
+
+ echo '';
+ echo '
' . esc_html( $msg->sender_name ) . ' ' . ($msg->sender_type === 'admin' ? '(Support)' : '(Kunde)') . ' - ' . $msg->created_at . '
';
+ echo '
' . wp_kses_post($msg->message) . '
';
+ if ( $msg->sender_type === 'admin' && !empty($msg->internal_note) ) {
+ echo '
Interne Notiz: ' . esc_html($msg->internal_note) . '
';
+ }
+ if ( $msg->file_url ) {
+ echo '
';
+ }
+ echo '
';
+ }
+
+ echo '';
+ exit;
+ }
+
+ private function handle_upload($file_input_name) {
+ if (empty($_FILES[$file_input_name]['name'])) return '';
+ $allowed_raw = get_option('wmt_allowed_filetypes', 'pdf, doc, docx, jpg, png');
+ $allowed_exts = array_map('trim', explode(',', strtolower($allowed_raw)));
+ $file_ext = strtolower(pathinfo($_FILES[$file_input_name]['name'], PATHINFO_EXTENSION));
+
+ if (!in_array($file_ext, $allowed_exts)) {
+ wp_die( "Fehler: Der Dateityp .{$file_ext} ist nicht erlaubt. Erlaubt sind: " . esc_html($allowed_raw) );
+ }
+ require_once(ABSPATH . 'wp-admin/includes/file.php');
+ $upload = wp_handle_upload($_FILES[$file_input_name], array('test_form' => false));
+ if (isset($upload['error'])) wp_die('Upload Fehler: ' . $upload['error']);
+ return isset($upload['url']) ? $upload['url'] : '';
+ }
+
+ private function send_discord_notification( $title, $description, $url ) {
+ $webhook = get_option( 'wmt_discord_webhook' );
+ if ( ! $webhook ) return;
+
+ $data = array(
+ "embeds" => array(
+ array(
+ "title" => $title,
+ "description" => $description,
+ "url" => $url,
+ "color" => 3447003,
+ "timestamp" => current_time( 'mysql' )
+ )
+ )
+ );
+
+ wp_remote_post( $webhook, array(
+ 'body' => wp_json_encode( $data ),
+ 'headers' => array( 'Content-Type' => 'application/json' ),
+ 'timeout' => 10
+ ) );
+ }
+
+ private function send_telegram_notification( $text ) {
+ $token = get_option( 'wmt_telegram_token' );
+ $chat_id = get_option( 'wmt_telegram_chat_id' );
+ if ( ! $token || ! $chat_id ) return;
+
+ $url = "https://api.telegram.org/bot{$token}/sendMessage";
+ wp_remote_post( $url, array(
+ 'body' => array(
+ 'chat_id' => $chat_id,
+ 'text' => $text,
+ 'parse_mode' => 'HTML'
+ ),
+ 'timeout' => 10
+ ) );
+ }
+
+ public function handle_delete_ticket() {
+ if ( isset( $_GET['action'] ) && $_GET['action'] === 'wmt_delete' && isset( $_GET['id'] ) ) {
+ $id = intval( $_GET['id'] );
+ $nonce = isset( $_GET['_wpnonce'] ) ? $_GET['_wpnonce'] : '';
+ if ( ! wp_verify_nonce( $nonce, 'wmt_delete_ticket_' . $id ) || ! current_user_can( 'manage_options' ) ) wp_die( 'Sicherheitsfehler' );
+ global $wpdb;
+ $wpdb->delete( $this->table_messages, array( 'ticket_id' => $id ) );
+ $wpdb->delete( $this->table_tickets, array( 'id' => $id ) );
+ wp_redirect( admin_url( 'admin.php?page=wmt_tickets&deleted=1' ) );
+ exit;
+ }
+ }
+
+ public function handle_admin_post() {
+ if ( ! isset( $_POST['wmt_nonce'] ) || ! wp_verify_nonce( $_POST['wmt_nonce'], 'wmt_admin_action' ) ) wp_die( 'Sicherheitsfehler' );
+
+ $msg_content = isset( $_POST['message'] ) ? wp_kses_post( $_POST['message'] ) : '';
+ $tid = intval( $_POST['ticket_id'] );
+
+ // STATUS LOGIK:
+ // Wenn nicht "Geschlossen" gewählt wurde, setzen wir es auf "In Bearbeitung"
+ $status = sanitize_text_field( $_POST['status'] );
+ if ( strtolower( $status ) !== 'geschlossen' ) {
+ $status = 'In Bearbeitung';
+ }
+
+ $dept = sanitize_text_field( $_POST['department'] );
+
+ // Prüfen Dropdown: User manuell ausgewählt?
+ $assigned = !empty($_POST['assigned_to']) ? intval( $_POST['assigned_to'] ) : null;
+
+ // Auto-Detection Logik
+ // Wenn im Dropdown "Niemand" ausgewählt ist (NULL), ABER der User gerade antwortet...
+ if ( empty($assigned) && (!empty($msg_content) || !empty($_FILES['ticket_file']['name'])) ) {
+ // ...dann weisen wir es dem aktuellen User zu.
+ $assigned = get_current_user_id();
+ }
+
+ $internal_note = sanitize_textarea_field( $_POST['internal_note'] );
+ $file_url = $this->handle_upload('ticket_file');
+
+ global $wpdb;
+ $old_ticket = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->table_tickets WHERE id = %d", $tid ) );
+
+ $wpdb->update( $this->table_tickets,
+ array( 'status' => $status, 'department' => $dept ?: null, 'assigned_to' => $assigned ),
+ array( 'id' => $tid )
+ );
+
+ if ( $old_ticket && $old_ticket->department !== $dept ) {
+ $new_dept_name = $dept ?: 'Keine';
+ $wpdb->insert( $this->table_messages, array(
+ 'ticket_id' => $tid, 'sender_name' => 'System', 'sender_type' => 'system', 'message' => "Abteilung geändert zu: {$new_dept_name}"
+ ));
+ }
+
+ // Handover Benachrichtigung: Wenn sich der zugewiesene User ändert
+ if ( $old_ticket->assigned_to != $assigned && $assigned ) {
+ $user = get_userdata( $assigned );
+ if ( $user ) {
+ $subject = "Ticket #{$tid} wurde Ihnen zugewiesen";
+ $body = "Hallo {$user->display_name},\n\nDas Ticket #{$tid} – „{$old_ticket->title}“ wurde Ihnen zur Bearbeitung zugewiesen.\n\nLink: " . admin_url( "admin.php?page=wmt_tickets&action=edit&id={$tid}" );
+ wp_mail( $user->user_email, $subject, $body );
+ }
+ }
+
+ if ( ! empty( $msg_content ) || ! empty( $file_url ) ) {
+ $current_user = wp_get_current_user();
+ $wpdb->insert( $this->table_messages, array(
+ 'ticket_id' => $tid, 'sender_name' => $current_user->display_name, 'sender_type' => 'admin',
+ 'message' => $msg_content, 'internal_note' => $internal_note, 'file_url' => $file_url
+ ));
+
+ $view_link = add_query_arg( array( 'wmt_view' => $tid, 'hash' => $old_ticket->ticket_hash ), home_url() );
+ $subject = "Neue Antwort zu Ihrem Ticket #{$tid}";
+ $body = "Hallo {$old_ticket->guest_name},\n\nEs gibt eine neue Antwort:\n{$msg_content}\n\nLink zum Ticket:\n{$view_link}";
+ if($file_url) $body .= "\n\nAnhang: {$file_url}";
+ wp_mail( $old_ticket->guest_email, $subject, $body );
+
+ } elseif ( ! empty( $internal_note ) ) {
+ $current_user = wp_get_current_user();
+ $wpdb->insert( $this->table_messages, array(
+ 'ticket_id' => $tid, 'sender_name' => $current_user->display_name, 'sender_type' => 'admin',
+ 'message' => null, 'internal_note' => $internal_note
+ ));
+ }
+
+ wp_redirect( admin_url( 'admin.php?page=wmt_tickets&action=edit&id=' . $tid . '&msg=sent' ) );
+ exit;
+ }
+
+ public function handle_csv_export() {
+ if ( ! isset( $_GET['wmt_action'] ) || $_GET['wmt_action'] !== 'export' || ! current_user_can( 'manage_options' ) ) return;
+
+ global $wpdb;
+ $search = isset( $_GET['s'] ) ? sanitize_text_field( $_GET['s'] ) : '';
+ $sql = "SELECT * FROM $this->table_tickets";
+ if($search) {
+ $sql .= $wpdb->prepare( " WHERE (title LIKE %s OR guest_name LIKE %s OR guest_email LIKE %s)", '%' . $wpdb->esc_like( $search ) . '%', '%' . $wpdb->esc_like( $search ) . '%', '%' . $wpdb->esc_like( $search ) . '%' );
+ }
+
+ $results = $wpdb->get_results( $sql );
+
+ header( 'Content-Type: text/csv' );
+ header( 'Content-Disposition: attachment; filename=tickets-export.csv' );
+
+ $output = fopen( 'php://output', 'w' );
+ fputcsv( $output, array( 'ID', 'Titel', 'Kategorie', 'Abteilung', 'Status', 'Priorität', 'Name', 'E-Mail', 'Zugewiesen', 'Datum' ) );
+
+ foreach ( $results as $row ) {
+ $assigned = $row->assigned_to ? get_userdata($row->assigned_to)->display_name : '';
+ fputcsv( $output, array( $row->id, $row->title, $row->category, $row->department, $row->status, $row->priority, $row->guest_name, $row->guest_email, $assigned, $row->created_at ) );
+ }
+ fclose( $output );
+ exit;
+ }
+
+ public function render_creation_form() {
+ ob_start();
+ if(isset($_GET['upload_error'])) {
+ echo '' . esc_html(urldecode($_GET['upload_error'])) . '
';
+ }
+ if( isset( $_GET['wmt_success'] ) ) {
+ echo 'Ticket erfolgreich erstellt! Bitte prüfen Sie Ihre E-Mails.
';
+ }
+ ?>
+
+
Neues Support Ticket erstellen
+
+
+ Ihr Name *
+
+ Ihre E-Mail *
+
+ Kategorie
+
+
+
+
+
+ Priorität
+
+
+
+
+
+ Betreff *
+
+ Nachricht *
+
+ Datei anhängen (Optional)
+
+ Ticket absenden
+
+
+ Eine E-Mail mit Links wurde gesendet.';
+ }
+ ?>
+
+
Meine Tickets finden
+
+
+ Tickets suchen
+
+
+ Kein Ticket ausgewählt.
';
+
+ global $wpdb;
+ $ticket = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->table_tickets WHERE id = %d AND ticket_hash = %s", $tid, $hash ) );
+ if ( ! $ticket ) return '';
+
+ $messages = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $this->table_messages WHERE ticket_id = %d ORDER BY created_at ASC", $tid ) );
+ $is_closed = ( strtolower($ticket->status) === 'geschlossen' );
+
+ ob_start();
+ ?>
+
+
+
Ticket #id; ?>: title ); ?>
+
Status: status ); ?>
+
+
+ sender_type === 'system' ): ?>
+
message); ?>
+ sender_type === 'admin' );
+ if(empty($msg->message) && empty($msg->file_url)) continue;
+ ?>
+
+
sender_name ); ?> created_at; ?>
+
message ) ); ?>
+ file_url ) : ?>
+
Datei ansehen
+
+
+
+
+
+
+
+
+ Antwort schreiben
+
+ Datei anhängen (Optional)
+
+ Antwort senden
+
+
+
Ticket geschlossen.
+
+
+ handle_upload('guest_file');
+
+ $dept = null;
+ $mapping_raw = get_option('wmt_departments', '');
+ if($mapping_raw) {
+ foreach(array_map('trim', explode(',', $mapping_raw)) as $pair) {
+ if(strpos($pair, ':') !== false) {
+ list($map_cat, $map_dept) = explode(':', $pair, 2);
+ if(trim($map_cat) === $cat) { $dept = trim($map_dept); break; }
+ }
+ }
+ }
+
+ global $wpdb;
+ $wpdb->insert( $this->table_tickets, array(
+ 'title' => $title, 'category' => $cat, 'priority' => $prio, 'department' => $dept,
+ 'guest_name' => $name, 'guest_email' => $email, 'ticket_hash' => $hash
+ ));
+ $tid = $wpdb->insert_id;
+ $wpdb->insert( $this->table_messages, array(
+ 'ticket_id' => $tid, 'sender_name' => $name, 'sender_type' => 'guest', 'message' => $msg, 'file_url' => $file_url
+ ));
+
+ $admin_link = admin_url( "admin.php?page=wmt_tickets&action=edit&id={$tid}" );
+ $guest_link = add_query_arg( array( 'wmt_view' => $tid, 'hash' => $hash ), home_url() );
+
+ $admin_email = get_option( 'wmt_admin_email', get_option('admin_email') );
+ wp_mail( $admin_email, "Neues Ticket #{$tid}: {$title}", "Von: {$name} ({$email})\nLink: {$admin_link}" );
+
+ $this->send_discord_notification( "Neues Ticket #{$tid}", "{$title}\nVon: {$name} ({$email})\nPriorität: {$prio}", $admin_link );
+ $this->send_telegram_notification( "Neues Ticket #{$tid} \nBetreff: {$title}\nVon: {$name} ({$email})\nPriorität: {$prio}\nLink: {$admin_link}" );
+
+ $notify_user_ids = get_option( 'wmt_new_ticket_notify_users', array() ); // FIX: Corrected array()
+ if ( is_array( $notify_user_ids ) && ! empty( $notify_user_ids ) ) {
+ foreach ( $notify_user_ids as $user_id ) {
+ $user = get_userdata( $user_id );
+ if ( $user ) {
+ $subject = "Neues Ticket #{$tid}: {$title}";
+ $body = "Hallo {$user->display_name},\n\nein neues Ticket wurde erstellt.\n\nBetreff: {$title}\nVon: {$name} ({$email})\nPriorität: {$prio}\n\nLink zum Ticket: {$admin_link}";
+ wp_mail( $user->user_email, $subject, $body );
+ }
+ }
+ }
+
+ wp_mail( $email, "Ihr Ticket #{$tid} wurde erstellt", "Hallo {$name},\n\nVielen Dank für Ihr Ticket.\nLink: {$guest_link}" );
+
+ wp_redirect( add_query_arg( 'wmt_success', '1', wp_get_referer() ) );
+ exit;
+ }
+
+ public function handle_guest_reply() {
+ if ( ! isset( $_POST['wmt_reply'] ) || ! wp_verify_nonce( $_POST['wmt_nonce'], 'wmt_guest_reply' ) ) return;
+
+ $tid = intval( $_POST['ticket_id'] );
+ $msg = sanitize_textarea_field( $_POST['message'] );
+ $file_url = $this->handle_upload('guest_file');
+
+ global $wpdb;
+ $ticket = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->table_tickets WHERE id = %d", $tid ) );
+ if(!$ticket) return;
+
+ $wpdb->insert( $this->table_messages, array(
+ 'ticket_id' => $tid, 'sender_name' => $ticket->guest_name, 'sender_type' => 'guest',
+ 'message' => $msg, 'file_url' => $file_url
+ ));
+
+ $admin_link = admin_url( "admin.php?page=wmt_tickets&action=edit&id={$tid}" );
+ $admin_email = get_option( 'wmt_admin_email', get_option('admin_email') );
+ wp_mail( $admin_email, "Neue Antwort zu Ticket #{$tid}", "Kunde hat geantwortet.\nLink: {$admin_link}" );
+
+ $this->send_discord_notification( "Neue Antwort in Ticket #{$tid}", "Kunde {$ticket->guest_name} hat geantwortet.", $admin_link );
+ $this->send_telegram_notification( "Neue Antwort in Ticket #{$tid} \nKunde: {$ticket->guest_name}\nLink: {$admin_link}" );
+
+ wp_redirect( add_query_arg( array( 'wmt_view' => $tid, 'hash' => $ticket->ticket_hash ), wp_get_referer() ) );
+ exit;
+ }
+
+ public function handle_guest_lookup() {
+ if ( ! isset( $_POST['wmt_lookup'] ) ) return;
+ $email = sanitize_email( $_POST['lookup_email'] );
+ if( ! is_email($email) ) return;
+ global $wpdb;
+ $tickets = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $this->table_tickets WHERE guest_email = %s", $email ) );
+ if( $tickets ) {
+ $body = "Hier sind Ihre Tickets:\n\n";
+ foreach( $tickets as $t ) {
+ $link = add_query_arg( array( 'wmt_view' => $t->id, 'hash' => $t->ticket_hash ), home_url() );
+ $body .= "#{$t->id}: {$t->title}\n{$link}\n\n";
+ }
+ wp_mail( $email, "Ihre Tickets", $body );
+ }
+ wp_redirect( add_query_arg( 'wmt_lookup_sent', '1', wp_get_referer() ) );
+ exit;
+ }
+}
+new WP_Multi_Ticket_Pro();