From b8647f468ae45c6432481660ba6054b7fd950b72 Mon Sep 17 00:00:00 2001 From: Ronny Grobel Date: Mon, 13 Apr 2026 20:56:18 +0200 Subject: [PATCH] Initial plugin commit --- .gitignore | 3 + README.txt | 10 + assets/kgv-contact-form.css | 31 ++ kgv-contact-form-pro.php | 591 ++++++++++++++++++++++++++++++++++++ 4 files changed, 635 insertions(+) create mode 100644 .gitignore create mode 100755 README.txt create mode 100755 assets/kgv-contact-form.css create mode 100755 kgv-contact-form-pro.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b6e650 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +Thumbs.db +*.zip diff --git a/README.txt b/README.txt new file mode 100755 index 0000000..27f6b5c --- /dev/null +++ b/README.txt @@ -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] diff --git a/assets/kgv-contact-form.css b/assets/kgv-contact-form.css new file mode 100755 index 0000000..2777dc3 --- /dev/null +++ b/assets/kgv-contact-form.css @@ -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}} diff --git a/kgv-contact-form-pro.php b/kgv-contact-form-pro.php new file mode 100755 index 0000000..08d4a35 --- /dev/null +++ b/kgv-contact-form-pro.php @@ -0,0 +1,591 @@ +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 '
Nachricht erfolgreich gesendet.
'; + } + + if (isset($_GET['kgv_error']) && $_GET['kgv_error'] === '1') { + echo '
Bitte alle Pflichtfelder korrekt ausfüllen.
'; + } + ?> +
+
+

+ + +

+ +

+ + +

+
+ +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ +

+ + + + + +

+ +

+
+ 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 '
'; + echo '

Kontaktformular – Nachrichten

'; + + if (isset($_GET['deleted'])) { + echo '

Nachricht gelöscht.

'; + } + if (isset($_GET['updated'])) { + echo '

Status aktualisiert.

'; + } + + echo '

Ungelesene Nachrichten: ' . esc_html($unread_count) . '

'; + + if (empty($messages)) { + echo '

Noch keine Nachrichten vorhanden.

'; + echo '
'; + return; + } + + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + + 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) + ? 'Gelesen' + : 'Ungelesen'; + + echo 'is_read === 0) ? ' style="font-weight:600;"' : '') . '>'; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo '
StatusDatumKategorieVonBetreffDatenschutzWeitergeleitet anMailstatusAktion
' . $status_badge . '' . esc_html(mysql2date('d.m.Y H:i', $msg->created_at)) . '' . esc_html($msg->category_name) . '' . esc_html($msg->sender_name) . '
' . esc_html($msg->sender_email) . '
' . esc_html($msg->subject) . '
' . nl2br(esc_html(wp_trim_words($msg->message, 24, '…'))) . '
' . (((int)$msg->privacy_accepted === 1) ? 'Ja' : 'Nein') . '' . esc_html($msg->recipient_email) . '' . (((int)$msg->is_sent === 1) ? 'Gesendet' : 'Fehler') . ''; + echo '' . (((int)$msg->is_read === 1) ? 'Als ungelesen markieren' : 'Als gelesen markieren') . ''; + echo ' | Anzeigen'; + echo ' | Löschen'; + echo '
'; + echo '

Nachricht:

'; + echo '
' . nl2br(esc_html($msg->message)) . '
'; + echo '
'; + echo '
'; + echo ''; +} + +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 '
'; + echo '

Kontaktformular – Kategorien

'; + + if (isset($_GET['saved'])) { + echo '

Kategorie gespeichert.

'; + } + if (isset($_GET['deleted'])) { + echo '

Kategorie gelöscht.

'; + } + if (isset($_GET['error'])) { + echo '

Bitte einen Namen und mindestens eine gültige E-Mail-Adresse eintragen.

'; + } + + echo '
'; + + echo '
'; + echo '

' . ($edit_item ? 'Kategorie bearbeiten' : 'Neue Kategorie') . '

'; + echo '
'; + wp_nonce_field('kgv_cf_save_category'); + echo ''; + + echo '


'; + echo '

'; + + echo '


'; + echo ''; + echo '
Mehrere E-Mail-Adressen mit Komma trennen, z. B. info@seite.de, vorstand@seite.de

'; + + echo '


'; + echo '

'; + + $checked = $edit_item ? ((int)$edit_item->is_active === 1) : true; + echo '

'; + + submit_button($edit_item ? 'Kategorie aktualisieren' : 'Kategorie speichern', 'primary', 'kgv_cf_save_category'); + echo '
'; + echo '
'; + + echo '
'; + echo ''; + echo ''; + + if (empty($categories)) { + echo ''; + } 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 ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + } + + echo '
NameE-MailSortierungStatusAktion
Keine Kategorien vorhanden.
' . esc_html($category->name) . '' . esc_html($category->recipient_email) . '' . esc_html($category->sort_order) . '' . ((int)$category->is_active === 1 ? 'Aktiv' : 'Inaktiv') . 'Bearbeiten | Löschen
'; + echo '
'; + echo '
'; + + echo '
'; + echo '

Zugriff für Mitarbeiter / Autoren

'; + echo '

Dieses Plugin gibt automatisch folgenden Rollen Leserechte auf die Kontaktanfragen:

'; + echo '
    '; + echo '
  • Administrator
  • '; + echo '
  • Redakteur
  • '; + echo '
  • Autor
  • '; + echo '
  • Mitarbeiter
  • '; + echo '
'; + echo '

Kategorien verwalten bleibt absichtlich nur für Administratoren freigeschaltet.

'; + echo '
'; + + echo '

Shortcode: [kgv_contact_form]

'; + echo '
'; +}