Archiv der Kategorie: Action und Filter

Custom Post Type Kochbuch: das Feintuning, mit Custom Taxonomies

Verwendung des Excerpts/Auszugs

Zuallererst möchte ich einstellen, dass man bei den Kochbüchern in der Blog View nicht den ganzen Inhalt angezeigt bekommt, der ist nämlich im Zweifelsfall recht lang, und ausserdem mit mehreren großformatigen Bildern versehen. Dafür gibt es in WordPress die Funktion des Auszugs (Engl. Excerpt), damit kann man einen kurzen Einführungstext erstellen, der dann z.B. in der Liste der Suchergebnisse statt dem ganzen Beitrag angezeigt wird. Den Auszug hatten wir in unserer Post-Type-Definition aus dem vorigen Beitrag noch nicht mit drin, das ist aber schnell nachgeholt. Wir ergänzen in der functions.php unseres Childthemes die Post-Type-Definition wie folgt:

function post_type_kochbuch() {
    register_post_type(
                'kochbuch',
                array(
                    'label' => __('Kochbuch'),
                    'public' => true,
                    'show_ui' => true,
                    'supports' => array(
                    'title',
                    'editor',
                    'excerpt',
                    'post-thumbnails',
                    'custom-fields',
                    'revisions')
                )
        );
    
     
}

Damit wird im Beitragseditor unter dem Content-Feld jetzt das Feld Auszug mit angezeigt, und wir können einen Kurztext eingeben. Wie und wo überall der angezeigt wird ist vom Theme abhängig, ich arbeite hier mit einem Child von Twenty Twelve, da geht man es so an:

Dafür klemmt man sich die content.php und legt eine Kopie ins ChildTheme-Verzeichnis. Dann macht man sich auf die Suche nach der Codezeile:

<?php if ( is_search() ) : // Only display Excerpts for Search ?>

Die ändert man wie folgt:

<?php if ( is_search() || is_home() ) : // Display Excerpts for Search and Homepage ?>

Und schon tauchen auf der Kochbücher-Seite nur noch die Titel und die Auszüge auf. Kleiner Pferdefuß: das gilt jetzt auch für die Rezepte-Blogseite, auch dort werden nur noch Titel und Excerpts(falls vorhanden) angezeigt – ist mir aber ganz recht so, ich finde das wesentlich übersichtlicher. Ich schau aber mal, ob ich da noch eine genauere Steuerungsmöglichkeit finde.

Kategorien und Schlagwörter für die Kochbücher

Dafür legen wir uns zwei Custom Taxonomies an, die nur für den Post Type Kochbuch gelten. Das ist nicht weiter kompliziert, hier der Eintrag für die functions.php:

function kochbuch_create_my_taxonomy() {

    register_taxonomy(
        'land',
        'kochbuch',
        array(
            'label' => __( 'Land' ),
            'rewrite' => array( 'slug' => 'land' ),
            'query_var' => 'land',
            'show_ui' => true,
            'hierarchical' => true,
        )
    );
      register_taxonomy( 
      'kochbuch-stichwort', 
      'kochbuch',
        array(
             'hierarchical' => false,
             'label' => __('Kochbuch-Stichwort'),
             'query_var' => 'kochbuch-stichwort',
             'show_ui' => true,
             'rewrite' => array('slug' => 'kochbuch-stichwort' )
        )
    );
}
add_action( 'init', 'kochbuch_create_my_taxonomy' );

Rot markiert sind die Einträge für die Benennungen und die Slugs der jeweiligen Taxonomie. Die grün markierte Option ‚kochbuch‚ am oberen Ende der Taxonomie-Definition sorgt dafür, daß die Taxonomie auch nur für den Post Type Kochbuch herangezogen wird. Der einzige Unterschied zwischen den beiden Taxonomien ist, dass ich für die Land-Taxonomie ‚hierarchical’=>true eingestellt habe, das erlaubt eine Schachtelung der Länder. Für die Kochbuch-Stichworte steht hierarchical => false, das ergibt eine Ebene wie man es von den Stichworten in Worpress gewohnt ist.

