diff --git a/wp-multi-ticket.php b/wp-multi-ticket.php
index d02e234..b1231f5 100644
--- a/wp-multi-ticket.php
+++ b/wp-multi-ticket.php
@@ -1,1331 +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();
+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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Einstellungen
+
+
+
+
+
+
+
+
+ '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();