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' ) ); // Hook für Update-Benachrichtigung add_action( 'admin_notices', array( $this, 'check_plugin_updates' ) ); // E-Mail-Konfiguration add_action( 'phpmailer_init', array( $this, 'configure_phpmailer' ) ); add_filter( 'wp_mail_from', array( $this, 'custom_wp_mail_from' ) ); add_filter( 'wp_mail_from_name', array( $this, 'custom_wp_mail_from_name' ) ); 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' ) ); } /** * Hilfsfunktion: Letzte Fehlermeldung aus dem Log holen */ private function get_last_error_log_entry() { $log_file = WP_CONTENT_DIR . '/debug.log'; if ( ! file_exists( $log_file ) || ! is_readable( $log_file ) ) return null; // Letzte 5KB der Datei lesen $lines = array_reverse( file( $log_file ) ); foreach ( $lines as $line ) { // Nach SMTP Debug Zeilen suchen if ( strpos( $line, 'SMTP Debug' ) !== false ) { return esc_html( trim( $line ) ); } // Nach PHP Fatal Warnings suchen if ( strpos( $line, 'Fatal error' ) !== false ) { return esc_html( trim( $line ) ); } } return null; } /** * FIX: Verbesserte Konfiguration für PHPMailer */ public function configure_phpmailer( $phpmailer ) { $smtp_enabled = get_option( 'wmt_smtp_enabled' ); if ( $smtp_enabled == '1' || $smtp_enabled === true ) { $encryption = get_option( 'wmt_smtp_encryption', '' ); // Prüfen, ob OpenSSL geladen ist if ( ( $encryption === 'tls' || $encryption === 'ssl' ) && ! extension_loaded('openssl') ) { error_log('WMT SMTP Error: OpenSSL extension missing.'); // Setzen einer Error-Info, die manuell abgerufen werden kann global $wmt_last_smtp_error; $wmt_last_smtp_error = "FATAL: PHP OpenSSL Extension fehlt. Bitte kontaktieren Sie Ihren Webhoster."; return; } $phpmailer->isSMTP(); $phpmailer->Host = get_option( 'wmt_smtp_host', 'localhost' ); $phpmailer->Port = get_option( 'wmt_smtp_port', 25 ); // SSL/TLS korrekt setzen if ( $encryption === 'tls' ) { $phpmailer->SMTPSecure = 'tls'; } elseif ( $encryption === 'ssl' ) { $phpmailer->SMTPSecure = 'ssl'; } else { $phpmailer->SMTPSecure = ''; } $smtp_auth = get_option( 'wmt_smtp_auth' ); $phpmailer->SMTPAuth = ( $smtp_auth == '1' || $smtp_auth === true ); if ( $phpmailer->SMTPAuth ) { $phpmailer->Username = get_option( 'wmt_smtp_username', '' ); $phpmailer->Password = get_option( 'wmt_smtp_password', '' ); // WICHTIG: Envelope Sender setzen $phpmailer->Sender = $phpmailer->Username; } if ( get_option( 'wmt_smtp_debug', false ) ) { $phpmailer->SMTPDebug = 3; // Client + Server $phpmailer->Debugoutput = function($str, $level) { error_log("WMT SMTP: $str"); }; } } $phpmailer->CharSet = 'UTF-8'; } /** * Setzt die Absender-E-Mail */ public function custom_wp_mail_from( $original_email_address ) { $custom_email = get_option( 'wmt_from_email', '' ); return $custom_email ? $custom_email : $original_email_address; } /** * Setzt den Absender-Namen */ public function custom_wp_mail_from_name( $original_email_from ) { $custom_name = get_option( 'wmt_from_name', '' ); return $custom_name ? $custom_name : $original_email_from; } /** * Verbesserte E-Mail-Versand-Funktion mit Logging */ private function send_mail( $to, $subject, $message, $attachments = array() ) { $html_message = '

' . get_bloginfo('name') . ' Support

' . wpautop( $message ) . '
'; $headers = array( 'Content-Type: text/html; charset=UTF-8', ); if ( get_option( 'wmt_bcc_admin', false ) ) { $admin_email = get_option( 'wmt_admin_email', get_option('admin_email') ); $headers[] = 'Bcc: ' . $admin_email; } $result = wp_mail( $to, $subject, $html_message, $headers, $attachments ); // Logging für Debugging if ( get_option( 'wmt_mail_logging', true ) ) { $error_msg = 'Mail delivery failed'; // Wenn es fehlschlägt, versuchen wir den letzten Fehler aus dem Log zu holen für mehr Details if ( ! $result ) { $log_entry = $this->get_last_error_log_entry(); if ( $log_entry ) { $error_msg .= " (Server Log: " . $log_entry . ")"; } } $logs = get_option( 'wmt_mail_logs', array() ); array_unshift( $logs, array( 'timestamp' => current_time( 'mysql' ), 'to' => $to, 'subject' => $subject, 'success' => $result ? 'YES' : 'NO', 'error' => $result ? '' : $error_msg )); // Nur letzte 50 behalten $logs = array_slice( $logs, 0, 50 ); update_option( 'wmt_mail_logs', $logs ); } if ( ! $result ) { error_log( 'WMT Mail Error: Failed to send email to ' . $to . ' - Subject: ' . $subject ); } return $result; } /** * Prüft auf Updates von der Git-URL und zeigt eine Admin-Notice an */ public function check_plugin_updates() { if ( ! current_user_can( 'update_plugins' ) ) { return; } if ( ! function_exists( 'get_plugin_data' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $plugin_data = get_plugin_data( __FILE__ ); $current_version = $plugin_data['Version']; $cache_key = 'wmt_ticket_update_check'; $cached = get_transient( $cache_key ); if ( false === $cached ) { $cached = array( 'version' => $current_version, 'url' => $this->update_url, ); // Gitea API – Releases $api_url = 'https://git.viper.ipv64.net/api/v1/repos/M_Viper/wp-multi-ticket/releases'; $response = wp_remote_get( $api_url, array( 'timeout' => 10, 'headers' => array( 'Accept' => 'application/json', 'User-Agent' => 'WP-Multi-Ticket-Updater', ), ) ); if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 ) { $releases = json_decode( wp_remote_retrieve_body( $response ), true ); if ( is_array( $releases ) && ! empty( $releases ) ) { // Neuestes Release (Gitea liefert neueste zuerst) $latest = $releases[0]; // Version (genau wie im Toolkit) if ( ! empty( $latest['tag_name'] ) ) { $cached['version'] = ltrim( $latest['tag_name'], 'vV' ); } elseif ( ! empty( $latest['name'] ) ) { $cached['version'] = ltrim( $latest['name'], 'vV' ); } // Exaktes ZIP-Asset suchen if ( ! empty( $latest['assets'] ) && is_array( $latest['assets'] ) ) { foreach ( $latest['assets'] as $asset ) { if ( ! empty( $asset['name'] ) && ! empty( $asset['browser_download_url'] ) && strtolower( $asset['name'] ) === 'wp-multi-ticket.zip' ) { $cached['url'] = $asset['browser_download_url']; break; } } } } } // Cache setzen set_transient( $cache_key, $cached, 12 * HOUR_IN_SECONDS ); } // Update-Hinweis anzeigen if ( version_compare( $cached['version'], $current_version, '>' ) ) { echo '
'; echo '

WP Multi Ticket Pro – Update verfügbar

'; echo '

Neue Version: ' . esc_html( $cached['version'] ) . '
'; echo 'Installiert: ' . esc_html( $current_version ) . '

'; echo '

'; echo 'Release ansehen '; echo 'Direkter Download (ZIP)'; echo '

'; echo '
'; } } public function load_analytics_scripts( $hook ) { 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() { $custom_font_color = get_option( 'wmt_font_color', '#6b7280' ); ?> 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' ) ); add_submenu_page( 'wmt_tickets', 'E-Mail Einstellungen', 'E-Mail Setup', 'manage_options', 'wmt_mail_settings', array( $this, 'render_mail_settings_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_settings_group', 'wmt_font_color' ); 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' ); // SMTP Settings register_setting( 'wmt_mail_settings_group', 'wmt_smtp_enabled' ); register_setting( 'wmt_mail_settings_group', 'wmt_smtp_host' ); register_setting( 'wmt_mail_settings_group', 'wmt_smtp_port' ); register_setting( 'wmt_mail_settings_group', 'wmt_smtp_auth' ); register_setting( 'wmt_mail_settings_group', 'wmt_smtp_username' ); register_setting( 'wmt_mail_settings_group', 'wmt_smtp_password' ); register_setting( 'wmt_mail_settings_group', 'wmt_smtp_encryption' ); register_setting( 'wmt_mail_settings_group', 'wmt_from_email' ); register_setting( 'wmt_mail_settings_group', 'wmt_from_name' ); register_setting( 'wmt_mail_settings_group', 'wmt_bcc_admin' ); register_setting( 'wmt_mail_settings_group', 'wmt_mail_logging' ); register_setting( 'wmt_mail_settings_group', 'wmt_smtp_debug' ); } public function render_mail_settings_page() { // Test-Mail senden if ( isset( $_POST['wmt_send_test_mail'] ) ) { // Prüfen des Nonce $nonce_value = isset( $_POST['wmt_test_nonce'] ) ? $_POST['wmt_test_nonce'] : ''; if ( ! wp_verify_nonce( $nonce_value, 'wmt_test_mail' ) ) { echo '
Sicherheitsfehler beim Senden der Test-E-Mail.
'; } else { $test_email = sanitize_email( $_POST['test_email'] ); // Debug-Modus temporär für Test aktivieren $orig_debug = get_option('wmt_smtp_debug', false); if(!$orig_debug) update_option('wmt_smtp_debug', '1'); $result = $this->send_mail( $test_email, 'Test E-Mail von WP Multi Ticket Pro', 'Wenn Sie diese E-Mail erhalten, funktioniert der E-Mail-Versand korrekt!' ); // Debug-Modus zurücksetzen update_option('wmt_smtp_debug', $orig_debug); if ( $result ) { echo '
✓ Test-E-Mail erfolgreich an ' . esc_html($test_email) . ' gesendet!
'; } else { echo '
✗ Test-E-Mail konnte nicht gesendet werden. Prüfen Sie die Tabelle unten für Details.
'; } } } // Logs löschen if ( isset( $_POST['wmt_clear_logs'] ) ) { // Manuelle Nonce-Prüfung da settings_fields hier nicht verwendet wird if ( check_admin_referer( 'wmt_clear_logs' ) ) { delete_option( 'wmt_mail_logs' ); echo '
Mail-Logs gelöscht.
'; } } $logs = get_option( 'wmt_mail_logs', array() ); ?>

E-Mail Einstellungen

⚠️ Wichtig: Wenn SMTP nicht funktioniert, prüfen Sie unten die Logs für den genauen Fehler.

Grundeinstellungen

Absender E-Mail

Die E-Mail-Adresse, von der Tickets versendet werden.

Absender Name

Der Name, der als Absender angezeigt wird.

BCC an Admin
E-Mail Logging

SMTP Einstellungen (empfohlen)

SMTP aktivieren

Empfohlen für zuverlässigen E-Mail-Versand.

SMTP Host

z.B. smtp.gmail.com, smtp.ionos.de, mail.ihr-provider.de

SMTP Port

Standard: 587 (TLS), 465 (SSL), 25 (keine Verschlüsselung)

Verschlüsselung
SMTP Authentifizierung
SMTP Benutzername

Meist Ihre E-Mail-Adresse

SMTP Passwort

⚠️ Wird unverschlüsselt in der Datenbank gespeichert!

Debug-Modus

Wichtig für den Test! Wird nach dem Test automatisch wieder ausgeschaltet.


Test-E-Mail senden

Test-E-Mail an

E-Mail Logs (letzte 50)

Zeit Empfänger Betreff Status
Server-Fehler:

Häufige Probleme & Lösungen

E-Mails kommen nicht an?

  1. Prüfen Sie das Log: Wenn die Test-Mail fehlschlägt, steht der genaue Fehler oben in der Tabelle unter "Server-Fehler".
  2. Gmail Nutzer: Verwenden Sie ein App-Passwort, nicht Ihr normales Passwort. Port 587 + TLS.
  3. OpenSSL Fehler: Wenn im Log steht "OpenSSL extension missing", kontaktieren Sie Ihren Hoster.
  4. Firewall: Manche Hoster blockieren Port 25 und 587.
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 "); ?>

Analytics Dashboard

Live Daten

Gesamt Tickets

Aktiv / Offen

Benötigt Aufmerksamkeit

Geschlossen

Letzte 30 Tage

Neue Eingänge

Status Verteilung

Top Kategorien

Workload pro Agent

Tickets pro Tag

Einstellungen

Kategorien
Kategorie ➔ Abteilung Zuordnung
Prioritäten
Status
Admin E-Mail
Erlaubte Dateiendungen

Kommagetrennt (z.B. pdf, doc, png).

Schriftfarbe

Wählen Sie die Standard-Schriftfarbe für das Ticket-System (inkl. Überschriften).

Textbausteine
$tpl ): ?>
×
'display_name' ) ); $selected_users = get_option( 'wmt_new_ticket_notify_users', array() ); if ( ! is_array( $selected_users ) ) $selected_users = array(); ?>