Das sollte schon genügen, um unsere Custom Taxonomies funktional zu machen:

custom_taxonomies

custom_taxonomies

Ich hatte übrigens beim Testen am Anfang das Problem, dass neue Taxonomie-Einträge sich nicht abspeichern liessen. Die Ursache war ein Plugin-Konflikt! Ich hatte auf der Testumgebung noch einen Haufen experimentelle Plugins am Laufen, die hab ich deaktiviert, dann hat es ganz normal funktioniert. Die Wurzel des Problems war anscheinend eine Schlamperei meinerseits, ich hatte in einigen Plugins vor dem öffnenden <?php-Tag Whitespaces drin, das mag WordPress gar nicht, da gibt es die bekannte Meldung „Das Plugin verursachte X Zeichen unerwartete Ausgabe…“

Wie man die neuen Taxonomien ins Template einfügt

Das hängt wieder ziemlich vom Theme ab, da muss man ein bisschen rumprobieren. Die Taxonomie Land bekommt man mit folgender Codezeile:

<div class="entry-meta">
            <?php echo get_the_term_list( $post->ID, 'land', 'Rezepte aus aller Welt: ', ', ', '' ); ?>
            
        </div>

Ich hab da gleich mal die div class= „entry meta“ mitgenommen, damit die Formatierung wie bei den WordPress-Standardkategorien ausfällt. Die Codezeile kommt in die content.php ins Child-Theme, und zwar zwischen dem Block für den entry-content und dem footer:

</div><!-- .entry-content -->
        <?php endif; ?>
        <div class="entry-meta">
            <?php echo get_the_term_list( $post->ID, 'land', 'Rezepte aus aller Welt: ', ', ', '' ); ?>
            
        </div>
        <footer class="entry-meta">

In der Blogansicht sieht das dann so aus:

land_anzeige

land_anzeige

Und so sieht es aus, wenn man auch noch die Stichworte an der selben Position ausgibt:

<div class="entry-meta">
            <?php echo get_the_term_list( $post->ID, 'land', 'Rezepte aus aller Welt: ', ', ', '' ); ?><br>
            <?php echo get_the_term_list( $post->ID, 'kocbuch-stichwort', 'Stichworte: ', ', ', '' ); ?>
            
        </div>

(im kocbuch-Stichwort ist ein Schreibfehler, bitte ignorieren)

Nicht vergessen: Permalinks updaten!

Sonst kriegt man die 404-Meldung, wenn man auf einen der Taxonomy-Einträge klickt. Unser Endergebnis sieht jetzt in der Blog-Ansicht so aus:

blogview_kochbuch

blogview_kochbuch

Wir haben: den Titel, den Excerpt, die Länder und die Stichworte. Paßt!

Jetzt möchte ich noch gern meinen neuen Post Type Kochbuch in einem Widget „Die neuesten Kochbücher“ ausgeben, und die Kochbuch-Länder ebenfalls, aber dazu gibt es einen neuen Beitrag.

Ne kleine Fingerübung: Beitragshitparade mit AJAX-Refresh

Weil ich mich dabei erwischt habe, dass ich immer wieder F5 drücke, wenn ich meine Beitragshitparade anschaue, bin ich auf die Idee gekommen, den Refresh der Anzeige alle x Sekunden automatisch einzubauen. Das geht in WordPress ganz flott, hatten wir auch alles so ähnlich schonmal, aber weil man so etwas immer wieder mal brauchen kann, hier der Code.

Wir basteln uns dafür natürlich ein Plugin

In das kommt zuallererst ein sehr kurzer Shortcode, der macht eigentlich nichts anderes als eine benannte Div  für die Ausgabe bereitzustellen.

