Contact Form 7, ein Honeypot und eine Idee

Ich nutze Contact Form 7 als Kontaktformular bei sehr vielen Kunden. Es ist einfach, flexibel und es gibt auch diverse Zusatzplugins inzwischen dafür. Für den Spamschutz bietet es Integrationen mit Akismet, Google reCaptcha und die interne Stoppliste (aus Einstellungen -> Diskussionen). Da alle drei Varianten ihre Nachteile haben, nutze ich meist das Honeypot-Plugin. Es hat jedoch einen großen Nachteil. Ich erkenne erst dann, ob der Honeypot eingebaut ist, wenn ich das Formular öffne. Daher hatte ich die Idee eine zusätzliche Spalte hinzuzufügen. Aber das ist komplizierter als gedacht …

Das Problem ist, dass Contact Form 7 die Übersichtsseite gar nicht als „Custom Post Type“-Liste baut. Es wird in class-contact-forms-list-table.php die Klasse WP_List_Table extended und eine eigene Liste definiert.

Die Methode, die die Spalten dieser Liste definiert wird wie folgt aufgerufen:

add_filter( 
'manage_' . $current_screen->id . '_columns', 
array( 'WPCF7_Contact_Form_List_Table', 'define_columns' ), 
10, 0
);

Wobei die Screen-ID toplevel_page_wpcf7 lautet.

Also machte ich mich auf die Suche, wie ich denn jetzt eine zusätzliche Spalte hinzufügen kann. Ich hatte ja schon erwähnt, dass ich kein großer Entwickler bin. Also schaue ich mir häufig ab, wie andere etwas machen. Dank Open-Source-Lizenzen und toller Community-Projekte geht das ganz gut. Ein Blick auf WPDirectory.net mit der Suche manage_(.*)wpcf7(.*)_columns zeigt mir ein paar Plugins. Einige enthalten nur Demo-Inhalte, aber es werden auch ein paar Plugins gefunden, die tatsächlich wohl das machen, was ich vorhabe.

Also versuche ich den Code zu extrahieren, der die Spalten hinzufügt. Aber es klappt bei mir nicht. Je tiefer ich in den Code des Plugins eintauche, desto mehr bemerke ich, dass hier irgendwas falsch läuft. Es ist viel zu kompliziert. Und dann dämmert es mir. Das Plugin baut einen neuen Menüpunkt ein, benutzt eine Custom-Post-Type-Liste und entfernt das Standardmenü von Contact Form 7. Aber warum?

Ich versuche es weiter … und habe erste Erfolge.

Über den manage_{$screen->id}_columns-Filter kann ich schon mal meine zusätzliche Spalte hinzufügen:

function define_honeypot_columns( $columns ) {
	$columns['cf7_honeypot'] = 'Honeypot';
	return $columns;
}
add_filter( 'manage_toplevel_page_wpcf7_columns', 'define_honeypot_columns', 20, 1 );

Das CSS ist statisch vorgegeben, daher muss ich mir für meine zusätzliche Spalte ein wenig Platz schaffen:

function modify_cf7_table() {
	echo '<style>
	/*
	 * CF7 List Table
	 */
	.fixed .column-title {
		width: 33%;
	}

	.fixed .column-shortcode {
		width: 33%;
	}
	  </style>';
}
add_action( 'admin_head', 'modify_cf7_table' );

Das sollte am Ende noch so ergänzt werden, dass es nur auf der CF7-Seite eingebunden wird. Aber zum Testen reicht es erst einmal.

Nun fehlt aber noch der Inhalt. Bei einem Custom Post Type habe ich zwei Filter. Zum einen manage_{$post_type}_posts_columns für die Spaltentitel und manage_{$post->post_type}_posts_custom_column als Action Hook für die Inhalte.

Nun gibt es aber keinen analogen Hook für manage_{$screen->id}_columns. Es gibt kein manage_{$screen->id}_custom_columns. Warum?

