Initial plugin commit

This commit is contained in:
2026-04-13 20:56:18 +02:00
commit b8647f468a
4 changed files with 635 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.DS_Store
Thumbs.db
*.zip

10
README.txt Executable file
View File

@@ -0,0 +1,10 @@
KGV Contact Form Pro v1.4.0
Neu:
- Autoren und Mitarbeiter können Kontaktanfragen im Backend sehen
- Kategorien bleiben nur für Administratoren bearbeitbar
- Mehrere E-Mail-Adressen pro Kategorie möglich
- Nachrichtenbereich, Datenschutz-Checkbox, gelesen/ungelesen
Shortcode:
[kgv_contact_form]

31
assets/kgv-contact-form.css Executable file
View File

@@ -0,0 +1,31 @@
.kgv-contact-form{
max-width:760px;
padding:24px;
border:1px solid #d9d9d9;
border-radius:16px;
background:#fff;
box-shadow:0 10px 24px rgba(0,0,0,.05);
}
.kgv-cf-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.kgv-cf-field{margin:0 0 16px}
.kgv-cf-field label{display:block;margin:0 0 8px;font-weight:600}
.kgv-contact-form input[type="text"],
.kgv-contact-form input[type="email"],
.kgv-contact-form select,
.kgv-contact-form textarea{
width:100%;padding:12px 14px;border:1px solid #cfcfcf;border-radius:10px;background:#fff;box-sizing:border-box;
transition:border-color .2s ease, box-shadow .2s ease;
}
.kgv-contact-form input[type="text"]:focus,
.kgv-contact-form input[type="email"]:focus,
.kgv-contact-form select:focus,
.kgv-contact-form textarea:focus{outline:none;border-color:#2271b1;box-shadow:0 0 0 3px rgba(34,113,177,.12)}
.kgv-cf-privacy{margin:8px 0 18px;font-size:.95rem;line-height:1.5}
.kgv-cf-privacy label{display:flex;gap:10px;align-items:flex-start}
.kgv-contact-form button{appearance:none;border:none;border-radius:10px;padding:12px 18px;font-weight:700;cursor:pointer;background:#2271b1;color:#fff}
.kgv-cf-alert{max-width:760px;margin:0 0 16px;padding:12px 14px;border-radius:12px}
.kgv-cf-alert-success{border:1px solid #c6e1c6;background:#ecf7ed}
.kgv-cf-alert-error{border:1px solid #e5b3b3;background:#fff2f2}
.kgv-cf-hp{position:absolute !important;left:-9999px !important;opacity:0 !important;pointer-events:none !important}
@media (max-width:700px){.kgv-cf-grid{grid-template-columns:1fr}.kgv-contact-form{padding:18px;border-radius:14px}}

591
kgv-contact-form-pro.php Executable file
View File

@@ -0,0 +1,591 @@
<?php
/**
* Plugin Name: KGV Kontakt Form
* Description: Kontaktformular mit Nachrichtenbereich, Kategorien, Routing je Kategorie, Datenschutz-Checkbox, gelesen/ungelesen, Mehrfach-E-Mail und Rollenfreigabe für Kontaktanfragen.
* Version: 1.4.0
* Author: Ronny Grobel
* Plugin URI: https://apex-project.de/
* Update URI: https://git.apex-project.de/RonnyG/KGV-Contact-Form
* GitHub Plugin URI: https://git.apex-project.de/RonnyG/KGV-Contact-Form
*/
if (!defined('ABSPATH')) exit;
define('KGV_CF_VERSION', '1.4.0');
define('KGV_CF_VIEW_CAP', 'kgv_view_contact_requests');
define('KGV_CF_MANAGE_CAP', 'manage_options');
register_activation_hook(__FILE__, 'kgv_cf_activate');
add_action('plugins_loaded', 'kgv_cf_maybe_upgrade');
add_action('admin_menu', 'kgv_cf_admin_menu');
add_action('admin_init', 'kgv_cf_handle_admin_actions');
add_action('init', 'kgv_cf_handle_form_submit');
add_action('wp_enqueue_scripts', 'kgv_cf_enqueue_assets');
add_shortcode('kgv_contact_form', 'kgv_cf_render_form');
function kgv_cf_messages_table() {
global $wpdb;
return $wpdb->prefix . 'kgv_cf_messages';
}
function kgv_cf_categories_table() {
global $wpdb;
return $wpdb->prefix . 'kgv_cf_categories';
}
function kgv_cf_activate() {
kgv_cf_run_schema_update();
kgv_cf_add_caps();
update_option('kgv_cf_db_version', KGV_CF_VERSION);
}
function kgv_cf_maybe_upgrade() {
$installed = get_option('kgv_cf_db_version', '');
if ($installed !== KGV_CF_VERSION) {
kgv_cf_run_schema_update();
kgv_cf_add_caps();
update_option('kgv_cf_db_version', KGV_CF_VERSION);
}
}
function kgv_cf_add_caps() {
$roles = ['administrator', 'editor', 'author', 'contributor'];
foreach ($roles as $role_name) {
$role = get_role($role_name);
if ($role && !$role->has_cap(KGV_CF_VIEW_CAP)) {
$role->add_cap(KGV_CF_VIEW_CAP);
}
}
}
function kgv_cf_run_schema_update() {
global $wpdb;
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$charset_collate = $wpdb->get_charset_collate();
$messages = kgv_cf_messages_table();
$categories = kgv_cf_categories_table();
$sql_messages = "CREATE TABLE $messages (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
created_at DATETIME NOT NULL,
category_id BIGINT UNSIGNED NULL,
category_name VARCHAR(190) NOT NULL DEFAULT '',
recipient_email TEXT NULL,
sender_name VARCHAR(190) NOT NULL DEFAULT '',
sender_email VARCHAR(190) NOT NULL DEFAULT '',
subject VARCHAR(255) NOT NULL DEFAULT '',
message LONGTEXT NOT NULL,
privacy_accepted TINYINT(1) NOT NULL DEFAULT 0,
is_read TINYINT(1) NOT NULL DEFAULT 0,
ip_address VARCHAR(100) NOT NULL DEFAULT '',
user_agent TEXT NULL,
is_sent TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id)
) $charset_collate;";
$sql_categories = "CREATE TABLE $categories (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(190) NOT NULL,
recipient_email TEXT NULL,
sort_order INT NOT NULL DEFAULT 0,
is_active TINYINT(1) NOT NULL DEFAULT 1,
PRIMARY KEY (id)
) $charset_collate;";
dbDelta($sql_messages);
dbDelta($sql_categories);
$count = (int) $wpdb->get_var("SELECT COUNT(*) FROM $categories");
if ($count === 0) {
$wpdb->insert($categories, [
'name' => 'Allgemein',
'recipient_email' => get_option('admin_email'),
'sort_order' => 10,
'is_active' => 1,
], ['%s', '%s', '%d', '%d']);
}
}
function kgv_cf_enqueue_assets() {
wp_enqueue_style(
'kgv-contact-form-style',
plugin_dir_url(__FILE__) . 'assets/kgv-contact-form.css',
[],
KGV_CF_VERSION
);
}
function kgv_cf_get_categories($only_active = false) {
global $wpdb;
$table = kgv_cf_categories_table();
$where = $only_active ? "WHERE is_active = 1" : "";
return $wpdb->get_results("SELECT * FROM $table $where ORDER BY sort_order ASC, name ASC");
}
function kgv_cf_get_category($id) {
global $wpdb;
$table = kgv_cf_categories_table();
return $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE id = %d", $id));
}
function kgv_cf_parse_recipient_emails($raw) {
$parts = array_map('trim', explode(',', (string) $raw));
$parts = array_filter($parts, function($email) {
return is_email($email);
});
if (empty($parts)) {
$parts = [get_option('admin_email')];
}
return array_values(array_unique($parts));
}
function kgv_cf_admin_menu() {
add_menu_page(
'Kontaktformular',
'Kontaktformular',
KGV_CF_VIEW_CAP,
'kgv-contact-form',
'kgv_cf_render_messages_page',
'dashicons-email-alt',
26
);
add_submenu_page(
'kgv-contact-form',
'Nachrichten',
'Nachrichten',
KGV_CF_VIEW_CAP,
'kgv-contact-form',
'kgv_cf_render_messages_page'
);
add_submenu_page(
'kgv-contact-form',
'Kategorien',
'Kategorien',
KGV_CF_MANAGE_CAP,
'kgv-contact-form-categories',
'kgv_cf_render_categories_page'
);
}
function kgv_cf_handle_admin_actions() {
if (!is_admin()) {
return;
}
global $wpdb;
if (isset($_POST['kgv_cf_save_category'])) {
if (!current_user_can(KGV_CF_MANAGE_CAP)) {
wp_die('Keine Berechtigung.');
}
check_admin_referer('kgv_cf_save_category');
$table = kgv_cf_categories_table();
$id = isset($_POST['category_id']) ? absint($_POST['category_id']) : 0;
$name = sanitize_text_field($_POST['name'] ?? '');
$recipient_email = sanitize_textarea_field($_POST['recipient_email'] ?? '');
$sort_order = isset($_POST['sort_order']) ? intval($_POST['sort_order']) : 0;
$is_active = !empty($_POST['is_active']) ? 1 : 0;
$parsed_emails = kgv_cf_parse_recipient_emails($recipient_email);
if ($name && !empty($parsed_emails)) {
$data = [
'name' => $name,
'recipient_email' => implode(', ', $parsed_emails),
'sort_order' => $sort_order,
'is_active' => $is_active,
];
if ($id) {
$wpdb->update($table, $data, ['id' => $id], ['%s', '%s', '%d', '%d'], ['%d']);
} else {
$wpdb->insert($table, $data, ['%s', '%s', '%d', '%d']);
}
wp_safe_redirect(admin_url('admin.php?page=kgv-contact-form-categories&saved=1'));
exit;
}
wp_safe_redirect(admin_url('admin.php?page=kgv-contact-form-categories&error=1'));
exit;
}
if (isset($_GET['kgv_cf_delete_category'])) {
if (!current_user_can(KGV_CF_MANAGE_CAP)) {
wp_die('Keine Berechtigung.');
}
check_admin_referer('kgv_cf_delete_category');
$id = absint($_GET['kgv_cf_delete_category']);
if ($id) {
$wpdb->delete(kgv_cf_categories_table(), ['id' => $id], ['%d']);
}
wp_safe_redirect(admin_url('admin.php?page=kgv-contact-form-categories&deleted=1'));
exit;
}
if (isset($_GET['kgv_cf_delete_message'])) {
if (!current_user_can(KGV_CF_VIEW_CAP)) {
wp_die('Keine Berechtigung.');
}
check_admin_referer('kgv_cf_delete_message');
$id = absint($_GET['kgv_cf_delete_message']);
if ($id) {
$wpdb->delete(kgv_cf_messages_table(), ['id' => $id], ['%d']);
}
wp_safe_redirect(admin_url('admin.php?page=kgv-contact-form&deleted=1'));
exit;
}
if (isset($_GET['kgv_cf_toggle_read'])) {
if (!current_user_can(KGV_CF_VIEW_CAP)) {
wp_die('Keine Berechtigung.');
}
check_admin_referer('kgv_cf_toggle_read');
$id = absint($_GET['kgv_cf_toggle_read']);
$message = $wpdb->get_row($wpdb->prepare(
"SELECT id, is_read FROM " . kgv_cf_messages_table() . " WHERE id = %d",
$id
));
if ($message) {
$new_value = ((int) $message->is_read === 1) ? 0 : 1;
$wpdb->update(
kgv_cf_messages_table(),
['is_read' => $new_value],
['id' => $id],
['%d'],
['%d']
);
}
wp_safe_redirect(admin_url('admin.php?page=kgv-contact-form&updated=1'));
exit;
}
}
function kgv_cf_render_form() {
$categories = kgv_cf_get_categories(true);
ob_start();
if (isset($_GET['kgv_sent']) && $_GET['kgv_sent'] === '1') {
echo '<div class="kgv-cf-alert kgv-cf-alert-success">Nachricht erfolgreich gesendet.</div>';
}
if (isset($_GET['kgv_error']) && $_GET['kgv_error'] === '1') {
echo '<div class="kgv-cf-alert kgv-cf-alert-error">Bitte alle Pflichtfelder korrekt ausfüllen.</div>';
}
?>
<form method="post" class="kgv-contact-form" novalidate>
<div class="kgv-cf-grid">
<p class="kgv-cf-field">
<label for="kgv_name">Name *</label>
<input type="text" id="kgv_name" name="kgv_name" required>
</p>
<p class="kgv-cf-field">
<label for="kgv_email">E-Mail *</label>
<input type="email" id="kgv_email" name="kgv_email" required>
</p>
</div>
<p class="kgv-cf-field">
<label for="kgv_category">Kategorie *</label>
<select id="kgv_category" name="kgv_category" required>
<option value="">Bitte auswählen</option>
<?php foreach ($categories as $category): ?>
<option value="<?php echo esc_attr($category->id); ?>"><?php echo esc_html($category->name); ?></option>
<?php endforeach; ?>
</select>
</p>
<p class="kgv-cf-field">
<label for="kgv_subject">Betreff *</label>
<input type="text" id="kgv_subject" name="kgv_subject" required>
</p>
<p class="kgv-cf-field">
<label for="kgv_message">Nachricht *</label>
<textarea id="kgv_message" name="kgv_message" required rows="7"></textarea>
</p>
<p class="kgv-cf-privacy">
<label>
<input type="checkbox" name="kgv_privacy" value="1" required>
Ich habe die Datenschutzhinweise gelesen und stimme der Verarbeitung meiner Angaben zur Bearbeitung der Anfrage zu. *
</label>
</p>
<input type="text" name="kgv_hp" value="" autocomplete="off" tabindex="-1" class="kgv-cf-hp">
<?php wp_nonce_field('kgv_contact_form_submit', 'kgv_nonce'); ?>
<p class="kgv-cf-submit">
<button type="submit" name="kgv_submit">Senden</button>
</p>
</form>
<?php
return ob_get_clean();
}
function kgv_cf_handle_form_submit() {
if (!isset($_POST['kgv_submit'])) {
return;
}
if (!isset($_POST['kgv_nonce']) || !wp_verify_nonce($_POST['kgv_nonce'], 'kgv_contact_form_submit')) {
return;
}
if (!empty($_POST['kgv_hp'])) {
return;
}
$name = sanitize_text_field($_POST['kgv_name'] ?? '');
$email = sanitize_email($_POST['kgv_email'] ?? '');
$category_id = absint($_POST['kgv_category'] ?? 0);
$subject = sanitize_text_field($_POST['kgv_subject'] ?? '');
$message = sanitize_textarea_field($_POST['kgv_message'] ?? '');
$privacy = !empty($_POST['kgv_privacy']) ? 1 : 0;
$category = $category_id ? kgv_cf_get_category($category_id) : null;
if (!$name || !is_email($email) || !$category || !$subject || !$message || !$privacy) {
kgv_cf_redirect_with_flag('kgv_error', '1');
}
$recipients = kgv_cf_parse_recipient_emails($category->recipient_email);
$body = "Neue Kontaktanfrage\n\n";
$body .= "Kategorie: " . $category->name . "\n";
$body .= "Name: " . $name . "\n";
$body .= "E-Mail: " . $email . "\n";
$body .= "Betreff: " . $subject . "\n";
$body .= "Datenschutz akzeptiert: Ja\n\n";
$body .= "Nachricht:\n" . $message . "\n";
$headers = [
'Content-Type: text/plain; charset=UTF-8',
'Reply-To: ' . $name . ' <' . $email . '>',
];
$sent = wp_mail($recipients, $subject, $body, $headers) ? 1 : 0;
global $wpdb;
$wpdb->insert(kgv_cf_messages_table(), [
'created_at' => current_time('mysql'),
'category_id' => $category->id,
'category_name' => $category->name,
'recipient_email' => implode(', ', $recipients),
'sender_name' => $name,
'sender_email' => $email,
'subject' => $subject,
'message' => $message,
'privacy_accepted' => $privacy,
'is_read' => 0,
'ip_address' => isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '',
'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_textarea_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '',
'is_sent' => $sent,
], ['%s','%d','%s','%s','%s','%s','%s','%s','%d','%d','%s','%s','%d']);
kgv_cf_redirect_with_flag('kgv_sent', '1');
}
function kgv_cf_redirect_with_flag($key, $value) {
$referer = wp_get_referer();
if (!$referer) {
$referer = home_url('/');
}
wp_safe_redirect(add_query_arg($key, $value, $referer));
exit;
}
function kgv_cf_render_messages_page() {
if (!current_user_can(KGV_CF_VIEW_CAP)) {
wp_die('Keine Berechtigung.');
}
global $wpdb;
$table = kgv_cf_messages_table();
$messages = $wpdb->get_results("SELECT * FROM $table ORDER BY is_read ASC, created_at DESC, id DESC LIMIT 500");
$unread_count = (int) $wpdb->get_var("SELECT COUNT(*) FROM $table WHERE is_read = 0");
echo '<div class="wrap">';
echo '<h1>Kontaktformular Nachrichten</h1>';
if (isset($_GET['deleted'])) {
echo '<div class="notice notice-success is-dismissible"><p>Nachricht gelöscht.</p></div>';
}
if (isset($_GET['updated'])) {
echo '<div class="notice notice-success is-dismissible"><p>Status aktualisiert.</p></div>';
}
echo '<p><strong>Ungelesene Nachrichten:</strong> ' . esc_html($unread_count) . '</p>';
if (empty($messages)) {
echo '<p>Noch keine Nachrichten vorhanden.</p>';
echo '</div>';
return;
}
echo '<table class="widefat striped">';
echo '<thead><tr>';
echo '<th>Status</th>';
echo '<th>Datum</th>';
echo '<th>Kategorie</th>';
echo '<th>Von</th>';
echo '<th>Betreff</th>';
echo '<th>Datenschutz</th>';
echo '<th>Weitergeleitet an</th>';
echo '<th>Mailstatus</th>';
echo '<th>Aktion</th>';
echo '</tr></thead><tbody>';
foreach ($messages as $msg) {
$delete_url = wp_nonce_url(
admin_url('admin.php?page=kgv-contact-form&kgv_cf_delete_message=' . absint($msg->id)),
'kgv_cf_delete_message'
);
$toggle_url = wp_nonce_url(
admin_url('admin.php?page=kgv-contact-form&kgv_cf_toggle_read=' . absint($msg->id)),
'kgv_cf_toggle_read'
);
$status_badge = ((int)$msg->is_read === 1)
? '<span style="display:inline-block;padding:4px 8px;background:#edf7ed;border:1px solid #bfd9bf;border-radius:999px;">Gelesen</span>'
: '<span style="display:inline-block;padding:4px 8px;background:#fff4e5;border:1px solid #e0c08c;border-radius:999px;font-weight:600;">Ungelesen</span>';
echo '<tr' . (((int)$msg->is_read === 0) ? ' style="font-weight:600;"' : '') . '>';
echo '<td>' . $status_badge . '</td>';
echo '<td>' . esc_html(mysql2date('d.m.Y H:i', $msg->created_at)) . '</td>';
echo '<td>' . esc_html($msg->category_name) . '</td>';
echo '<td><strong>' . esc_html($msg->sender_name) . '</strong><br><a href="mailto:' . esc_attr($msg->sender_email) . '">' . esc_html($msg->sender_email) . '</a></td>';
echo '<td><strong>' . esc_html($msg->subject) . '</strong><br>' . nl2br(esc_html(wp_trim_words($msg->message, 24, '…'))) . '</td>';
echo '<td>' . (((int)$msg->privacy_accepted === 1) ? 'Ja' : 'Nein') . '</td>';
echo '<td>' . esc_html($msg->recipient_email) . '</td>';
echo '<td>' . (((int)$msg->is_sent === 1) ? 'Gesendet' : 'Fehler') . '</td>';
echo '<td>';
echo '<a href="' . esc_url($toggle_url) . '">' . (((int)$msg->is_read === 1) ? 'Als ungelesen markieren' : 'Als gelesen markieren') . '</a>';
echo ' | <a href="#" onclick="this.nextElementSibling.style.display=\'block\';return false;">Anzeigen</a>';
echo ' | <a href="' . esc_url($delete_url) . '" onclick="return confirm(\'Nachricht wirklich löschen?\')">Löschen</a>';
echo '<div style="display:none; margin-top:10px; padding:12px; background:#fff; border:1px solid #ddd; min-width:360px; font-weight:400;">';
echo '<p><strong>Nachricht:</strong></p>';
echo '<div>' . nl2br(esc_html($msg->message)) . '</div>';
echo '</div>';
echo '</td>';
echo '</tr>';
}
echo '</tbody></table>';
echo '</div>';
}
function kgv_cf_render_categories_page() {
if (!current_user_can(KGV_CF_MANAGE_CAP)) {
wp_die('Keine Berechtigung.');
}
$edit_id = isset($_GET['edit']) ? absint($_GET['edit']) : 0;
$edit_item = $edit_id ? kgv_cf_get_category($edit_id) : null;
$categories = kgv_cf_get_categories(false);
echo '<div class="wrap">';
echo '<h1>Kontaktformular Kategorien</h1>';
if (isset($_GET['saved'])) {
echo '<div class="notice notice-success is-dismissible"><p>Kategorie gespeichert.</p></div>';
}
if (isset($_GET['deleted'])) {
echo '<div class="notice notice-success is-dismissible"><p>Kategorie gelöscht.</p></div>';
}
if (isset($_GET['error'])) {
echo '<div class="notice notice-error is-dismissible"><p>Bitte einen Namen und mindestens eine gültige E-Mail-Adresse eintragen.</p></div>';
}
echo '<div style="display:grid; grid-template-columns:minmax(320px,420px) 1fr; gap:24px; align-items:start; margin-top:20px;">';
echo '<div style="background:#fff; border:1px solid #ddd; padding:20px;">';
echo '<h2 style="margin-top:0;">' . ($edit_item ? 'Kategorie bearbeiten' : 'Neue Kategorie') . '</h2>';
echo '<form method="post">';
wp_nonce_field('kgv_cf_save_category');
echo '<input type="hidden" name="category_id" value="' . esc_attr($edit_item ? $edit_item->id : 0) . '">';
echo '<p><label for="kgv_cf_name"><strong>Name</strong></label><br>';
echo '<input type="text" id="kgv_cf_name" name="name" value="' . esc_attr($edit_item ? $edit_item->name : '') . '" class="regular-text" required></p>';
echo '<p><label for="kgv_cf_email"><strong>Weiterleitung an E-Mail</strong></label><br>';
echo '<textarea id="kgv_cf_email" name="recipient_email" rows="4" class="large-text" required>' . esc_textarea($edit_item ? $edit_item->recipient_email : '') . '</textarea>';
echo '<br><span style="color:#666;">Mehrere E-Mail-Adressen mit Komma trennen, z. B. info@seite.de, vorstand@seite.de</span></p>';
echo '<p><label for="kgv_cf_sort"><strong>Sortierung</strong></label><br>';
echo '<input type="number" id="kgv_cf_sort" name="sort_order" value="' . esc_attr($edit_item ? $edit_item->sort_order : 0) . '" class="small-text"></p>';
$checked = $edit_item ? ((int)$edit_item->is_active === 1) : true;
echo '<p><label><input type="checkbox" name="is_active" value="1" ' . checked($checked, true, false) . '> Aktiv</label></p>';
submit_button($edit_item ? 'Kategorie aktualisieren' : 'Kategorie speichern', 'primary', 'kgv_cf_save_category');
echo '</form>';
echo '</div>';
echo '<div>';
echo '<table class="widefat striped">';
echo '<thead><tr><th>Name</th><th>E-Mail</th><th>Sortierung</th><th>Status</th><th>Aktion</th></tr></thead><tbody>';
if (empty($categories)) {
echo '<tr><td colspan="5">Keine Kategorien vorhanden.</td></tr>';
} else {
foreach ($categories as $category) {
$edit_url = admin_url('admin.php?page=kgv-contact-form-categories&edit=' . absint($category->id));
$delete_url = wp_nonce_url(
admin_url('admin.php?page=kgv-contact-form-categories&kgv_cf_delete_category=' . absint($category->id)),
'kgv_cf_delete_category'
);
echo '<tr>';
echo '<td>' . esc_html($category->name) . '</td>';
echo '<td>' . esc_html($category->recipient_email) . '</td>';
echo '<td>' . esc_html($category->sort_order) . '</td>';
echo '<td>' . ((int)$category->is_active === 1 ? 'Aktiv' : 'Inaktiv') . '</td>';
echo '<td><a href="' . esc_url($edit_url) . '">Bearbeiten</a> | <a href="' . esc_url($delete_url) . '" onclick="return confirm(\'Kategorie wirklich löschen?\')">Löschen</a></td>';
echo '</tr>';
}
}
echo '</tbody></table>';
echo '</div>';
echo '</div>';
echo '<div style="margin-top:24px; padding:16px; background:#fff; border:1px solid #ddd;">';
echo '<h2 style="margin-top:0;">Zugriff für Mitarbeiter / Autoren</h2>';
echo '<p>Dieses Plugin gibt automatisch folgenden Rollen Leserechte auf die Kontaktanfragen:</p>';
echo '<ul style="list-style:disc; padding-left:18px;">';
echo '<li>Administrator</li>';
echo '<li>Redakteur</li>';
echo '<li>Autor</li>';
echo '<li>Mitarbeiter</li>';
echo '</ul>';
echo '<p><strong>Kategorien verwalten</strong> bleibt absichtlich nur für Administratoren freigeschaltet.</p>';
echo '</div>';
echo '<p style="margin-top:20px;"><strong>Shortcode:</strong> <code>[kgv_contact_form]</code></p>';
echo '</div>';
}