commit 684477df4b5baa4ffba2ec40d27dd85b65cebf82 Author: Ronny Grobel Date: Sun Apr 12 22:22:59 2026 +0200 Initial plugin commit 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/assets/css/admin.css b/assets/css/admin.css new file mode 100755 index 0000000..b071379 --- /dev/null +++ b/assets/css/admin.css @@ -0,0 +1,6 @@ +.kgv-admin-wrap .kgv-admin-card{background:#fff;border:1px solid #dcdcde;border-radius:10px;padding:20px;margin-top:16px} +.kgv-admin-header{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap} +.kgv-form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px}.kgv-form-field{display:flex;flex-direction:column;gap:6px}.kgv-form-field-full{grid-column:1/-1} +.kgv-form-field input,.kgv-form-field select,.kgv-form-field textarea{width:100%} +.kgv-admin-actions{display:flex;gap:10px;margin-top:16px} +@media (max-width:782px){.kgv-form-grid{grid-template-columns:1fr}} diff --git a/assets/css/front.css b/assets/css/front.css new file mode 100755 index 0000000..40beaa3 --- /dev/null +++ b/assets/css/front.css @@ -0,0 +1,88 @@ +.kgv-termine-wrap{display:grid;gap:18px} +.kgv-termin-card,.kgv-termin-single{background:#fff;border:1px solid #e5e7eb;border-radius:14px;overflow:hidden;box-shadow:0 8px 24px rgba(0,0,0,.05)} +.kgv-termin-card-inner,.kgv-termin-single-inner{padding:20px} +.kgv-termin-datebox{display:inline-block;margin-bottom:12px;font-size:14px;font-weight:600;color:#6b7280} +.kgv-termin-title{margin:0 0 10px;font-size:1.3rem;line-height:1.2} +.kgv-termin-meta{display:flex;gap:14px;flex-wrap:wrap;color:#4b5563;font-size:14px;margin-bottom:12px} +.kgv-termin-excerpt,.kgv-termin-content{color:#374151;line-height:1.65} +.kgv-termin-actions{margin-top:16px}.kgv-termin-btn,.kgv-termin-back{display:inline-block;padding:10px 14px;border-radius:10px;text-decoration:none;background:#e5e7eb;color:#111827} +.kgv-termin-single-hero{padding:20px;background:#f3f4f6;border-bottom:1px solid #e5e7eb}.kgv-termin-single-hero h2{margin:0 0 10px} +.kgv-termin-back{margin-bottom:14px} +.kgv-termine-empty,.kgv-termine-notice{padding:16px;border:1px solid #e5e7eb;border-radius:12px;background:#fff} + +.kgv-termin-single-hero { + position: relative; +} + +.kgv-termin-single-hero .kgv-termin-datebox { + position: absolute; + top: 20px; + right: 20px; + + font-size: 16px; + font-weight: 600; + + background: #fff; + border: 1px solid #e5e7eb; + border-radius: 10px; + padding: 6px 10px; + + color: #111827; +} +.kgv-termin-single-hero .kgv-termin-datebox { + position: absolute; + top: 20px; + right: 20px; + + font-size: 16px; + font-weight: 600; + + background: none; + border: none; + padding: 0; + + color: #6b7280; +} + +.kgv-sidebar-box{ + padding:12px; +} + +.kgv-sidebar-title{ + margin:0 0 8px; +} + +.kgv-sidebar-list{ + display:grid; + gap:2px; +} + +.kgv-sidebar-item{ + display:grid; + grid-template-columns:80px 1fr; + align-items:center; + gap:8px; + text-decoration:none; + padding:3px 0; + border-radius:0; +} + +.kgv-sidebar-item:hover{ + background:none; +} + +.kgv-sidebar-date{ + font-size:12px; + font-weight:600; + color:#6b7280; + white-space:nowrap; +} + +.kgv-sidebar-text{ + font-size:13px; + color:#111827; + line-height:1.2; + white-space:nowrap; + overflow:hidden; + text-overflow:ellipsis; +} \ No newline at end of file diff --git a/includes/class-kgv-termine-plugin.php b/includes/class-kgv-termine-plugin.php new file mode 100755 index 0000000..dbf202b --- /dev/null +++ b/includes/class-kgv-termine-plugin.php @@ -0,0 +1,520 @@ +table_name = $wpdb->prefix . KGV_TERMINE_TABLE; + + register_activation_hook(KGV_TERMINE_FILE, array($this, 'activate')); + + add_action('admin_menu', array($this, 'register_admin_menu')); + add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); + add_action('wp_enqueue_scripts', array($this, 'enqueue_front_assets')); + add_action('init', array($this, 'register_shortcodes')); + add_action('admin_post_kgv_save_termin', array($this, 'handle_save_termin')); + add_action('admin_post_kgv_delete_termin', array($this, 'handle_delete_termin')); + add_filter('query_vars', array($this, 'register_query_vars')); + } + + public function activate() { + $this->create_table(); + } + + private function create_table() { + global $wpdb; + + require_once ABSPATH . 'wp-admin/includes/upgrade.php'; + + $charset_collate = $wpdb->get_charset_collate(); + $sql = "CREATE TABLE {$this->table_name} ( + id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + title VARCHAR(255) NOT NULL, + event_date DATETIME NOT NULL, + owner VARCHAR(255) NULL, + location VARCHAR(255) NULL, + summary TEXT NULL, + description LONGTEXT NULL, + is_published TINYINT(1) NOT NULL DEFAULT 1, + created_at DATETIME NOT NULL, + updated_at DATETIME NOT NULL, + PRIMARY KEY (id), + KEY event_date (event_date), + KEY is_published (is_published) + ) {$charset_collate};"; + + dbDelta($sql); + } + + public function enqueue_admin_assets($hook) { + if (strpos((string) $hook, 'kgv-termine') === false) { + return; + } + + wp_enqueue_style( + 'kgv-termine-admin-style', + KGV_TERMINE_URL . 'assets/css/admin.css', + array(), + '1.0.2' + ); + } + + public function enqueue_front_assets() { + wp_enqueue_style( + 'kgv-termine-front-style', + KGV_TERMINE_URL . 'assets/css/front.css', + array(), + '1.0.2' + ); + } + + public function register_query_vars($vars) { + $vars[] = 'kgv_termin'; + return $vars; + } + + public function register_admin_menu() { + add_menu_page( + 'Termine', + 'Termine', + 'manage_options', + 'kgv-termine', + array($this, 'render_list_page'), + 'dashicons-calendar-alt', + 25 + ); + + add_submenu_page( + 'kgv-termine', + 'Alle Termine', + 'Alle Termine', + 'manage_options', + 'kgv-termine', + array($this, 'render_list_page') + ); + + add_submenu_page( + 'kgv-termine', + 'Neuer Termin', + 'Neuer Termin', + 'manage_options', + 'kgv-termine-new', + array($this, 'render_form_page') + ); + } + + public function register_shortcodes() { + add_shortcode('kgv_termine', array($this, 'render_termine_shortcode')); + add_shortcode('kgv_termin_detail', array($this, 'render_detail_shortcode')); + add_shortcode('kgv_termine_sidebar', array($this, 'render_sidebar_shortcode')); + } + + public function handle_save_termin() { + if (!current_user_can('manage_options')) { + wp_die('Keine Berechtigung.'); + } + + check_admin_referer('kgv_save_termin'); + $this->save_termin_from_request(); + } + + public function handle_delete_termin() { + if (!current_user_can('manage_options')) { + wp_die('Keine Berechtigung.'); + } + + $id = isset($_GET['termin_id']) ? absint($_GET['termin_id']) : 0; + + if (!$id || !isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'kgv_delete_termin_' . $id)) { + wp_safe_redirect(admin_url('admin.php?page=kgv-termine&message=delete_failed')); + exit; + } + + global $wpdb; + $wpdb->delete($this->table_name, array('id' => $id), array('%d')); + wp_safe_redirect(admin_url('admin.php?page=kgv-termine&message=deleted')); + exit; + } + + private function save_termin_from_request() { + global $wpdb; + + $id = isset($_POST['termin_id']) ? absint($_POST['termin_id']) : 0; + $title = sanitize_text_field(wp_unslash($_POST['title'] ?? '')); + $event_date = sanitize_text_field(wp_unslash($_POST['event_date'] ?? '')); + $owner = sanitize_text_field(wp_unslash($_POST['owner'] ?? '')); + $location = sanitize_text_field(wp_unslash($_POST['location'] ?? '')); + $summary = sanitize_textarea_field(wp_unslash($_POST['summary'] ?? '')); + $description = wp_kses_post(wp_unslash($_POST['description'] ?? '')); + $is_published = isset($_POST['is_published']) ? (int) wp_unslash($_POST['is_published']) : 1; + $is_published = $is_published === 1 ? 1 : 0; + + if ($title === '' || $event_date === '' || strtotime($event_date) === false) { + $target = $id ? admin_url('admin.php?page=kgv-termine-new&termin_id=' . $id . '&message=missing') : admin_url('admin.php?page=kgv-termine-new&message=missing'); + wp_safe_redirect($target); + exit; + } + + $mysql_date = date('Y-m-d H:i:s', strtotime($event_date)); + $now = current_time('mysql'); + + $data = array( + 'title' => $title, + 'event_date' => $mysql_date, + 'owner' => $owner, + 'location' => $location, + 'summary' => $summary, + 'description' => $description, + 'is_published' => $is_published, + 'updated_at' => $now, + ); + + $formats = array('%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s'); + + if ($id) { + $wpdb->update($this->table_name, $data, array('id' => $id), $formats, array('%d')); + wp_safe_redirect(admin_url('admin.php?page=kgv-termine&message=updated')); + exit; + } + + $data['created_at'] = $now; + $wpdb->insert($this->table_name, $data, array('%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s')); + + wp_safe_redirect(admin_url('admin.php?page=kgv-termine&message=created')); + exit; + } + + public function render_list_page() { + global $wpdb; + + $rows = $wpdb->get_results("SELECT * FROM {$this->table_name} ORDER BY event_date ASC, id ASC"); + $message = isset($_GET['message']) ? sanitize_text_field(wp_unslash($_GET['message'])) : ''; + ?> +
+
+

Termine

+ Neuen Termin anlegen +
+ + render_notice($message); ?> + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TitelDatumVerantwortlichOrtStatusAktionen
Noch keine Termine angelegt.
title); ?>event_date))); ?>owner ?: '—'); ?>location ?: '—'); ?>is_published === 1 ? 'Veröffentlicht' : 'Entwurf'; ?> + Bearbeiten + | + Löschen +
+
+
+ get_row($wpdb->prepare("SELECT * FROM {$this->table_name} WHERE id = %d", $id)); + } + + $title = $row ? $row->title : ''; + $event_date = $row ? date('Y-m-d\TH:i', strtotime($row->event_date)) : ''; + $owner = $row ? $row->owner : ''; + $location = $row ? $row->location : ''; + $summary = $row ? $row->summary : ''; + $description = $row ? $row->description : ''; + $is_published = $row ? (int) $row->is_published : 1; + ?> +
+
+