<?php
/*
Plugin Name: Ajax Hitparade
Plugin URI: http://localhost/wp_ajax/wp-content/plugins/wp-ajax
Description: Ausgabe der beliebtesten Beiträge mit Ajax Refresh
Version: 1.0
Author: Evi Leu
Author URI: http://www.evileu.de
*/


 function a_ausgabe(){
  return "Die 10 beliebtesten Rezepte<br><div id='ajax_ausgabe'>...</div>";
  
 }
 add_shortcode('ajax_ausgabe', 'a_ausgabe');

Dann geben wir WordPress bekannt, dass wir einen Ajax-Call ausführen wollen:

/**************ajax-action für wordpress definieren*/ 
add_action( 'wp_ajax_hitparade_action', 'hitparade_action' );
add_action( 'wp_ajax_nopriv_hitparade_action', 'hitparade_action' );


Das Javascript

Jetzt binden wir unsere js-Datei mit dem Script ein, und geben ihr mit dem wp_localize_script den Pfad zur Ajax-URL von WordPress mit:

/*********Externes Script einbinden und Pfad zur admin-ajax.php übergeben*/
function myhitparade_js() {
      
    wp_enqueue_script('myhitparade-js', plugins_url().'/ajax-hitparade/wp_ajax_hitparade.js', array('jquery'), false, false);
    
    wp_localize_script( 'myhitparade-js', 'my_ajaxurl', admin_url( 'admin-ajax.php' ) );
 
   }
add_action('wp_enqueue_scripts', 'myhitparade_js');

Die Script-Datei wp_ajax_hitparade.js liegt in unserem Plugin-Verzeichnis und sieht so aus:

$(document).ready(function() {
     setInterval(myFunction, 1000)  
       
    });
    
        
        function myFunction(){
                
        
        var data = {
            'action': 'hitparade_action',
            
            };

        // der volle Pfad zur admin-ajax.php liegt auf my_ajaxurl
        jQuery.post(my_ajaxurl, data, function(response) {
                    
            document.getElementById("ajax_ausgabe").innerHTML = response;
            
        });
        } //*********************End myFunction

Der Witz ist hier natürlich das setInterval(), das führt unsere Funktion alle X Millisekunden aus. Ansonsten geben wir der Funktion nur mit, dass sie die Callback-Funktion hitparade_action ausführen soll, es gibt keine zu übergebenden Parameter ausser der Ajax-URL. Das Ergebnis des Calls wird in die div ajax_ausgabe geschrieben.

Die Callback-Funktion

Kommt als Letztes in unser Plugin, sie ist sehr simpel und besteht nur aus einer Query auf die Tabelle Counter und einem foreach für die Ausgabe:

/********Callback-Funktion**************/
function hitparade_action() {
    global $wpdb; 
  
    $alleposts = $wpdb->get_results( "SELECT * from counter ORDER BY zaehler DESC limit 10");
    foreach($alleposts as $einpost){
        
        echo $einpost->titel." ".$einpost->zaehler."</br>";
}
    /**********************************************/
    
    
    wp_die(); // this is required to terminate immediately and return a proper response
} //**********End function hitparade_action

Das sollts gewesen sein! Das Script checkt alle 1000 Millisekunden die Datenbank neu ab und aktualisiert die Anzeige dementsprechend. Viel Spaß beim Nachbauen!

WordPress AJAX Autocomplete für Minimalisten

Ich bin ja bekanntermassen ziemlich hartnäckig, und obwohl ich die Lösung für das Autocomplete-Feld von David Nash toll finde, hats mir doch keine Ruhe gelassen. Das muss doch noch ein bisschen einfacher gehen, dachte ich mir und hab den Codex durchforstet. Zum Thema Using Ajax in Plugins bin ich fündig geworden, da stand doch ein (Codex-unüblich nachvollziehbares) Beispiel drin, über das habe ich mich hergemacht. Das Javascript für den Ajax-Call wird hier nicht als externes Script enqueued, sondern in die Plugin-Datei integriert. Ist aber nicht schlimm, das Script ist nämlich sehr überschaubar – aber jetzt mal der Reihe nach.

