Symbolbild, welches ein stilisierte WordPress-Logo zeigt

Alte Multisite-Installationen und WordPress 6.8.0

Wer eine sehr alte WordPress Multisite Installation betreibt, die noch vor WordPress 3.5 angelegt wurde, hat mit der neuen WordPress Version 6.8.0 vom 15. April eine etwas unschöne Erfahrung machen können: Alle Medien waren plötzlich nicht mehr zugreifbar.

Hintergrund

Die Ursache lag darin, dass bereits ab Version 3.5 (diese Version kam immerhin bereits am 11. Dezember 2012!) die Verwendung des internen Uploadverzeichnisses von wp-content/blogs.dir/$site_id/files hin zu wp-content/uploads/sites/$site_id geändert wurde.
Da WordPress an der Stelle sehr abwärtscompatibel gebaut wurde, gab es trotz dieser langen Zeit bislang keine Probleme. So gab es in der Multisite zusätzlich noch ein Aufruf der Datei includes/ms-files.php, die über die .htaccess aufgerufen wurde und alle Zugriffe auf die Dateien entsprechend umleitete:

RewriteRule ^files/(.+) wp-includes/ms-files.php?file=$1 [L]

In der PHP-Datei wurde dann beim Aufruf geschaut, welche Instanz gerade aufgerufen wurde und dann die jeweilige Mediendatei aus dem Ordner wp-content/blogs.dir/$site_id/files an den Browser geschickt.

Dies funktionierte bislang ohne Probleme. Und da auch im Bereich der Netzwerk-Einrichtung, in der die notwendigen Einstellungen der Multisite in der wp-config.php und der .htaccess angegeben wurden, diese zeile noch vorhanden war, gab es keinen Anlass etwas zu ändern. Auch die Version 6.8.0 zeigte diese Angabe auch weiter brav an. Man kam daher auch nicht auf die Idee und sah keine Notwendigkeit irgendetwas zu ändern.

Bei Multisite-Installationen ab der Version 3.5 wurde die ms-files.php jedoch garnicht mehr verwendet und die Angabe in der .htaccess entfiel ersatzlos.

In der Version 6.8.0 kam es jedoch zu einem Bug in einer von ms-files.php aufgerufenen Funktion. Vgl: Call to undefined function is_super_admin() in /wp-includes/ms-files.php. Diese sorgte dann dafür, dass die PHP-Datei an der Stelle einen fatalen Fehler erzeute und also das Bild oder die Mediendatei nicht an den Browser zurücklieferte.

Lösungen

  1. Der faule Quick-Fix für Leute, die Geduld haben wäre: Nichts tun. Abwarten bis die Version 6.8.1 mit dem Bugfix kommt.
  2. Der nicht ganz so faule Quick-Fix wäre es, wie in Kommentar geschrieben, die ms-files.php zu bearbeiten und entweder den gesamten if-Block ab Zeile 24 auszukommentieren oder zumindest das Statement mit is_super_admin() zu entfernen, so daß am Ende nur noch folgende Bedingung da steht:
    if ( '1' === $current_blog->archived || '1' === $current_blog->spam || '1' === $current_blog->deleted ) {
  3. Die nachhaltige Lösung wäre jedoch, die Multisite auf das 3.5er Standard upzugraden.

Alte Multisite auf den Version 3.5 Standard updaten

Hierzu gibt es ein Artikel aus dem Jahr 2012, welcher das Vorgehen Schritt für Schritt erklärt: Dumping ms-files.

Auch wenn da eigentlich alles steht, würde ich das gern nochmal in den wesentlichen Punkten zusammenfassen und ergänzen. Folgendes Vorgehen ist zu machen.

1. Wir setzen die WordPress-Installation in den Wartungsmodus. Dies können wir am einfachsten mit Shell-Zugriff auf das WordPress-Verzeichnis machen, indem wir eine Datei namens .maintenance mit folgenden Inhalt erstellen und in das Root-Verzeichnis der WordPress-Installation ablegen:

<?php 
$upgrading = time();
?>

2. Du machst ein Datenbank-Update, für den Fall, dass du bei den folgenden Schritten daneben greifst. Oder du bist mutig, es ist Freitagabend und Deployday. Wie auch immer: Don’t blame me.

3. Wir verschieben alle Dateien aus wp-content/blogs.dir/$site_id/files nach wp-content/uploads/sites/$site_id

4. Wir rufen unsere Datenbank mit einem geeigneten Tool wie PHPMyAdmin auf und entfernen mittels folgenden SQL-Befehl die alten Upload-Pfade:

UPDATE wp_$site_id_options SET option_value = '' WHERE option_name IN ('upload_path', 'upload_url_path');

5. Wir aktualisieren alle Posts, die etwaige alte Direktpfade auf /files/ haben, indem wir alls posts und alle postmeta Tabellen der jeweiligen Sites aktualisieren:

UPDATE wp_$site_id_posts SET post_content = REPLACE(post_content, '/files/', '/wp-content/uploads/sites/5/') WHERE post_content LIKE '%/files/%';

UPDATE wp_$site_id_postmeta SET meta_value = REPLACE(meta_value, '/files/', '/wp-content/uploads/sites/5/') WHERE meta_value LIKE '%/files/%';

6. Die Schritte 3, 4 und 5 wiederholen wir für alle Instanzen mit den jeweiligen Ids $site_id. Welche Ids es gibt, sieht man ja in der Netzwerkinstallation, bzw. direkt in der blogs.dir eben die alten Verzeichnisnamen.

7. Wir sagen der Multsite-Installation, dass es nicht ms-files.php verwenden soll, indem wir diesen SQL-Befehl ergänzen:

INSERT INTO `my_database`.`wp_sitemeta` (`meta_id`, `site_id`, `meta_key`, `meta_value`) VALUES (NULL, '1', 'ms_files_rewriting', '0');

8. Wir öffnen die .htaccess und löschen die Zeile

RewriteRule ^files/(.+) wp-includes/ms-files.php?file=$1 [L] 

oder kommentieren diese aus.

9. Wir prüfen die Datei wp-config.php, ob in dieser folgende Konstanten definiert sind und wenn ja, kommentieren wir diese aus:

define( 'UPLOADBLOGSDIR', 'wp-content/blogs.dir' );
define( 'UPLOADS', 'wp-content/blogs.dir' );
define( 'BLOGUPLOADDIR', … );

10. Wir entfernen die .maintenance-Datei wieder und drücken die Daumen, dass alles geklappt hat.

Im oben verlinkten Artikel werden noch ein paar Edge-Cases behandelt. Falls die obigen 10 Punkte nicht zum Erfolg führten, wird es möglicherweise dort noch erwähnt.

Ein Indiz, dass man alles richtig gemacht hat, neben der Tatsache dass die Bilder wieder sichtbar sind, ist es, dass in der Netzwerk-Konfiguration nun auch nicht mehr die Zeile mit ms-files.php für die .htaccess vorgeschlagen wird.
Diese Sache (Schritt 7) hatte ich nämlich auch erst übersehen und mich gewundert, dass weiterhin auf blogs.dir zugegriffen wurde.

Skript zur Automatisierung

Austin Ginder hat im Artikel Removing Legacy ms-files.php from Multisite ein Skript bereitgestellt, welches ein Teil der obigen Aufgaben automatisieren kann.

Hierzu bedarf es jedoch eines Servers auf dem auch das wp-cli Skript installiert wurde.