Benachrichtigungen

Discord Webhook URL

Wird bei neuem Ticket und neuer Gast-Antwort gesendet.

Telegram Bot Token
Telegram Chat ID

Für Gruppen/Kanäle mit -100 beginnen.

Zusätzliche Benachrichtigungen
bei neuem Ticket

Diese User erhalten zusätzlich eine E-Mail bei jedem neuen Ticket (Strg/Cmd klicken für Mehrfachauswahl).

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 ''; echo ''; echo ''; echo ''; echo '
'; $export_url = admin_url( 'admin.php?page=wmt_tickets&wmt_action=export' ); if($search) $export_url = add_query_arg( 's', $search, $export_url ); echo 'CSV Export'; echo '
'; echo ''; echo ''; 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 ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; } echo '
IDBetreffKat.Abt.PrioGast InfoStatusZugewiesenAktionen
#' . $t->id . '' . esc_html( $t->title ) . '' . esc_html( $t->category ) . '' . ( $t->department ? esc_html($t->department) : '-' ) . '' . esc_html( $t->priority ) . '' . esc_html( $t->guest_name ) . '
' . esc_html( $t->guest_email ) . '
' . esc_html( $t->status ) . '' . esc_html( $assigned_name ) . ''; 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 '
'; } 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 = ''; 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

