lighttp, redirects und rewrites

Wenns mal wieder performant sein muss, ist oft der Griff zu lighty (http://www.lighttpd.net/) ein probates Mittel.

Seine Performance-Vorteile gegenüber dem Apache2 aus der Dose (http://httpd.apache.org/) erreicht der lighttpd unter anderem dadurch, dass er sich das aufwendige Parsen von .htaccess-Files spart. Der Pferdefuß dabei ist allerdings, dass serverseitige rewrites und redirects nichtmehr einfach in ein File im betroffenen Verzeichnis gekippt werden können sondern direkt in die Config von lighttpd geschrieben werden.

Gerade in Verbindung mit einer TYPO3-Umgebung kann der Einsatz von lighttpd als Webserver allerdings schnell zur Herausforderung werden. Seiten werden umbenannt, realurl generiert die Pfade neu und – hoppla, plötzlich gehen natürlich Links, die noch auf den alten Titel verweisen, auf eine 404-Seite oder im schlimmsten Fall komplett ins Leere. Natürlich muss eine Lösung her: die Redirects um dem Browser (und natürlich auch der Suchmaschine) gleich noch mitzuteilen, dass sich die URL geändert hat. Nehmen wir daher ein Beispiel:

Die bisherige Seite „diese-seite-ist-toll.html“ heißt nun „diese-seite-ist-spitze.html“. Links auf http://www.beispielseite.de/diese-seite-ist-toll.html“ führen also ab sofort ins Leere, genau so wie Links, die auf Unterseiten verweisen (z.B. „http://www.beispielseite.de/diese-seite-ist-toll/eine-unterseite.html“). Das Ziel ist also, alle URLs, die „diese-seite-ist-toll“ beinhalten, sollen umgeleitet werden auf „diese-seite-ist-spitze“ und gleichzeitig soll der HTTP-Status „301 – Moved permanently“ gesendet werden, um Browser und Suchmaschine zu sagen, dass die bisherige URL nichtmehr gültig ist. Dazu muss die lighttpd-Konfiguration angepasst werden:

Die Datei lighttpd.conf sieht für TYPO3-Umgebungen mit installierter realurl-Extension i.d.R. bereits einen rewrite-Block vor, der in etwa folgendermaßen aussieht:

url.rewrite-once = (
    "^/(typo3|phpmyadmin/|info/|typo3temp/|typo3conf/|uploads/|fileadmin/|t3lib/|robots\.txt|clear\.gif|favicon\.ico).*$" => "$0",
    "^/$" => "index.php",
    "^typo3$" => "typo3/index_re.php",
    ".html\?([^*]+)" => "index.php?$1",
    ".html$" => "index.php",
    "^([^*]+)\.([^*])+\.html\?([^*]+)$" => "index.php?id=$1&type=$2&$3",
    "^/.*\?([^*]+)" => "index.php?$1",
    "^/.*$" => "index.php"
)

Dazu fügen wir nun den Redirect-Block ein, der folgendermaßen aussieht:

url.redirect-code = 301
url.redirect = (
        "^/diese-seite-ist-toll(/.*|\.html.*)" => "http://www.beispielseite.de/diese-seite-ist-spitze$1",
)

Testet man nun die Funktion, wird man feststellen: es funktioniert nicht. Eine unangenehme Eigenschaft des lighty ist, dass die Reihenfolge, in der Redirects und Rewrites abgearbeitet werden, fest vorgegeben ist, d.h. es ist irrelevant, an welcher Stelle in der Config eine Rewrite- oder Redirect-Direktive eingefügt wird, es werden stets und immer zuerst die Rewrites und dann die Redirects abgearbeitet. Mit diesem Wissen ist es logisch, dass nichts passiert denn der lighty kommt garnicht dazu, die URL zu redirecten.

Abhilfe hierfür schafft man dadurch, dass man die URLs, die umgeleitet werden sollen, von sämtlichen Rewrite-Regeln ausnimmt. In userem Beispiel wäre das die Erweiterung der Direktive „url.rewrite-once durch das Statement

"^/diese-seite-ist-toll(/.*|\.html.*)" => "$0",

Danach lighttpd neu starten und siehe da, der Lighty leitet die fragliche URL um und sendet wie gewünscht vorher den 301-Header.

TYPO3 – Suche erweitern

Wer des öfteren mal die Funktionalität von TYPO3 mit eigenen Extensions erweitert, wird des Öfteren auf das Problem stoßen, dass bei der Erweiterung von Tabellen oder beim anlegen von Extension-spezifischen Datenbanktabellen bei der hauseigenen Suche außen vor bleiben.
Hier ist es sinnvoll, der Suche mitzuteilen, es möge doch bei der Suche auch zusätzliche Felder berücksichtigen. Dies lässt sich einfach und – im Gegensatz zu IF-Abfragen – einfach über TypoScript lösen.

Um der Suche die neuen Datenbank-Tabellen und/oder Felder mitzuteilen, geht man folgendermaßen vor.

Standard-Wert für "Überschriften und Schlagwörter": 
tt_content.search.30.dataArray.20.valueArray.10.value = pages.title-subtitle-keywords-description
Standard-Wert für "Seiteninhalt": 
tt_content.search.30.dataArray.20.valueArray.20.value = pages.title-subtitle-keywords-description : tt_content.header-bodytext-imagecaption

Zur neuen Definition leert man diese zuerst:

tt_content.search.30.dataArray.20.valueArray.10.value >
tt_content.search.30.dataArray.20.valueArray.20.value >

Danach definiert man den Wert neu indem man dem Standard die jeweiligen Felder anhängt:

tt_content.search.30.dataArray.20.valueArray.10.value = pages.title-subtitle-keywords-description-[zusätzliche Werte in der Tabelle "pages"]:[eine zusätzliche Tabelle]-[zusätzliche Felder dieser Tabelle]
tt_content.search.30.dataArray.20.valueArray.20.value = pages.title-subtitle-keywords-description:tt_content.header-bodytext-imagecaption-[zusätzliche Werte in der Tabelle "pages"] : [eine zusätzliche Tabelle]-[zusätzliche Felder dieser Tabelle]

Damit wird auch der Mechanismus klar, wie die zu durchsuchenden Felder definiert werden. Tabellen werden mit Doppelpunkt getrennt, die in der jeweiligen Tabelle zu durchsuchenden Spalten werden mit Dashes („-„) angehängt.

Abschließend muss man die Felder noch als zulässige Suchfelder definieren:

tt_content.search.20.allowedCols = [sämtliche Felder, die als Suchfelder definiert wurden, müssen hier im selben Schema eingetragen werden]

Zusätzlich zur Erweiterung der vorhandenen Filter („Überschriften und Schlagwörter“ und „Seiteninhalt“) ist es ebenso möglich, zusätzliche Filter einzufügen. Dazu fügt man dem Array

tt_content.search.30.dataArray.20.valueArray

einfach ein neues Element hinzu:

tt_content.search.30.dataArray.20.valueArray.30{
    label = Eine neue Option im Selectfeld
    value = [die mit dieser Option zu durchsuchenden Datenbank-Tabellen und Felder, Schema identisch]
}

Natürlich kann man analog auch Optionen entfernen.

TYPO3 Session Cookies

TYPO3 Session Cookies verändern ihren Wert, solange kein Inhalt im Session Array gespeichert ist.

Abgesehen davon, dass TYPO3 mit diesem Verhalten alleine steht, und es im Framework auch gänzlich anders dokumentiert ist (bugs#12606), funktioniert das natürlich.
…wenn man nicht gerade einen Proxy programmiert und verzweifelt.

Dokumentation von Extensions

RTFM – wenn denn wirklich eines da wäre… Viel zu oft trifft man auf TYPO3-Extensions – auch eigene – deren Dokumentation mit etwas Glück mager ausgefallen ist, oft aber schlicht nicht existiert. Dies ist ärgerlich, weil man dadurch im TER nur schwer abschätzen kann, ob eine vorhandene Extension die gesuchte Funktionalität bietet, und weil bei individuellen (Kunden-)Extensions nach gewisser Zeit oft Kollegen (und man selbst) auch nicht mehr so genau weiß, was die Extension tut, wie sie es tut oder warum sie es so tut.
Aber warum dokumentieren wir so wenig? Mit ein paar wenigen Schritten und mit Vorlagen erleichtern wir uns selbst, unseren Kollegen und der Community das Leben. Also auf geht’s!

In „How typo3.org works“ beschreibt Kasper Automatismen rund um die TYPO3-Plattform incl. TER. Uns interessiert hierbei insbesondere der Abschnitt „1.3. Writing documentation for TYPO3“. Dort wird erklärt, wie aus einem einfachen OpenOffice-Manual die Online-Ansichten der Manuals entstehen, und warum die Struktur und Formatierung dabei entscheidend ist.
Ein Documentation template für korrekt formatierte Manuals mit zahlreichen Hinweisen steht im TER bereit.

Es ist wirklich kein Hexenwerk. Packen wir also mal wieder unseren inneren Schweinehund, und dokumentieren unsere Extensions.

Links:

URL Rewrite Regeln fuer lighttpd und TYPO3

Hier der Regelsatz, der auch mit wkhtmltopdf und diversen News Extensions klarkommt:

url.rewrite-once = (
                        "^/(typo3|info/|typo3temp/|typo3conf/|uploads/|fileadmin/|t3lib/|robots\.txt|clear\.gif|favicon\.ico).*$" => "$0",
                        "^/$" => "index.php",
                        "^typo3$" => "typo3/index_re.php",
                        ".html\?([^*]+)" => "index.php?$1",
                        ".html$" => "index.php",
                        "^([^*]+)\.([^*])+\.html\?([^*]+)$" => "index.php?id=$1&type=$2&$3",
                        "^/.*\?([^*]+)" => "index.php?$1",
                        "^/.*$" => "index.php",
)