Ich finde einen Artikel von WP Engineer, der beschreibt, was Contact Form 7 hier macht. Aber ich finde hier keinen Hinweis, wie ich darauf zugreifen kann. Denn abweichend von dem Artikel wird die Klasse nicht in einer Variablen gespeichert, so dass ich keinen einfachen Zugriff auf die Instanz habe.

Interessant auch die Warnung zur Nutzung der Klasse:

Note: This class’s access is marked as private. That means it is not intended for use by plugin and theme developers as it is subject to change without warning in any future WordPress release. If you would still like to make use of the class, you should make a copy to use and distribute with your own project, or else use it at your own risk.

Das Problem ist, dass Contact Form 7 in seiner Klasse keine Filter eingebaut hat. Daher greifen hier keine der üblichen Filtermöglichkeiten.

Lässt sich das überhaupt lösen? Ich kann die Klasse nochmal extenden, aber das ändert ja nichts an CF7. Da Contact Form 7 ja weiterhin die eigene Klasse nutzt.

Eigentlich wäre die Idee gewesen, am Ende via Suchfunktion zu testen, ob in dem Kontaktformular der Honeypot-Shortcode verbaut ist und dann ein Checkmark (oder Text) anzuzeigen. Dann wäre der Einbau direkt auf der Übersicht für alle Formulare direkt sichtbar und man müsste nicht jedes Formular nochmal aufrufen.

So langsam verstehe ich warum das Plugin, dass ich am Anfang gefunden habe, hier die Liste der Formulare manuell nachgebaut hat. Weil es keine (einfache) Möglichkeit gibt, die Tabelle anzupassen. Ich habe das mal als Issue erstellt. Mal schauen, ob der Entwickler willig ist, hier einen Filter (oder auch mehr) einzubauen, um so die Seite weiter anpassen zu können.

Habe ich was übersehen? Gibt es doch einen Filter, den ich nutzen kann? Meinetwegen auch nicht als eigene Spalte, sondern angehängt an den Titel … oder ist die einzige Möglichkeit wirklich eine eigene Liste? Ich freue mich über eure Ideen dazu in den Kommentaren.

Update: Habe beim Plugin mal nach zusätzlichen Filtern gefragt: https://github.com/takayukister/contact-form-7/issues/402
Und auch die Idee beim Honeypot-Plugin vorgestellt: https://github.com/nocean/cf7-honeypot/issues/15

3 Antworten auf Contact Form 7, ein Honeypot und eine Idee

  1. Hey Torsten,

    eventuell geht es doch über eine neue Kindklasse (meine Antwort im DEWP-Slack hat sich damit erübrigt 🙂 ) : du könntest versuchen, über `remove_filter()` den Filter zu entfernen, der `array( ‚WPCF7_Contact_Form_List_Table‘, ‚define_columns‘ ),` aufruft und dann einen eigenen `add_filter()`-Aufruf machen, dem du deine Klasse übergibst.

    Ich bin mir nicht sicher, ob das irgendwelche Nebeneffekte haben könnte, aber zumindest im GitHub-Repo scheint diese `WPCF7_Contact_Form_List_Table`-Klasse nur von dem Filter genutzt zu werden.

    Viele Grüße
    Florian

    • Leider ist define_columns gar nicht mein Problem. Das definiert ja nur die Spaltenheader und die erwische ich ja mit dem manage_{$screen->id}_columns Filter. Ich kann ja mich ja einfach beim Filter dahinter einhängen. Ich muss aber den Inhalt der Spalte definieren und das passiert in der Klasse über column_$custom Funktionen, die dann den Inhalt schreiben. Ich müsste der Klasse eine weitere Funktion hinzufügen, aber da scheitere ich daran, wie ich auf die Klasse Zugriff habe. Ich sehe nicht, dass da irgendwo die Klasse in eine Variabel instanziert wird mit der ich auf die Klasse zugreifen kann.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.