« Zurück Ticket drucken id ), 'wmt_delete_ticket_' . $ticket->id ); echo 'Ticket löschen'; ?>
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

Abteilung
Status

Automatik: Bei Antworten wird das Ticket automatisch auf "In Bearbeitung" gesetzt (außer Sie wählen "Geschlossen").

Priorität priority ); ?>
Zuweisen an

Auto-Detect: Wenn Sie antworten, wird das Ticket automatisch Ihnen zugewiesen (sofern noch niemand zugewiesen). Wählen Sie hier einen Kollegen aus, um das Ticket zu übergeben.

Antwort & Notizen
'message', 'media_buttons' => false, 'textarea_rows' => 10, 'teeny' => false); wp_editor( $content, 'wmt_message_editor', $settings ); ?>

Wird gesendet an guest_email ); ?>.

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 '

Ticket #' . $id . ': ' . esc_html( $ticket->title ) . '

'; 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: $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 if ( empty($assigned) && (!empty($msg_content) || !empty($_FILES['ticket_file']['name'])) ) { $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 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}" ); // HIER: Sendet HTML E-Mail mit Template $this->send_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}"; // HIER: Sendet HTML E-Mail mit Template $this->send_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

Eine E-Mail mit Links wurde gesendet.'; } ?>

Meine Tickets finden

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 '

