Initial plugin commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.zip
|
||||
22
assets/admin.css
Executable file
22
assets/admin.css
Executable file
@@ -0,0 +1,22 @@
|
||||
.post-type-kgv_faq #post-body-content .editor-post-title,
|
||||
.post-type-kgv_faq #titlediv #title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.post-type-kgv_faq .misc-pub-section.misc-pub-post-status,
|
||||
.post-type-kgv_faq .misc-pub-section.curtime {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.post-type-kgv_faq .column-kgv_faq_order {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.post-type-kgv_faq .column-kgv_faq_answer {
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
.taxonomy-kgv_faq_cat .wrap h1,
|
||||
.post-type-kgv_faq .wrap h1 {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
91
assets/front.css
Executable file
91
assets/front.css
Executable file
@@ -0,0 +1,91 @@
|
||||
.kgv-faq-wrapper {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.kgv-faq-filter {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.kgv-faq-filter select,
|
||||
.kgv-faq-search {
|
||||
min-width: 240px;
|
||||
max-width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid #d0d7de;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.kgv-faq-search-wrap {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.kgv-faq-list {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.kgv-faq-item {
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.kgv-faq-question {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.kgv-faq-toggle {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 16px 18px;
|
||||
background: #fff;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.kgv-faq-toggle:hover,
|
||||
.kgv-faq-toggle:focus {
|
||||
background: #f9fafb;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.kgv-faq-icon {
|
||||
flex: 0 0 auto;
|
||||
font-size: 22px;
|
||||
line-height: 1;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.kgv-faq-toggle.is-open .kgv-faq-icon {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.kgv-faq-answer {
|
||||
border-top: 1px solid #eef2f7;
|
||||
}
|
||||
|
||||
.kgv-faq-answer__inner {
|
||||
padding: 16px 18px 18px;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.kgv-faq-answer__inner > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.kgv-faq-answer__inner > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.kgv-faq-item.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
44
assets/front.js
Executable file
44
assets/front.js
Executable file
@@ -0,0 +1,44 @@
|
||||
document.addEventListener('click', function (event) {
|
||||
const button = event.target.closest('[data-kgv-faq-toggle]');
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
|
||||
const panelId = button.getAttribute('aria-controls');
|
||||
const panel = document.getElementById(panelId);
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isOpen = button.getAttribute('aria-expanded') === 'true';
|
||||
|
||||
button.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
|
||||
button.classList.toggle('is-open', !isOpen);
|
||||
panel.classList.toggle('is-open', !isOpen);
|
||||
|
||||
if (isOpen) {
|
||||
panel.setAttribute('hidden', '');
|
||||
} else {
|
||||
panel.removeAttribute('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('input', function (event) {
|
||||
const input = event.target.closest('[data-kgv-faq-search]');
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wrapper = input.closest('[data-kgv-faq]');
|
||||
if (!wrapper) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = input.value.trim().toLowerCase();
|
||||
const items = wrapper.querySelectorAll('[data-kgv-faq-item]');
|
||||
|
||||
items.forEach(function (item) {
|
||||
const text = item.textContent.toLowerCase();
|
||||
item.classList.toggle('is-hidden', value && !text.includes(value));
|
||||
});
|
||||
});
|
||||
303
kgv-faq.php
Executable file
303
kgv-faq.php
Executable file
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: KGV FAQ
|
||||
* Description: FAQ-Verwaltung mit Fragen, Antworten, Kategorien, sauberem Backend und Shortcode-Ausgabe.
|
||||
* Version: 1.0.0
|
||||
* Author: Ronny Grobel
|
||||
* Update URI: https://git.apex-project.de/RonnyG/KGV-FAQ
|
||||
* GitHub Plugin URI: https://git.apex-project.de/RonnyG/KGV-FAQ
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
final class KGV_FAQ_Plugin {
|
||||
|
||||
const VERSION = '1.0.0';
|
||||
const POST_TYPE = 'kgv_faq';
|
||||
const TAXONOMY = 'kgv_faq_cat';
|
||||
|
||||
public function __construct() {
|
||||
add_action('init', [$this, 'register_post_type']);
|
||||
add_action('init', [$this, 'register_taxonomy']);
|
||||
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
|
||||
add_action('wp_enqueue_scripts', [$this, 'enqueue_front_assets']);
|
||||
|
||||
add_filter('manage_' . self::POST_TYPE . '_posts_columns', [$this, 'admin_columns']);
|
||||
add_action('manage_' . self::POST_TYPE . '_posts_custom_column', [$this, 'admin_column_content'], 10, 2);
|
||||
add_filter('manage_edit-' . self::POST_TYPE . '_sortable_columns', [$this, 'sortable_columns']);
|
||||
|
||||
add_shortcode('kgv_faq', [$this, 'faq_shortcode']);
|
||||
|
||||
register_activation_hook(__FILE__, [$this, 'activate']);
|
||||
register_deactivation_hook(__FILE__, [$this, 'deactivate']);
|
||||
}
|
||||
|
||||
public function activate() {
|
||||
$this->register_post_type();
|
||||
$this->register_taxonomy();
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
public function deactivate() {
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
public function register_post_type() {
|
||||
register_post_type(self::POST_TYPE, [
|
||||
'labels' => [
|
||||
'name' => 'FAQ',
|
||||
'singular_name' => 'FAQ',
|
||||
'add_new' => 'Neu hinzufügen',
|
||||
'add_new_item' => 'Neue Frage hinzufügen',
|
||||
'edit_item' => 'Frage bearbeiten',
|
||||
'new_item' => 'Neue Frage',
|
||||
'view_item' => 'Frage ansehen',
|
||||
'search_items' => 'Fragen durchsuchen',
|
||||
'not_found' => 'Keine Fragen gefunden',
|
||||
'not_found_in_trash' => 'Keine Fragen im Papierkorb gefunden',
|
||||
'menu_name' => 'FAQ',
|
||||
],
|
||||
'public' => false,
|
||||
'publicly_queryable' => false,
|
||||
'exclude_from_search' => true,
|
||||
'has_archive' => false,
|
||||
'rewrite' => false,
|
||||
'query_var' => false,
|
||||
'show_ui' => true,
|
||||
'show_in_menu' => true,
|
||||
'show_in_nav_menus' => false,
|
||||
'show_in_admin_bar' => true,
|
||||
'show_in_rest' => true,
|
||||
'menu_icon' => 'dashicons-editor-help',
|
||||
'supports' => ['title', 'editor', 'excerpt', 'page-attributes'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function register_taxonomy() {
|
||||
register_taxonomy(self::TAXONOMY, [self::POST_TYPE], [
|
||||
'labels' => [
|
||||
'name' => 'FAQ-Kategorien',
|
||||
'singular_name' => 'FAQ-Kategorie',
|
||||
'search_items' => 'Kategorien durchsuchen',
|
||||
'all_items' => 'Alle Kategorien',
|
||||
'edit_item' => 'Kategorie bearbeiten',
|
||||
'update_item' => 'Kategorie aktualisieren',
|
||||
'add_new_item' => 'Neue Kategorie hinzufügen',
|
||||
'new_item_name' => 'Neuer Kategoriename',
|
||||
'menu_name' => 'Kategorien',
|
||||
],
|
||||
'public' => false,
|
||||
'publicly_queryable' => false,
|
||||
'hierarchical' => true,
|
||||
'show_ui' => true,
|
||||
'show_admin_column' => true,
|
||||
'rewrite' => false,
|
||||
'query_var' => false,
|
||||
'show_in_rest' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function enqueue_admin_assets($hook) {
|
||||
global $post_type;
|
||||
|
||||
$allowed = ['post.php', 'post-new.php', 'edit.php', 'term.php', 'edit-tags.php'];
|
||||
|
||||
if (!in_array($hook, $allowed, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($post_type !== self::POST_TYPE && (!isset($_GET['taxonomy']) || sanitize_key(wp_unslash($_GET['taxonomy'])) !== self::TAXONOMY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style(
|
||||
'kgv-faq-admin',
|
||||
plugin_dir_url(__FILE__) . 'assets/admin.css',
|
||||
[],
|
||||
self::VERSION
|
||||
);
|
||||
}
|
||||
|
||||
public function enqueue_front_assets() {
|
||||
wp_enqueue_style(
|
||||
'kgv-faq-front',
|
||||
plugin_dir_url(__FILE__) . 'assets/front.css',
|
||||
[],
|
||||
self::VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'kgv-faq-front',
|
||||
plugin_dir_url(__FILE__) . 'assets/front.js',
|
||||
[],
|
||||
self::VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public function admin_columns($columns) {
|
||||
$new_columns = [];
|
||||
|
||||
foreach ($columns as $key => $label) {
|
||||
if ($key === 'date') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$new_columns[$key] = $label;
|
||||
|
||||
if ($key === 'title') {
|
||||
$new_columns['kgv_faq_order'] = 'Reihenfolge';
|
||||
$new_columns['kgv_faq_answer'] = 'Antwort';
|
||||
}
|
||||
}
|
||||
|
||||
$new_columns['date'] = 'Datum';
|
||||
|
||||
return $new_columns;
|
||||
}
|
||||
|
||||
public function admin_column_content($column, $post_id) {
|
||||
if ($column === 'kgv_faq_order') {
|
||||
echo (int) get_post_field('menu_order', $post_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($column === 'kgv_faq_answer') {
|
||||
$content = get_post_field('post_content', $post_id);
|
||||
$text = wp_strip_all_tags($content);
|
||||
$text = mb_substr($text, 0, 120);
|
||||
echo esc_html($text ? $text . '…' : '—');
|
||||
}
|
||||
}
|
||||
|
||||
public function sortable_columns($columns) {
|
||||
$columns['kgv_faq_order'] = 'menu_order';
|
||||
return $columns;
|
||||
}
|
||||
|
||||
public function faq_shortcode($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'category' => '',
|
||||
'limit' => 100,
|
||||
'show_filter' => '1',
|
||||
'open_first' => '0',
|
||||
'search' => '0',
|
||||
], $atts, 'kgv_faq');
|
||||
|
||||
$selected_category = '';
|
||||
|
||||
if (!empty($_GET['faq_cat'])) {
|
||||
$selected_category = sanitize_title(wp_unslash($_GET['faq_cat']));
|
||||
} elseif (!empty($atts['category'])) {
|
||||
$selected_category = sanitize_title($atts['category']);
|
||||
}
|
||||
|
||||
$query_args = [
|
||||
'post_type' => self::POST_TYPE,
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => max(1, (int) $atts['limit']),
|
||||
'orderby' => ['menu_order' => 'ASC', 'title' => 'ASC'],
|
||||
'order' => 'ASC',
|
||||
];
|
||||
|
||||
if ($selected_category) {
|
||||
$query_args['tax_query'] = [[
|
||||
'taxonomy' => self::TAXONOMY,
|
||||
'field' => 'slug',
|
||||
'terms' => $selected_category,
|
||||
]];
|
||||
}
|
||||
|
||||
$faq_query = new WP_Query($query_args);
|
||||
$terms = get_terms([
|
||||
'taxonomy' => self::TAXONOMY,
|
||||
'hide_empty' => true,
|
||||
]);
|
||||
|
||||
$uid = wp_unique_id('kgv-faq-');
|
||||
$open_first = $atts['open_first'] === '1';
|
||||
$show_search = $atts['search'] === '1';
|
||||
|
||||
ob_start();
|
||||
|
||||
echo '<div class="kgv-faq-wrapper" data-kgv-faq>';
|
||||
|
||||
if ($atts['show_filter'] === '1' && !is_wp_error($terms) && !empty($terms)) {
|
||||
echo '<form class="kgv-faq-filter" method="get">';
|
||||
|
||||
foreach ($_GET as $key => $value) {
|
||||
if ($key === 'faq_cat' || is_array($value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
echo '<input type="hidden" name="' . esc_attr($key) . '" value="' . esc_attr(wp_unslash($value)) . '">';
|
||||
}
|
||||
|
||||
echo '<label for="' . esc_attr($uid . '-cat') . '"><strong>Kategorie:</strong></label> ';
|
||||
echo '<select id="' . esc_attr($uid . '-cat') . '" name="faq_cat" onchange="this.form.submit()">';
|
||||
echo '<option value="">Alle Fragen</option>';
|
||||
|
||||
foreach ($terms as $term) {
|
||||
echo '<option value="' . esc_attr($term->slug) . '" ' . selected($selected_category, $term->slug, false) . '>';
|
||||
echo esc_html($term->name);
|
||||
echo '</option>';
|
||||
}
|
||||
|
||||
echo '</select>';
|
||||
echo '</form>';
|
||||
}
|
||||
|
||||
if ($show_search) {
|
||||
echo '<div class="kgv-faq-search-wrap">';
|
||||
echo '<input type="search" class="kgv-faq-search" placeholder="Frage suchen …" aria-label="Frage suchen" data-kgv-faq-search>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
if ($faq_query->have_posts()) {
|
||||
echo '<div class="kgv-faq-list">';
|
||||
|
||||
$index = 0;
|
||||
|
||||
while ($faq_query->have_posts()) {
|
||||
$faq_query->the_post();
|
||||
|
||||
$faq_id = get_the_ID();
|
||||
$button_id = $uid . '-button-' . $faq_id;
|
||||
$panel_id = $uid . '-panel-' . $faq_id;
|
||||
$is_open = $open_first && $index === 0;
|
||||
|
||||
echo '<article class="kgv-faq-item" data-kgv-faq-item>';
|
||||
echo '<h3 class="kgv-faq-question">';
|
||||
echo '<button type="button" class="kgv-faq-toggle' . ($is_open ? ' is-open' : '') . '" id="' . esc_attr($button_id) . '" aria-expanded="' . ($is_open ? 'true' : 'false') . '" aria-controls="' . esc_attr($panel_id) . '" data-kgv-faq-toggle>';
|
||||
echo '<span>' . esc_html(get_the_title()) . '</span>';
|
||||
echo '<span class="kgv-faq-icon" aria-hidden="true">+</span>';
|
||||
echo '</button>';
|
||||
echo '</h3>';
|
||||
|
||||
echo '<div class="kgv-faq-answer' . ($is_open ? ' is-open' : '') . '" id="' . esc_attr($panel_id) . '" role="region" aria-labelledby="' . esc_attr($button_id) . '"' . ($is_open ? '' : ' hidden') . '>';
|
||||
echo '<div class="kgv-faq-answer__inner">';
|
||||
echo wp_kses_post(wpautop(get_the_content()));
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
|
||||
echo '</article>';
|
||||
|
||||
$index++;
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
wp_reset_postdata();
|
||||
} else {
|
||||
echo '<p>Keine Fragen gefunden.</p>';
|
||||
}
|
||||
|
||||
echo '</div>';
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
new KGV_FAQ_Plugin();
|
||||
28
readme.txt
Executable file
28
readme.txt
Executable file
@@ -0,0 +1,28 @@
|
||||
KGV FAQ – WordPress Plugin
|
||||
|
||||
Beschreibung:
|
||||
KGV FAQ ist ein WordPress-Plugin für einen strukturierten FAQ-Bereich im Backend und die Ausgabe im Frontend per Shortcode.
|
||||
|
||||
Installation:
|
||||
1. ZIP in WordPress unter `Plugins > Installieren > Plugin hochladen` hochladen
|
||||
2. Plugin aktivieren
|
||||
3. Danach einmal `Einstellungen > Permalinks > Speichern` ausführen
|
||||
|
||||
Funktionen:
|
||||
- eigener FAQ-Bereich im WordPress-Backend
|
||||
- Fragen als Titel
|
||||
- Antworten über den Editor
|
||||
- FAQ-Kategorien
|
||||
- Reihenfolge über Seitenattribute / Reihenfolge
|
||||
- Ausgabe im Frontend per Shortcode
|
||||
|
||||
Shortcodes:
|
||||
- `[kgv_faq]`
|
||||
- `[kgv_faq category="allgemein"]`
|
||||
- `[kgv_faq show_filter="0"]`
|
||||
- `[kgv_faq open_first="1"]`
|
||||
- `[kgv_faq search="1"]`
|
||||
|
||||
Empfohlene Nutzung:
|
||||
- eine normale WordPress-Seite `FAQ` anlegen
|
||||
- dort den Shortcode `[kgv_faq]` einfügen
|
||||
Reference in New Issue
Block a user