1890 lines
100 KiB
PHP
1890 lines
100 KiB
PHP
<?php
|
||
/*
|
||
* Plugin Name: WP Multi Ticket Pro
|
||
* Plugin URI: https://git.viper.ipv64.net/M_Viper/wp-multi-ticket
|
||
* Description: Leistungsstarkes Ticket- und Support-System für WordPress mit Gast-Tickets, Admin-Antworten, CSV-Export, Dashboard-Widgets und integriertem Update-Management über Gitea.
|
||
* Version: 1.3
|
||
* Author: M_Viper
|
||
* Author URI: https://m-viper.de
|
||
* Requires at least: 6.7.2
|
||
* Tested up to: 6.7.2
|
||
* Requires PHP: 7.4
|
||
* License: GPL-2.0-or-later
|
||
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||
* Text Domain: wp-multi-ticket
|
||
* Domain Path: /languages
|
||
* Tags: ticket, support, helpdesk, customer-support, guest-tickets, wp-multi
|
||
* Support: https://t.me/M_Viper04
|
||
*/
|
||
|
||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||
|
||
class WP_Multi_Ticket_Pro {
|
||
|
||
private $table_tickets;
|
||
private $table_messages;
|
||
private $update_url = 'https://git.viper.ipv64.net/M_Viper/wp-multi-ticket/releases';
|
||
|
||
public function __construct() {
|
||
global $wpdb;
|
||
$this->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 = '
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<style>
|
||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; background-color: #f4f4f4; margin: 0; padding: 20px; }
|
||
.container { max-width: 600px; margin: 0 auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
||
.header { background: #0073aa; color: white; padding: 20px; text-align: center; border-radius: 8px 8px 0 0; margin: -20px -20px 20px -20px; }
|
||
.header h2 { margin: 0; color: white; }
|
||
.content { padding: 0 10px; }
|
||
.footer { text-align: center; padding: 20px; font-size: 12px; color: #666; border-top: 1px solid #eee; margin-top: 20px; }
|
||
.button { background: #0073aa; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block; margin: 10px 0; font-weight: bold; }
|
||
.button:hover { background: #005177; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h2>' . get_bloginfo('name') . ' Support</h2>
|
||
</div>
|
||
<div class="content">
|
||
' . wpautop( $message ) . '
|
||
</div>
|
||
<div class="footer">
|
||
<p>Diese E-Mail wurde automatisch vom Support-System generiert.</p>
|
||
<p><a href="' . home_url() . '">' . get_bloginfo('name') . '</a></p>
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html>';
|
||
|
||
$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 '<div class="notice notice-warning is-dismissible">';
|
||
echo '<p><strong>WP Multi Ticket Pro – Update verfügbar</strong></p>';
|
||
echo '<p>Neue Version: <strong>' . esc_html( $cached['version'] ) . '</strong><br>';
|
||
echo 'Installiert: <strong>' . esc_html( $current_version ) . '</strong></p>';
|
||
echo '<p>';
|
||
echo '<a href="' . esc_url( $this->update_url ) . '" target="_blank" class="button">Release ansehen</a> ';
|
||
echo '<a href="' . esc_url( $cached['url'] ) . '" target="_blank" class="button button-primary">Direkter Download (ZIP)</a>';
|
||
echo '</p>';
|
||
echo '</div>';
|
||
}
|
||
}
|
||
|
||
|
||
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' );
|
||
|
||
?>
|
||
<style>
|
||
.wmt-box { max-width: 800px; margin: 20px auto; background: #fff; padding: 25px; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.08); font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; color: <?php echo esc_attr($custom_font_color); ?>; }
|
||
|
||
.wmt-box h1, .wmt-box h2, .wmt-box h3 {
|
||
color: <?php echo esc_attr($custom_font_color); ?>;
|
||
}
|
||
|
||
.wmt-btn { background: #0073aa; color: #fff; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 14px; text-decoration: none; display: inline-block; line-height: 1.4; }
|
||
.wmt-btn:hover { background: #005177; }
|
||
.wmt-btn.danger { background: #dc3545; }
|
||
.wmt-btn.danger:hover { background: #a71d2a; }
|
||
.wmt-input, .wmt-select, .wmt-textarea { width: 100%; padding: 10px; margin-bottom: 15px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; }
|
||
.wmt-label { display: block; font-weight: bold; margin-bottom:5px; }
|
||
|
||
.wmt-chat { background: #f4f6f8; padding: 20px; border-radius: 8px; margin-bottom: 20px; max-height: 600px; overflow-y: auto; border: 1px solid #eee; }
|
||
.wmt-bubble { margin-bottom: 15px; clear: both; padding: 15px; border-radius: 12px; max-width: 80%; position: relative; font-size: 14px; line-height:1.5; box-shadow: 0 1px 2px rgba(0,0,0,0.1); }
|
||
.wmt-left { float: left; background: #fff; border-bottom-left-radius: 2px; }
|
||
.wmt-right { float: right; background: #e3f2fd; border-bottom-right-radius: 2px; border: 1px solid #bbdefb; }
|
||
.wmt-meta { font-size: 11px; color: #888; margin-top: 8px; display: block; }
|
||
|
||
.wmt-system-msg { text-align: center; margin: 20px 0; font-size: 12px; color: #666; background: #e9ecef; padding: 5px 10px; border-radius: 20px; display: inline-block; width: 100%; box-sizing: border-box; border: 1px solid #ced4da; }
|
||
.wmt-internal-note { background: #eee; border-left: 4px solid #6c757d; padding: 10px; margin-bottom: 10px; font-size: 12px; color: #333; font-style: italic; display: block; }
|
||
.wmt-internal-note strong { display: block; font-style: normal; color: #495057; margin-bottom: 3px; }
|
||
.wmt-alert { padding: 15px; background: #d4edda; color: #155724; border: 1px solid #c3e6cb; margin-bottom: 20px; border-radius: 4px; }
|
||
.wmt-error { padding: 15px; background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; margin-bottom: 20px; border-radius: 4px; }
|
||
.wmt-warning { padding: 15px; background: #fff3cd; color: #856404; border: 1px solid #ffeeba; margin-bottom: 20px; border-radius: 4px; }
|
||
|
||
.wmt-avatar { width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; font-weight: bold; font-size: 14px; margin-right: 10px; flex-shrink: 0; text-transform: uppercase; }
|
||
.wmt-msg-row { display: flex; align-items: flex-start; margin-bottom: 15px; }
|
||
|
||
.wmt-export-btn, .wmt-print-btn { float: right; margin-top: 5px; background: #28a745; }
|
||
.wmt-print-btn { background: #6c757d; }
|
||
.wmt-template-box { border: 1px solid #ccc; padding: 15px; background: #f9f9f9; margin-bottom: 10px; border-radius: 4px; position: relative; }
|
||
.wmt-tpl-row { display: flex; gap: 10px; margin-bottom: 5px; }
|
||
.wmt-tpl-row select, .wmt-tpl-row input { margin-bottom: 0; width: auto; }
|
||
.wmt-tpl-remove { position: absolute; top: 10px; right: 10px; color: #a00; text-decoration: none; font-weight: bold; cursor: pointer; }
|
||
.wmt-tpl-remove:hover { color: #d00; }
|
||
|
||
/* Analytics Styles */
|
||
.wmt-analytics-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
||
.wmt-analytics-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin-bottom: 30px; }
|
||
.wmt-stat-card { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.05); transition: transform 0.2s; }
|
||
.wmt-stat-card:hover { transform: translateY(-2px); box-shadow: 0 4px 6px rgba(0,0,0,0.05); }
|
||
.wmt-stat-card h3 { margin: 0 0 5px 0; font-size: 13px; color: #6b7280; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }
|
||
.wmt-stat-card .number { font-size: 28px; font-weight: 700; color: #111827; margin: 0; line-height: 1.2; }
|
||
.wmt-stat-card .trend { font-size: 12px; margin-top: 5px; color: #6b7280; }
|
||
|
||
.wmt-stat-card.blue { border-top: 4px solid #3b82f6; }
|
||
.wmt-stat-card.red { border-top: 4px solid #ef4444; }
|
||
.wmt-stat-card.green { border-top: 4px solid #10b981; }
|
||
.wmt-stat-card.orange { border-top: 4px solid #f59e0b; }
|
||
|
||
.wmt-charts-wrapper { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
|
||
.wmt-chart-container { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; padding: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.05); }
|
||
.wmt-chart-container h3 { margin-top: 0; color: #374151; font-size: 16px; border-bottom: 1px solid #f3f4f6; padding-bottom: 10px; }
|
||
.wmt-full-chart { grid-column: span 2; }
|
||
|
||
@media (max-width: 1200px) {
|
||
.wmt-analytics-grid { grid-template-columns: repeat(2, 1fr); }
|
||
.wmt-charts-wrapper { grid-template-columns: 1fr; }
|
||
.wmt-full-chart { grid-column: span 1; }
|
||
}
|
||
@media (max-width: 600px) {
|
||
.wmt-analytics-grid { grid-template-columns: 1fr; }
|
||
}
|
||
|
||
/* CSS Fallback Styles */
|
||
.wmt-css-chart-row { display: flex; align-items: center; margin-bottom: 8px; }
|
||
.wmt-css-label { width: 120px; font-size: 13px; font-weight: 600; text-align: right; margin-right: 10px; }
|
||
.wmt-css-bar-bg { flex-grow: 1; background: #f3f4f6; height: 24px; border-radius: 4px; overflow: hidden; position: relative; }
|
||
.wmt-css-bar-fill { height: 100%; background: #3b82f6; display: flex; align-items: center; padding-left: 10px; color: #fff; font-size: 11px; white-space: nowrap; transition: width 0.5s ease; }
|
||
.wmt-css-bar-val { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); font-size: 11px; color: #6c757d; font-weight: bold; }
|
||
|
||
/* Mail Log Styles */
|
||
.wmt-mail-log-table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
||
.wmt-mail-log-table th, .wmt-mail-log-table td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
|
||
.wmt-mail-log-table th { background: #f5f5f5; font-weight: bold; }
|
||
.wmt-mail-success { color: #155724; font-weight: bold; }
|
||
.wmt-mail-failed { color: #721c24; font-weight: bold; }
|
||
</style>
|
||
<?php
|
||
}
|
||
|
||
public function create_tables() {
|
||
global $wpdb;
|
||
$charset_collate = $wpdb->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 '<div class="wmt-error">Sicherheitsfehler beim Senden der Test-E-Mail.</div>';
|
||
} 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 '<div class="wmt-alert">✓ Test-E-Mail erfolgreich an ' . esc_html($test_email) . ' gesendet!</div>';
|
||
} else {
|
||
echo '<div class="wmt-error">✗ Test-E-Mail konnte nicht gesendet werden. Prüfen Sie die Tabelle unten für Details.</div>';
|
||
}
|
||
}
|
||
}
|
||
|
||
// 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 '<div class="wmt-alert">Mail-Logs gelöscht.</div>';
|
||
}
|
||
}
|
||
|
||
$logs = get_option( 'wmt_mail_logs', array() );
|
||
|
||
?>
|
||
<div class="wrap">
|
||
<h1>E-Mail Einstellungen</h1>
|
||
|
||
<div class="wmt-warning">
|
||
<strong>⚠️ Wichtig:</strong> Wenn SMTP nicht funktioniert, prüfen Sie unten die Logs für den genauen Fehler.
|
||
</div>
|
||
|
||
<form method="post" action="options.php">
|
||
<?php settings_fields( 'wmt_mail_settings_group' ); ?>
|
||
|
||
<h2>Grundeinstellungen</h2>
|
||
<table class="form-table">
|
||
<tr>
|
||
<th>Absender E-Mail</th>
|
||
<td>
|
||
<input type="email" name="wmt_from_email" value="<?php echo esc_attr( get_option('wmt_from_email', get_option('admin_email')) ); ?>" class="regular-text">
|
||
<p class="description">Die E-Mail-Adresse, von der Tickets versendet werden.</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Absender Name</th>
|
||
<td>
|
||
<input type="text" name="wmt_from_name" value="<?php echo esc_attr( get_option('wmt_from_name', get_bloginfo('name') . ' Support') ); ?>" class="regular-text">
|
||
<p class="description">Der Name, der als Absender angezeigt wird.</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>BCC an Admin</th>
|
||
<td>
|
||
<label>
|
||
<input type="checkbox" name="wmt_bcc_admin" value="1" <?php checked( get_option('wmt_bcc_admin'), 1 ); ?>>
|
||
Admin erhält BCC-Kopie aller E-Mails
|
||
</label>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>E-Mail Logging</th>
|
||
<td>
|
||
<label>
|
||
<input type="checkbox" name="wmt_mail_logging" value="1" <?php checked( get_option('wmt_mail_logging', 1), 1 ); ?>>
|
||
E-Mail-Versand protokollieren (empfohlen für Debugging)
|
||
</label>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
<h2>SMTP Einstellungen (empfohlen)</h2>
|
||
<table class="form-table">
|
||
<tr>
|
||
<th>SMTP aktivieren</th>
|
||
<td>
|
||
<label>
|
||
<input type="checkbox" name="wmt_smtp_enabled" value="1" <?php checked( get_option('wmt_smtp_enabled'), 1 ); ?>>
|
||
SMTP für E-Mail-Versand verwenden
|
||
</label>
|
||
<p class="description">Empfohlen für zuverlässigen E-Mail-Versand.</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>SMTP Host</th>
|
||
<td>
|
||
<input type="text" name="wmt_smtp_host" value="<?php echo esc_attr( get_option('wmt_smtp_host', 'localhost') ); ?>" class="regular-text">
|
||
<p class="description">z.B. smtp.gmail.com, smtp.ionos.de, mail.ihr-provider.de</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>SMTP Port</th>
|
||
<td>
|
||
<input type="number" name="wmt_smtp_port" value="<?php echo esc_attr( get_option('wmt_smtp_port', 587) ); ?>" class="small-text">
|
||
<p class="description">Standard: 587 (TLS), 465 (SSL), 25 (keine Verschlüsselung)</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Verschlüsselung</th>
|
||
<td>
|
||
<select name="wmt_smtp_encryption">
|
||
<option value="">Keine</option>
|
||
<option value="tls" <?php selected( get_option('wmt_smtp_encryption'), 'tls' ); ?>>TLS (empfohlen)</option>
|
||
<option value="ssl" <?php selected( get_option('wmt_smtp_encryption'), 'ssl' ); ?>>SSL</option>
|
||
</select>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>SMTP Authentifizierung</th>
|
||
<td>
|
||
<label>
|
||
<input type="checkbox" name="wmt_smtp_auth" value="1" <?php checked( get_option('wmt_smtp_auth', 1), 1 ); ?>>
|
||
SMTP Authentifizierung verwenden
|
||
</label>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>SMTP Benutzername</th>
|
||
<td>
|
||
<input type="text" name="wmt_smtp_username" value="<?php echo esc_attr( get_option('wmt_smtp_username') ); ?>" class="regular-text" autocomplete="off">
|
||
<p class="description">Meist Ihre E-Mail-Adresse</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>SMTP Passwort</th>
|
||
<td>
|
||
<input type="password" name="wmt_smtp_password" value="<?php echo esc_attr( get_option('wmt_smtp_password') ); ?>" class="regular-text" autocomplete="new-password">
|
||
<p class="description">⚠️ Wird unverschlüsselt in der Datenbank gespeichert!</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Debug-Modus</th>
|
||
<td>
|
||
<label>
|
||
<input type="checkbox" name="wmt_smtp_debug" value="1" <?php checked( get_option('wmt_smtp_debug'), 1 ); ?>>
|
||
SMTP Debug aktivieren (schreibt Logs für jeden Versand)
|
||
</label>
|
||
<p class="description">Wichtig für den Test! Wird nach dem Test automatisch wieder ausgeschaltet.</p>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
<?php submit_button( 'Einstellungen speichern' ); ?>
|
||
</form>
|
||
|
||
<hr>
|
||
|
||
<h2>Test-E-Mail senden</h2>
|
||
<form method="post">
|
||
<input type="hidden" id="wmt_test_nonce" name="wmt_test_nonce" value="<?php echo wp_create_nonce('wmt_test_mail'); ?>" />
|
||
<table class="form-table">
|
||
<tr>
|
||
<th>Test-E-Mail an</th>
|
||
<td>
|
||
<input type="email" name="test_email" value="<?php echo esc_attr( get_option('wmt_admin_email', get_option('admin_email')) ); ?>" class="regular-text" required>
|
||
<button type="submit" name="wmt_send_test_mail" class="button button-primary">Test-Mail senden</button>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</form>
|
||
|
||
<?php if ( ! empty( $logs ) ): ?>
|
||
<hr>
|
||
<h2>E-Mail Logs (letzte 50)</h2>
|
||
<form method="post">
|
||
<input type="hidden" name="wmt_clear_logs" value="1" />
|
||
<?php wp_nonce_field( 'wmt_clear_logs' ); ?>
|
||
<button type="submit" name="submit" class="button">Logs löschen</button>
|
||
</form>
|
||
<table class="wmt-mail-log-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Zeit</th>
|
||
<th>Empfänger</th>
|
||
<th>Betreff</th>
|
||
<th>Status</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ( $logs as $log ): ?>
|
||
<tr>
|
||
<td><?php echo esc_html( $log['timestamp'] ); ?></td>
|
||
<td><?php echo esc_html( $log['to'] ); ?></td>
|
||
<td><?php echo esc_html( $log['subject'] ); ?></td>
|
||
<td class="<?php echo $log['success'] === 'YES' ? 'wmt-mail-success' : 'wmt-mail-failed'; ?>">
|
||
<?php echo $log['success'] === 'YES' ? '✓ Gesendet' : '✗ Fehler'; ?>
|
||
<?php if ( ! empty( $log['error'] ) ): ?>
|
||
<div style="font-size: 11px; margin-top: 5px; color: #d63638; word-break: break-all; background: #f9f9f9; padding: 5px; border: 1px solid #eee; max-width: 500px;">
|
||
<strong>Server-Fehler:</strong><br>
|
||
<?php echo esc_html( $log['error'] ); ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<?php endif; ?>
|
||
|
||
<hr>
|
||
<h2>Häufige Probleme & Lösungen</h2>
|
||
<div style="background: #f9f9f9; padding: 20px; border-left: 4px solid #0073aa; margin-top: 20px;">
|
||
<h3>E-Mails kommen nicht an?</h3>
|
||
<ol>
|
||
<li><strong>Prüfen Sie das Log:</strong> Wenn die Test-Mail fehlschlägt, steht der genaue Fehler oben in der Tabelle unter "Server-Fehler".</li>
|
||
<li><strong>Gmail Nutzer:</strong> Verwenden Sie ein <strong>App-Passwort</strong>, nicht Ihr normales Passwort. Port 587 + TLS.</li>
|
||
<li><strong>OpenSSL Fehler:</strong> Wenn im Log steht "OpenSSL extension missing", kontaktieren Sie Ihren Hoster.</li>
|
||
<li><strong>Firewall:</strong> Manche Hoster blockieren Port 25 und 587.</li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
public function render_analytics_page() {
|
||
global $wpdb;
|
||
|
||
$total_tickets = $wpdb->get_var("SELECT COUNT(*) FROM $this->table_tickets");
|
||
|
||
if ( $total_tickets == 0 ) {
|
||
echo '<div class="wrap">';
|
||
echo '<div style="background: #fff; padding: 50px; text-align: center; border-radius: 8px; border: 1px solid #eee; max-width: 600px; margin: 50px auto;">';
|
||
echo '<h2 style="color: #555;">Noch keine Daten</h2>';
|
||
echo '<p style="color: #777; font-size: 16px;">Es wurden noch keine Tickets erstellt.</p>';
|
||
echo '<a href="' . admin_url('admin.php?page=wmt_tickets') . '" class="wmt-btn">Zu den Tickets</a>';
|
||
echo '</div></div>';
|
||
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
|
||
");
|
||
|
||
?>
|
||
<div class="wrap">
|
||
<div class="wmt-analytics-header">
|
||
<h1>Analytics Dashboard</h1>
|
||
<span style="color: #666; font-size: 14px;">Live Daten</span>
|
||
</div>
|
||
|
||
<div class="wmt-analytics-grid">
|
||
<div class="wmt-stat-card blue">
|
||
<h3>Gesamt Tickets</h3>
|
||
<p class="number"><?php echo intval($total_tickets); ?></p>
|
||
</div>
|
||
<div class="wmt-stat-card red">
|
||
<h3>Aktiv / Offen</h3>
|
||
<p class="number"><?php echo intval($open_tickets); ?></p>
|
||
<p class="trend">Benötigt Aufmerksamkeit</p>
|
||
</div>
|
||
<div class="wmt-stat-card green">
|
||
<h3>Geschlossen</h3>
|
||
<p class="number"><?php echo intval($closed_tickets); ?></p>
|
||
</div>
|
||
<div class="wmt-stat-card orange">
|
||
<h3>Letzte 30 Tage</h3>
|
||
<p class="number"><?php echo intval($new_tickets_30d); ?></p>
|
||
<p class="trend">Neue Eingänge</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="wmt-charts-wrapper">
|
||
<!-- Status -->
|
||
<div class="wmt-chart-container">
|
||
<h3>Status Verteilung</h3>
|
||
<div style="height: 250px; position: relative;"><canvas id="statusChart"></canvas></div>
|
||
</div>
|
||
|
||
<!-- Kategorien -->
|
||
<div class="wmt-chart-container">
|
||
<h3>Top Kategorien</h3>
|
||
<div style="height: 250px; position: relative;"><canvas id="catChart"></canvas></div>
|
||
</div>
|
||
|
||
<!-- Agent Performance -->
|
||
<div class="wmt-chart-container">
|
||
<h3>Workload pro Agent</h3>
|
||
<div style="height: 250px; position: relative;"><canvas id="agentChart"></canvas></div>
|
||
</div>
|
||
|
||
<!-- Timeline -->
|
||
<div class="wmt-chart-container">
|
||
<h3>Tickets pro Tag</h3>
|
||
<div style="height: 250px; position: relative;"><canvas id="timelineChart"></canvas></div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
||
|
||
|
||
<script type="text/javascript">
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
|
||
const statusLabels = <?php echo json_encode(array_column($status_data, 'status'), JSON_UNESCAPED_UNICODE); ?>;
|
||
const statusCounts = <?php echo json_encode(array_column($status_data, 'count'), JSON_UNESCAPED_UNICODE); ?>;
|
||
const catLabels = <?php echo json_encode(array_column($cat_data, 'category'), JSON_UNESCAPED_UNICODE); ?>;
|
||
const catCounts = <?php echo json_encode(array_column($cat_data, 'count'), JSON_UNESCAPED_UNICODE); ?>;
|
||
const agentLabels = <?php echo json_encode(array_column($agent_data, 'display_name'), JSON_UNESCAPED_UNICODE); ?>;
|
||
const agentCounts = <?php echo json_encode(array_column($agent_data, 'count'), JSON_UNESCAPED_UNICODE); ?>;
|
||
const timeLabels = <?php echo json_encode(array_column($timeline_data, 'date'), JSON_UNESCAPED_UNICODE); ?>;
|
||
const timeCounts = <?php echo json_encode(array_column($timeline_data, 'count'), JSON_UNESCAPED_UNICODE); ?>;
|
||
|
||
const localUrl = "<?php echo plugins_url( 'chart.js', __FILE__ ); ?>";
|
||
console.log("WMT DEBUG: Suche nach Chart.js auf: " + localUrl);
|
||
|
||
if (typeof Chart === 'undefined') {
|
||
console.warn("WMT WARN: Chart.js ist nicht geladen. Nutze CSS Fallback.");
|
||
|
||
const statusContainer = document.getElementById('statusChart').parentElement;
|
||
let statusHtml = '<div style="display:grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 10px; background: #fff3cd; border: 1px solid #ffeeba; border-radius: 4px; color: #856404;">Chart.js konnte nicht geladen werden (Lokale Datei fehlt?). Nutze CSS Fallback:</div>';
|
||
statusLabels.forEach((label, i) => {
|
||
statusHtml += `<div style="background: #f9fafb; padding: 10px; border-radius: 4px; border: 1px solid #eee;"><strong>${label}:</strong> ${statusCounts[i]}</div>`;
|
||
});
|
||
statusHtml += '</div>';
|
||
statusContainer.innerHTML = statusHtml;
|
||
|
||
const catContainer = document.getElementById('catChart').parentElement;
|
||
let catHtml = '<div style="padding:10px 0;">';
|
||
if (catLabels.length > 0) {
|
||
let maxCat = Math.max(...catCounts) || 1;
|
||
catLabels.forEach((label, i) => {
|
||
let pct = (catCounts[i] / maxCat) * 100;
|
||
catHtml += `
|
||
<div class="wmt-css-chart-row">
|
||
<div class="wmt-css-label">${label}</div>
|
||
<div class="wmt-css-bar-bg">
|
||
<div class="wmt-css-bar-fill" style="width: ${pct}%">
|
||
${catCounts[i]}
|
||
</div>
|
||
<div class="wmt-css-bar-val">${catCounts[i]}</div>
|
||
</div>
|
||
</div>`;
|
||
});
|
||
} else {
|
||
catHtml = '<div style="padding:50px; text-align:center; color:#999;">Keine Kategorien.</div>';
|
||
}
|
||
catHtml += '</div><div style="margin-top:20px; font-size:11px; color:#999; text-align:center;">* Lokale Chart.js Datei fehlt oder JS blockiert.</div>';
|
||
catContainer.innerHTML = catHtml;
|
||
|
||
const agentContainer = document.getElementById('agentChart').parentElement;
|
||
let agentHtml = '<div style="padding:10px 0;">';
|
||
if (agentLabels.length > 0) {
|
||
let maxAgent = Math.max(...agentCounts) || 1;
|
||
agentLabels.forEach((label, i) => {
|
||
let pct = (agentCounts[i] / maxAgent) * 100;
|
||
agentHtml += `
|
||
<div class="wmt-css-chart-row">
|
||
<div class="wmt-css-label">${label}</div>
|
||
<div class="wmt-css-bar-bg">
|
||
<div class="wmt-css-bar-fill" style="background:#34d399; width: ${pct}%">
|
||
${agentCounts[i]}
|
||
</div>
|
||
<div class="wmt-css-bar-val">${agentCounts[i]}</div>
|
||
</div>
|
||
</div>`;
|
||
});
|
||
} else {
|
||
agentHtml = '<div style="padding:50px; text-align:center; color:#999;">Keine zugewiesenen Tickets.</div>';
|
||
}
|
||
agentHtml += '</div>';
|
||
agentContainer.innerHTML = agentHtml;
|
||
|
||
const timeContainer = document.getElementById('timelineChart').parentElement;
|
||
let timeHtml = '<div style="padding:10px 0;"><ul style="list-style:none; padding:0;">';
|
||
if (timeLabels.length > 0) {
|
||
timeLabels.forEach((date, i) => {
|
||
timeHtml += `<li style="display:flex; justify-content:space-between; border-bottom:1px solid #eee; padding:8px 0;"><span>${date}</span> <strong>${timeCounts[i]}</strong></li>`;
|
||
});
|
||
} else {
|
||
timeHtml = '<div style="padding:50px; text-align:center; color:#999;">Keine Timeline Daten.</div>';
|
||
}
|
||
timeHtml += '</ul></div>';
|
||
timeContainer.innerHTML = timeHtml;
|
||
|
||
} else {
|
||
console.log("WMT SUCCESS: Chart.js geladen. Erstelle Diagramme.");
|
||
|
||
const commonOptions = {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
plugins: {
|
||
legend: { position: 'bottom' }
|
||
}
|
||
};
|
||
|
||
new Chart(document.getElementById('statusChart'), {
|
||
type: 'doughnut',
|
||
data: {
|
||
labels: statusLabels,
|
||
datasets: [{
|
||
data: statusCounts,
|
||
backgroundColor: ['#3b82f6', '#10b981', '#ef4444', '#f59e0b', '#8b5cf6', '#9ca3af'],
|
||
borderWidth: 0
|
||
}]
|
||
},
|
||
options: { ...commonOptions, cutout: '70%' }
|
||
});
|
||
|
||
new Chart(document.getElementById('catChart'), {
|
||
type: 'bar',
|
||
data: {
|
||
labels: catLabels,
|
||
datasets: [{
|
||
label: 'Anzahl',
|
||
data: catCounts,
|
||
backgroundColor: '#60a5fa',
|
||
borderRadius: 4
|
||
}]
|
||
},
|
||
options: { ...commonOptions, scales: { y: { beginAtZero: true, grid: { display: false } }, x: { grid: { display: false } } } }
|
||
});
|
||
|
||
if (agentLabels.length > 0) {
|
||
new Chart(document.getElementById('agentChart'), {
|
||
type: 'bar',
|
||
data: {
|
||
labels: agentLabels,
|
||
datasets: [{
|
||
label: 'Zugewiesene Tickets',
|
||
data: agentCounts,
|
||
backgroundColor: '#34d399',
|
||
borderRadius: 4
|
||
}]
|
||
},
|
||
options: { ...commonOptions, indexAxis: 'y', scales: { x: { beginAtZero: true } } }
|
||
});
|
||
} else {
|
||
document.getElementById('agentChart').parentElement.innerHTML = '<div style="padding:50px; text-align:center; color:#999;">Keine zugewiesenen Tickets.</div>';
|
||
}
|
||
|
||
if (timeLabels.length > 0) {
|
||
new Chart(document.getElementById('timelineChart'), {
|
||
type: 'line',
|
||
data: {
|
||
labels: timeLabels,
|
||
datasets: [{
|
||
label: 'Neue Tickets',
|
||
data: timeCounts,
|
||
borderColor: '#818cf8',
|
||
backgroundColor: 'rgba(129, 140, 248, 0.1)',
|
||
fill: true,
|
||
tension: 0.4,
|
||
pointRadius: 2
|
||
}]
|
||
},
|
||
options: { ...commonOptions, scales: { x: { ticks: { maxTicksLimit: 7 } } } }
|
||
});
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
<?php
|
||
}
|
||
|
||
public function render_settings_page() {
|
||
$templates = get_option( 'wmt_templates', array() );
|
||
if ( is_string($templates) ) $templates = array();
|
||
?>
|
||
<div class="wrap">
|
||
<h1>Einstellungen</h1>
|
||
<form method="post" action="options.php">
|
||
<?php settings_fields( 'wmt_settings_group' ); ?>
|
||
<table class="form-table">
|
||
<tr><th>Kategorien</th><td><input type="text" name="wmt_categories" value="<?php echo esc_attr( get_option('wmt_categories', 'Allgemein, Technik, Billing') ); ?>" class="regular-text"></td></tr>
|
||
<tr><th>Kategorie ➔ Abteilung Zuordnung</th><td><input type="text" name="wmt_departments" value="<?php echo esc_attr( get_option('wmt_departments', 'Technik:IT, Billing:Buchhaltung') ); ?>" class="large-text" style="width:100%;"></td></tr>
|
||
<tr><th>Prioritäten</th><td><input type="text" name="wmt_priorities" value="<?php echo esc_attr( get_option('wmt_priorities', 'Hoch, Mittel, Niedrig') ); ?>" class="regular-text"></td></tr>
|
||
<tr><th>Status</th><td><input type="text" name="wmt_statuses" value="<?php echo esc_attr( get_option('wmt_statuses', 'Offen, In Bearbeitung, Wartet auf Antwort, Geschlossen') ); ?>" class="regular-text"></td></tr>
|
||
<tr><th>Admin E-Mail</th><td><input type="email" name="wmt_admin_email" value="<?php echo esc_attr( get_option('wmt_admin_email', get_option('admin_email') ) ); ?>" class="regular-text"></td></tr>
|
||
<tr><th>Erlaubte Dateiendungen</th><td><input type="text" name="wmt_allowed_filetypes" value="<?php echo esc_attr( get_option('wmt_allowed_filetypes', 'pdf, doc, docx, jpg, jpeg, png, txt') ); ?>" class="regular-text"><p class="description">Kommagetrennt (z.B. pdf, doc, png).</p></td></tr>
|
||
<!-- Schriftfarbe Einstellung -->
|
||
<tr>
|
||
<th>Schriftfarbe</th>
|
||
<td>
|
||
<input type="color" name="wmt_font_color" value="<?php echo esc_attr( get_option('wmt_font_color', '#6b7280') ); ?>" class="regular-text">
|
||
<p class="description">Wählen Sie die Standard-Schriftfarbe für das Ticket-System (inkl. Überschriften).</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Textbausteine</th>
|
||
<td>
|
||
<div id="wmt-templates-container">
|
||
<?php foreach( $templates as $index => $tpl ): ?>
|
||
<div class="wmt-template-box">
|
||
<a href="#" class="wmt-tpl-remove" onclick="removeTemplate(this); return false;">×</a>
|
||
<div class="wmt-tpl-row">
|
||
<select name="wmt_templates[<?php echo $index; ?>][type]" style="width: 120px;">
|
||
<option value="public" <?php selected($tpl['type'], 'public'); ?>>Öffentlich</option>
|
||
<option value="internal" <?php selected($tpl['type'], 'internal'); ?>>Intern</option>
|
||
</select>
|
||
<input type="text" name="wmt_templates[<?php echo $index; ?>][name]" value="<?php echo esc_attr($tpl['name']); ?>" class="regular-text" placeholder="Name der Vorlage" style="flex-grow:1;">
|
||
</div>
|
||
<textarea name="wmt_templates[<?php echo $index; ?>][content]" class="large-text" rows="3"><?php echo esc_textarea($tpl['content']); ?></textarea>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<button type="button" class="button" onclick="addTemplate()">+ Neue Box hinzufügen</button>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
<?php submit_button(); ?>
|
||
</form>
|
||
</div>
|
||
|
||
<script type="text/javascript">
|
||
function addTemplate() {
|
||
var container = document.getElementById('wmt-templates-container');
|
||
var count = container.children.length;
|
||
var div = document.createElement('div');
|
||
div.className = 'wmt-template-box';
|
||
div.innerHTML = `
|
||
<a href="#" class="wmt-tpl-remove" onclick="removeTemplate(this); return false;">×</a>
|
||
<div class="wmt-tpl-row">
|
||
<select name="wmt_templates[${count}][type]" style="width: 120px;">
|
||
<option value="public">Öffentlich</option>
|
||
<option value="internal">Intern</option>
|
||
</select>
|
||
<input type="text" name="wmt_templates[${count}][name]" class="regular-text" placeholder="Name der Vorlage" style="flex-grow:1;">
|
||
</div>
|
||
<textarea name="wmt_templates[${count}][content]" class="large-text" rows="3"></textarea>
|
||
`;
|
||
container.appendChild(div);
|
||
}
|
||
function removeTemplate(btn) { btn.parentElement.remove(); }
|
||
</script>
|
||
<?php
|
||
}
|
||
|
||
public function render_notifications_page() {
|
||
$all_users = get_users( array( 'orderby' => 'display_name' ) );
|
||
$selected_users = get_option( 'wmt_new_ticket_notify_users', array() );
|
||
if ( ! is_array( $selected_users ) ) $selected_users = array();
|
||
?>
|
||
<div class="wrap">
|
||
<h1>Benachrichtigungen</h1>
|
||
<form method="post" action="options.php">
|
||
<?php settings_fields( 'wmt_notifications_group' ); ?>
|
||
<table class="form-table">
|
||
<tr>
|
||
<th>Discord Webhook URL</th>
|
||
<td>
|
||
<input type="url" name="wmt_discord_webhook" value="<?php echo esc_attr( get_option('wmt_discord_webhook') ); ?>" class="regular-text" placeholder="https://discord.com/api/webhooks/...">
|
||
<p class="description">Wird bei neuem Ticket und neuer Gast-Antwort gesendet.</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Telegram Bot Token</th>
|
||
<td>
|
||
<input type="text" name="wmt_telegram_token" value="<?php echo esc_attr( get_option('wmt_telegram_token') ); ?>" class="regular-text">
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Telegram Chat ID</th>
|
||
<td>
|
||
<input type="text" name="wmt_telegram_chat_id" value="<?php echo esc_attr( get_option('wmt_telegram_chat_id') ); ?>" class="regular-text">
|
||
<p class="description">Für Gruppen/Kanäle mit -100 beginnen.</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Zusätzliche Benachrichtigungen<br>bei neuem Ticket</th>
|
||
<td>
|
||
<select name="wmt_new_ticket_notify_users[]" multiple size="10" style="width: 400px; height: 200px;">
|
||
<?php foreach ( $all_users as $user ): ?>
|
||
<option value="<?php echo $user->ID; ?>" <?php echo in_array( $user->ID, $selected_users ) ? 'selected' : ''; ?>>
|
||
<?php echo esc_html( $user->display_name ); ?> (<?php echo esc_html( $user->user_email ); ?>)
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<p class="description">Diese User erhalten <strong>zusätzlich</strong> eine E-Mail bei jedem neuen Ticket (Strg/Cmd klicken für Mehrfachauswahl).</p>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
<?php submit_button(); ?>
|
||
</form>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
public function add_dashboard_widget() {
|
||
wp_add_dashboard_widget(
|
||
'wmt_dashboard_widget',
|
||
'Support Tickets',
|
||
array( $this, 'render_dashboard_widget' ),
|
||
'high'
|
||
);
|
||
}
|
||
|
||
public function render_dashboard_widget() {
|
||
global $wpdb;
|
||
$count = $wpdb->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 '<div class="wmt-box" style="padding: 15px; margin: 0; border: 1px solid #ccd0d4; background: #fff;">';
|
||
if ( $count > 0 ) {
|
||
echo '<div style="font-size: 24px; font-weight: bold; color: #d63638; line-height: 1;">' . intval( $count ) . '</div>';
|
||
echo '<div style="color: #666;">Offene Tickets</div>';
|
||
} else {
|
||
echo '<div style="font-size: 24px; font-weight: bold; color: #28a745; line-height: 1;">0</div>';
|
||
echo '<div style="color: #666;">Keine offenen Tickets</div>';
|
||
}
|
||
echo '<a href="' . $link . '" class="wmt-btn" style="display: block; text-align: center; margin-top: 10px;">Alle Tickets ansehen</a>';
|
||
echo '</div>';
|
||
}
|
||
|
||
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 '<div class="notice notice-success is-dismissible"><p>Ticket erfolgreich gelöscht.</p></div>';
|
||
}
|
||
|
||
$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 '<div class="wrap"><h1>Ticket Übersicht</h1>';
|
||
|
||
echo '<div style="margin-bottom: 20px; display:flex; gap:10px; align-items:center; flex-wrap:wrap;">';
|
||
echo '<form method="get" style="display:flex; gap:10px; align-items:center;">';
|
||
echo '<input type="hidden" name="page" value="wmt_tickets">';
|
||
echo '<input type="text" name="s" value="' . esc_attr($search) . '" placeholder="Suche..." class="regular-text">';
|
||
echo '<select name="status_filter" class="regular-text">';
|
||
echo '<option value="">Alle Status</option>';
|
||
foreach( array_map('trim', explode(',', get_option('wmt_statuses'))) as $st ) {
|
||
$sel = selected($status_filter, $st, false);
|
||
echo '<option value="' . esc_attr($st) . '" ' . $sel . '>' . esc_html($st) . '</option>';
|
||
}
|
||
echo '</select>';
|
||
echo '<input type="submit" value="Filtern" class="button">';
|
||
echo '</form>';
|
||
|
||
$export_url = admin_url( 'admin.php?page=wmt_tickets&wmt_action=export' );
|
||
if($search) $export_url = add_query_arg( 's', $search, $export_url );
|
||
echo '<a href="' . $export_url . '" class="wmt-btn wmt-export-btn">CSV Export</a>';
|
||
echo '</div>';
|
||
|
||
echo '<table class="wp-list-table widefat fixed striped">';
|
||
echo '<thead><tr><th>ID</th><th>Betreff</th><th>Kat.</th><th>Abt.</th><th>Prio</th><th>Gast Info</th><th>Status</th><th>Zugewiesen</th><th>Aktionen</th></tr></thead>';
|
||
echo '<tbody>';
|
||
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 '<tr>';
|
||
echo '<td>#' . $t->id . '</td>';
|
||
echo '<td>' . esc_html( $t->title ) . '</td>';
|
||
echo '<td>' . esc_html( $t->category ) . '</td>';
|
||
echo '<td>' . ( $t->department ? esc_html($t->department) : '-' ) . '</td>';
|
||
echo '<td style="'.$prio_style.'">' . esc_html( $t->priority ) . '</td>';
|
||
echo '<td><strong>' . esc_html( $t->guest_name ) . '</strong><br><small>' . esc_html( $t->guest_email ) . '</small></td>';
|
||
echo '<td>' . esc_html( $t->status ) . '</td>';
|
||
echo '<td>' . esc_html( $assigned_name ) . '</td>';
|
||
echo '<td>';
|
||
echo '<a href="' . admin_url( 'admin.php?page=wmt_tickets&action=edit&id=' . $t->id ) . '" class="button">Bearbeiten</a> ';
|
||
$delete_url = wp_nonce_url( admin_url( 'admin.php?page=wmt_tickets&action=wmt_delete&id=' . $t->id ), 'wmt_delete_ticket_' . $t->id );
|
||
echo '<a href="' . $delete_url . '" class="button wmt-btn danger" onclick="return confirm(\'Wirklich löschen?\')">Löschen</a>';
|
||
echo '</td>';
|
||
echo '</tr>';
|
||
}
|
||
echo '</tbody></table></div>';
|
||
}
|
||
|
||
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 '<div class="notice notice-success is-dismissible"><p>Aktualisierung erfolgreich!</p></div>';
|
||
}
|
||
|
||
$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 = '<option value="' . $content . '">' . $name . '</option>';
|
||
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');
|
||
|
||
?>
|
||
<div class="wrap">
|
||
<h1>Ticket #<?php echo $ticket->id; ?> bearbeiten</h1>
|
||
<div style="margin-bottom: 10px;">
|
||
<a href="<?php echo admin_url( 'admin.php?page=wmt_tickets' ); ?>" class="button">« Zurück</a>
|
||
<a href="#" onclick="wmt_print_ticket(<?php echo $ticket->id; ?>); return false;" class="wmt-btn wmt-print-btn" style="margin-left:10px;">Ticket drucken</a>
|
||
<?php
|
||
$delete_url = wp_nonce_url( admin_url( 'admin.php?page=wmt_tickets&action=wmt_delete&id=' . $ticket->id ), 'wmt_delete_ticket_' . $ticket->id );
|
||
echo '<a href="' . $delete_url . '" class="button wmt-btn danger" style="margin-left:10px;" onclick="return confirm(\'Ticket wirklich löschen?\')">Ticket löschen</a>';
|
||
?>
|
||
</div>
|
||
|
||
<div class="wmt-box" style="margin-top:20px; border:1px solid #ccc;">
|
||
<div class="wmt-chat">
|
||
<?php foreach ( $messages as $msg ) :
|
||
if( $msg->sender_type === 'system' ): ?>
|
||
<div class="wmt-system-msg"><strong>System:</strong> <?php echo esc_html($msg->message); ?> <small><?php echo $msg->created_at; ?></small></div>
|
||
<?php continue; endif;
|
||
|
||
$is_admin = ( $msg->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)];
|
||
?>
|
||
<div class="wmt-msg-row" style="flex-direction: <?php echo $is_admin ? 'row' : 'row-reverse'; ?>;">
|
||
<div class="wmt-avatar" style="background:<?php echo $bg_color; ?>;"><?php echo esc_html($initial); ?></div>
|
||
<div class="wmt-bubble wmt-<?php echo $align; ?>" style="background:<?php echo $bg; ?>;">
|
||
<strong><?php echo esc_html( $msg->sender_name ); ?></strong> <small><?php echo $msg->created_at; ?></small>
|
||
<?php if( $is_admin ) echo ' <span style="color:#0073aa;">(Support)</span>'; ?>
|
||
<?php if( $is_admin && !empty($msg->internal_note) ) : ?>
|
||
<div class="wmt-internal-note"><strong>Interne Notiz:</strong><?php echo nl2br( esc_html( $msg->internal_note ) ); ?></div>
|
||
<?php endif; ?>
|
||
<?php if( !empty($msg->message) ): ?>
|
||
<div style="margin-top:5px;"><?php echo wp_kses_post($msg->message); ?></div>
|
||
<?php else: ?>
|
||
<p style="font-size:11px; color:#999; font-style:italic;">Keine öffentliche Nachricht.</p>
|
||
<?php endif; ?>
|
||
<?php if ( $msg->file_url ) : ?>
|
||
<a href="<?php echo esc_url( $msg->file_url ); ?>" target="_blank" style="display:inline-block; margin-top:10px; text-decoration:none; background:#e9ecef; padding:5px 10px; border-radius:4px; border:1px solid #ccc;">Datei herunterladen</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
|
||
<hr>
|
||
|
||
<form method="post" action="<?php echo admin_url( 'admin-post.php' ); ?>" enctype="multipart/form-data">
|
||
<input type="hidden" name="action" value="wmt_admin_reply">
|
||
<?php wp_nonce_field( 'wmt_admin_action', 'wmt_nonce' ); ?>
|
||
<input type="hidden" name="ticket_id" value="<?php echo $ticket->id; ?>">
|
||
|
||
<table class="form-table">
|
||
<tr>
|
||
<th>Abteilung</th>
|
||
<td>
|
||
<select name="department" class="regular-text">
|
||
<option value="">-- Keine --</option>
|
||
<?php foreach( $dept_opts as $d ): ?>
|
||
<option value="<?php echo esc_attr($d); ?>" <?php selected($ticket->department, $d); ?>><?php echo esc_html($d); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Status</th>
|
||
<td>
|
||
<select name="status" class="regular-text">
|
||
<?php foreach( $status_opts as $s ): ?>
|
||
<option value="<?php echo esc_attr($s); ?>" <?php selected($ticket->status, $s); ?>><?php echo esc_html($s); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<p class="description"><strong>Automatik:</strong> Bei Antworten wird das Ticket automatisch auf "In Bearbeitung" gesetzt (außer Sie wählen "Geschlossen").</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Priorität</th>
|
||
<td><strong style="font-size: 16px; color: #333;"><?php echo esc_html( $ticket->priority ); ?></strong></td>
|
||
</tr>
|
||
<tr>
|
||
<th>Zuweisen an</th>
|
||
<td>
|
||
<select name="assigned_to" class="regular-text">
|
||
<option value="">-- Niemand --</option>
|
||
<?php foreach( $all_users as $u ): ?>
|
||
<option value="<?php echo $u->ID; ?>" <?php selected($ticket->assigned_to, $u->ID); ?>><?php echo esc_html( $u->display_name ); ?> (<?php echo esc_html( $u->user_email ); ?>)</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<p class="description"><strong>Auto-Detect:</strong> 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.</p>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<th>Antwort & Notizen</th>
|
||
<td>
|
||
<label class="wmt-label" style="color:#666; font-size:12px;">Interne Notiz (Optional)</label>
|
||
<select id="wmt_internal_template_select" class="regular-text" style="margin-bottom: 10px;"><option value="">-- Interne Vorlage --</option><?php echo $tpl_internal; ?></select>
|
||
<textarea name="internal_note" class="large-text" rows="3" placeholder="Notizen für Kollegen..."></textarea>
|
||
|
||
<hr style="margin: 20px 0; border:0; border-top:1px dashed #eee;">
|
||
|
||
<label class="wmt-label">Öffentliche Nachricht (Optional)</label>
|
||
<select id="wmt_public_template_select" class="regular-text" style="margin-bottom: 10px;"><option value="">-- Vorlage auswählen --</option><?php echo $tpl_public; ?></select>
|
||
|
||
<?php
|
||
$content = '';
|
||
$settings = array('textarea_name' => 'message', 'media_buttons' => false, 'textarea_rows' => 10, 'teeny' => false);
|
||
wp_editor( $content, 'wmt_message_editor', $settings );
|
||
?>
|
||
|
||
<label class="wmt-label" style="margin-top:10px;">Datei anhängen (Optional)</label>
|
||
<input type="file" name="ticket_file">
|
||
|
||
<p class="description">
|
||
Wird gesendet an <?php echo esc_html( $ticket->guest_email ); ?>.
|
||
</p>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
<?php submit_button( 'Speichern & Benachrichtigen' ); ?>
|
||
</form>
|
||
</div>
|
||
|
||
<script type="text/javascript">
|
||
document.getElementById('wmt_public_template_select').addEventListener('change', function() {
|
||
var editor = tinymce.get('wmt_message_editor');
|
||
if(this.value && editor) {
|
||
editor.setContent(editor.getContent() + (editor.getContent() ? '\n\n' : '') + this.value);
|
||
this.value = "";
|
||
}
|
||
});
|
||
document.getElementById('wmt_internal_template_select').addEventListener('change', function() {
|
||
var ta = document.querySelector('textarea[name="internal_note"]');
|
||
if(this.value && ta) { ta.value += (ta.value ? '\n\n' : '') + this.value; this.value = ""; }
|
||
});
|
||
|
||
function wmt_print_ticket(id) {
|
||
var url = '<?php echo admin_url( 'admin.php?page=wmt_tickets' ); ?>&wmt_print=1&id=' + id;
|
||
window.open(url, '_blank', 'width=800,height=600,scrollbars=yes');
|
||
}
|
||
</script>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
private function render_print_view( $id ) {
|
||
if ( ! current_user_can( 'manage_options' ) ) return;
|
||
|
||
global $wpdb;
|
||
$ticket = $wpdb->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 '<!DOCTYPE html><html><head><title>Ticket #' . $id . '</title>';
|
||
echo '<style type="text/css">
|
||
body { font-family: Helvetica, Arial, sans-serif; color: #333; margin: 20px; background: white; }
|
||
.header { border-bottom: 2px solid #000; padding-bottom: 20px; margin-bottom: 30px; }
|
||
.info-row { display: flex; justify-content: space-between; margin-bottom: 5px; }
|
||
.info-label { font-weight: bold; }
|
||
.chat-item { margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid #eee; }
|
||
.chat-meta { font-size: 12px; color: #666; margin-bottom: 5px; }
|
||
.chat-content { line-height: 1.5; font-size: 14px; }
|
||
.internal { background: #f9f9f9; padding: 10px; border-left: 3px solid #999; margin-top: 10px; font-size: 12px; font-style: italic; }
|
||
.system { text-align: center; background: #eee; padding: 5px; margin: 20px 0; font-size: 11px; color: #666; }
|
||
.file-link { font-size: 12px; margin-top: 5px; color: #0066cc; }
|
||
</style></head><body>';
|
||
|
||
echo '<div class="header"><h1>Ticket #' . $id . ': ' . esc_html( $ticket->title ) . '</h1></div>';
|
||
|
||
echo '<div class="info-row"><span class="info-label">Kunde:</span> ' . esc_html( $ticket->guest_name ) . ' (' . esc_html( $ticket->guest_email ) . ')</div>';
|
||
echo '<div class="info-row"><span class="info-label">Kategorie:</span> ' . esc_html( $ticket->category ) . '</div>';
|
||
echo '<div class="info-row"><span class="info-label">Abteilung:</span> ' . ($ticket->department ? esc_html($ticket->department) : '-') . '</div>';
|
||
echo '<div class="info-row"><span class="info-label">Status:</span> ' . esc_html( $ticket->status ) . '</div>';
|
||
echo '<div class="info-row"><span class="info-label">Priorität:</span> ' . esc_html( $ticket->priority ) . '</div>';
|
||
$assigned = $ticket->assigned_to ? get_userdata($ticket->assigned_to)->display_name : 'Niemand';
|
||
echo '<div class="info-row"><span class="info-label">Zugewiesen an:</span> ' . esc_html($assigned) . '</div>';
|
||
|
||
echo '<h2 style="margin-top: 40px; border-bottom: 1px solid #ccc; padding-bottom: 10px;">Verlauf</h2>';
|
||
|
||
foreach ( $messages as $msg ) {
|
||
if ( $msg->sender_type === 'system' ) {
|
||
echo '<div class="system">System: ' . esc_html($msg->message) . ' (' . $msg->created_at . ')</div>';
|
||
continue;
|
||
}
|
||
|
||
echo '<div class="chat-item">';
|
||
echo '<div class="chat-meta"><strong>' . esc_html( $msg->sender_name ) . '</strong> ' . ($msg->sender_type === 'admin' ? '(Support)' : '(Kunde)') . ' - ' . $msg->created_at . '</div>';
|
||
echo '<div class="chat-content">' . wp_kses_post($msg->message) . '</div>';
|
||
if ( $msg->sender_type === 'admin' && !empty($msg->internal_note) ) {
|
||
echo '<div class="internal">Interne Notiz: ' . esc_html($msg->internal_note) . '</div>';
|
||
}
|
||
if ( $msg->file_url ) {
|
||
echo '<div class="file-link">Datei: <a href="' . esc_url( $msg->file_url ) . '">' . esc_html(basename($msg->file_url)) . '</a></div>';
|
||
}
|
||
echo '</div>';
|
||
}
|
||
|
||
echo '<script>window.print();</script></body></html>';
|
||
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 <strong>.{$file_ext}</strong> 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 '<div class="wmt-error">' . esc_html(urldecode($_GET['upload_error'])) . '</div>';
|
||
}
|
||
if( isset( $_GET['wmt_success'] ) ) {
|
||
echo '<div class="wmt-alert">Ticket erfolgreich erstellt! Bitte prüfen Sie Ihre E-Mails.</div>';
|
||
}
|
||
?>
|
||
<div class="wmt-box">
|
||
<h2>Neues Support Ticket erstellen</h2>
|
||
<form method="post" enctype="multipart/form-data">
|
||
<?php wp_nonce_field( 'wmt_guest_create', 'wmt_nonce' ); ?>
|
||
<label class="wmt-label">Ihr Name *</label>
|
||
<input type="text" name="guest_name" class="wmt-input" required>
|
||
<label class="wmt-label">Ihre E-Mail *</label>
|
||
<input type="email" name="guest_email" class="wmt-input" required>
|
||
<label class="wmt-label">Kategorie</label>
|
||
<select name="category" class="wmt-select">
|
||
<?php foreach( array_map('trim', explode(',', get_option('wmt_categories'))) as $c ): ?>
|
||
<option><?php echo esc_html($c); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<label class="wmt-label">Priorität</label>
|
||
<select name="priority" class="wmt-select">
|
||
<?php foreach( array_map('trim', explode(',', get_option('wmt_priorities'))) as $p ): ?>
|
||
<option><?php echo esc_html($p); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
<label class="wmt-label">Betreff *</label>
|
||
<input type="text" name="title" class="wmt-input" required>
|
||
<label class="wmt-label">Nachricht *</label>
|
||
<textarea name="message" class="wmt-textarea" rows="10" required></textarea>
|
||
<label class="wmt-label">Datei anhängen (Optional)</label>
|
||
<input type="file" name="guest_file" class="wmt-input">
|
||
<button type="submit" name="wmt_create" class="wmt-btn">Ticket absenden</button>
|
||
</form>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
public function render_lookup_form() {
|
||
ob_start();
|
||
if( isset( $_GET['wmt_lookup_sent'] ) ) {
|
||
echo '<div class="wmt-alert">Eine E-Mail mit Links wurde gesendet.</div>';
|
||
}
|
||
?>
|
||
<div class="wmt-box">
|
||
<h2>Meine Tickets finden</h2>
|
||
<form method="post">
|
||
<input type="email" name="lookup_email" class="wmt-input" placeholder="ihre@email.de" required>
|
||
<button type="submit" name="wmt_lookup" class="wmt-btn">Tickets suchen</button>
|
||
</form>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
public function render_ticket_view() {
|
||
$tid = isset( $_GET['wmt_view'] ) ? intval( $_GET['wmt_view'] ) : 0;
|
||
$hash = isset( $_GET['hash'] ) ? sanitize_text_field( $_GET['hash'] ) : '';
|
||
if ( ! $tid || ! $hash ) return '<div class="wmt-box"><p>Kein Ticket ausgewählt.</p></div>';
|
||
|
||
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 '<div class="wmt-box" style="background:#ffebee;"><p>Zugriff verweigert.</p></div>';
|
||
|
||
$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();
|
||
?>
|
||
<div class="wmt-box">
|
||
<div style="border-bottom:1px solid #eee; padding-bottom:10px; margin-bottom:10px;">
|
||
<h2>Ticket #<?php echo $ticket->id; ?>: <?php echo esc_html( $ticket->title ); ?></h2>
|
||
<p><strong>Status:</strong> <?php echo esc_html( $ticket->status ); ?></p>
|
||
</div>
|
||
<div class="wmt-chat">
|
||
<?php foreach ( $messages as $msg ) :
|
||
if( $msg->sender_type === 'system' ): ?>
|
||
<div class="wmt-system-msg"><?php echo esc_html($msg->message); ?></div>
|
||
<?php continue; endif;
|
||
|
||
$is_admin = ( $msg->sender_type === 'admin' );
|
||
if(empty($msg->message) && empty($msg->file_url)) continue;
|
||
?>
|
||
<div class="wmt-bubble wmt-<?php echo $is_admin ? 'left' : 'right'; ?>">
|
||
<strong><?php echo esc_html( $msg->sender_name ); ?></strong> <small><?php echo $msg->created_at; ?></small>
|
||
<p><?php echo nl2br( esc_html( $msg->message ) ); ?></p>
|
||
<?php if ( $msg->file_url ) : ?>
|
||
<a href="<?php echo esc_url( $msg->file_url ); ?>" target="_blank" style="display:inline-block; margin-top:10px; text-decoration:none; background:#e9ecef; padding:5px 10px; border-radius:4px; border:1px solid #ccc;">Datei ansehen</a>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php if ( ! $is_closed ) : ?>
|
||
<form method="post" enctype="multipart/form-data">
|
||
<?php wp_nonce_field( 'wmt_guest_reply', 'wmt_nonce' ); ?>
|
||
<input type="hidden" name="ticket_id" value="<?php echo $ticket->id; ?>">
|
||
<label class="wmt-label">Antwort schreiben</label>
|
||
<textarea name="message" class="wmt-textarea" rows="4" required></textarea>
|
||
<label class="wmt-label" style="margin-top:10px;">Datei anhängen (Optional)</label>
|
||
<input type="file" name="guest_file" class="wmt-input">
|
||
<button type="submit" name="wmt_reply" class="wmt-btn">Antwort senden</button>
|
||
</form>
|
||
<?php else : ?>
|
||
<div class="wmt-alert">Ticket geschlossen.</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
public function handle_guest_creation() {
|
||
if ( ! isset( $_POST['wmt_create'] ) || ! wp_verify_nonce( $_POST['wmt_nonce'], 'wmt_guest_create' ) ) return;
|
||
|
||
$name = sanitize_text_field( $_POST['guest_name'] );
|
||
$email = sanitize_email( $_POST['guest_email'] );
|
||
$cat = sanitize_text_field( $_POST['category'] );
|
||
$prio = sanitize_text_field( $_POST['priority'] );
|
||
$title = sanitize_text_field( $_POST['title'] );
|
||
$msg = sanitize_textarea_field( $_POST['message'] );
|
||
$hash = wp_generate_password( 20, false );
|
||
|
||
$file_url = $this->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( "<b>Neues Ticket #{$tid}</b>\n<b>Betreff:</b> {$title}\n<b>Von:</b> {$name} ({$email})\n<b>Priorität:</b> {$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( "<b>Neue Antwort in Ticket #{$tid}</b>\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();
|