Zugriff verweigert.

'; $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
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') ); // Admin Benachrichtigung (HTML) $admin_body = "Ein neues Ticket wurde erstellt.\n\nBetreff: {$title}\nVon: {$name} ({$email})\nPriorität: {$prio}\n\nLink: {$admin_link}"; $this->send_mail( $admin_email, "Neues Ticket #{$tid}: {$title}", $admin_body ); $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() ); 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}"; // HTML Benachrichtigung an zusätzliche User $this->send_mail( $user->user_email, $subject, $body ); } } } // Gast Bestätigung (HTML) $guest_body = "Hallo {$name},\n\nVielen Dank für Ihr Ticket #{$tid}.\nWir werden uns so schnell wie möglich bei Ihnen melden.\n\nLink zum Ticket:\n{$guest_link}"; $this->send_mail( $email, "Ihr Ticket #{$tid} wurde erstellt", $guest_body ); 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') ); // Admin Benachrichtigung über Gast Antwort (HTML) $admin_body = "Der Kunde {$ticket->guest_name} hat auf Ticket #{$tid} geantwortet.\n\nLink: {$admin_link}"; $this->send_mail( $admin_email, "Neue Antwort zu Ticket #{$tid}", $admin_body ); $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"; } // HTML E-Mail senden $this->send_mail( $email, "Ihre Tickets", $body ); } wp_redirect( add_query_arg( 'wmt_lookup_sent', '1', wp_get_referer() ) ); exit; } } new WP_Multi_Ticket_Pro();