From b3e51f24a20811b5659cdec9824e892a873a9b9a Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 22 Dec 2025 10:26:33 +0000 Subject: [PATCH] =?UTF-8?q?Dateien=20nach=20=E2=80=9Ed2s-askki=E2=80=9C=20?= =?UTF-8?q?hochladen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- d2s-askki/d2s-askki.php | 646 ++++++++++++++++++++++++++++++++++++++++ d2s-askki/style.css | 70 +++++ 2 files changed, 716 insertions(+) create mode 100644 d2s-askki/d2s-askki.php create mode 100644 d2s-askki/style.css diff --git a/d2s-askki/d2s-askki.php b/d2s-askki/d2s-askki.php new file mode 100644 index 0000000..571fccc --- /dev/null +++ b/d2s-askki/d2s-askki.php @@ -0,0 +1,646 @@ + '#10a37f', + 'gpt_fg' => '#ffffff', + 'grok_bg' => '#000000', + 'grok_fg' => '#ffffff', + 'pplx_bg' => '#1557ff', + 'pplx_fg' => '#ffffff', + + 'radius' => '999px', + 'pad_y' => '0.65rem', + 'pad_x' => '1rem', + 'gap' => '0.5rem', + 'shadow' => '0 6px 20px rgba(0,0,0,.12)', + 'shadow_h' => '0 8px 26px rgba(0,0,0,.18)', + 'scale_h' => '1.02', + ]; +} + +/* ========================= + * Option getters + * ========================= */ + +function d2s_ki_get_prompt_prefix(): string { + $v = get_option(D2S_KI_OPT_PROMPT, d2s_ki_default_prompt_prefix()); + $v = is_string($v) ? trim(wp_strip_all_tags($v)) : ''; + return $v !== '' ? $v : d2s_ki_default_prompt_prefix(); +} + +function d2s_ki_get_hint_text(): string { + $v = get_option(D2S_KI_OPT_HINT, d2s_ki_default_hint_text()); + $v = is_string($v) ? trim(wp_strip_all_tags($v)) : ''; + return $v !== '' ? $v : d2s_ki_default_hint_text(); +} + +function d2s_ki_get_excluded_cat_ids(): array { + $v = get_option(D2S_KI_OPT_EXCL_CATS, []); + if (!is_array($v)) return []; + return array_values(array_filter(array_map('intval', $v), fn($id) => $id > 0)); +} + +function d2s_ki_get_mode(): string { + $v = get_option(D2S_KI_OPT_MODE, d2s_ki_default_mode()); + $allowed = ['auto','shortcode','both']; + return in_array($v, $allowed, true) ? $v : d2s_ki_default_mode(); +} + +function d2s_ki_get_post_types(): array { + $v = get_option(D2S_KI_OPT_POST_TYPES, d2s_ki_default_post_types()); + if (!is_array($v)) $v = d2s_ki_default_post_types(); + $v = array_values(array_filter(array_map('sanitize_key', $v))); + + $existing = get_post_types(['public' => true], 'names'); + $v = array_values(array_filter($v, fn($pt) => isset($existing[$pt]))); + return $v ?: d2s_ki_default_post_types(); +} + +function d2s_ki_get_style(): array { + $v = get_option(D2S_KI_OPT_STYLE, d2s_ki_default_style()); + if (!is_array($v)) $v = []; + return array_merge(d2s_ki_default_style(), $v); +} + +/* ========================= + * Logic: Should display? + * ========================= */ + +function d2s_ki_is_allowed_on_post(int $post_id, string $context = 'auto'): bool { + if (!$post_id) return false; + + $pt = get_post_type($post_id); + if (!$pt) return false; + + $allowed_pts = d2s_ki_get_post_types(); + if (!in_array($pt, $allowed_pts, true)) return false; + + if ($pt === 'post') { + $excluded = d2s_ki_get_excluded_cat_ids(); + if ($excluded && has_category($excluded, $post_id)) return false; + } + + $mode = d2s_ki_get_mode(); + if ($context === 'auto' && !in_array($mode, ['auto','both'], true)) return false; + if ($context === 'shortcode' && !in_array($mode, ['shortcode','both'], true)) return false; + + return true; +} + +/* ========================= + * Prompt + Providers + * ========================= */ + +/** + * Prompt bauen: + * - Promptprefix + * - LEERZEILE + * - URL + * + * Vorteil: immer sauber getrennt (kein "…?https://") + */ +function d2s_ki_build_prompt(string $post_url): string { + $prefix = trim(d2s_ki_get_prompt_prefix()); + + // Falls jemand im Prefix am Ende ein Satzzeichen ohne Leerzeichen hat: egal, wir trennen fix. + // 2 Zeilen sind in Chat-UIs super lesbar. + return $prefix . "\n\n" . $post_url; +} + +function d2s_ki_providers(string $prompt): array { + $q = http_build_query(['q' => $prompt], '', '&', PHP_QUERY_RFC3986); + + return [ + [ + 'key' => 'chatgpt', + 'name' => 'Mit ChatGPT erklären lassen', + 'href' => 'https://chatgpt.com/?' . $q, + 'title' => 'Öffnet ChatGPT mit vorbefüllter Nachricht', + 'icon' => '💬', + ], + [ + 'key' => 'grok', + 'name' => 'Mit Grok erklären lassen', + 'href' => 'https://grok.com/?' . $q, + 'title' => 'Öffnet Grok mit vorbefüllter Nachricht', + 'icon' => '🧠', + ], + [ + 'key' => 'perplexity', + 'name' => 'Mit Perplexity erklären lassen', + 'href' => 'https://www.perplexity.ai/search?' . $q, + 'title' => 'Öffnet Perplexity mit vorbefüllter Anfrage', + 'icon' => '🔎', + ], + ]; +} + +/* ========================= + * Rendering + * ========================= */ + +function d2s_ki_render_markup(int $post_id): string { + $post_url = get_permalink($post_id); + $prompt = d2s_ki_build_prompt($post_url); + $links = d2s_ki_providers($prompt); + $hint = d2s_ki_get_hint_text(); + + ob_start(); ?> +
+ + + + + + + + + + +
+ $s['gpt_bg'], + '--d2s-gpt-fg' => $s['gpt_fg'], + '--d2s-grok-bg' => $s['grok_bg'], + '--d2s-grok-fg' => $s['grok_fg'], + '--d2s-pplx-bg' => $s['pplx_bg'], + '--d2s-pplx-fg' => $s['pplx_fg'], + '--d2s-radius' => $s['radius'], + '--d2s-pad-y' => $s['pad_y'], + '--d2s-pad-x' => $s['pad_x'], + '--d2s-gap' => $s['gap'], + '--d2s-shadow' => $s['shadow'], + '--d2s-shadow-h' => $s['shadow_h'], + '--d2s-scale-h' => $s['scale_h'], + ]; + + $css = ":root{\n"; + foreach ($vars as $k => $v) { + if ($v === '' || $v === null) continue; + $css .= " {$k}: {$v};\n"; + } + $css .= "}\n"; + + wp_add_inline_style('d2s-ki-buttons', $css); +} +add_action('wp_enqueue_scripts', 'd2s_ki_enqueue_assets'); + +/* ========================= + * Admin Settings Page + * ========================= */ + +function d2s_ki_admin_menu() { + add_options_page( + 'D2S KI-Erklärbuttons', + 'D2S KI-Erklärbuttons', + 'manage_options', + 'd2s-ki-buttons', + 'd2s_ki_admin_page_render' + ); +} +add_action('admin_menu', 'd2s_ki_admin_menu'); + +function d2s_ki_admin_init() { + + register_setting('d2s_ki_buttons_settings', D2S_KI_OPT_PROMPT, [ + 'type' => 'string', + 'sanitize_callback' => 'd2s_ki_sanitize_textline_required', + 'default' => d2s_ki_default_prompt_prefix(), + ]); + + register_setting('d2s_ki_buttons_settings', D2S_KI_OPT_HINT, [ + 'type' => 'string', + 'sanitize_callback' => 'd2s_ki_sanitize_textline_required', + 'default' => d2s_ki_default_hint_text(), + ]); + + register_setting('d2s_ki_buttons_settings', D2S_KI_OPT_EXCL_CATS, [ + 'type' => 'array', + 'sanitize_callback' => 'd2s_ki_sanitize_excluded_categories', + 'default' => [], + ]); + + register_setting('d2s_ki_buttons_settings', D2S_KI_OPT_MODE, [ + 'type' => 'string', + 'sanitize_callback' => 'd2s_ki_sanitize_mode', + 'default' => d2s_ki_default_mode(), + ]); + + register_setting('d2s_ki_buttons_settings', D2S_KI_OPT_POST_TYPES, [ + 'type' => 'array', + 'sanitize_callback' => 'd2s_ki_sanitize_post_types', + 'default' => d2s_ki_default_post_types(), + ]); + + register_setting('d2s_ki_buttons_settings', D2S_KI_OPT_STYLE, [ + 'type' => 'array', + 'sanitize_callback' => 'd2s_ki_sanitize_style', + 'default' => d2s_ki_default_style(), + ]); + + add_settings_section( + 'd2s_ki_main', + 'Allgemein', + '__return_false', + 'd2s-ki-buttons' + ); + + add_settings_field( + 'd2s_ki_prompt_prefix', + 'Prompt-Text (Prefix)', + 'd2s_ki_field_prompt_prefix', + 'd2s-ki-buttons', + 'd2s_ki_main' + ); + + add_settings_field( + 'd2s_ki_hint_text', + 'Hint-Text unter den Buttons', + 'd2s_ki_field_hint_text', + 'd2s-ki-buttons', + 'd2s_ki_main' + ); + + add_settings_field( + 'd2s_ki_mode', + 'Einfüge-Modus', + 'd2s_ki_field_mode', + 'd2s-ki-buttons', + 'd2s_ki_main' + ); + + add_settings_field( + 'd2s_ki_post_types', + 'Anzeigen auf', + 'd2s_ki_field_post_types', + 'd2s-ki-buttons', + 'd2s_ki_main' + ); + + add_settings_field( + 'd2s_ki_excluded_cats', + 'Kategorien ausschließen (nur Posts)', + 'd2s_ki_field_excluded_categories', + 'd2s-ki-buttons', + 'd2s_ki_main' + ); + + add_settings_section( + 'd2s_ki_style', + 'Styling', + '__return_false', + 'd2s-ki-buttons' + ); + + add_settings_field( + 'd2s_ki_style_colors', + 'Farben', + 'd2s_ki_field_style_colors', + 'd2s-ki-buttons', + 'd2s_ki_style' + ); + + add_settings_field( + 'd2s_ki_style_layout', + 'Layout', + 'd2s_ki_field_style_layout', + 'd2s-ki-buttons', + 'd2s_ki_style' + ); + + add_settings_field( + 'd2s_ki_style_fx', + 'Effekte', + 'd2s_ki_field_style_fx', + 'd2s-ki-buttons', + 'd2s_ki_style' + ); +} +add_action('admin_init', 'd2s_ki_admin_init'); + +/* ===== Sanitizers ===== */ + +function d2s_ki_sanitize_textline_required($v): string { + if (!is_string($v)) $v = ''; + $v = trim(wp_strip_all_tags($v)); + return $v !== '' ? $v : '—'; +} + +function d2s_ki_sanitize_excluded_categories($v): array { + if (!is_array($v)) return []; + return array_values(array_unique(array_filter(array_map('intval', $v), fn($id) => $id > 0))); +} + +function d2s_ki_sanitize_mode($v): string { + $allowed = ['auto','shortcode','both']; + return in_array($v, $allowed, true) ? $v : d2s_ki_default_mode(); +} + +function d2s_ki_sanitize_post_types($v): array { + if (!is_array($v)) $v = []; + $v = array_values(array_filter(array_map('sanitize_key', $v))); + + $existing = get_post_types(['public' => true], 'names'); + $v = array_values(array_filter($v, fn($pt) => isset($existing[$pt]))); + + return $v ?: d2s_ki_default_post_types(); +} + +function d2s_ki_sanitize_hex_color_or_empty($v): string { + $v = is_string($v) ? trim($v) : ''; + if ($v === '') return ''; + $c = sanitize_hex_color($v); + return $c ? $c : ''; +} + +function d2s_ki_sanitize_css_len($v, string $fallback): string { + $v = is_string($v) ? trim($v) : ''; + if ($v === '') return $fallback; + if (preg_match('/^[0-9.\s%a-zA-Z()+,-]+$/', $v)) return $v; + return $fallback; +} + +function d2s_ki_sanitize_css_value($v, string $fallback): string { + $v = is_string($v) ? trim($v) : ''; + if ($v === '') return $fallback; + if (preg_match('/^[0-9.\s%a-zA-Z()+,\/-]+$/', $v)) return $v; + return $fallback; +} + +function d2s_ki_sanitize_style($v): array { + $d = d2s_ki_default_style(); + if (!is_array($v)) $v = []; + + return [ + 'gpt_bg' => d2s_ki_sanitize_hex_color_or_empty($v['gpt_bg'] ?? $d['gpt_bg']) ?: $d['gpt_bg'], + 'gpt_fg' => d2s_ki_sanitize_hex_color_or_empty($v['gpt_fg'] ?? $d['gpt_fg']) ?: $d['gpt_fg'], + 'grok_bg' => d2s_ki_sanitize_hex_color_or_empty($v['grok_bg'] ?? $d['grok_bg']) ?: $d['grok_bg'], + 'grok_fg' => d2s_ki_sanitize_hex_color_or_empty($v['grok_fg'] ?? $d['grok_fg']) ?: $d['grok_fg'], + 'pplx_bg' => d2s_ki_sanitize_hex_color_or_empty($v['pplx_bg'] ?? $d['pplx_bg']) ?: $d['pplx_bg'], + 'pplx_fg' => d2s_ki_sanitize_hex_color_or_empty($v['pplx_fg'] ?? $d['pplx_fg']) ?: $d['pplx_fg'], + + 'radius' => d2s_ki_sanitize_css_len($v['radius'] ?? '', $d['radius']), + 'pad_y' => d2s_ki_sanitize_css_len($v['pad_y'] ?? '', $d['pad_y']), + 'pad_x' => d2s_ki_sanitize_css_len($v['pad_x'] ?? '', $d['pad_x']), + 'gap' => d2s_ki_sanitize_css_len($v['gap'] ?? '', $d['gap']), + + 'shadow' => d2s_ki_sanitize_css_value($v['shadow'] ?? '', $d['shadow']), + 'shadow_h' => d2s_ki_sanitize_css_value($v['shadow_h'] ?? '', $d['shadow_h']), + 'scale_h' => d2s_ki_sanitize_css_value($v['scale_h'] ?? '', $d['scale_h']), + ]; +} + +/* ===== Admin Fields ===== */ + +function d2s_ki_field_prompt_prefix() { + $val = d2s_ki_get_prompt_prefix(); + echo ''; + echo '

Wird an die KI übergeben. Die URL wird automatisch in einer neuen Zeile angehängt.

'; +} + +function d2s_ki_field_hint_text() { + $val = d2s_ki_get_hint_text(); + echo ''; + echo '

Text unter den Buttons. Leer lassen (oder „—“) um ihn praktisch auszublenden.

'; +} + +function d2s_ki_field_mode() { + $mode = d2s_ki_get_mode(); + $opts = [ + 'auto' => 'Automatisch unter Content einfügen', + 'shortcode' => 'Nur per Shortcode (du platzierst es selbst)', + 'both' => 'Beides (Auto + Shortcode)', + ]; + echo ''; + echo '

Shortcode: [d2s_ki_buttons]

'; +} + +function d2s_ki_field_post_types() { + $selected = d2s_ki_get_post_types(); + $pts = get_post_types(['public' => true], 'objects'); + + echo '
'; + foreach ($pts as $pt) { + if ($pt->name === 'attachment') continue; + $checked = in_array($pt->name, $selected, true) ? 'checked' : ''; + echo ''; + } + echo '
'; + echo '

Für Seiten: page aktivieren.

'; +} + +function d2s_ki_field_excluded_categories() { + $selected = d2s_ki_get_excluded_cat_ids(); + $cats = get_categories(['hide_empty' => false]); + + if (!$cats) { + echo 'Keine Kategorien gefunden.'; + return; + } + + echo '
'; + echo '

Häkchen setzen = Buttons werden in dieser Kategorie nicht angezeigt (nur Posts).

'; + + foreach ($cats as $cat) { + $id = (int)$cat->term_id; + $checked = in_array($id, $selected, true) ? 'checked' : ''; + echo ''; + } + echo '
'; +} + +function d2s_ki_field_style_colors() { + $s = d2s_ki_get_style(); + $name = D2S_KI_OPT_STYLE; + + echo ''; + + $rows = [ + ['ChatGPT Hintergrund', "{$name}[gpt_bg]", $s['gpt_bg']], + ['ChatGPT Text', "{$name}[gpt_fg]", $s['gpt_fg']], + ['Grok Hintergrund', "{$name}[grok_bg]", $s['grok_bg']], + ['Grok Text', "{$name}[grok_fg]", $s['grok_fg']], + ['Perplexity Hintergrund', "{$name}[pplx_bg]", $s['pplx_bg']], + ['Perplexity Text', "{$name}[pplx_fg]", $s['pplx_fg']], + ]; + + foreach ($rows as [$label, $field, $val]) { + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo '
'.esc_html($label).' '; + echo ''; + echo '
'; +} + +function d2s_ki_field_style_layout() { + $s = d2s_ki_get_style(); + $name = D2S_KI_OPT_STYLE; + + echo ''; + + $rows = [ + ['Border-Radius', "{$name}[radius]", $s['radius'], 'z.B. 999px / 14px'], + ['Padding Y', "{$name}[pad_y]", $s['pad_y'], 'z.B. 0.65rem'], + ['Padding X', "{$name}[pad_x]", $s['pad_x'], 'z.B. 1rem'], + ['Gap', "{$name}[gap]", $s['gap'], 'z.B. 0.5rem'], + ]; + + foreach ($rows as [$label, $field, $val, $hint]) { + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo '
'.esc_html($label).'
'; +} + +function d2s_ki_field_style_fx() { + $s = d2s_ki_get_style(); + $name = D2S_KI_OPT_STYLE; + + echo ''; + + $rows = [ + ['Shadow', "{$name}[shadow]", $s['shadow'], 'z.B. 0 6px 20px rgba(0,0,0,.12)'], + ['Shadow Hover', "{$name}[shadow_h]", $s['shadow_h'], 'z.B. 0 8px 26px rgba(0,0,0,.18)'], + ['Scale Hover', "{$name}[scale_h]", $s['scale_h'], 'z.B. 1.02'], + ]; + + foreach ($rows as [$label, $field, $val, $hint]) { + echo ''; + echo ''; + echo ''; + echo ''; + } + + echo '
'.esc_html($label).'
'; +} + +function d2s_ki_admin_page_render() { + if (!current_user_can('manage_options')) return; + + echo '
'; + echo '

D2S KI-Erklärbuttons

'; + echo '
'; + + settings_fields('d2s_ki_buttons_settings'); + do_settings_sections('d2s-ki-buttons'); + submit_button('Speichern'); + + echo '
'; + + echo '
'; + echo '

Info

'; + echo '

Shortcode: [d2s_ki_buttons]

'; + echo '

Prompt-Beispiel (Trennung):
' . esc_html(d2s_ki_get_prompt_prefix()) . "\\n\\nhttps://example.com/dein-artikel/

"; + echo '
'; +} \ No newline at end of file diff --git a/d2s-askki/style.css b/d2s-askki/style.css new file mode 100644 index 0000000..aa74f79 --- /dev/null +++ b/d2s-askki/style.css @@ -0,0 +1,70 @@ +/* Donau2Space – KI-Erklärbuttons + Styling via CSS-Variablen (werden im Admin gesetzt) */ + +:root { + --d2s-gpt-bg: #10a37f; + --d2s-gpt-fg: #ffffff; + + --d2s-grok-bg: #000000; + --d2s-grok-fg: #ffffff; + + --d2s-pplx-bg: #1557ff; + --d2s-pplx-fg: #ffffff; + + --d2s-radius: 999px; + --d2s-pad-y: 0.65rem; + --d2s-pad-x: 1rem; + --d2s-gap: 0.5rem; + + --d2s-shadow: 0 6px 20px rgba(0,0,0,.12); + --d2s-shadow-h: 0 8px 26px rgba(0,0,0,.18); + --d2s-scale-h: 1.02; +} + +.d2s-ki-buttons { + margin-top: 1.25rem; + display: flex; + flex-wrap: wrap; + gap: var(--d2s-gap); + align-items: center; + font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif; +} + +.d2s-ki-btn { + display: inline-flex; + align-items: center; + gap: 0.55rem; + padding: var(--d2s-pad-y) var(--d2s-pad-x); + border-radius: var(--d2s-radius); + text-decoration: none; + font-weight: 700; + box-shadow: var(--d2s-shadow); + transition: transform .12s ease, box-shadow .12s ease, opacity .12s ease; + line-height: 1; +} + +.d2s-ki-btn:focus { + outline: 2px solid rgba(0,0,0,.25); + outline-offset: 2px; +} + +.d2s-ki-btn:hover { + transform: scale(var(--d2s-scale-h)); + box-shadow: var(--d2s-shadow-h); +} + +.d2s-ki-chatgpt { background: var(--d2s-gpt-bg); color: var(--d2s-gpt-fg); } +.d2s-ki-grok { background: var(--d2s-grok-bg); color: var(--d2s-grok-fg); } +.d2s-ki-perplexity { background: var(--d2s-pplx-bg); color: var(--d2s-pplx-fg); } + +.d2s-ki-icon { font-size: 1.05em; } +.d2s-ki-text { white-space: nowrap; } + +.d2s-ki-hint { + opacity: 0.7; + margin-left: 0.25rem; +} + +@media (prefers-color-scheme: dark) { + .d2s-ki-hint { opacity: 0.85; } +} \ No newline at end of file