Als ich neulich für die Homepage einer meiner Bands ein Template zur Darstellung von Terminen / Events erstellt habe, bin ich auf ein Problem gestoßen, an das ich zu beginn absolut nicht gedacht habe.

Warum muss ich geplante Beiträge überhaupt sichtbar machen?

Auf der besagten Homepage meiner Band (Link folgt, sobald die Seite fertig ist und online gegangen ist 😉 ) möchte ich gerne bevorstehende und vergangene Termine anzeigen. Einmal als Übersicht in einer Liste und noch einmal jedes Event als Einzel-Seite mit allen nötigen Informationen. Dazu habe ich mir einen Custom Post Type „Termine“ erstellt und wollte die jeweiligen Beiträge für mein Termin-Template benutzen. Um die Einträge zu filtern (bevorstehend oder vergangen) und auch zu sortieren, wollte ich das Veröffentlichungsdatum benutzen. Soweit hat auch alles wunderbar funktioniert, bis mir auffiel, dass bei den bevorstehenden, geplanten Terminen nicht die sprechende, und auch eingestellte URL-Struktur verwendet wurde, sondern die ganz einfache URL mit URL-Parametern.
Somit hätte mein ganzer Plan nicht funktioniert weil sich die Beitrags-URL nach Veröffentlichung geändert hätte und Google regelmäßig auf 404 not found Fehler gestoßen wäre.

Problem mit get_permalink()

Die Funktion get_permalink() gibt bei einem geplanten Post in der Zukunft leider nicht den gewünschten Permalink zurück sondern den einfachen Link mit angefügten URL-Parametern.
Bei einem Beitrag mit dem Status „publish“, also veröffentlicht, wird der richtige Permalink ausgegeben.

Das ganze kann in etwa so ausschauen:

https://www.axels-blog.de/?p=123

Gewünscht war aber ein sprechender Link mit dieser Struktur:

https://www.axels-blog.de/wordpress-googlebot-aufrufe-zugriffe-tracken/

Also begab ich mich auf die Suche im Internet und stieß auf viele Vorschläge, die meiner Meinung nach viel zu kompliziert für dieses „kleine“ Problem waren. „Irgendwie muss das einfacher gehen“, dachte ich mir.

Und siehe da. Ich bin auf einen Beitrag von DAEXT gestoßen, der eine wirklich schöne Lösung dafür parat hat.

Den Status eines Beitrags auslesen

Als erstes müssen wir den Status eines Posts auslesen um zu erfahren ob es sich um einen geplanten Beitrag (im englischen auch „scheduled post“) mit dem Status future handelt.

Auch dafür hat WordPress schon eine Funktion vorbereitet: get_post_status()

$post_status = get_post_status($post_id);

Diese Zeile liest den Status eines Posts aus und schreibt ihn in die Variable „$post_status“. Die Funktion gibt einem den Status des Posts der mitgegebenen ID aus. Falls keine ID angegeben wird, wird der Status des letzten Posts ermittelt.

Folgende Werte gibt es für den jeweiligen Status eines Posts:

  • publish
  • future
  • draft
  • pending
  • private
  • trash
  • auto-draft
  • inherit

In unserem Fall müssen wir erkennen ob es sich bei dem Beitrag um einen geplanten Beitrag handelt, der in der Zukunft veröffentlicht wird. Also sollte die Ausgabe der Funktion „future“ sein.

Future-Permalink generieren

Da die Funktion get_permalink() bei einem geplanten Beitrag lediglich eine URL mit Parametern erzeugt und damit alle Regeln die man unter Einstellungen > Permalinks getroffen hat ignoriert, benutzen wir hier die Funktion get_sample_permalink():

$permalink_a = get_sample_permalink($post_id);

Die Funktion akzeptiert als ersten Parameter die ID eines Posts (beachtet, dass es noch mehr Parameter gibt. Schaut hierzu einfach in die Dokumentation von WordPress) und gibt ein numerisches Array zurück mit der URL des Posts und einem Platzhalter anstelle des Namen des Beitrags an erster Stelle und an zweiter Stelle den Namen des Beitrags.
Das nachfolgende Array zeigt ein Beispiel der Ausgabe der Funktion:

[
    0 => 'https://www.axels-blog.de/%postname%/',
    1 => 'wordpress-googlebot-aufrufe-zugriffe-tracken'
]

Um hieraus den richtigen Permalink zu erzeugen müssen wir den Platzhalter durch den Namen des Posts ersetzen.
Folgende Regular Expression (Regex) löst diese Aufgabe:

$permalink = preg_replace('/\%postname\%/', $permalink_a[1], $permalink_a[0]);

Zu beachten ist, dass seit einiger Zeit die Funktion get_sample_permalink() nicht mehr in allen Teilen von WordPress verfügbar ist. Hierdurch kann es zu der Fehlermeldung „Fatal error: Call to undefined function get_sample_permalink()“ kommen. Um das zu verhindern fügt man die Datei in der die Funktion definiert wird ein, bevor wir sie das erste mal aufrufen.

require_once(ABSPATH . 'wp-admin/includes/post.php');

Der komplette Code

Nun können wir die einzelnen Teile zusammensetzen und erhalten folgenden Code den ich direkt in die Datei functions.php meines Themes kopiert habe.

/**
 * Gets the permalink of the post.
 *
 * Note that if the post status is 'future' the value of the permalink field is generated with the get_sample_permalink() function.
 *
 * @param $post_id The post ID.
 * @param $require True if the wp-admin/includes/post.php file should be required.
 *
 * @return String The permalink of the post associated with the provided post ID.
 */
function get_permalink_alternative($post_id, $require = false){

    $post_status = get_post_status($post_id);

    /**
     * If the post status is 'future' the permalink is generated with the get_future_permalink() 
     * function. Otherwise it's generated with the get_permalink() function.
     */
    if($post_status === 'future'){

     if($require){
       require_once(ABSPATH . 'wp-admin/includes/post.php');
     }

     $permalink_a = get_sample_permalink($post_id);
     $permalink = preg_replace('/\%postname\%/', $permalink_a[1], $permalink_a[0]);

    }else{

      $permalink = get_permalink($post_id);

    }

 return $permalink;

}

Zu erwähnen ist noch, dass bei meinem Custom Post Type der Platzhalter nicht %postname ist sondern %pagename. Kann man in obigem Code aber einfach abändern.

Bei Zeiten schreibe ich noch einen eigenen Beitrag zu meinem Termin-Template und erkläre euch, wie ich das gelöst habe.

Update:

Mir ist soeben aufgefallen, dass man die Single Page von einem geplanten Beitrag nur angezeigt bekommt, wenn man angemeldet ist. Sobald man abgemeldet ist und auf den Permalink klickt bekommt man die 404 Seite zu sehen. Aber auch hierfür gibt es eine Lösung, die ich euch in diesem Post erkläre.