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 = '
' . 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.
Test-E-Mail senden
E-Mail Logs (letzte 50)
Zeit
Empfänger
Betreff
Status
Server-Fehler:
Häufige Probleme & Lösungen
E-Mails kommen nicht an?
Prüfen Sie das Log: Wenn die Test-Mail fehlschlägt, steht der genaue Fehler oben in der Tabelle unter "Server-Fehler".
Gmail Nutzer: Verwenden Sie ein App-Passwort , nicht Ihr normales Passwort. Port 587 + TLS.
OpenSSL Fehler: Wenn im Log steht "OpenSSL extension missing", kontaktieren Sie Ihren Hoster.
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
");
?>
Aktiv / Offen
Benötigt Aufmerksamkeit
Letzte 30 Tage
Neue Eingänge
'display_name' ) );
$selected_users = get_option( 'wmt_new_ticket_notify_users', array() );
if ( ! is_array( $selected_users ) ) $selected_users = array();
?>
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:
$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
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') );
// 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();