Die Vorgabe

Ich möchte gern ein Textfeld, in das ich Suchbegriffe eingeben kann, und das soll mir dann aus der Datenbank alle Post Titles ausgeben, in denen der Suchbegriff vorkommt. Das soll etwa so aussehen:

schoko

schoko

Wir basteln uns ein Plugin

Natürlich brauchen wir dazu wieder ein Formular, und ich werde auch wieder die HTML5-Datalist verwenden. Wir packen das Ganze in ein Plugin, damit wir vom Theme unabhängig sind, und tun Formular und Javascript in einen Shortcode, damit wir das ganze in einem beliebigen Beitrag oder auf einer beliebigen Seite auch ausgeben können.

Zuerst mal nur das Plugin ohne JScript und Ajax-Funktion:

<?php
/*
Plugin Name: WordPressAjax Testplugin
Plugin URI: http://localhost/wp_ajax/wp-content/plugins/wp-ajax
Description: Zum Testen von Ajax mit WordPress
Version: 1.0
Author: Evi Leu
Author URI: http://www.evileu.de
*/


/*************************************************************/
//*********Formular als Shortcode einbinden
 function asuche(){
    
    echo "<input type = 'text' id = 'textfeld' autocomplete = 'off'
 onkeyup = 'myFunction(this.value);' list = 'suchliste'>";
    
    echo "<datalist id='suchliste'>";
    echo "<div id = 'liste'>";
    echo "</div>";    
    echo "</datalist>";
    
 }
 add_shortcode('asuche', 'asuche');

Ich hab hier schon mal den Funktionsaufruf bei onkeyup mit drin, der kriegt als Parameter den Inhalt des Textfeldes mit, Script kommt gleich. Die Verbindung vom Textfeld zur Datalist geht über die id suchliste. Autocomplete = ‚off‘ damit der Browser nicht reinpfuscht. In der Datalist hab ich eine weitere Div mit der id liste angelegt, in die schreibt nachher mein Ajax-Call seine Rückgabewerte.

Das integrierte Javascript

Wie gesagt, das Scripterl ist so kurz, dass man es nicht in eine externe Datei auslagern muss, wir nehmen es in die Funktion asuche mit rein, das sieht dann so aus:

function asuche(){ ?>
     
    <script>
        function myFunction(wert){
              
        var aktwert = wert;
        var data = {
            'action': 'my_action',
            'whatever': aktwert
            
        };

        // since 2.8 ajaxurl is always defined in the admin header and points to admin-ajax.php
        // wir sind aber nicht auf den Admin-Pages, deswegen der volle Pfad
        jQuery.post('http://localhost/wp_ajax/wp-admin/admin-ajax.php', data, function(response) {
        //    jQuery.post(ajaxurl, data, function(response) {
            
            document.getElementById("liste").innerHTML = response;
            
        });
        } //*********************End myFunction
        
    </script>
    <?php
...

Was passiert hier? Zuerstmal wird der als Parameter übergebene Wert des Textfeldes auf die Variable aktwert gelegt, diese kommt in die data-Parameter des Ajaxcalls mit rein. Der erste Parameter mit dem ‚action‘ definiert, welche Funktion nachher aufgerufen werden soll, die kommt gleich im Anschluss. Mit dem JQuery.post(…) wird die Anfrage an den admin-ajax.php weitergeleitet und die response definiert. Die macht nichts anderes als die Div mit der id liste mit dem Rückgabewert der aufgerufenen Funktion zu belegen, konkret füllt sie die Options-Liste unserer Datalist.

Was weniger schön ist: die Url zur admin-ajax.php steht hier noch als langer Rattenschwanz mit drin, das könnte man sicher schöner lösen. Wenn man zum Beispiel das JQuery-Script auslagern und per enqueue laden würde, könnte man mit wp_localize_script den Pfad als Variable mitgeben – aber das sind jetzt schon Feinheiten.

Damit der Funktionsaufruf klappt: Add Action

Ich habs zweimal drin, sowohl für die Admin-Ansicht als auch für nicht eingeloggte User:

add_action( 'wp_ajax_my_action', 'my_action' );
add_action( 'wp_ajax_nopriv_my_action', 'my_action' );

Jetzt fehlt uns noch die tatsächliche Funktion, die den Übergebenen Parameter aktwert entgegennimmt und und die passenden  Werte aus der Datenbank abruft.

Die PHP-Funktion

Da wir uns im Kontext von WordPress bewegen, brauchen wir keinen externen Datenbankzugriff mit mysqli oder PDO, sondern können wie gewohnt mit dem $wpdb-Objekt arbeiten. Die Funktion sieht so aus:

function my_action() {
    global $wpdb; 

    $whatever = $_POST['whatever'];
    
    /**********************************************/
    $alleposts = $wpdb->get_results( "SELECT * from ".$wpdb->prefix."posts 
where post_title like '%".$whatever."%' 
    and post_type like 'post' and post_status like 'publish'");
    foreach($alleposts as $einpost){
        
        echo "<option>".$einpost->post_title."</option>";
}
    /**********************************************/
    
    
    wp_die(); // this is required to terminate immediately and return a proper response
} //**********End function my_action

Unsere übergebene Variable whatever wird nicht anders verarbeitet, als ob sie aus einem Formular direkt käme, dafür sorgt der Call mit dem jQuery.post(…). Der Rest ist wirklich simpel, ich suche mir aus der Tabelle wp_posts alle Einträge, die unseren Suchbegriff matchen, gebe die gefundenen Post Titel mit der Foreach-Schleife aus und klebe noch die Option-Tags für unsere Datalist dran. Fertig ist unser Autocomplete-Feld!

suchwort_back

suchwort_back

Nachtrag: JS ausgelagert und Pfad übergeben

Wie ich oben schon angemerkt habe, ist es nicht so ideal dass im Javascript der volle Pfad zur admin-ajax.php steht. Das geht auch besser. Das Script kommt aus der Plugin-Datei raus, ich nenne es mal wp_ajax.js, und es sieht so aus:

function myFunction(wert){
                
        var aktwert = wert;
        var data = {
            'action': 'my_action',
            'whatever': aktwert
            };

        // der volle Pfad zur admin-ajax.php liegt auf my_ajaxurl
        jQuery.post(my_ajaxurl, data, function(response) {
                    
            document.getElementById("liste").innerHTML = response;
            
        });
        } //*********************End myFunction

Die Variable my_ajaxurl kriegt ihren Wert über wp_localize_script, das geht in einem Aufwasch mit dem Einbinden der externen Javascript-Datei, dazu stellt man folgenden Code an den Anfang des Plugins:

/*********Externes Script einbinden und Pfad zur admin-ajax.php übergeben*/
function mysite_js() {
      
    wp_enqueue_script('mysite-js', plugins_url().'/wp_ajax/wp_ajax.js', array('jquery'), false, false);
    
    wp_localize_script( 'mysite-js', 'my_ajaxurl', admin_url( 'admin-ajax.php' ) );
 
   }
add_action('wp_enqueue_scripts', 'mysite_js');

Dann kann das JS aus der Plugindatei raus, das sorgt für mehr Übersicht und ist wesentlich eleganter gelöst.

Was ich jetzt noch gern hätte

Wenn man einen Rezepttitel angewählt und auf Return gedrückt hat, möchte ich gerne das gewählte Rezept anzeigen lassen. Aber dazu muss ich mir noch ein, zwei Gedanken machen, meine bisherige Lösung mit der angehängten Rezept-ID ist nämlich eine ziemliche Krücke, das müsste eleganter gehen. Ich geh mal forschen…. und dafür gibts dann einen neuen Beitrag.

Hooks – jetzt kommt Action ins Spiel

Was sind WordPress Hooks?

Das kann man googlen, und z.B. die netzialisten haben einen sehr informativen Beitrag zu dem Thema geschrieben. Sehr kurz und knapp gesagt, Hooks sind definierte Ankerpunkte im WordPress-Code, an denen man eigene Funktionen einhängen kann. Dabei unterscheidet man zwischen Action Hooks und Filter Hooks, dazu gleich mehr. Eine umfassende Liste aller WordPress Hooks hat Adam R. Brown in seiner Hook Database zusammengetragen. Auch der Codex zur Plugin API bietet umfangreiche Information zum Thema.

Unterschied zwischen Action und Filter

Ich zitiere hier mal schlicht und unbefangen den Codex:

You can sometimes accomplish the same goal with either an action or a filter. For example, if you want your plugin to change the text of a post, you might add an action function to publish_post (so the post is modified as it is saved to the database), or a filter function to the_content (so the post is modified as it is displayed in the browser screen)

Also: wenn man Beiträge gezielt verändern möchte, z.B. eine Copyright-Zeile an jeden veröffentlichten Beitrag anhängen, gibt es (mindestens) zwei Wege zum Ziel. Hier wird empfohlen: entweder man verwendet einen action-Hook auf den Event publish_post, der beim Speichern eines Beitrags in der Datenbank die Copyright-Zeile anhängt. Oder man hakt sich mit einem Filter auf the_content an den Inhalt des Beitrags ran, und hängt die Copyright-Zeile beim Anzeigen des Beitrags in der Einzelansicht an. So weit so gut – ich hab mir allerdings einen Wolf gegooglet, um ein funktionierendes Beispiel für den action hook zu finden, leider bis jetzt ohne brauchbares Ergebnis. Das mit dem Filter dagegen geht relativ easy:

Der Filter

könnte zum Beispiel so aussehen:

function copyright_nach_post_content($content){
if (is_single()) {	
	$content .= '<p>Hier kommt dein Copyright (C) EL 2017 hin </p>';
}
	return $content;
}
add_filter( "the_content", "copyright_nach_post_content" );

Dieser Code kommt in die functions.php unseres Child-Themes und zieht jedesmal, wenn ein Beitrag aufgerufen wird. Die Abfrage auf if(is_single()) stellt sicher, dass auch wirklich nur Beiträge in der Einzelansicht betroffen sind.

Der entsprechende Action-Hook

war wie oben bereits gesagt nicht aufzufinden. Dann machen wir halt was anderes, wo richtig Action drin ist:

Wir versenden eine Email, wenn ein neuer Beitrag veröffentlicht wurde

Dabei stelle ich euch die simple, aber sehr effektive Funktion wp_mail vor. Der Code sieht so aus:

add_action('publish_post', 'send_emails_on_new_post');

function send_emails_on_new_post($post)
{
    $emails = "evi.s.leu@gmx.de"; 
    $title = get_the_title($post->ID);
    $url = get_permalink($post->ID);
    $message = "Link zum Beitrag: \n{$url}";
 
    wp_mail($emails, "Neuer Beitrag! {$title}", $message); 
}

Zuerst wird der action-hook auf das Ereignis publish_post gesetzt und mit unserer Funktion verknüpft. Diese bekommt als Parameter den $post mit, darüber können wir auf die Attribute des aktuellen Beitrags zugreifen, das tun wir mit der $post->ID. Wir holen uns den Titel und den Permalink und basteln uns daraus eine kleine E-Mail-Nachricht an den oben eingesetzten Empfänger.Die Funktion wp_mail braucht ganz minimalistisch wirklich nur 3 Parameter, den Empfänger, den Betreff und den Inhalt, und schon fluppts! Der Empfänger wird jedesmal benachrichtigt, wenn ein neuer Beitrag veröffentlicht wurde.

Ein übersichtlicher Beitrag zur wp_mail-Funktion ist hier bei OSTraining zu finden, die kompletten Informationen findet ihr natürlich im Codex.