+
+ + render_notice($message); ?> + +
+ + + + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ +
+ + Abbrechen +
+
+
+ array('success', 'Termin wurde angelegt.'), + 'updated' => array('success', 'Termin wurde aktualisiert.'), + 'deleted' => array('success', 'Termin wurde gelöscht.'), + 'delete_failed' => array('error', 'Termin konnte nicht gelöscht werden.'), + 'missing' => array('error', 'Titel und Datum sind Pflichtfelder.'), + ); + + if (!isset($map[$message])) { + return; + } + + list($type, $text) = $map[$message]; + printf('

%2$s

', esc_attr($type), esc_html($text)); + } + + private function get_frontend_rows($limit = 0) { + global $wpdb; + + if ($limit > 0) { + return $wpdb->get_results($wpdb->prepare( + "SELECT * FROM {$this->table_name} WHERE is_published = 1 ORDER BY event_date ASC, id ASC LIMIT %d", + $limit + )); + } + + return $wpdb->get_results("SELECT * FROM {$this->table_name} WHERE is_published = 1 ORDER BY event_date ASC, id ASC"); + } + + private function get_row($id) { + global $wpdb; + return $wpdb->get_row($wpdb->prepare("SELECT * FROM {$this->table_name} WHERE id = %d", $id)); + } + + public function render_termine_shortcode($atts) { + $atts = shortcode_atts(array( + 'limit' => 0, + 'detail' => 1, + ), $atts, 'kgv_termine'); + + $detail_id = absint(get_query_var('kgv_termin')); + if (!$detail_id && isset($_GET['kgv_termin'])) { + $detail_id = absint(wp_unslash($_GET['kgv_termin'])); + } + + $base_url = $this->get_current_page_url(false); + + if ($detail_id && (int) $atts['detail'] === 1) { + return $this->render_detail_by_id($detail_id, $base_url); + } + + $rows = $this->get_frontend_rows(absint($atts['limit'])); + + if (empty($rows)) { + return '
Aktuell sind keine Termine verfügbar.
'; + } + + ob_start(); + echo '
'; + foreach ($rows as $row) { + $link = add_query_arg('kgv_termin', (int) $row->id, $base_url); + echo '
'; + echo '
'; + echo '
' . esc_html(wp_date('d.m.Y H:i', strtotime($row->event_date))) . '
'; + echo '

' . esc_html($row->title) . '

'; + echo '
'; + if (!empty($row->owner)) { + echo 'Verantwortlich: ' . esc_html($row->owner) . ''; + } + if (!empty($row->location)) { + echo 'Ort: ' . esc_html($row->location) . ''; + } + echo '
'; + if (!empty($row->summary)) { + echo '
' . esc_html($row->summary) . '
'; + } + echo ''; + echo '
'; + echo '
'; + } + echo '
'; + return ob_get_clean(); + } + + public function render_detail_shortcode($atts) { + $atts = shortcode_atts(array( + 'id' => 0, + ), $atts, 'kgv_termin_detail'); + + $id = absint($atts['id']); + if (!$id) { + return '
Keine Termin-ID übergeben.
'; + } + + return $this->render_detail_by_id($id, ''); + } + + private function render_detail_by_id($id, $back_url = '') { + $row = $this->get_row($id); + + if (!$row || !(int) $row->is_published) { + return '
Termin nicht gefunden.
'; + } + + ob_start(); + echo '
'; + echo '
'; + if ($back_url) { + echo '← Zurück zur Übersicht'; + } + echo '
' . esc_html(wp_date('d.m.Y H:i', strtotime($row->event_date))) . '
'; + echo '

' . esc_html($row->title) . '

'; + echo '
'; + if (!empty($row->owner)) { + echo 'Verantwortlich: ' . esc_html($row->owner) . ''; + } + if (!empty($row->location)) { + echo 'Ort: ' . esc_html($row->location) . ''; + } + echo '
'; + echo '
'; + echo '
'; + if (!empty($row->summary)) { + echo '

' . esc_html($row->summary) . '

'; + } + echo '
' . wpautop(wp_kses_post($row->description)) . '
'; + echo '
'; + echo '
'; + return ob_get_clean(); + } + + + +public function render_sidebar_shortcode($atts) { + $atts = shortcode_atts(array( + 'limit' => 5, + ), $atts, 'kgv_termine_sidebar'); + + $rows = $this->get_frontend_rows(max(1, min(20, absint($atts['limit'])))); + + if (empty($rows)) { + return '
Keine Termine
'; + } + + $base_url = $this->get_current_page_url(false); + + ob_start(); + echo '
'; + echo '

Nächste Termine

'; + echo '
'; + + foreach ($rows as $row) { + $link = add_query_arg('kgv_termin', (int) $row->id, $base_url); + + echo ''; + echo '' . esc_html(wp_date('d.m.Y', strtotime($row->event_date))) . ''; + echo '' . esc_html($row->title) . ''; + echo ''; + } + + echo '
'; + echo '
'; + + return ob_get_clean(); +} + private function get_current_page_url($with_query = true) { + $page_url = ''; + + if (is_singular() && get_queried_object_id()) { + $page_url = get_permalink(get_queried_object_id()); + } + + if (!$page_url) { + global $wp; + $page_url = home_url(add_query_arg(array(), $wp->request ? '/' . ltrim($wp->request, '/') . '/' : '/')); + } + + if (!$with_query) { + return remove_query_arg(array_keys($_GET), $page_url); + } + + return $page_url; + } +} diff --git a/kgv-termine-plugin.php b/kgv-termine-plugin.php new file mode 100755 index 0000000..336282d --- /dev/null +++ b/kgv-termine-plugin.php @@ -0,0 +1,24 @@ +