tickets = $tickets;
$this->messages = $messages;
$this->attachments = $attachments;
$this->activity = $activity;
}
public function init() {
add_action( 'admin_menu', array( $this, 'register_menu' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
add_action( 'admin_post_dpsupport_create_ticket', array( $this, 'handle_create_ticket' ) );
add_action( 'admin_post_dpsupport_add_message', array( $this, 'handle_add_message' ) );
add_action( 'admin_post_dpsupport_update_ticket', array( $this, 'handle_update_ticket' ) );
add_action( 'admin_post_dpsupport_archive_ticket', array( $this, 'handle_archive_ticket' ) );
add_action( 'admin_post_dpsupport_restore_ticket', array( $this, 'handle_restore_ticket' ) );
add_action( 'admin_post_dpsupport_delete_ticket', array( $this, 'handle_delete_ticket' ) );
add_action( 'admin_post_dpsupport_save_settings', array( $this, 'handle_save_settings' ) );
add_action( 'admin_post_dpsupport_send_test_email', array( $this, 'handle_send_test_email' ) );
add_action( 'admin_post_dpsupport_create_support_page', array( $this, 'handle_create_support_page' ) );
add_action( 'admin_post_dpsupport_resend_guest_link', array( $this, 'handle_resend_guest_link' ) );
}
public function register_menu() {
add_menu_page(
__( '24laser Support Tickets', '24laser-support-tickets' ),
__( 'Support', '24laser-support-tickets' ),
'dpsupport_view_support',
'dpsupport-support',
array( $this, 'render_tickets_page' ),
'dashicons-sos',
56
);
add_submenu_page( 'dpsupport-support', __( 'Tickets', '24laser-support-tickets' ), __( 'Tickets', '24laser-support-tickets' ), 'dpsupport_view_support', 'dpsupport-support', array( $this, 'render_tickets_page' ) );
add_submenu_page( 'dpsupport-support', __( 'Neues Ticket', '24laser-support-tickets' ), __( 'Neues Ticket', '24laser-support-tickets' ), 'dpsupport_manage_support', 'dpsupport-support-new', array( $this, 'render_new_ticket_page' ) );
add_submenu_page( 'dpsupport-support', __( 'Statistiken', '24laser-support-tickets' ), __( 'Statistiken', '24laser-support-tickets' ), 'dpsupport_view_support', 'dpsupport-support-statistics', array( $this, 'render_statistics_page' ) );
add_submenu_page( 'dpsupport-support', __( 'Einstellungen', '24laser-support-tickets' ), __( 'Einstellungen', '24laser-support-tickets' ), 'dpsupport_manage_settings', 'dpsupport-support-settings', array( $this, 'render_settings_page' ) );
}
public function enqueue_assets( $hook ) {
if ( false === strpos( (string) $hook, 'dpsupport-support' ) ) {
return;
}
wp_enqueue_style( 'dpsupport-admin', DPSUPPORT_URL . 'assets/css/admin.css', array(), DPSUPPORT_VERSION );
wp_enqueue_script( 'dpsupport-admin', DPSUPPORT_URL . 'assets/js/admin.js', array(), DPSUPPORT_VERSION, true );
}
private function guard( $capability = 'dpsupport_view_support' ) {
if ( ! current_user_can( $capability ) ) {
wp_die( esc_html__( 'Du hast keine Berechtigung für diesen Bereich.', '24laser-support-tickets' ) );
}
}
public function render_tickets_page() {
$this->guard();
$this->render_notice();
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only ticket navigation; capability checks are enforced before rendering.
$ticket_id = isset( $_GET['ticket_id'] ) ? absint( $_GET['ticket_id'] ) : 0;
if ( $ticket_id ) {
$this->render_ticket_detail( $ticket_id );
return;
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only list filters/pagination; these values never mutate state.
$status = isset( $_GET['status'] ) ? sanitize_key( wp_unslash( $_GET['status'] ) ) : '';
$priority = isset( $_GET['priority'] ) ? sanitize_key( wp_unslash( $_GET['priority'] ) ) : '';
$category = isset( $_GET['category'] ) ? sanitize_key( wp_unslash( $_GET['category'] ) ) : '';
$search = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : '';
$archive = isset( $_GET['archive'] ) && 'archived' === sanitize_key( wp_unslash( $_GET['archive'] ) ) ? 'archived' : 'active';
$paged = max( 1, isset( $_GET['paged'] ) ? absint( $_GET['paged'] ) : 1 );
// phpcs:enable WordPress.Security.NonceVerification.Recommended
$per_page = 20;
$args = compact( 'status', 'priority', 'category', 'search', 'archive', 'paged', 'per_page' );
$tickets = $this->tickets->query( $args );
$total = $this->tickets->count( $args );
$statuses = $this->tickets->statuses();
$priorities = $this->tickets->priorities();
$categories = $this->tickets->categories();
$sources = $this->activity->creation_sources( wp_list_pluck( $tickets, 'id' ) );
?>
1 ) {
echo '
' . wp_kses_post( paginate_links( array( 'base' => add_query_arg( 'paged', '%#%' ), 'format' => '', 'current' => $paged, 'total' => $total_pages, 'type' => 'plain' ) ) ) . '
';
}
?>
guard( 'dpsupport_manage_support' );
$this->render_notice();
$categories = $this->tickets->categories();
$priorities = $this->tickets->priorities();
?>
tickets->get( $ticket_id );
if ( ! $ticket ) {
wp_die( esc_html__( 'Ticket wurde nicht gefunden.', '24laser-support-tickets' ) );
}
$messages = $this->messages->get_for_ticket( $ticket_id );
$activity = $this->activity->get_for_ticket( $ticket_id );
$statuses = $this->tickets->statuses();
$priorities = $this->tickets->priorities();
$categories = $this->tickets->categories();
$source = $this->activity->creation_source( $ticket_id );
$attachment_map = array();
foreach ( (array) $this->attachments->get_for_ticket( $ticket_id ) as $attachment ) {
$attachment_map[ absint( $attachment->message_id ) ][] = absint( $attachment->attachment_id );
}
$guest_url = empty( $ticket->customer_id ) && 'frontend_guest' === $source ? DPSUPPORT_Guest_Ticket_Access::ticket_url( $ticket ) : '';
?>
← subject ); ?>
ticket_number ); ?> · source_label( $source ) ); ?>
archived_at ) ) : ?>
customer_name ?: '—' ); ?>customer_email ?: '—' ); ?>
status ] ?? $ticket->status ); ?>
priority ] ?? $ticket->priority ); ?>
category ] ?? $ticket->category ); ?>
is_internal ) ? 'internal' : $message->author_type; ?>
author_name ?: ( 'customer' === $message->author_type ? __( 'Kunde', '24laser-support-tickets' ) : __( 'Support', '24laser-support-tickets' ) ) ); ?>created_at ) ); ?>is_internal ) ) : ?> · body ) ); ?>
id ] ) ) : ?>
archived_at ) && 'closed' !== $ticket->status ) : ?>
guard();
$stats = ( new DPSUPPORT_Statistics() )->overview( 30 );
?>
guard( 'dpsupport_manage_settings' );
$this->render_notice();
$categories = $this->tickets->categories();
$category_lines = array();
foreach ( $categories as $key => $label ) {
$category_lines[] = $key . ' | ' . $label;
}
$page_id = absint( get_option( DPSUPPORT_Guest_Ticket_Access::OPTION_PAGE_ID, 0 ) );
$page_url = $page_id ? get_permalink( $page_id ) : '';
?>
guard( 'dpsupport_manage_support' );
check_admin_referer( 'dpsupport_create_ticket' );
$subject = sanitize_text_field( wp_unslash( $_POST['subject'] ?? '' ) );
$message = wp_kses_post( wp_unslash( $_POST['message'] ?? '' ) );
if ( '' === trim( $subject ) || '' === trim( wp_strip_all_tags( $message ) ) ) {
$this->redirect_new( 'error', __( 'Betreff und Nachricht sind erforderlich.', '24laser-support-tickets' ) );
}
$ticket_id = $this->tickets->create( array( 'customer_name' => sanitize_text_field( wp_unslash( $_POST['customer_name'] ?? '' ) ), 'customer_email' => sanitize_email( wp_unslash( $_POST['customer_email'] ?? '' ) ), 'subject' => $subject, 'category' => sanitize_key( wp_unslash( $_POST['category'] ?? '' ) ), 'status' => 'new', 'priority' => sanitize_key( wp_unslash( $_POST['priority'] ?? 'normal' ) ) ) );
if ( is_wp_error( $ticket_id ) ) {
$this->redirect_new( 'error', $ticket_id->get_error_message() );
}
$message_id = $this->messages->create( $ticket_id, array( 'author_type' => 'staff', 'author_user_id' => get_current_user_id(), 'body' => $message, 'is_internal' => 0 ) );
if ( is_wp_error( $message_id ) ) {
$this->tickets->delete_permanently( $ticket_id );
$this->redirect_new( 'error', $message_id->get_error_message() );
}
$this->activity->add( $ticket_id, 'ticket_created', __( 'Ticket wurde im Backend erstellt.', '24laser-support-tickets' ) );
$this->handle_upload( $ticket_id, $message_id );
DPSUPPORT_Notifications::notify_customer_ticket_created( $ticket_id, $message_id, false );
do_action( 'dpsupport_ticket_created', $ticket_id, 'backend' );
$this->redirect_ticket( $ticket_id, 'success', __( 'Ticket wurde angelegt.', '24laser-support-tickets' ) );
}
public function handle_add_message() {
$this->guard( 'dpsupport_reply_support' );
$ticket_id = absint( $_POST['ticket_id'] ?? 0 );
check_admin_referer( 'dpsupport_add_message_' . $ticket_id );
$ticket = $this->tickets->get( $ticket_id );
if ( ! $ticket ) {
$this->redirect_list( 'error', __( 'Ticket wurde nicht gefunden.', '24laser-support-tickets' ) );
}
$body = wp_kses_post( wp_unslash( $_POST['message'] ?? '' ) );
if ( '' === trim( wp_strip_all_tags( $body ) ) ) {
$this->redirect_ticket( $ticket_id, 'error', __( 'Bitte gib eine Nachricht ein.', '24laser-support-tickets' ) );
}
$is_internal = ! empty( $_POST['is_internal'] );
$body = DPSUPPORT_Workflow::prepare_staff_message( $body, $ticket, $is_internal, get_current_user_id() );
$message_id = $this->messages->create( $ticket_id, array( 'author_type' => 'staff', 'author_user_id' => get_current_user_id(), 'body' => $body, 'is_internal' => $is_internal ? 1 : 0 ) );
if ( is_wp_error( $message_id ) ) {
$this->redirect_ticket( $ticket_id, 'error', $message_id->get_error_message() );
}
$this->tickets->touch( $ticket_id );
$this->activity->add( $ticket_id, $is_internal ? 'internal_note' : 'staff_reply', $is_internal ? __( 'Interne Notiz hinzugefügt.', '24laser-support-tickets' ) : __( 'Support hat geantwortet.', '24laser-support-tickets' ) );
$this->handle_upload( $ticket_id, $message_id );
if ( ! $is_internal ) {
DPSUPPORT_Notifications::notify_customer_staff_reply( $ticket_id, $message_id );
do_action( 'dpsupport_staff_replied', $ticket_id, $message_id );
}
$this->redirect_ticket( $ticket_id, 'success', __( 'Nachricht wurde gespeichert.', '24laser-support-tickets' ) );
}
public function handle_update_ticket() {
$this->guard( 'dpsupport_manage_support' );
$ticket_id = absint( $_POST['ticket_id'] ?? 0 );
check_admin_referer( 'dpsupport_update_ticket_' . $ticket_id );
$ok = $this->tickets->update( $ticket_id, array( 'customer_name' => sanitize_text_field( wp_unslash( $_POST['customer_name'] ?? '' ) ), 'customer_email' => sanitize_email( wp_unslash( $_POST['customer_email'] ?? '' ) ), 'subject' => sanitize_text_field( wp_unslash( $_POST['subject'] ?? '' ) ), 'status' => sanitize_key( wp_unslash( $_POST['status'] ?? '' ) ), 'priority' => sanitize_key( wp_unslash( $_POST['priority'] ?? '' ) ), 'category' => sanitize_key( wp_unslash( $_POST['category'] ?? '' ) ) ) );
if ( $ok ) {
$this->activity->add( $ticket_id, 'ticket_updated', __( 'Ticketdaten wurden aktualisiert.', '24laser-support-tickets' ) );
}
$this->redirect_ticket( $ticket_id, $ok ? 'success' : 'error', $ok ? __( 'Ticket wurde gespeichert.', '24laser-support-tickets' ) : __( 'Ticket konnte nicht gespeichert werden.', '24laser-support-tickets' ) );
}
public function handle_archive_ticket() {
$this->guard( 'dpsupport_manage_support' );
$ticket_id = absint( $_POST['ticket_id'] ?? 0 );
check_admin_referer( 'dpsupport_archive_ticket_' . $ticket_id );
$this->tickets->archive( $ticket_id );
$this->activity->add( $ticket_id, 'archived', __( 'Ticket wurde archiviert.', '24laser-support-tickets' ) );
$this->redirect_list( 'success', __( 'Ticket wurde archiviert.', '24laser-support-tickets' ) );
}
public function handle_restore_ticket() {
$this->guard( 'dpsupport_manage_support' );
$ticket_id = absint( $_POST['ticket_id'] ?? 0 );
check_admin_referer( 'dpsupport_restore_ticket_' . $ticket_id );
$this->tickets->restore( $ticket_id );
$this->activity->add( $ticket_id, 'restored', __( 'Ticket wurde wiederhergestellt.', '24laser-support-tickets' ) );
$this->redirect_ticket( $ticket_id, 'success', __( 'Ticket wurde wiederhergestellt.', '24laser-support-tickets' ) );
}
public function handle_delete_ticket() {
$this->guard( 'dpsupport_delete_tickets' );
$ticket_id = absint( $_POST['ticket_id'] ?? 0 );
check_admin_referer( 'dpsupport_delete_ticket_' . $ticket_id );
foreach ( $this->attachments->get_attachment_ids_for_ticket( $ticket_id ) as $attachment_id ) {
wp_delete_attachment( $attachment_id, true );
}
$this->tickets->delete_permanently( $ticket_id );
$this->redirect_list( 'success', __( 'Ticket wurde dauerhaft gelöscht.', '24laser-support-tickets' ), 'archived' );
}
public function handle_save_settings() {
$this->guard( 'dpsupport_manage_settings' );
check_admin_referer( 'dpsupport_save_settings' );
update_option( 'dpsupport_notification_email', sanitize_text_field( wp_unslash( $_POST['notification_email'] ?? '' ) ) );
update_option( 'dpsupport_support_from_name', sanitize_text_field( wp_unslash( $_POST['from_name'] ?? '' ) ) );
update_option( 'dpsupport_support_from_email', sanitize_email( wp_unslash( $_POST['from_email'] ?? '' ) ) );
update_option( 'dpsupport_support_reply_to_email', sanitize_email( wp_unslash( $_POST['reply_to_email'] ?? '' ) ) );
update_option( 'dpsupport_notify_staff_new_ticket', ! empty( $_POST['notify_staff_new'] ) ? 'yes' : 'no' );
update_option( 'dpsupport_notify_staff_customer_reply', ! empty( $_POST['notify_staff_reply'] ) ? 'yes' : 'no' );
update_option( 'dpsupport_notify_customer_ticket_created', ! empty( $_POST['notify_customer_created'] ) ? 'yes' : 'no' );
update_option( 'dpsupport_notify_customer_staff_reply', ! empty( $_POST['notify_customer_reply'] ) ? 'yes' : 'no' );
update_option( 'dpsupport_default_status', $this->tickets->sanitize_status( sanitize_key( wp_unslash( $_POST['default_status'] ?? 'new' ) ) ) );
update_option( 'dpsupport_default_priority', $this->tickets->sanitize_priority( sanitize_key( wp_unslash( $_POST['default_priority'] ?? 'normal' ) ) ) );
$categories = array();
$raw_lines = preg_split( '/\r\n|\r|\n/', sanitize_textarea_field( wp_unslash( $_POST['categories'] ?? '' ) ) );
foreach ( (array) $raw_lines as $line ) {
$parts = array_map( 'trim', explode( '|', $line, 2 ) );
$key = sanitize_key( $parts[0] ?? '' );
$label = sanitize_text_field( $parts[1] ?? '' );
if ( $key && $label ) {
$categories[ $key ] = $label;
}
}
if ( ! $categories ) {
$categories = array( 'allgemein' => __( 'Allgemein', '24laser-support-tickets' ), 'sonstiges' => __( 'Sonstiges', '24laser-support-tickets' ) );
}
update_option( 'dpsupport_categories', $categories );
$default_category = sanitize_key( wp_unslash( $_POST['default_category'] ?? 'allgemein' ) );
update_option( 'dpsupport_default_category', isset( $categories[ $default_category ] ) ? $default_category : array_key_first( $categories ) );
update_option( 'dpsupport_frontend_form_enabled', ! empty( $_POST['frontend_enabled'] ) ? 'yes' : 'no' );
update_option( 'dpsupport_frontend_form_guests', ! empty( $_POST['frontend_guests'] ) ? 'yes' : 'no' );
update_option( 'dpsupport_frontend_form_title', sanitize_text_field( wp_unslash( $_POST['frontend_title'] ?? '' ) ) );
update_option( 'dpsupport_frontend_form_intro', sanitize_textarea_field( wp_unslash( $_POST['frontend_intro'] ?? '' ) ) );
update_option( 'dpsupport_frontend_form_success', sanitize_text_field( wp_unslash( $_POST['frontend_success'] ?? '' ) ) );
$this->redirect_settings( 'success', __( 'Einstellungen wurden gespeichert.', '24laser-support-tickets' ) );
}
public function handle_send_test_email() {
$this->guard( 'dpsupport_manage_settings' );
check_admin_referer( 'dpsupport_send_test_email' );
$result = DPSUPPORT_Notifications::send_test_email( sanitize_email( wp_unslash( $_POST['recipient'] ?? '' ) ) );
$this->redirect_settings( ! empty( $result['sent'] ) ? 'success' : 'error', $result['message'] ?? __( 'Test abgeschlossen.', '24laser-support-tickets' ) );
}
public function handle_create_support_page() {
$this->guard( 'dpsupport_manage_settings' );
check_admin_referer( 'dpsupport_create_support_page' );
$page_id = absint( get_option( DPSUPPORT_Guest_Ticket_Access::OPTION_PAGE_ID, 0 ) );
if ( $page_id && 'trash' !== get_post_status( $page_id ) ) {
$this->redirect_settings( 'notice', __( 'Es ist bereits eine Supportseite hinterlegt.', '24laser-support-tickets' ) );
}
$page_id = wp_insert_post( array( 'post_title' => __( 'Support', '24laser-support-tickets' ), 'post_content' => '[dpsupport_form]', 'post_status' => 'publish', 'post_type' => 'page' ), true );
if ( is_wp_error( $page_id ) ) {
$this->redirect_settings( 'error', $page_id->get_error_message() );
}
update_option( DPSUPPORT_Guest_Ticket_Access::OPTION_PAGE_ID, absint( $page_id ) );
DPSUPPORT_Guest_Ticket_Access::remember_current_page( get_permalink( $page_id ) );
$this->redirect_settings( 'success', __( 'Supportseite wurde erstellt.', '24laser-support-tickets' ) );
}
public function handle_resend_guest_link() {
$this->guard( 'dpsupport_manage_support' );
$ticket_id = absint( $_POST['ticket_id'] ?? 0 );
check_admin_referer( 'dpsupport_resend_guest_link_' . $ticket_id );
$ticket = $this->tickets->get( $ticket_id );
if ( ! $ticket || ! empty( $ticket->customer_id ) || 'frontend_guest' !== $this->activity->creation_source( $ticket_id ) ) {
$this->redirect_ticket( $ticket_id, 'error', __( 'Gastzugang ist für dieses Ticket nicht verfügbar.', '24laser-support-tickets' ) );
}
$result = DPSUPPORT_Notifications::send_guest_access_link_detailed( $ticket_id );
if ( ! empty( $result['sent'] ) ) {
$this->activity->add( $ticket_id, 'guest_access_resent', __( 'Persönlicher Gastzugangslink wurde erneut per E-Mail gesendet.', '24laser-support-tickets' ) );
}
$this->redirect_ticket( $ticket_id, ! empty( $result['sent'] ) ? 'success' : 'error', $result['message'] ?? __( 'Versand abgeschlossen.', '24laser-support-tickets' ) );
}
private function handle_upload( $ticket_id, $message_id ) {
return DPSUPPORT_Upload_Handler::handle( $ticket_id, $message_id, $this->attachments );
}
private function source_label( $source ) {
$labels = array( 'backend' => __( 'Backend', '24laser-support-tickets' ), 'customer_portal' => __( 'Kundenkonto', '24laser-support-tickets' ), 'frontend_customer' => __( 'Frontend · Kunde', '24laser-support-tickets' ), 'frontend_guest' => __( 'Frontend · Gast', '24laser-support-tickets' ), 'unknown' => __( 'Unbekannt', '24laser-support-tickets' ) );
return $labels[ $source ] ?? $labels['unknown'];
}
private function render_notice() {
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only flash-message parameters generated by the plugin after a safe redirect.
$type = isset( $_GET['dpsupport_notice_type'] ) ? sanitize_key( wp_unslash( $_GET['dpsupport_notice_type'] ) ) : '';
$text = isset( $_GET['dpsupport_notice'] ) ? sanitize_text_field( wp_unslash( $_GET['dpsupport_notice'] ) ) : '';
// phpcs:enable WordPress.Security.NonceVerification.Recommended
if ( ! $text ) {
return;
}
$class = 'error' === $type ? 'notice-error' : ( 'notice' === $type ? 'notice-warning' : 'notice-success' );
echo '' . esc_html( $text ) . '
';
}
private function notice_args( $type, $message ) {
return array( 'dpsupport_notice_type' => sanitize_key( $type ), 'dpsupport_notice' => sanitize_text_field( $message ) );
}
private function redirect_ticket( $ticket_id, $type, $message ) {
wp_safe_redirect( add_query_arg( array_merge( array( 'page' => 'dpsupport-support', 'ticket_id' => absint( $ticket_id ) ), $this->notice_args( $type, $message ) ), admin_url( 'admin.php' ) ) );
exit;
}
private function redirect_list( $type, $message, $archive = 'active' ) {
$args = array_merge( array( 'page' => 'dpsupport-support' ), $this->notice_args( $type, $message ) );
if ( 'archived' === $archive ) {
$args['archive'] = 'archived';
}
wp_safe_redirect( add_query_arg( $args, admin_url( 'admin.php' ) ) );
exit;
}
private function redirect_new( $type, $message ) {
wp_safe_redirect( add_query_arg( array_merge( array( 'page' => 'dpsupport-support-new' ), $this->notice_args( $type, $message ) ), admin_url( 'admin.php' ) ) );
exit;
}
private function redirect_settings( $type, $message ) {
wp_safe_redirect( add_query_arg( array_merge( array( 'page' => 'dpsupport-support-settings' ), $this->notice_args( $type, $message ) ), admin_url( 'admin.php' ) ) );
exit;
}
}