Zum Inhalt springen

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_now und wpce_notice_now (der Zeitpunkt, den die Blöcke für „jetzt“ nehmen) und wpce_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.

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_upcoming und 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.

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.

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.

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.

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.

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 ist wpce_doc_url da.
  • wpce_doc_url läuft auch, wenn die Basis leer ist oder das Plugin für den Schlüssel keine Seite kennt ($url ist 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, $url unverändert zurück.
  • Blöcke. Der Block-Editor fragt den Filter für jeden Block, der auf dem Server registriert ist (mit block.json und register_block_type()) und nicht von WordPress selbst stammt (core/…). Der Schlüssel ist block:<Blockname>, zum Beispiel block:beispiel-album/album. So bekommt auch der Block eines Add-ons „Hilfe zu diesem Block“. Für den Listen-Block fragt er außerdem block: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.

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.

  • Der Filter kommt zu spät. wpce_template_pattern_content und wpce_theme_single_closing_pattern laufen auf init: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_problems darf nur Problem-Objekte enthalten, alles andere filtert das Plugin heraus. wpce_entry_types bekommt 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.