Schlagwort: 🏠

  • jeopardy-auth


    Jeopardy – Login-Brücke


    🏠

    Das Modul meldet Nutzer mit ihrem WordPress-Konto bei der separaten Jeopardy-App auf jeopardy.weinify.de an. Statt eines eigenen Logins in der App stellt WordPress ein kurzlebiges, signiertes JWT aus, das die App einmal prüft und gegen ein eigenes Session-Cookie tauscht. Login, Registrierung und Logout laufen über die normalen Seiten der Benutzerverwaltung.

    Allgemein

    Anmelden aus der App heraus


    Das Konto-Menü oben rechts in der Jeopardy-App verlinkt auf diese Site mit dem Parameter weinhold_jeopardy_auth (login, register oder logout) und einem Rücksprungpfad. Wer noch nicht eingeloggt ist, landet auf der Login- bzw. Registrierungsseite der Benutzerverwaltung und kommt danach automatisch zurück. Eingeloggte Nutzer werden sofort mit einem frisch signierten Token zur App weitergeleitet und sind dort angemeldet.

    Was die Anmeldung in der App erlaubt


    Jeder angemeldete Nutzer darf in der App eigene Boards anlegen, sie öffentlich schalten, als Vorlage einreichen und seine eigenen sowie alle öffentlichen Boards hosten. Mitspielen per Handy und die Board-Ansicht am Beamer gehen weiterhin ohne Konto. Administratoren dieser Site (Recht manage_options) bekommen zusätzlich ein Admin-Kennzeichen im Token und dürfen in der App alle Boards und Vorlagen sehen und pflegen.

    Abmelden


    Logout in der App löscht das Session-Cookie der App und meldet anschließend auch bei WordPress ab (über die Logout-URL mit Nonce), danach geht es zurück zur App.

    Dateien

    modules/jeopardy_auth.php – einzige Datei des Moduls: Einstieg über den Query-Parameter, Rücksprung-Prüfung, JWT-Signierung, Erweiterung der erlaubten Redirect-Hosts und Admin-Statusseite.

    Gegenstück in der App (Repo VPS-Addons, Ordner Jeopardy): backend/src/routes/auth.ts (Token prüfen, Session-Cookie jeopardy_session) und backend/src/shared/access.ts (Rechte für Boards).

    Für Entwickler

    Kein eigener Datenbank-State


    Das Modul hat weder Tabelle noch Optionen noch Capabilities und deshalb auch kein maybe_install() und kein cleanup(). Es signiert bei jedem Aufruf nur ein Token aus vorhandenen Nutzerdaten. Ein eigenes Recht zum Hosten gibt es bewusst nicht: jeder eingeloggte Nutzer darf sich anmelden, die feineren Regeln (Ersteller, öffentlich, Admin) entscheidet die App.

    Ablauf und Einstieg


    weinhold_jeopardy_auth_handle_request() hängt mit Priorität 1 an template_redirect und reagiert nur, wenn weinhold_jeopardy_auth gesetzt ist. Nicht eingeloggte Nutzer werden zur Auth-Seite der Benutzerverwaltung geschickt (weinhold_user_management_get_auth_page_url), mit redirect_to auf genau diese Einstiegs-URL, damit sie danach eingeloggt hier landen. Der Rücksprungpfad wird von weinhold_jeopardy_auth_sanitize_return() geprüft: nur Pfade mit genau einem führenden Schrägstrich, sonst die Startseite der App.

    ParameterBeschreibung
    weinhold_jeopardy_authlogin, register oder logout
    returnPfad in der App, auf den nach der Anmeldung zurückgesprungen wird (Standard /)

    Token


    weinhold_jeopardy_auth_sign_token() baut ein HS256-JWT von Hand (Base64url und hash_hmac(), keine Composer-Bibliothek). Das Token läuft nach 120 Sekunden ab und wird im URL-Fragment an {App}/auth/callback übergeben, damit es nicht in Server-Logs landet. Die App tauscht es sofort gegen ihr eigenes httpOnly-Session-Cookie (30 Tage).

    ClaimInhalt
    subWordPress-Nutzer-ID
    nameAnzeigename
    avatarURL des Profilbilds
    profile_urlProfilseite der Benutzerverwaltung
    admintrue, wenn der Nutzer auf dieser Site manage_options hat
    iatAusstellungszeitpunkt
    expAblauf (Ausstellung plus 120 Sekunden)

    Konfiguration


    KonstanteBeschreibung
    JEOPARDY_JWT_SECRETPflicht, in wp-config.php. Muss exakt mit JEOPARDY_JWT_SECRET in der .env der App übereinstimmen
    JEOPARDY_APP_URLOptional, Standard https://jeopardy.weinify.de (zusätzlich per Filter weinhold_jeopardy_auth_app_url änderbar)

    Besonderheiten

    Vorbild ist das Modul PPNP Game – Login-Brücke. Anders als dort gibt es keinen Button-Shortcode und keinen AJAX-Endpunkt: die App verlinkt direkt auf die Einstiegs-URL, und der Rücksprung zur App ist ein normaler Redirect.

    Die App liegt nicht im Multisite-Netzwerk. Damit der Rücksprung nach dem Logout über wp_safe_redirect() funktioniert, ergänzt das Modul den Host der App-URL über den Filter allowed_redirect_hosts.

    Die Hilfsfunktion weinhold_user_management_add_redirect_to_url() wird absichtlich nicht verwendet: sie dekodiert das Ziel mehrfach und hängt es unkodiert an, wodurch der return-Parameter verloren ginge. Stattdessen wird redirect_to selbst mit rawurlencode() angehängt.

    Rechte-Änderungen (z. B. jemand wird Administrator) greifen in der App erst nach einer neuen Anmeldung, weil das Admin-Kennzeichen im Session-Cookie der App gespeichert ist.

    Versionshistorie

    VersionDatumÄnderung
    1.0.005.10.2026Erste Veröffentlichung: Login, Registrierung und Logout für die Jeopardy-App per kurzlebigem JWT.
    1.1.005.10.2026Token trägt das Admin-Kennzeichen (manage_options), Grundlage für Board-Besitz, öffentliche Boards und Vorlagen in der App.


  • ppnp-game-auth


    PPNP Game – Login-Brücke


    Das Modul stellt eingeloggten Nutzern ein kurzlebiges, signiertes JWT aus, mit dem sich der separate Node.js-Spieldienst (ppnp.weinify.de/game) authentifiziert. Es gibt keinen eigenen Login-Nachbau – der Node-Dienst prüft nur die Signatur und vertraut WordPress als Identitätsquelle.

    Allgemein

    Spielstart per Button


    Ein Shortcode zeigt einen „Spiel starten“-Button für eingeloggte, freigeschaltete Nutzer. Ein Klick holt per AJAX ein frisch signiertes Token und leitet direkt zum Node.js-Spieldienst weiter, der das Token aus dem URL-Fragment liest.

    Drei getrennte Spiel-Berechtigungen


    Neben der reinen Spielfreigabe (view-Recht über die normale Modulberechtigung) gibt es drei eigene, im Nutzerprofil einzeln vergebbare Capabilities: Stammdaten pflegen (Typen, Attacken, Pyxlinge), neue Welten erstellen und den Beta-Bereich verwalten. Editoren und Administratoren erhalten die ersten beiden automatisch über ihre Rolle, die Beta-Berechtigung bewusst nur Administratoren.

    Spielerliste für den Node-Dienst


    Der Node-Dienst kann per authentifiziertem Callback die vollständige Liste aller fürs Spiel freigeschalteten Nutzer abrufen – Grundlage für die Spieler-Zuweisung pro Welt im Spieldienst selbst.

    Shortcodes

    ShortcodeBeschreibung
    weinhold_ppnp_game_launchButton für eingeloggte, freigeschaltete Nutzer, der ein signiertes Spiel-Login-Token holt und zum Node.js-Spiel weiterleitet. Keine Parameter

    Dateien

    modules/ppnp_game_auth.php – einzige Datei des Moduls: Capability-Verwaltung, JWT-Signierung, AJAX-Endpunkte (Token-Ausstellung, Spielerliste), Shortcode, Admin-Statusseite, Cleanup.

    Für Entwickler

    Kein eigener Datenbank-State


    Anders als fast alle anderen Module gibt es kein maybe_install() und keine eigene Tabelle: Das Modul signiert bei jedem Aufruf nur ein kurzlebiges Token aus bereits vorhandenen WordPress-Nutzerdaten. weinhold_ppnp_game_auth_cleanup() existiert trotzdem, weil die drei Capabilities echter, dauerhafter State sind, der beim Zurücksetzen sauber von allen Rollen UND individuell zugewiesenen Nutzern entfernt werden muss.

    Selbst gebautes HS256-JWT ohne Bibliothek


    weinhold_ppnp_game_auth_sign_token() baut das JWT von Hand (Base64url-Encoding, HMAC-SHA256-Signatur über hash_hmac()) statt eine Composer-Bibliothek einzubinden – der Node-Dienst validiert nur die Signatur mit demselben Secret (PPNP_GAME_JWT_SECRET aus der wp-config.php), es findet keine eigene Session-Verwaltung im Spieldienst statt. Das Token trägt neben der Nutzer-ID auch die drei Capability-Flags direkt im Payload und läuft nach 120 Sekunden ab – bewusst nur als kurzlebiges Sprungbrett zum Aufbau der WebSocket-Verbindung gedacht, nicht als dauerhaftes Zugangs-Credential.

    Server-zu-Server per Shared Secret


    Der Callback für die Spielerliste läuft über einen nopriv-Ajax-Hook (der Node-Dienst hat keine WordPress-Session) und wird stattdessen über ein zweites, vom JWT-Secret getrenntes Shared Secret abgesichert (Header X-PPNP-Game-Secret, Konstante PPNP_GAME_API_SECRET, Vergleich per hash_equals() gegen Timing-Angriffe). Die Freigabeprüfung nutzt exakt dieselbe Funktion wie beim Token-Ausstellen, damit „wer darf spielen“ nur eine einzige Quelle der Wahrheit hat.

    Besonderheiten

    Dieses Modul ist das dokumentierte Vorbild für den MCP-Server-Manager: Beide nutzen signierte Tokens, ein zweites Shared Secret für Server-zu-Server-Calls und Capability-basierte statt tabellenbasierte Zugriffsrechte. Der MCP Server Manager weicht bewusst in zwei Punkten ab (dynamische statt fester Capabilities, langlebige statt kurzlebige Tokens) – wer den Aufbau des einen Moduls versteht, erkennt die Struktur des anderen sofort wieder.

    Zwei komplett getrennte Secrets für zwei komplett getrennte Zwecke: PPNP_GAME_JWT_SECRET signiert Nutzer-Tokens, PPNP_GAME_API_SECRET schützt den Server-zu-Server-Callback – ein kompromittiertes Secret legt nie beide Kanäle gleichzeitig offen.

    Die Beta-Berechtigung wird absichtlich NICHT wie die anderen beiden Capabilities automatisch an Editoren vergeben, obwohl der Code dafür identisch aufgebaut ist – reine Vorsichtsmaßnahme, damit ein unfertiger Beta-Bereich nicht versehentlich für alle Spielleiter sichtbar wird.

    Versionshistorie

    VersionDatumÄnderung
    1.0.022.09.2026Einführung der Versionierung.


  • recaptcha


    reCAPTCHA


    Das reCAPTCHA-Modul prüft Login, Registrierung, Passwort-vergessen und Kommentare mit Googles reCAPTCHA Enterprise auf Bot-Verhalten und blockiert oder protokolliert verdächtige Anfragen anhand eines Score-Schwellwerts.

    Allgemein

    Score statt Bilderrätsel


    Statt sichtbarer Bilderrätsel nutzt das Modul Googles reCAPTCHA Enterprise im Hintergrund: Jede geschützte Formular-Aktion liefert einen Score zwischen 0 und 1, der mit einem einstellbaren Schwellwert verglichen wird (Standard 0,5). Besucher merken im Normalfall nichts davon.

    Vier schützbare Formulare


    Einzeln zuschaltbar: Login, Registrierung, Passwort vergessen und Kommentare. Bei einer Unterschreitung des Schwellwerts entscheidet die Einstellung „Aktion bei Fehlschlag“ darüber, ob die Anfrage blockiert (block) oder nur protokolliert wird (log_only), ohne den Nutzer zu stören.

    Statistik-Dashboard


    Jede Prüfung wird protokolliert und im Dashboard mit Kennzahlen (Gesamt/Durchgelassen/Blockiert/Block-Rate/Ø Score), Zeitverlauf, Score-Verteilung und Aufschlüsselung nach Formular-Typ ausgewertet – filterbar nach Zeitraum, Website und IP-Adresse, inklusive CSV-Export. Alte Log-Einträge werden nach einer konfigurierbaren Aufbewahrungsfrist (Standard 90 Tage) automatisch per täglichem Cronjob gelöscht.

    Dateien

    modules/recaptcha.php – einzige Datei des Moduls: Einstellungen, Verschlüsselung, Frontend-Integration, Formular-Prüfungen, Logging-Tabelle, Statistik-Dashboard, CSV-Export, Cleanup.

    Für Entwickler

    Verschlüsselter API-Key


    Site Key, API-Key und Projekt-ID für die Google-Cloud-Anbindung werden zentral eingetragen; der API-Key wird wie beim E-Mail-Versand-Modul mit AES-256-CBC verschlüsselt gespeichert (Schlüssel aus dem WordPress-AUTH_KEY abgeleitet). Ein Test-Button in den Einstellungen prüft die Zugangsdaten direkt gegen Google, ohne einen echten Seitenaufruf zu benötigen.

    Fail-Open bei Google-Ausfall


    weinhold_recaptcha_verify() ruft die reCAPTCHA-Enterprise-API auf und liefert Score sowie Ergebnis zurück. Ist Google nicht erreichbar, gilt die Prüfung als bestanden (fail-open) statt alle Nutzer auszusperren – bewusste Entscheidung gegen einen kompletten Login-Ausfall bei einem Google-seitigen Problem. Debug-Informationen zum letzten Aufruf werden eine Stunde als Transient für die Fehlersuche vorgehalten.

    Login-Prüfung ohne Security-Manager-Zählung


    Die Login-Prüfung hängt bewusst am Hook login_init statt am späteren wp_authenticate_user – dadurch feuert bei einem fehlgeschlagenen reCAPTCHA-Score niemals der Hook wp_login_failed, und das Security-Manager-Modul zählt reCAPTCHA-Fehlschläge folglich nicht als fehlgeschlagenen Login-Versuch für seine eigene Sperrlogik.

    Automatische Formular-Erkennung


    Das eingebundene JavaScript erkennt Login- (Felder log/pwd), Registrierungs- und Passwort-vergessen-Formulare automatisch anhand ihrer Standard-Feldnamen und erzeugt beim Laden proaktiv ein Token. Eigene Formulare lassen sich zusätzlich über das Attribut data-wf-recaptcha einbinden.

    Besonderheiten

    Obwohl der Modul-Header Scope: site trägt (also pro Website aktivierbar ist), gibt es keine site-spezifischen Einstellungen mehr – Site Key, API-Key, Schwellwert und alle Schutz-Schalter sind ausschließlich netzwerkweit konfigurierbar. weinhold_recaptcha_cleanup() hat entsprechend nichts mehr zu bereinigen, nur der Netzwerk-Cleanup entfernt die globale Konfiguration und die Log-Tabelle.

    Fail-Open bedeutet: Ist die Google-API nicht erreichbar, lässt das Modul Anfragen lieber durch als Nutzer komplett auszusperren – Verfügbarkeit hat hier bewusst Vorrang vor lückenloser Bot-Abwehr.

    Versionshistorie

    VersionDatumÄnderung
    1.0.022.09.2026Einführung der Versionierung.


  • rudelroots


    RudelRoots


    Das RudelRoots-Modul verwaltet Zutaten für die Barf-Fütterung (biologisch artgerechte Rohfleisch-Fütterung von Hunden) in Sets und berechnet daraus benötigte Mengen und Kosten – wahlweise nach Anzahl Tagen oder nach bereits vorhandener Menge einer Zutat.

    Allgemein

    Zutaten & Sets


    Jede Zutat erhält Name, Beschreibung, Bild, optionalen Einkaufslink, einen Grammwert pro 5 kg Hundegewicht und Tag sowie einen Preis pro 100 g. Zutaten werden zu Sets gruppiert (z. B. für unterschiedliche Fütterungspläne); ein Set kann als Standard markiert werden und wird dann beim Aufruf des Rechners automatisch vorausgewählt.

    Bedarfsrechner mit zwei Modi


    Im Modus „Planung nach Tagen“ gibt man die gewünschte Anzahl Tage ein, das Modul berechnet für jede Zutat des gewählten Sets die benötigte Menge und die Kosten. Im Modus „Planung nach vorhandener Menge“ wählt man stattdessen eine bereits vorhandene Zutat mit ihrer Menge in Gramm aus – das Modul errechnet daraus, für wie viele ganze Tage der Vorrat reicht, und wie viel von den übrigen Zutaten des Sets für denselben Zeitraum benötigt wird.

    Übersichtsliste


    Eine separate Ansicht zeigt alle hinterlegten Zutaten als Kartenraster mit Bild, Beschreibung, Preis pro 100 g und optionalem Kauflink.

    Shortcodes

    ShortcodeBeschreibung
    weinhold_rudelroots_listKartenraster aller Zutaten mit Bild, Beschreibung und Preis. Keine Parameter
    weinhold_rudelroots_calculatorBedarfsrechner mit Set-Auswahl und den zwei Berechnungsmodi. Keine Parameter
    rudelroots_list/ rudelroots_calculator Legacy-Aliase ohne weinhold_-Präfix, bereits in bestehenden Beiträgen verwendet und aus Kompatibilitätsgründen weiterhin aktiv

    Dateien

    DateiInhalt
    modules/rudelroots.phpModul-Header, Initialisierung, Shortcode-Registrierung inkl. Legacy-Aliase, Audit-Log-Integration
    modules/rudelroots/db.phpTabellen, Installation/Cleanup, CRUD für Zutaten und Sets
    modules/rudelroots/admin.phpAdmin-Oberfläche, Formularverarbeitung, Bild-Upload
    modules/rudelroots/frontend.phpBeide Shortcode-Renderer inkl. Rechenlogik für die zwei Kalkulationsmodi

    Für Entwickler

    Datenbank


    TabelleSpalten(Auswahl)
    weinhold_rudelroots_ingredientsname, description, link_url, image_url, gramm_per_5kg_per_day, price_per_100g, sort_order
    weinhold_rudelroots_setsname, description, image_url, is_default, sort_order
    weinhold_rudelroots_set_ingredientsset_id, ingredient_id(eindeutige Kombination)

    Rechenlogik


    weinhold_rudelroots_calculate_mode_a() skaliert gramm_per_5kg_per_day jeder Zutat linear mit der eingegebenen Tagesanzahl. weinhold_rudelroots_calculate_mode_b() rechnet umgekehrt: aus der vorhandenen Menge einer gewählten Zutat und ihrem Tagesbedarf ergibt sich die exakte sowie die abgerundete Anzahl reichender Tage, die dann auf alle übrigen Zutaten des Sets angewendet wird.

    Bild-Upload & Audit-Log


    Bilder für Zutaten und Sets laufen über wp_handle_upload() mit eigens gesetztem Upload-Verzeichnis- und Mime-Type-Filter statt über die volle Mediathek. Speichern und Löschen von Zutaten und Sets wird – sofern das Audit-Log-Modul aktiv ist – über weinhold_audit_log_log_event() protokolliert (Events ingredient_saved, ingredient_deleted, set_saved, set_deleted).

    Besonderheiten

    Die Legacy-Shortcode-Aliase (rudelroots_list, rudelroots_calculator ohne weinhold_-Präfix) wurden bewusst nicht entfernt, obwohl das Framework inzwischen konsequent das weinhold_-Präfix nutzt – bereits veröffentlichte Beiträge verwenden noch die alten Namen und sollen weiter funktionieren.

    Beim Löschen einer Zutat oder eines Sets werden zuerst die Verknüpfungen in weinhold_rudelroots_set_ingredients entfernt und erst danach der Haupteintrag – ohne Fremdschlüssel-Constraint auf Datenbankebene übernimmt das Modul diese Reihenfolge selbst.

    Wird beim Speichern eines Sets is_default gesetzt, entfernt das Modul das Flag zuerst bei allen anderen Sets – es kann also immer nur genau ein Standard-Set geben.

    Versionshistorie

    VersionDatumÄnderung
    1.0.022.09.2026Einführung der Versionierung.


  • mcp-server-manager


    MCP Server Manager


    Das MCP-Server-Manager-Modul verwaltet Nutzerzugriffe auf die selbstgehosteten MCP-Server unter mcp-server.weinify.de: Es nimmt den Server-Katalog vom Gateway entgegen, gibt eingeloggten Nutzern API-Tokens für KI-Clients wie Claude aus und prüft diese Tokens für den Gateway – inklusive vollem OAuth-2.1-Connector-Support.

    Allgemein

    Katalog & Freischaltung


    Der Gateway meldet regelmäßig seinen aktuellen Server-Katalog (ID, Name, Beschreibung, optionaler Web-Pfad) an dieses Modul, das ihn lokal spiegelt. Für jeden Server im Katalog entsteht zur Laufzeit eine eigene Berechtigung; Administratoren schalten Nutzer im WordPress-Profil oder über eine Freigabe-Tabelle auf der Modul-Admin-Seite pro Server frei.

    API-Tokens für KI-Clients


    Freigeschaltete Nutzer erzeugen über ein Dashboard eigene, langlebige API-Tokens (gültig 5 Jahre) mit eigener Bezeichnung und optional eingeschränktem Server-Scope, um sie in die Konfiguration von KI-Clients wie Claude Desktop einzutragen. Jeder Token lässt sich jederzeit einzeln widerrufen.

    OAuth-2.1-Connector-Support


    Für Anbindungen, die einen klassischen OAuth-Login-Flow statt eines manuell eingetragenen Tokens erwarten (Connector), spielt WordPress selbst den Authorization Server: Dynamic Client Registration, Login samt Einwilligungsseite und Token-Ausstellung laufen alle über dieses Modul und münden am Ende im selben Token-Format wie die manuell erzeugten Tokens.

    Eingebettete Web-Oberflächen


    Für Server mit eigener Web-Oberfläche lässt sich diese direkt in eine weinify.de-Seite einbetten – das Modul prägt dafür automatisch einen kurzlebigen, auf genau diesen Server begrenzten Token, ohne dass Nutzer manuell etwas eintragen müssen.

    Shortcodes

    ShortcodeBeschreibung
    weinhold_mcp_server_manager_dashboardDashboard für eingeloggte Nutzer: eigene MCP-Server-Zugriffe einsehen, API-Tokens erzeugen und widerrufen. Keine Parameter
    weinhold_mcp_server_manager_embedBettet die Web-Oberfläche eines MCP-Servers per iframe ein, mit drei Zuständen (bitte einloggen / kein Zugriff / normale Anzeige). Parameter server: Server-ID aus dem Katalog, z. B. „memory“ (muss einen Web-Pfad hinterlegt haben)

    Dateien

    modules/mcp_server_manager.php – einzige Datei des Moduls: Katalog-Spiegel, Capability-Verwaltung, JWT-Signierung/-Prüfung, Token-Speicher, OAuth-2.1/DCR-Endpunkte, Shortcodes, Admin-Oberflächen, Cleanup.

    Für Entwickler

    Dynamische Capabilities statt eigener ACL-Tabelle


    Statt einer eigenen Datenbanktabelle für Zugriffsrechte nutzt das Modul WordPress-Capabilities: Pro Server im Katalog entsteht zur Laufzeit eine Capability mcp_access_{id}. weinhold_mcp_server_manager_get_user_server_ids() ist die einzige Quelle der Wahrheit dafür, welcher Nutzer welchen Server sehen darf – sowohl fürs eigene Dashboard als auch für die Token-Prüfung des Gateways. Eine separate Verwaltungs-Capability (manage_mcp_server_users) regelt, wer die Server-Zuordnung anderer Nutzer bearbeiten darf; nur Administratoren erhalten sie automatisch.

    Selbst signierte JWTs, Widerruf per jti


    Tokens sind HS256-signierte JWTs (Secret aus der Konstante MCP_JWT_SECRET in der wp-config.php), enthalten aber zusätzlich eine eindeutige jti. Diese wird beim Erzeugen als Metadatensatz im User-Meta abgelegt; weinhold_mcp_server_manager_verify_token() prüft Signatur, Ablaufzeit UND ob die jti noch in dieser Liste steht – ein Token ist also trotz gültiger Signatur sofort ungültig, sobald er im Dashboard widerrufen wurde. Die effektiven Server-IDs eines Tokens sind die Schnittmenge aus dem beim Erzeugen gewählten Scope und den AKTUELLEN Nutzer-Berechtigungen: Ein Token kann also nie mehr, als der Nutzer JETZT darf, selbst wenn der Scope bei der Erzeugung weiter war.

    Server-zu-Server ohne Login


    Katalog-Sync und Token-Prüfung kommen vom Gateway selbst, ohne WordPress-Session. Beide Endpunkte laufen über nopriv-Ajax-Hooks und werden stattdessen über ein Shared Secret (Header X-MCP-Gateway-Secret, Konstante MCP_WP_API_SECRET) mit hash_equals() abgesichert – ein eigenes Secret, getrennt vom JWT-Signing-Secret.

    OAuth-2.1-Endpunkte


    .well-known/oauth-authorization-server wird über den parse_request-Hook bedient, weil RFC 8414 einen festen Pfad ohne admin-ajax.php-Präfix verlangt. Dynamic Client Registration (RFC 7591) nimmt praktisch jede Registrierung mit gültigen Redirect-URIs (https oder localhost) an und vergibt nur eine Client-ID ohne Secret – Public-Client-Modell, abgesichert über PKCE statt Client-Secret-Verwaltung. Registrierung, Authorize-Endpunkt und Token-Endpunkt antworten bewusst NICHT im WordPress-Ajax-Hüllformat ({„success“:true,“data“:…}), sondern mit unverpackten JSON-Antworten nach OAuth-Spezifikation, da ein echter OAuth-Client die WP-Hülle nicht verstehen würde. Connector-Tokens laufen mit 180 Tagen Gültigkeit deutlich kürzer als die 5 Jahre manuell erzeugter Tokens.

    Besonderheiten

    Der Modul-Header trägt Scope: site, das Modul selbst orientiert sich aber am Muster des PPNP-Spiel-Auth-Moduls (signierte Tokens, Shared-Secret für Server-zu-Server-Calls, Capability-basierte statt tabellenbasierte Rechte) – mit zwei bewussten Abweichungen: Die Server-Liste ist dynamisch statt drei fest einprogrammierter Capabilities, und Tokens sind langlebig und darum per jti widerrufbar statt rein zustandslos wie PPNPs 120-Sekunden-Bootstrap-Token.

    Der eingebettete Web-Oberflächen-Token liest der jeweilige MCP-Server direkt aus dem URL-Fragment nach dem Raute-Zeichen aus – Fragmente werden nie an den Server mitgesendet, sondern bleiben rein clientseitig im Browser, was den Token vor Server-Logs schützt.

    Trotz voller OAuth-2.1-Unterstützung gibt es keinen Refresh-Grant: Connector-Tokens laufen nach 180 Tagen schlicht ab und müssen neu autorisiert werden, sind aber jederzeit vorher über das Dashboard „Deine Tokens“ widerrufbar.

    Versionshistorie

    VersionDatumÄnderung
    1.0.022.09.2026Einführung der Versionierung.


  • bastard-des-monats


    Bastard des Monats


    Das BdM-Modul verwaltet und präsentiert Schandtaten im Cyberpunk-Terminal-Stil. Mitglieder reichen Einträge über ein Formular ein, ein Admin schaltet sie frei – der zuletzt freigeschaltete Eintrag gilt als amtierender „Bastard des Monats“.

    Allgemein

    In der Praxis – bastard-des-monats.weinify.de


    Das Modul ist in der Praxis vollständig umgesetzt auf bastard-des-monats.weinify.de. Dort lässt sich live sehen, wie das Terminal-UI aussieht und wie Einträge präsentiert werden.

    Einreichen & Freischalten


    Über das Einsende-Formular reicht jeder Besucher (Name, E-Mail, „Luder“, Tatzeit, Schandtat als Text) einen Vorschlag ein. Neue Einreichungen erhalten den Status eingereicht und erscheinen auf der Wand als anonymisierter, verschlüsselt wirkender Platzhalter-Eintrag – erst nach Freischaltung im Admin-Bereich (Status freigegeben) werden Name des Zeugen, Titel und Schandtat-Text öffentlich sichtbar.

    Der amtierende Champion


    Der zuletzt freigeschaltete Eintrag wird prominent als „Amtierender Champion der Inkompetenz“ hervorgehoben. Es gibt keine automatische Monats-Rotation oder Abstimmung – welcher Eintrag Champion ist, ergibt sich rein daraus, welcher zuletzt vom Admin freigegeben wurde.

    Administration


    Eingereichte Schandtaten können im Dashboard freigegeben, bearbeitet oder gelöscht werden. Beim Löschen kann eine individuelle Begründung angegeben werden, die dem Einsender automatisch per E-Mail zugeschickt wird. Zusätzlich benachrichtigt das Modul konfigurierbare Admin-E-Mail-Adressen automatisch über jede neue Einreichung.

    Shortcodes

    ShortcodeBeschreibung
    weinhold_bdm_submit_formDas Einsende-Formular im Terminal-Design
    weinhold_bdm_listDie „Wand der Schande“: zeigt alle eingereichten und freigeschalteten Einträge
    bdm_submit_formLegacy-Alias für weinhold_bdm_submit_form
    bdm_listLegacy-Alias für weinhold_bdm_list

    Dateien

    modules/bdm_einsender.php – einzige Datei des Moduls: Datenbank, Formular-Verarbeitung, Admin-Moderation, E-Mail-Benachrichtigungen, beide Frontend-Shortcodes.

    Für Entwickler

    Datenbank (Tabelle wp_bdm_submissions, Praefix aus wpdb prefix)


    SpalteBedeutung
    submitted_at,event_timeEinreichungszeitpunkt bzw. Zeitpunkt der Schandtat selbst
    einsender,emailAngaben zum Einsender
    luderName der/des Genannten
    schandtatFreitext-Beschreibung
    statuseingereicht oder freigegeben

    Eigenes Tabellenpraefix (wpdb prefix statt des sonst im Framework ueblichen wpdb base_prefix) – dieses Modul ist bewusst nicht Multisite-übergreifend gedacht, jede Site hat ihre eigene Tabelle.

    E-Mail-Benachrichtigungen


    Bei jeder neuen Einreichung werden konfigurierbare Admin-Adressen benachrichtigt (Option weinhold_bdm_admin_notify_emails, mehrere Adressen per Zeilenumbruch oder Komma getrennt; ohne Konfiguration greift die WordPress-Admin-E-Mail als Fallback) über die zentrale weinhold_send_module_mail()-Funktion mit dem Template bdm_new_submission. Beim Ablehnen/Löschen eines Eintrags geht zusätzlich eine individuelle Absage direkt per wp_mail() an den Einsender – unabhängig vom Modul-Template-System.

    Besonderheiten

    Keine Community-Abstimmung: Trotz des Namens „Bastard des Monats“ gibt es weder eine öffentliche Bewertungsfunktion noch eine automatische monatliche Rotation – die redaktionelle Auswahl liegt vollständig bei der Freischaltung durch einen Admin.

    Redirect-Fix nach dem Absenden: Nach erfolgreicher Einreichung leitet das Formular per Post/Redirect/Get-Muster auf dieselbe Seite mit ?sent=1 weiter, um ein versehentliches Doppel-Absenden bei Browser-Neuladen zu verhindern.

    Historisch gewachsener Code: Der Admin-Renderer enthält mehrere per if (false && …) bewusst deaktivierte Codeblöcke – eine ältere Version der POST-Verarbeitung, die inzwischen komplett in weinhold_bdm_handle_admin_actions() (Hook admin_init) verlagert wurde, aber als toter Code stehen geblieben ist.

    Versionshistorie

    VersionDatumÄnderung
    1.0.022.09.2026Einführung der Versionierung.


  • pnp-book


    PnP Buch

    Das Regelwerk im Browser

    Das PnP-Book-Modul verwandelt eine WordPress-Seite in ein vollständiges Rollenspielbuch. Kapitel, Unterkapitel, automatisches Inhaltsverzeichnis und clientseitige Suche sind integriert – im DnD-Buch-Look & Feel.

    Shortcode

    [[weinhold_pnp_book]] – Rendert das vollständige Buch auf einer WordPress-Seite inklusive Navigation, Inhaltsverzeichnis und Suchfunktion.

    Inhalte verwalten

    Kapitel und Unterkapitel werden im Admin-Dashboard angelegt und können per Drag & Drop sortiert werden. Das Inhaltsverzeichnis wird automatisch aus der Kapitelstruktur generiert.

    Suche

    Die Suchfunktion durchsucht alle Kapitelinhalte clientseitig in Echtzeit – ohne Seitenneuladung. Ideal für umfangreiche Regelwerke, in denen man schnell eine bestimmte Regel nachschlagen möchte.

    Versionshistorie

    1.0.0 – 22.09.2026 – Einführung der Versionierung.

  • lol-comp-generator


    LoL Comp Generator


    Der LoL Comp Generator erstellt auf Knopfdruck zufällige Team-Kompositionen für League of Legends aus einer verwaltbaren Champion-Datenbank – inklusive Speichern, Teilen und einem zusätzlichen Challenge-Würfel-Modus.

    Allgemein

    Champions, Sets & Presets


    Die Champion-Liste wird im Admin-Dashboard gepflegt. Champions werden zu Sets gruppiert (benannte Champion-Pools je Rolle), mehrere Sets wiederum zu Presets zusammengefasst – ein Preset bestimmt also, aus welchen Sets pro Rolle beim Generieren gezogen wird.

    Generieren, Speichern, Teilen


    Jede generierte Komposition erhält einen eindeutigen Hash und lässt sich als Link teilen – der Aufruf mit ?lol_session=HASH öffnet direkt die Detailansicht dieser Session, inklusive optionalem Ergebnis, Screenshot, Tags und Notizen.

    Challenges


    Über einen Umschalter im Generator lässt sich zusätzlich ein Challenge-Modus aktivieren: Aus konfigurierten Kategorien wird per Würfel-Button eine zufällige Challenge gezogen (Name, Beschreibung, optional beteiligte Champions). Ergebnisse lassen sich inklusive Screenshot direkt per Discord-Webhook posten.

    Statistiken & Verlauf


    Eine chronologische Verlaufsliste sowie Statistiken (Win-Rates, Top-Champions, Leaderboard, Hall of Shame) runden das Modul ab.

    Shortcodes

    ShortcodeBeschreibung
    weinhold_lol_comp_generatorDer Hauptgenerator (3-Schritt-Flow) mit integriertem Challenge-Modus. Mit ?lol_session=HASH direkt zur Session-Detailansicht
    weinhold_lol_comp_historyChronologische Liste aller gespeicherten Sessions mit Tag-Filter
    weinhold_lol_comp_statsStatistiken: Win-Rates, Top-Champions, Leaderboard, Hall of Shame

    Dateien

    DateiInhalt
    modules/lol_comp_generator.phpLade-Stub: Init, Audit-Log- und Discord-Integration, alle AJAX-Registrierungen, Admin- und Cleanup-Einstiegspunkte
    modules/lol_comp_generator/db.phpDatenbank-Installation (10 Tabellen) und alle Lese-/Schreibfunktionen
    modules/lol_comp_generator/admin.phpAdmin-Oberfläche: Champions, Sets, Presets, Sessions verwalten
    modules/lol_comp_generator/frontend.phpFrontend-Shortcodes: Generator, Challenges, Verlauf, Statistiken

    Für Entwickler

    Datenbank (Auswahl)


    TabelleInhalt
    weinhold_lol_championsChampion-Stammdaten
    weinhold_lol_sets,weinhold_lol_set_championsSets und ihre Champion-Zuordnung
    weinhold_lol_presets,weinhold_lol_preset_setsPresets und ihre Set-Zuordnung
    weinhold_lol_sessionsGenerierte/gespeicherte Kompositionen (hash, preset_id, mode, result, screenshot_path, tags, notes)
    weinhold_lol_session_playersPro Session: Spieler, Rolle, verwendetes Set, gezogener Champion
    weinhold_lol_challenge_categories,weinhold_lol_challenges,weinhold_lol_challenge_championsChallenge-Oberkategorien, einzelne Challenges und ihre Champion-Zuordnung

    Berechtigungen


    weinhold_lol_comp_generator_current_user_can_view()/_can_edit() setzen zusätzlich zur Modul-Berechtigung immer einen eingeloggten Nutzer voraus – anders als bei rein view-basierten Modulen ist hier auch das reine Ansehen nicht für Gäste vorgesehen.

    Discord-Integration


    Zwei getrennte Discord-Integrationen über den Filter weinhold_discord_module_integrations: lol_comp_generator (neue gespeicherte Session) und lol_comp_generator_challenge (manuell per Button gepostetes Challenge-Ergebnis). Der Challenge-Discord-Versand läuft dabei NICHT über die zentrale weinhold_discord_fire_module_event()-Funktion wie bei anderen Modulen, sondern baut den Webhook-Request inkl. optionalem Bild-Multipart-Upload (Screenshot als Anhang) direkt selbst zusammen.

    AJAX-Endpunkte


    Über 20 registrierte wp_ajax_*-Actions, grob in vier Gruppen: Admin-Verwaltung (Champions/Sets/Presets speichern, löschen, synchronisieren), Frontend-Generator (Daten laden, Session speichern, Ergebnis setzen, Screenshot hochladen/ausliefern, Tags/Notizen, Replay), Challenge-Verwaltung (Kategorien und Challenges speichern/löschen, Discord-Versand) sowie ein öffentlicher (nopriv) Endpunkt für Statistiken.

    Besonderheiten

    Statistik-Endpunkt (get_stats) und Screenshot-Auslieferung (serve_screenshot) sind bewusst auch für nicht eingeloggte Besucher freigegeben (wp_ajax_nopriv_*), obwohl der Rest des Moduls Login voraussetzt – Statistiken und geteilte Screenshots sollen auch ohne Konto einsehbar sein.

    Sessions werden über einen eindeutigen hash (nicht die numerische ID) referenziert – verhindert das Erraten fortlaufender Session-IDs beim Teilen von Links.

    Der Challenge-Discord-Versand baut den Multipart-Request für Bild-Uploads manuell zusammen (eigener Boundary-String), da WordPress‘ wp_remote_post() keine native Unterstützung für multipart/form-data mit Dateianhang bietet.

    Versionshistorie

    VersionDatumÄnderung
    1.0.022.09.2026Einführung der Versionierung.


  • media-player


    Media Player


    Das Media-Player-Modul bringt einen vollwertigen Audioplayer mit Playlisten-Verwaltung ins Weinhold Framework. Titel, Interpret, Cover-Bild, Download-Button, Favoriten und eigene Playlisten für eingeloggte Nutzer sind komplett integriert.

    Allgemein

    Playlisten verwalten


    Im Dashboard unter Weinhold → Media Player werden Playlisten angelegt und Titel aus der WordPress-Mediathek hinzugefügt. Jeder Titel kann mit eigenen Metadaten (Titel, Interpret, Beschreibung, Cover-Bild) versehen werden, die die Mediathek-Angaben überschreiben. Playlisten und Titel lassen sich per Drag & Drop sortieren.

    Für eingeloggte Nutzer: Favoriten & eigene Playlisten


    Eingeloggte Besucher können Titel als Favorit markieren und eigene Playlisten aus dem vorhandenen Track-Angebot zusammenstellen – gespeichert pro Nutzer und pro Seite, auf der der Player eingebettet ist. Auch die zuletzt eingestellte Lautstärke wird pro Nutzer gemerkt. Der Player bietet dafür eigene Reiter: „Alle Tracks“, „Favoriten“ und „Meine Playlists“.

    Gestaltung


    Ein eigenes Custom-CSS-Feld im Dashboard erlaubt Anpassungen am Frontend-Design zusätzlich zur mitgelieferten Standardvorlage.

    Shortcodes

    ShortcodeBeschreibung
    weinhold_media_playerRendert den Player. Attribute: playlists (kommagetrennte Playlist-IDs, Standard alle), show_playlist_names (1/0), show_playlist_icons (1/0), collapsed (1/0, Liste initial eingeklappt starten)

    Beispiel: nur bestimmte Playlisten und eingeklappt starten: playlists=“1,3,5″ collapsed=“1″.

    Dateien

    modules/media_player.php – einzige PHP-Datei des Moduls: Einstellungen, Track-Aufbereitung, AJAX-Handler für Favoriten/Lautstärke/eigene Playlisten, Shortcode-Rendering, Admin-Oberfläche, Cleanup. Frontend-Player-Logik in assets/weinhold_media_player_front.js, Admin-UI (Mediathek-Auswahl, Drag&Drop) in assets/weinhold_media_player_admin.js.

    Für Entwickler

    Datenhaltung


    Playlisten/Tracks liegen als verschachteltes Array in der Option weinhold_media_player_settings (kein eigenes DB-Schema, Tracks referenzieren Anhänge der WordPress-Mediathek per media_id). Nutzerbezogene Daten (Favoriten, eigene Playlisten, Lautstärke) liegen als User-Meta, jeweils zusätzlich nach Blog-ID und – bei Favoriten/eigenen Playlisten – nach page_context getrennt (weinhold_media_player_get_page_context_key(): Beitrags-/Seiten-ID, sonst ein Hash der Request-URI).

    AJAX-Endpunkte


    ActionZweck
    weinhold_media_player_toggle_favoriteTrack als Favorit markieren/entfernen
    weinhold_media_player_save_volumeLautstärke des Nutzers speichern
    weinhold_media_player_save_user_playlistsEigene Playlisten des Nutzers speichern (als JSON übergeben)

    Alle drei erfordern einen eingeloggten Nutzer und einen Nonce, kein nopriv-Pendant.

    Datenübergabe ans Frontend


    Der komplette Player-Zustand (Tracks, Playlisten, eigene Playlisten, Favoriten, Lautstärke, Login-Status) wird als JSON im data-media-player-Attribut des Wrapper-Divs ausgegeben, nicht per wp_localize_script() – das Frontend-JS liest es direkt aus dem DOM.

    Besonderheiten

    Cover-Bild-Fallback: Ist für einen Track kein eigenes Cover hinterlegt, wird automatisch das Vorschaubild der Mediendatei selbst als Cover verwendet (falls vorhanden), bevor ganz auf ein Cover verzichtet wird.

    Legacy-Migration bei Favoriten: Ältere Installationen speicherten Favoriten ohne page_context-Trennung. Beim ersten Zugriff auf eine noch nicht existierende page-context-spezifische Favoritenliste wird automatisch auf den alten, globalen Schlüssel zurückgegriffen und in den neuen migriert.

    Cleanup entfernt Nutzer-Metadaten musterbasiert per SQL LIKE (z. B. alle weinhold_media_player_playlists_{blog_id}_*-Einträge über alle Nutzer hinweg), nicht nur die globalen Plugin-Einstellungen – wichtig, weil pro Nutzer und Seitenkontext eigene Meta-Keys entstehen, die sich nicht einzeln aufzählen lassen.

    Versionshistorie

    VersionDatumÄnderung
    1.0.022.09.2026Einführung der Versionierung.


  • charakterboegen


    Charakterbögen

    PPNP – Welten, Trainer, Pyxlinge

    Das Charakterbögen-Modul verwaltet und zeigt Charakterbögen für das Pen-&-Paper-Rollenspiel PPNP („Pen, Paper and Pyxlinge“, inspiriert von Pokémon) als strukturiertes Frontend. Es bildet die vollständige Datenhierarchie ab: Welten enthalten Trainer, Trainer besitzen Pyxlinge.

    Shortcode

    [[weinhold_character_sheets]] – Rendert die komplette Charakterbögen-Übersicht auf einer WordPress-Seite. Die Darstellung ist für Desktop und Mobilgeräte optimiert.

    Datenverwaltung

    Welten, Trainer und Pyxlinge werden über das Admin-Dashboard gepflegt. Jede Entität kann mit eigenen Eigenschaften, Bildern und Werten versehen werden. Die Daten werden in einer eigenen Datenbankstruktur gespeichert – völlig unabhängig von WordPress-Beiträgen.

    Pro Site

    Das Modul ist pro Subsite konfiguriert. Verschiedene Subsites können unterschiedliche Welten und Kampagnen verwalten, ohne sich gegenseitig zu beeinflussen.

    Versionshistorie

    1.0.1 – 28.10.2026 – Rebranding: Hub-Untertitel nennt Pyxlinge statt Pokémon.

    1.0.0 – 22.09.2026 – Einführung der Versionierung.