Filter und Hooks
Das Plugin hat Hooks an den Stellen, wo ein Verein, eine Agentur oder ein Add-on etwas anders haben will: Wörter, Reihenfolgen, Seitengrößen, Prüfungen. Diese Seite nennt die Hooks, die für andere gedacht sind. Jeder hat im Quelltext einen Docblock mit seinen Parametern.
Alle Beispiele gehören in die functions.php des Childs oder in ein Add-on. Die Namen der Hooks haben das Präfix wpce_. Die Hooks des Themes haben das Präfix wpce_theme_ und stehen am Ende.
Was nicht auf der Seite steht. Hooks ohne Docblock und solche, die für Tests und Vorschau gemacht sind, sind intern und können sich ändern. Dazu gehören
wpce_entries_nowundwpce_notice_now(der Zeitpunkt, den die Blöcke für „jetzt“ nehmen) undwpce_template_blocks_offered(welche Blöcke der Block-Einfüger anbietet). Verlass dich nicht auf sie.
Für einen Test hat sich ein Weg bewährt, der ohne Datenbank auskommt: Einen Filter mit add_filter() vor dem Laden von WordPress anmelden (nach wp-includes/plugin.php, vor wp-load.php) und die Funktion aufrufen, die ihn auslöst. So sind die Beispiele unten geprüft, soweit dabei geprüft steht.
Wörter und Namen
Abschnitt betitelt „Wörter und Namen“Ein Verein nennt einen Kurs „Dienst“ oder eine Abteilung „Gruppe“. Für die häufigen Fälle bringt das Plugin seit 1.0.0 fertige Wörter mit: Der Verein wählt in den Vereinsdaten die „Art des Vereins“ (Sportverein, Feuerwehr, Musikverein, soziale Einrichtung, anderer Verein), siehe Name und Logo. Dafür hängt das Plugin sich mit Priorität 5 an die ersten vier Filter dieser Tabelle. Dein Filter hat das letzte Wort, sobald du ihn mit der üblichen Priorität 10 anmeldest. Alles, was die Art des Vereins nicht kennt, setzt du selbst.
| Hook | Parameter | Wirkung |
|---|---|---|
wpce_post_type_labels |
array $labels, string $post_type |
Beschriftungen eines Inhaltstyps: alles, was WordPress nutzt (Menüpunkt, „Keine Kurse gefunden“), und die Wörter der Seiten-Vorlagen |
wpce_taxonomy_labels |
array $labels, string $taxonomy |
dasselbe für Taxonomien: Aus „Kursart“ wird „Dienstart“ |
wpce_field_label |
string $label, string $key, string $context |
Beschriftung eines einzelnen Feldes, $context ist admin (Meta-Box) oder editor (Block-Editor) |
wpce_meta_box_title |
string $title, string $box_id, string $post_type |
Titel einer Meta-Box |
wpce_template_section_head |
array $head, string $post_type, string $section |
Kicker, Überschrift und Satz eines Abschnitts einer Seite; $head hat die Schlüssel kicker, heading, lead |
// Aus Kursen werden Dienste; die Überschriften der Seiten folgen daraus.add_filter( 'wpce_post_type_labels', static function ( array $labels, string $post_type ): array { if ( 'wpce_course' === $post_type ) { $labels['name'] = 'Dienste'; $labels['singular_name'] = 'Dienst'; } return $labels;}, 10, 2 );
// Aus „Leitung“ wird „Ausbilder“. Der Filter bekommt den Meta-Key.add_filter( 'wpce_field_label', static fn( string $label, string $key ): string => '_wpce_course_coach' === $key ? 'Ausbilder' : $label, 10, 2 );
// Der Abschnitt „Personen“ auf der Seite eines Kurses.add_filter( 'wpce_template_section_head', static function ( array $head, string $post_type, string $section ): array { if ( 'wpce_course' === $post_type && 'people' === $section ) { $head['kicker'] = 'Dein Team'; $head['heading'] = 'Wer leitet das?'; } return $head;}, 10, 3 );(Das erste Beispiel setzt Wörter, die „Feuerwehr und Rettungsdienst“ schon mitbringt. Für Wörter, die keine Art kennt, geht es genauso.)
Geprüft: Mit dem ersten Filter heißt der Typ „Dienste“ und die abgeleitete Beschriftung für „Alle …“ „Alle Dienste“. Mit dem zweiten gibt das Feld „Ausbilder“ zurück, mit dem dritten bekommt der Abschnitt people die neuen Wörter.
Zwei Dinge dazu:
- Die Überschriften der Seiten werden aus den Labels gebaut, nicht aus festen Wörtern. Der Filter für die Post-Type-Labels bekommt alle Labels. Die zusätzlichen Schlüssel für die Seiten (
wpce_all,wpce_more,wpce_upcomingund weitere) leitet das Plugin nach dem Filter aus dem Plural ab, wenn du sie nicht selbst setzt. - Ein Label aus einem einzelnen Leerzeichen lässt seine Zeile im Abschnittskopf weg.
Setzt ein anderes Plugin denselben Schlüssel mit derselben Priorität, entscheidet die Reihenfolge der Anmeldung. Gib deinem Filter 99, wenn du das letzte Wort willst.
Add-ons und Daten
Abschnitt betitelt „Add-ons und Daten“| Hook | Art | Parameter | Wirkung |
|---|---|---|---|
wpce_register_post_types |
Aktion | – | hier meldet ein Add-on seine Post Types an, siehe Eigene Inhaltstypen |
wpce_entry_types |
Filter | array $types |
die Inhaltstypen des Listen-Blocks und ihre Vorbelegungen (Layout, Reihenfolge, Form des Bildes, Felder) |
wpce_entry_problems |
Filter | Problem[] $problems, WP_Post $post, array $values |
die Probleme, die nach dem Speichern zu einem Eintrag gefunden wurden; hier hängst du eigene Prüfungen an oder entfernst eine |
wpce_upserted |
Aktion | int $id, string $post_type, string $source, string $external_id |
feuert, wenn Identity::upsert() einen Eintrag samt aller Felder geschrieben hat. Die Hooks von WordPress feuern früher, bevor die Felder da sind |
// Kurse im Listen-Block standardmäßig als Tabelle.add_filter( 'wpce_entry_types', static function ( array $types ): array { $types['wpce_course']['layout'] = 'table'; return $types;} );
// Eine eigene Prüfung: Ein Eintrag braucht einen Hinweis, wenn er kein Titelbild hat.add_filter( 'wpce_entry_problems', static function ( array $problems, WP_Post $post ): array { if ( ! has_post_thumbnail( $post ) ) { $problems[] = new \WPCE\Plugin\Validation\Problem( 'post_title', 'Titelbild', 'Es gibt noch kein Titelbild.', \WPCE\Plugin\Validation\Problem::HINT, 'no_image' ); } return $problems;}, 10, 2 );Geprüft: Der erste Filter macht aus dem Layout der Personen list (so im Test, mit dem Typ der Personen statt der Kurse), und der zweite hängt ein Problem mit dem Code test an die Probleme eines Kurses. Der Konstruktor von Problem hat sechs Parameter: field, label, message, severity (Problem::WARNING oder Problem::HINT), code und anchor.
Der Listen-Block
Abschnitt betitelt „Der Listen-Block“| Hook | Parameter | Wirkung |
|---|---|---|
wpce_archive_per_page |
int $per_page, string $post_type |
wie viele Einträge eine Seite einer Übersicht zeigt (Standard: Kurse, Personen, Abteilungen, Partner 100, Standorte 50, Termine 30, sonst 24) |
wpce_hide_undated_entries |
bool $hide, WP_Query $query |
ob Listen der Website Einträge ohne Tag auslassen. false listet alle |
wpce_hide_incomplete_entries |
bool $hide, WP_Query $query |
ob die Website Einträge auslässt, denen ihre eine nötige Angabe fehlt: eine Person ohne Namen, ein Mitgliedsbeitrag ohne Betrag. false listet alle. Das Backend, WP-CLI, Cron und die Migrationen sehen immer alles |
wpce_entries_month_groups_from |
int $rows, string $post_type |
ab wie vielen Zeilen eine Terminliste Monatsköpfe hat. 1 gibt jeder Liste ihre Monate |
wpce_entries_term_rank |
int $rank, WP_Term $term |
die Stelle eines Begriffs unter den Überschriften einer gegliederten Liste: kleiner steht weiter vorn, bei gleichem Rang entscheidet der Name |
wpce_term_says_nothing |
bool $nothing, string $taxonomy |
ob die Begriffe einer Taxonomie in Kennzeichen und Fakten wegfallen, weil jeder Eintrag denselben trägt |
wpce_entry_facts_defaults |
string[] $ids, string $post_type |
die Zeilen, die die Faktenbox eines Inhaltstyps standardmäßig zeigt (zum Beispiel meta:_wpce_course_term_details, tax:wpce_course_type) |
// Übersichten der Termine: zwölf je Seite.add_filter( 'wpce_archive_per_page', static fn( int $n, string $post_type ): int => 'wpce_event' === $post_type ? 12 : $n, 10, 2 );
// Termine ohne Tag auch auf der Website zeigen.add_filter( 'wpce_hide_undated_entries', '__return_false' );
// Auch Personen ohne Namen und Beiträge ohne Betrag zeigen.add_filter( 'wpce_hide_incomplete_entries', '__return_false' );
// Die Faktenbox eines Kurses zeigt nur die ersten beiden Zeilen.add_filter( 'wpce_entry_facts_defaults', static fn( array $ids, string $post_type ): array => 'wpce_course' === $post_type ? array_slice( $ids, 0, 2 ) : $ids, 10, 2 );Geprüft: Der Filter für die Seitengröße liefert für Termine 12 und für Kurse weiterhin 100. Mit __return_false meldet UndatedEntries::hides() false. Die Faktenbox eines Kurses zeigt statt vier Zeilen die beiden ersten.
Die Filter für Rang, Monatsgruppen, wpce_term_says_nothing und wpce_hide_incomplete_entries sind nur gegen den Quelltext geprüft.
Seiten der Inhaltstypen
Abschnitt betitelt „Seiten der Inhaltstypen“Das Plugin liefert Einzelseite und Übersicht für Kurs, Termin, Person, Standort, Abteilung und Label, dazu die Übersicht je Begriff ihrer Taxonomien. Diese Hooks gehören einem Theme.
| Hook | Parameter | Wirkung |
|---|---|---|
wpce_template_pattern_content |
string $content, string $kind, string $post_type, string $slug |
der Inhalt eines Pattern-Templates des Plugins. $kind ist single oder archive. Hier stellst du Blöcke voran oder hängst sie an, ohne das Pattern zu ersetzen |
wpce_page_band_class |
string $css_class |
die Klasse, die jedes zweite Band einer Seite trägt. Leer: Das Plugin zeichnet das Band aus seinen Token |
wpce_page_kicker_class |
string $css_class |
die Klasse des Themes für die Dachzeile eines Abschnitts. Leer: keine |
wpce_page_brief_text |
int $length |
bis zu wie vielen Zeichen ein Text als kurz gilt. Die Faktenbox steht dann als Reihe über dem Text. 0 lässt sie neben jedem Text |
wpce_entry_head_breadcrumbs |
array $attributes, string $post_type |
Attribute des Blocks core/breadcrumbs im Seitenkopf, damit der Pfad so aussieht wie auf den Seiten des Themes |
wpce_entry_head_image_layout |
string $layout, WP_Post $post, int $width, int $height |
wie das Titelbild im Seitenkopf steht: aside (neben dem Text, Standard) oder band (darunter, so breit wie der Kopf) |
// Jede Personenseite endet mit dem Hauptknopf-Band des Themes.// Auf Kurs, Termin und Abteilung hängt das Theme dieses Band schon selbst an.add_filter( 'wpce_template_pattern_content', static function ( string $content, string $kind, string $post_type ): string { return 'single' === $kind && 'wpce_person' === $post_type ? $content . '<!-- wp:pattern {"slug":"wpce-theme/cta-band"} /-->' : $content;}, 10, 3 );
// Der Pfad im Seitenkopf: klein, mit einem eigenen Trenner.add_filter( 'wpce_entry_head_breadcrumbs', static fn( array $attributes ): array => $attributes + array( 'fontSize' => 'small', 'separator' => '›' ) );wpce_template_pattern_content läuft einmal je Anfrage, wenn die Patterns registriert werden (init, Priorität 25). Melde den Filter vor diesem Zeitpunkt an. Was er zurückgibt, steht im <main class="wpce-page"> des Templates und ist das, was der Website-Editor zeigt. Auf ein Pattern, das das Theme unter demselben Slug selbst registriert hat, wird er nicht angewandt.
Geprüft: Mit dem Filter steht der angehängte Pattern-Block im Pattern wpce/template-single-course, und nicht im Pattern der Übersicht.
Karte, Kontakt, Suchmaschinen
Abschnitt betitelt „Karte, Kontakt, Suchmaschinen“| Hook | Parameter | Wirkung |
|---|---|---|
wpce_map_autoload |
bool $autoload, array $address |
ob die Karte ohne Nachfrage lädt, wie die Einstellung „Karten ohne Nachfrage laden“. Die Antwort steht im Markup und damit in jedem Seiten-Cache. Sie darf nicht vom Besucher abhängen |
wpce_map_consent_managed |
bool $managed |
ob ein Consent-Werkzeug über das Laden der Karte entscheidet. Das Werkzeug muss window.wpceConsent auf der Seite bereitstellen |
wpce_map_preview_daily_limit |
int $limit |
wie viele Vorschaubilder der Karte die Website je 24 Stunden höchstens zusammensetzt (Standard 20, mindestens 1). Die 24 Stunden sind ein gleitendes Fenster: Jedes neue Bild verlängert den Zähler um weitere 24 Stunden, nach dem Erreichen der Grenze läuft er 24 Stunden nach dem letzten gezählten Bild ab. Jedes Bild holt rund zwölf Kartenkacheln vom Kartenanbieter, siehe Karte und Datenschutz |
wpce_whatsapp_calling_code |
string $code, string $country |
die Landesvorwahl, die vor nationale WhatsApp-Nummern („0170 …“) gesetzt wird, ohne „+“. Leer: Die Nummer bekommt keinen Link |
wpce_json_ld |
array $data, array $club |
die strukturierten Daten des Vereins. @type folgt der „Art des Vereins“ (SportsClub, EmergencyService, PerformingGroup, NGO oder Organization; ohne Wahl SportsClub). Ein leeres Array druckt nichts, auch die Website-Angabe nicht. Das Plugin schreibt <, > und & als Escape-Folge in das Skript-Element, ein Wert mit </script> bricht die Ausgabe darum nicht ab |
wpce_json_ld_graph |
array $graph |
der ganze Graph (@context und @graph): zuerst der Verein, danach die Website. Er umschließt wpce_json_ld. Ein leeres Array oder ein Graph ohne @graph druckt nichts |
wpce_seo_plugins |
array $known |
die SEO-Plugins, bei denen der Reiter „Website“ der Einstellungen warnt, als Liste Konstante => Name (ab Werk Yoast SEO, Rank Math SEO, SEOPress, All in One SEO, The SEO Framework, Slim SEO). Ein SEO-Plugin gilt als aktiv, wenn seine Konstante definiert ist |
add_filter( 'wpce_json_ld', static function ( array $data ): array { $data['slogan'] = 'Wir bewegen Musterstadt'; return $data;} );
// Nationale WhatsApp-Nummern eines Vereins in Liechtenstein. Ab Werk kennt das Plugin DE, AT und CH.add_filter( 'wpce_whatsapp_calling_code', static fn( string $code ): string => '' === $code ? '423' : $code );Geprüft: Mit dem ersten Filter trägt ClubJsonLd::data() den Schlüssel slogan neben @type: SportsClub (so lange die Art des Vereins nicht geändert ist). wpce_whatsapp_calling_code, wpce_map_autoload, wpce_map_consent_managed, wpce_map_preview_daily_limit und wpce_seo_plugins sind nur gegen den Quelltext geprüft.
Links zur Doku im Backend
Abschnitt betitelt „Links zur Doku im Backend“Das Plugin führt von seinen Bildschirmen auf diese Doku: „Hilfe zu dieser Seite“ oben rechts auf Listen, Masken und Kategorien der Inhaltstypen, auf „Vereinsdaten“ und den Einstellungen (dort folgt das Ziel dem offenen Reiter), „Hilfe zu diesem Block“ am Ende der Block-Einstellungen, „Hilfe zu den ersten Schritten“ im Dashboard-Kasten und „Dokumentation“ in der Plugin-Liste. Basisadresse und Zuordnung stehen an einer Stelle, WPCE\Plugin\Admin\Documentation (BASE_URL, PAGES).
| Hook | Parameter | Wirkung |
|---|---|---|
wpce_doc_base_url |
string $base_url |
die Adresse, unter der die Doku liegt (Standard https://wp-club-elements.de/doku/). Ein leerer Wert schaltet alle Links ab |
wpce_doc_url |
string $url, string $key |
die Adresse einer einzelnen Seite; leer blendet den Link aus. $key nennt die Stelle: post_type:<Post Type>, settings:<Reiter> (club, contact, bank, legal, website, permalinks, map), block:<Blockname>, block:wpce/entries:<Post Type> (der Listen-Block, der diesen Inhaltstyp zeigt), block:club (alle Vereinsblöcke), dashboard, plugin |
// Die Doku liegt als Kopie im Intranet der Agentur, mit denselben Pfaden.add_filter( 'wpce_doc_base_url', static fn(): string => 'https://hilfe.example.org/vereinswebsite/' );
// Eine Seite ersetzen, eine ausblenden, Seiten für den Typ und die Blöcke eines Add-ons ergänzen.add_filter( 'wpce_doc_url', static function ( string $url, string $key ): string { return match ( $key ) { 'settings:permalinks' => '', 'post_type:wpce_event' => 'https://hilfe.example.org/termine-bei-uns/', 'post_type:wpce_album' => 'https://hilfe.example.org/alben/', 'block:beispiel-album/album' => 'https://hilfe.example.org/album-block/', 'block:wpce/entries:wpce_album' => 'https://hilfe.example.org/alben-zeigen/', default => $url, };}, 10, 2 );Was du wissen musst:
- An die Basisadresse hängt das Plugin die Pfade dieser Doku an (
inhalte/termine/,seiten-bauen/karte/). Wer nur die Basis ändert, braucht dort dieselben Pfade. Für alles andere istwpce_doc_urlda. wpce_doc_urlläuft auch, wenn die Basis leer ist oder das Plugin für den Schlüssel keine Seite kennt ($urlist dann leer). So bekommt der Typ eines Add-ons seinen Link auf Liste, Maske und Kategorien, und sein Block den Link in den Block-Einstellungen. Gib für jeden Schlüssel, der nicht deiner ist,$urlunverändert zurück.- Blöcke. Der Block-Editor fragt den Filter für jeden Block, der auf dem Server registriert ist (mit
block.jsonundregister_block_type()) und nicht von WordPress selbst stammt (core/…). Der Schlüssel istblock:<Blockname>, zum Beispielblock:beispiel-album/album. So bekommt auch der Block eines Add-ons „Hilfe zu diesem Block“. Für den Listen-Block fragt er außerdemblock:wpce/entries:<Post Type>für jeden Inhaltstyp, der registriert ist. Ohne Antwort führt der Link zur Seite des Listen-Blocks. Die Blöcke von WordPress (core/…) bekommen absichtlich keinen Link. - Die Listen-Blöcke der Basis. „Mitgliedsbeiträge“ und „Downloads“ führen zu ihren eigenen Seiten (Die Beitragstabelle, Downloads). Du kannst auch diese Seiten ersetzen oder ausblenden.
- Reiter. Reiter der Einstellungen (
settings:<Reiter>) kennt das Plugin nur für seine eigenen. Ein Add-on hat keine Reiter auf den Einstellungsseiten, darum gibt es dafür keinen Schlüssel, den es ergänzen könnte. - Jeder Link öffnet in einem neuen Tab und sagt einem Screenreader, auf welche Website er führt.
Geprüft mit Documentation::url(): Mit dem ersten Filter liegt die Seite der Kurse unter https://hilfe.example.org/vereinswebsite/inhalte/kurse-und-trainingszeiten/. Mit dem zweiten ist die Adresse für settings:permalinks leer, die der Termine und die des Albums sind die aus dem Beispiel, und der Reiter „Karte“ behält seine Seite. Welche Schlüssel der Block-Editor für Blöcke fragt, ist gegen den Quelltext von Documentation::block_keys() gelesen, nicht in einem laufenden Editor probiert. Wo die Links im Backend stehen, ist nur gegen den Quelltext geprüft.
Die Hooks des Themes
Abschnitt betitelt „Die Hooks des Themes“wpce-theme hat eigene Filter. Sie sind im Quelltext des Themes mit einem Docblock beschrieben.
| Hook | Parameter | Wirkung |
|---|---|---|
wpce_theme_motion |
string[] $features |
die eingeschalteten Bewegungs-Effekte, siehe Die Bewegung dämpfen oder ausschalten |
wpce_theme_motion_reveal_selector |
string $selector |
die Auswahl der Elemente, die beim Scrollen einsteigen |
wpce_theme_single_closing_pattern |
string $pattern, string $post_type |
das Pattern des Themes (Dateiname ohne .php), das die Einzelseite eines Eintrags schließt; leer für keines |
wpce_theme_archive_closing_pattern |
string $pattern, string $post_type |
dasselbe für die Übersicht eines Inhaltstyps |
wpce_theme_page_head_feature_style |
int $style |
der Abschnittsstil (1 bis 5) des Kopfes einer Hauptseite mit Einleitung; Standard 4 |
wpce_theme_header_badge |
string $badge |
emergency zeigt „Notruf 112“ in der Kopfleiste, alles andere nichts |
wpce_theme_part_layout |
string $layout, string $part |
der Aufbau von Kopf oder Fuß, solange niemand im Website-Editor einen gewählt hat. $part ist header oder footer, $layout der Wert aus der Stilvariante (header.layout: bar, centered, menu-button; footer.layout: columns, row, centered), leer ohne Wert. Ein unbekannter Wert gibt den ersten der Reihe |
wpce_theme_header_over_picture |
bool $transparent |
ob der Kopf auf dem Bild liegt, mit dem eine Seite beginnt. Der Wert ist true, wenn die Stilvariante header.over-picture auf transparent setzt |
// Die Einzelseite eines Kurses endet ohne Hauptknopf-Band.add_filter( 'wpce_theme_single_closing_pattern', static fn( string $pattern, string $post_type ): string => 'wpce_course' === $post_type ? '' : $pattern, 10, 2 );Geprüft: Mit diesem Filter enthält das Pattern wpce/template-single-course das Band nicht mehr, das Pattern der Termine behält es. Auch dieser Filter wird gefragt, solange das Plugin die Patterns registriert (init:25). Melde ihn vorher an.
Was schiefgeht
Abschnitt betitelt „Was schiefgeht“- Der Filter kommt zu spät.
wpce_template_pattern_contentundwpce_theme_single_closing_patternlaufen aufinit:25. Ein Filter, den du später anmeldest, kommt nie zum Zug. - Ein anderer Filter ist schneller. Gleiche Priorität heißt: Wer später angemeldet ist, gewinnt. Gib deinem Filter eine hohe Priorität (
99), wenn du das letzte Wort willst. - Der falsche Rückgabetyp.
wpce_entry_problemsdarf nurProblem-Objekte enthalten, alles andere filtert das Plugin heraus.wpce_entry_typesbekommt unvollständige Definitionen zurück ergänzt, aber ein unbekannter Post Type fällt weg. - Ein Filter, der vom Besucher abhängt. Das gilt für
wpce_map_autoload: Die Antwort landet im Seiten-Cache und gilt dann für alle.