Nur der Inhalt zählt

Content-Management-Systeme sind en vogue. Mit den passenden Perl-Modulen und etwas Programmierarbeit lässt sich ein CMS auf die eigenen Bedürfnisse hin programmieren.

In Pocket speichern vorlesen Druckansicht 21 Kommentare lesen
Lesezeit: 21 Min.
Von
  • Marc Lehmann
Inhaltsverzeichnis

Ein Content-Management-System dient, wie der Name schon sagt, zum Verwalten von Inhalten. Das hier vorgestellte CMS soll Webseiten verwalten und die Eingabe neuer Webseiten ermöglichen. Trotz geringer Größe wird es alles bieten, was man für eigene Erweiterungen braucht. Die Struktur ist denkbar einfach: Es gibt zwei Datenbanktabellen content und style (siehe Listing 1). Die erste enthält den Namen der Webseite (oder des Teilstücks), den Inhalt und einen Verweis auf ein Stylesheet. Diese Stylesheets stehen in der zweiten Tabelle style.

Mehr Infos

Listing 1: Tabellen definieren

create table content (
id int(10) unsigned not null auto_increment,
name varchar(100) not null,
style int(10) unsigned not null,
text text not null,
primary key (id),
unique key (name)
);

create table style (
id int(10) unsigned not null auto_increment,
name varchar(100) not null,
text text not null,
primary key (id),
unique key (name)
);

Mit ‘Stylesheet’ ist allerdings kein CSS gemeint, sondern ein XSLT-Dokument (Extensible Stylesheet Language Transformations). Damit bleiben in Hinsicht auf gemeinsames Seitendesign, dynamisches Umstellen von Elementen, Einbetten anderer Module und Protokollunabängigkeit kaum Wünsche offen (s. das XSLT-Tutorial [1]). Durch Austauschen des Stylesheet kann das CMS nicht nur HTML, sondern auch andere XML-Dialekte erzeugen - etwa WML für WAP oder XSL, um daraus PDF zu generieren.

Richtig interessant wird das Ganze, wenn man in den Dokumenten und Stylesheets eine Programmiersprache benutzen kann. Da Perl alleine etwas nackt ist, kommen etliche Perl-Module hinzu. Das wichtigste und größte davon nennt sich ‘PApp’ und enthält alles, was ein CMS braucht.

PApp stellt ein Framework für die Applikationsentwicklung dar: Neben dem Einbetten von Perl in XML- oder andere Dokumente verwaltet es die Session- und Benutzerdaten, internationalisiert die Applikation und bietet viele Funktionen, um immer wieder auftretende Aufgaben (zum Beispiel Caching von Datenbank und Statement-Handles) so angenehm wie möglich für den Programmierer zu gestalten.

Listing 2 stellt den Rumpf eines PApp-Programms dar. Diese XML-Datei beschreibt eine komplette Anwendung und entspricht damit einem Hauptprogramm, in das man andere Module importieren kann. Sie definiert direkt oder indirekt alle ‘Webseiten’. Es spielt keine Rolle, ob mehrere (kurze) Seiten in einer Datei oder eine (lange) Seite über mehrere Dateien verteilt sind.

Mehr Infos

Listing 2: Hauptprogramm des CMS

<package name="xcms"> <domain lang="*">

<translate fields="content.text style.text" style="auto" lang="*">

<import src="macro/editform"/>

<perl><![CDATA[
use PApp::XSLT;
use PApp::PCode qw(pcode2perl pxml2pcode);
use Convert::Scalar ':utf8';
]]></perl>

<!-- weitere Anweisungen und Module -->

</domain> </package>

package, das erste Element, erledigt im Wesentlichen dasselbe wie die gleichnamige Perl-Anweisung: Einen neuen Namensraum deklarieren, in dem seitenübergreifende Variablen sichtbar sind. Das zweite Element, domain, hat mit der Internationalisierung zu tun: Mit ihm werden verschiedene Übersetzungsdomänen deklariert und die im Quellcode verwendete Sprache festgelegt. PApp nimmt deshalb an, dass alle Texte innerhalb des domain-Elements Englisch sind und ersetzt sie gegebenenfalls zur Laufzeit.

Fast alle Seiten entstehen dynamisch aus einer Datenbank und müssen ebenfalls übersetzbar sein. Dazu muss PApp erfahren, wo diese zusätzlichen Seiten stehen, und genau das leistet die translate-Anweisung: Die Spalten text in den Tabellen content und style sind zu übersetzen. lang="*" bedeutet, dass man mehrere Sprachen mischen kann. Das anschließende import-Element entspricht einer use-Anweisung in Perl. Man kann damit neben normalen Perl-Modulen PApp-Dateien angeben. Das hier angezogene macro/editform-Paket ist ein Teil von PApp; es wird von der Editor-Seite benutzt.

Darauf folgt ein perl-Element, das korrekt gequoteten Perl-Code (&lt; statt <, &amp; statt &) enthalten kann. Konsequenter Einsatz einer XML-CDATA-Klausel vermeidet an dieser Stelle Überraschungen. Außerhalb von Modulen definierte Perl-Elemente werden genau einmal, beim Laden der Anwendung, ausgeführt. Die use-Anweisungen darin hätte ebenso ein <import> erledigen können.

Mit dem Administrationsmodul von PApp kann man seine Anwendungen einrichten (Abb. 1).

Bevor man diese Applikation benutzen kann (indem man entweder das Programm papp-admin oder die Admin-Applikation bemüht), sollte man noch die Tabellen erzeugen (s. Abb. 1).

Wer die Applikationen zum Beispiel mit .../exec.cgi/xcms ausprobiert, erhält nur eine Fehlermeldung, da PApp nichts findet, was es anzeigen könnte. Die erste Seite sollte her. Einzelne Webseiten unter PApp heißen module, mehrere von ihnen bilden die gesamte Anwendung. Module oder Packages kann man in andere Module/Packages einbauen und so wiederverwenden, ein einzelnes Modul muss also nicht immer eine ganze Webseite erzeugen.

Um Daten in das System einpflegen zu können, benötigt man eine Eingabemöglichkeit. Zwar sollte dieser Seiteneditor selbst als dynamische Seite in der Datenbank stehen, aber diese Forderung endet im ‘Brauche Henne fürs Eierlegen’-Dilemma - er muss also konventionell (mit PApp-Modulen) realisiert werden. Listing 3 zeigt das edit-Modul, das an Stelle des Kommentars in Listing 2 stehen sollte.

Mehr Infos

Listing 3: Das edit-Modul

<module name="edit">
<phtml><![CDATA[
<:access_p "admin" or force_login __"admin access required":>
<html>
<:for $table ("content", "style") {:>

<?ef_begin "edit_row", -table => $table:>
<?$table:>
<?
ef_relation \$S{edit_id},
["id, name from $table order by 1"],
undef() => __"[NEW]";
:>
<?ef_submit __"Go":>
<?ef_end:>

<:}:>
</html>
]]></phtml></module>

Listing 3 ist zwar ‘nur’ HTML mit eingebettetem Perl, hat es aber in sich. Ein module-Element beschreibt ein Modul. Sein Name taucht in der URL wieder auf: Ist die Applikation unter .../exec.cgi/xcms erreichbar, wird das Modul edit mit der URL .../exec.cgi/xcms/edit aufgerufen. Innerhalb des module-Elements könnte man nun ein perl-Element benutzen. Darin enthaltenen Perl-Code würde PApp ausführen, zum Beispiel mit print-Anweisungen eine HTML-Seite ausgeben. Um XML (oder HTML) und Perl zu mischen, bietet sich das phtml-Element an. An dessen Anfang erwartet PApp HTML, innerhalb schaltet <: vom HTML- in den Perl-Modus um, :> arbeitet umgekehrt. Diese Umschalter verhalten sich wie ein Semikolon in Perl, das heißt wie ein Statement. Man kann daher beispielsweise eine Schleife in einem Perl-Fragment beginnen, dazwischen HTML ausgeben und in einem anderen Teil die Schleife beenden. Genau das macht das edit-Modul, indem es über die beiden SQL-Tabellen style und content iteriert, die ja beide editierbar sein sollen.

Innerhalb der Schleife benutzt das Skript die Editform-Bibliothek, um HTML-Formulare zu erzeugen. Das läuft immer nach dem gleichen Schema ab: ef_begin (Start des Formulars), mehrere Aufrufe von ef_string oder anderen Funktionen, um Eingabeelemente zu definieren, gefolgt von ef_end, was das Formular schließt. ef_begin akzeptiert so genannte ‘surl’-Argumente, benannt nach der Funktion, die in PApp URLs erzeugt. Sie bestehen aus zwei optionalen Teilen: dem Modulnamen, gefolgt von beliebig vielen Argument-Wert-Paaren. Wenn man dem Link folgt (das heißt in diesem Fall das Formular abschickt), zeigt PApp das angegebene Modul an, beziehungsweise das aktuelle, falls kein Name angegeben wurde. Außerdem kann man dem Zielmodul so Parameter übergeben.

In Modulen gibt es zwei spezielle Hashes: %S und %A. Ersterer steht für ‘state’ und ist persistent: Legt man auf einer Seite etwas in ihm ab, ist dieser Wert bei allen folgenden Seitenaufrufen verfügbar. Die Locale-Einstellungen des Benutzers sind zum Beispiel in %S gespeichert, denn wenn er die Sprache umstellt, sollte dies auf allen folgenden Seiten geschehen. Ablegen kann man Werte, indem man eine normale Zuweisung ($S{key} = $value) ausführt oder indem man sie als surl-Argument angibt: Argumentnamen ohne führendes Minuszeichen stehen für Einträge in %S.

Anders dagegen %A (‘Argument’). Dessen Inhalt ist nur auf einer speziellen Seite gültig und verschwindet, wenn die Seite erzeugt wurde. Man kann sie ausschließlich durch ‘surl’-Argumente setzen, deren Namen mit einem Minuszeichen beginnen:

ef_begin "edit_row", -table => $table

erzeugt also ein Formular, das nach dem Abschicken auf das edit_row-Modul verzweigt und $A{table} auf den Wert von $table setzt.

Nach dem einleitenden ef_begin gibt das Skript den Tabellenname aus. Das erledigt <?$table:> statt des längeren <:print table:>. Nach <? folgt ein Perl-Ausdruck, dessen Resultat in die HTML-Ausgabe interpoliert wird; <: wirft das Ergebnis weg.

Danach gibt ef_relation eine HTML-Selectbox aus (s. [[bild_url4] Abb. 2]). Alle editform-Funktionen, die Eingabeelemente erzeugen, erwarten als Parameter nicht den aktuellen Wert, sondern eine Referenz darauf. Nach dem Abschicken des Formulars schreibt PApp den neuen Wert in diese Referenz zurück - die Variable ist an das HTML-Element ‘gebunden’. Besonders sinnvoll ist dies, wenn man SQL-Spalten an Formular-Elemente bindet.

Da die Selectbox eine Relation zwischen der ID-Spalte in der SQL-Tabelle und der Namensspalte darstellt und dies in der Praxis oft vorkommt, stellt Editform die ef_relation-Funktion zur Verfügung. Sie erwartet eine Referenz auf eine Variable, einen SQL-Select-Ausdruck (ohne ‘SELECT’) und beliebig viele zusätzliche Schlüssel-Wert-Paare, die die Auswahlbox anzeigen soll. Im konkreten Fall werden hier die IDs den Namen gegenübergestellt, ergänzt um den Wert undef, der einen neuen Eintrag erzeugt und deshalb den Text [NEW] erhält.

Mit poedit lassen sich die Übersetzungen einer Website verwalten (Abb. 4).

Die Unterstriche vor Strings (__) markieren den darauffolgenden String als ‘zu übersetzen’: PApp merkt sich diese Zeichenketten und ersetzt sie, falls notwendig, während der Laufzeit durch ihre Übersetzungen. Diese Übersetzungen müssen die Entwickler von Hand einpflegen (zum Beispiel mit poedit, s. Abb. 4) - vor betrunkenen Babelfischen muss man also keine Angst haben.

Beim Aufruf gibt die Seite edit zwei Formulare mit je einer Selectbox zur Dokumentauswahl und einem Submit-Knopf aus. Wird dieser angewählt, verzweigt PApp auf das edit_row-Modul und setzt $A{table} auf den Namen der zu editierenden Tabelle. Gleichzeitig erhält $S{edit_id} durch das ef_relation-Element die Nummer des Dokuments oder undef, um ein neues zu erzeugen.

Zum Bearbeiten einzelner Seiten dient das von Listing 4 erzeugte Formular (Abb. 3).

Das edit_row-Modul, mit dem man ein einzelnes Dokument (beziehungsweise eine einzelne Zeile einer SQL-Tabelle, daher der Name) editieren kann, ist ähnlich aufgebaut (Listing 4, Abb. 3). Hier erscheint zwischen module und phtml das XML-Element state. Mit ihm kann man einzelnen Elementen aus %S besondere Attribute zuordnen, zum Beispiel local. Ein local-Eintrag in %S bleibt in allen Modulen erhalten, die ihn ebenfalls als local markiert haben. Wechselt man in ein anderes Modul, verschwindet der Wert automatisch aus %S. Ein anderes Attribut ist preferences. So markierte Werte (beispielsweise die eingestellte Sprache) werden dem aktuellen Benutzer zugeordnet und über eine Sitzung hinaus gespeichert.

Mehr Infos

Listing 4: Das edit_row-Modul

<module name="edit_row" nosession="edit">
<state keys="edit_table edit_id" local="yes"/>
<phtml><![CDATA[
<html>

<?slink __"[CANCEL/BACK]", "edit":>

<:
$row = $A{row}
|| new PApp::DataRef 'DB_row',
table => $A{table},
where => [id => $S{edit_id}],
preload => 1,
autocommit => 0,
delay => 1,
utf8 => 1,
:>

<?ef_begin -row => $row:>
<?ef_submit __"Save", -save => 1:><br />
<?ef_submit __"Delete", -discard => 1:><br />
<?ef_string \$row->{name}, 60:><br />
#if exists $row->{style}
<?ef_relation \$row->{style},
["id, name from style order by 1"],
0 => __"[NONE]":><br />
#endif
<?ef_text \$row->{text}, 80:><br />
<?ef_end:>

#if $A{save}
<:$row->flush:>
#endif
#if $A{discard}
<:$row->delete:>
<:abort_to "edit":>
#endif

</html>
]]></phtml></module>

Im phtml-Element findet man zuerst die Anweisung

<?slink __"[CANCEL]", "edit":>

slink ist die Standardfunktion, um Verweise zu erzeugen. Als erstes Argument erwartet sie den Link-Text, gefolgt von den bekannten ‘surl’-Argumenten. slink gibt selbst nichts aus, sondern liefert den Verweis als Ergebnis zurück. Die obige Zeile erzeugt also einen Verweis zurück auf das edit-Modul.

Die folgende ‘Monster’-Anweisung zeigt das letzte wirklich Neue in diesem Modul:

<:
$row = $A{row}
|| new PApp::DataRef 'DB_row',
table => $A{table},
where => [id => $S{edit_id}],
preload => 1,
autocommit => 0,
delay => 1,
utf8 => 1,
:>

Praktischerweise kann man direkt die von allen Editform-Funktionen erwartete Referenz auf eine Spalte in einer SQL-Tabelle (oder eine Datei, oder...) erzeugen. Dies tut das Statement mit Hilfe des PApp::DataRef-Moduls - allerdings nur, wenn $A{row} leer ist, also lediglich beim ersten Aufruf.

table gibt die SQL-Tabelle an, where wählt die Zeile aus. Die folgenden drei Parameter geben an, dass die Werte sofort gelesen und nicht beim Freigeben des Objektes zurückgeschrieben werden. Der letzte Parameter fungiert als Bugfix: Viele Datenbanken wissen nicht, ob ein Perl-String in UTF8 oder ISO-8859-1 kodiert ist. Damit man keine unangenehmen Überraschungen erlebt, sorgt der utf8-Parameter dafür, dass Strings immer als UTF8 in der Datenbank landen.

Der Konstruktor liefert eine Referenz auf einen Hash zurück, dessen Elemente (Spaltennamen) die Werte der SQL-Zeile enthalten. Man kann Filter für diese Werte definieren und vor allem kann man sie als Argumente für die Editform-Funktionen verwenden. Das folgende Formular beginnt mit:

<:ef_begin -row => $row:>

das heißt, es verzweigt auf das aktuelle Modul und setzt $A{row} auf die SQL-Referenz. Dadurch erzeugen wiederholte Aufrufe keine neuen Referenzen, was schon deshalb nicht ginge, weil $A{table} nur beim ersten Mal gesetzt ist.

Mehr Infos

In den beiden folgenden ef_submit-Aufrufen entstehen Submit-Elemente. Analog zu slink enthalten sie zuerst den Inhalt (die Beschriftung), dann ‘surl’-Argumente. So könnten verschiedene Submit-Knöpfe unterschiedliche Zielmodule verwenden.

ef_string liefert ein Eingabefeld mit 60 Zeichen Breite. Der als erstes Argument \$row->{name} angegebene Dokumentname erscheint als Voreinstellung und kann modifziert werden. Dabei entsteht kein neues Dokument, es ändert sich nur der Name des alten. Die beiden SQL-Tabellen style und content sind nicht identisch: content enthält eine zusätzliche Spalte für die Stylesheet-ID. Diesen Unterschied testet die Zeile

#if exists $row->{style}

Die ‘#’-Syntax ist nur ein bisschen ‘syntactic sugar’. Sie entspricht

if (exists $row->{style}) {

ef_text ist nicht schwer zu erschließen; es erzeugt ein Textarea-Element.

Es folgen zwei Abfragen, die feststellen, welchen Submit-Knopf der Benutzer betätigte. Dazu testen sie das Argument, das der jeweilige Knopf setzt, und speichern die Zeile (bei ‘Save’) beziehungsweise löschen sie aus der Datenbank (‘Delete’). In beiden Fällen wird wieder in das edit-Modul verzweigt. Eine bessere Implementierung würde die Formulardaten an dieser Stelle prüfen und nur speichern, wenn alles korrekt ausgefüllt ist.

Damit ist die Eingabeseite abgeschlossen. Es fehlt eine Möglichkeit, damit in die Datenbank geschriebene Seiten auch anzuzeigen. Da viele davon vorhanden sein können, muss es ein Auswahlverfahren geben. Theoretisch könnte man den Namen der Seite in einer Variablen, etwa in %S, speichern. Eleganter wäre es aber, dafür das PApp-Modulsystem zu benutzen, da es die entsprechende Logik ohnehin bereitstellt.

Fordert ein Anwender eine Seite an, die es in der Applikation nicht gibt, wird das Modul mit dem speziellen Namen * ausgegeben. In ihm kann man über die Variable $PApp::module den ursprünglichen Namen erreichen und ihn dazu benutzen, die passende Seite aus der Datenbank zu holen und anzuzeigen.

Darum kümmert sich im Beispiel die Funktion cins, die den Namen der Seite übergeben bekommt und sich um den Rest (holen, ausführen, ausgeben) kümmert (Listing 5). Zur Definition von Funktionen dienen in PApp macro-Elemente. In ihnen kann man Perl-Funktionen definieren und dabei auch phtml-Elemente verwenden. Die Definition von cins beginnt so:

<macro name="cins" args="$name">

Das Attribut args definiert Namen für Argumente. Zuerst holt sich cins den Inhalt der Seite aus der Datenbank. Dazu benutzt es die Funktion sql_fetch aus dem Modul PApp::SQL. Dieses ist trotz des Namens nicht Teil der PApp-Distribution, sondern getrennt auf CPAN erhältlich und deshalb auch in anderen Programmen einsetzbar.

Mehr Infos

Listing 5: Makro zur Anzeige der Datenbankseiten

<macro name="cins" args="$name"><phtml><![CDATA[<:
sql_fetch \my($content, $style),
"select text, style from content where name = ?",
$name;
die "No such page: $name" unless $content;
$content = capture {
eval pcode2perl pxml2pcode utf8_on $content;
die if $@;
};
if ($style) {
sql_fetch \$style,
"select text from style where id = ?",
$style;
$style = capture {
eval pcode2perl pxml2pcode utf8_on $style;
die if $@;
};
$style = new PApp::XSLT stylesheet => "data:,$style";
$content = $style->apply_string("<content page='$name'>$content</content>");
}
:><?$content:><:
:>]]></phtml></macro>

sql_fetch kombiniert sql_exec und fetch. Die Syntax des Letzteren sieht so aus:

$sth = sql_exec [optionaler $DBH],
[optionale bind_columns-Argumente]
SQL-Anweisung,
[optionale Parameter für die SQL-Anweisung];

Gibt man keinen DBI-Datenbankhandle an, wird ein vorher festgelegter Standardhandle benutzt. Wer will, kann gleich Variablenreferenzen für einen bind_columns-Aufruf mitgeben. Solche Ergebnisvariablen sind wesentlich schneller als das häufig benutzte my ($a, $b, $c) = $sth->fetchrow_array. Die SQL-Anweisung ist ein ganz normaler String mit optionalen Platzhaltern (wie bei prepare), für die man nach der Anweisung gleich die Belegung angeben kann (execute). Der Vorteil von sql_exec gegenüber der direkten Verwendung von prepare, execute und bind_columns ist nicht nur die reduzierte Zahl von Aufrufen, sondern auch ein eingebautes Caching der Statement-Handles.

Ein typischer Einsatz von sql_exec sieht so aus:

my $st = sql_exec \my($content, $style),
"select text, style from content\
where name = ?",
$name;
die "Eintrag $name existiert nicht" unless $st;
while ($st->fetch) {
# benutze $content und $style
}

Dieser Code sucht nach einem Eintrag, dessen Namensfeld den Wert $name enthält. Davon sollte es nur genau einen geben. Kürzer lässt sich das schreiben als:

sql_fetch \my($content, $style),
"select text, style from content\
where name = ?",
$name;

Damit hat cins den Inhalt der Seite und die Nummer (id) des damit verknüpften Stylesheet. Da der Inhalt ebenfalls Perl-Code enthalten kann, wird er durch dieselben Filter geschickt wie phtml-Elemente: Die Funktion pxml2pcode nimmt ‘pxml’ (alles, was eingebetteten Code enthält) und wandelt es in eine interne Zwischenform. Daraus erzeugt pcode2perl normalen Perl-Code. Der wiederum gibt, wenn man ihn evaluiert, den Inhalt auf der Standardausgabe aus (wie print):

eval pcode2perl pxml2pcode utf8_on $content;

utf8_on setzt das UTF-8-Flag der Variablen: MySQL kennt kein UTF-8 und liefert alles als Latin-1 zurück (oder binär), sodass man Perl an die UTF8-Kodierung des Skalars erinnern muss.

Damit cins die Ausgabe weiter verarbeiten kann, fängt sie die capture-Funkton ein:

$content = capture {
eval pcode2perl pxml2pcode utf8_on $content;
die if $@;
};

Da eval gleichzeitig den Perl-Code übersetzt (erwünscht) und alle Fehler abfängt (unerwünscht), muss die Funktion Letztere mit einem die weiterreichen, sonst ist die Seite im Fehlerfall einfach leer.

Ist $style 0, gibt die Funktion den Inhalt der Seite direkt aus. Andernfalls holt sie das Stylesheet mit sql_fetch aus der Datenbank, unterzieht es derselben Behandlung wie vorher den Inhalt und wendet das so erzeugte XSLT-Script auf $content an:

$style = new PApp::XSLT stylesheet => "data:,$style";
$content = $style->apply_string
("<content page='$name'>
$content</content>");

PApp::XSLT ist eine Schnittstelle zu XSLT-Prozessoren. Damit kann man zwischen mehreren Implementierungen (zum Beispiel Sablotron oder Gnome-LibXSLT) wählen, ohne dass sich die API ändert. Der Inhalt wird nicht direkt transformiert, sondern in ein content-Element verpackt. Dadurch kann das Stylesheet den Namen der Seite benutzen. Dieser Umweg ist Geschmackssache: Er nimmt die Möglichkeit, verschiedene Encodings zu verwenden oder Entities zu definieren, erspart es aber, das in jeder Seite einzeln tun zu müssen. Zum Schluss gibt :><?$content:><: den Inhalt von $content aus, und das Mini-Modul

<module name="*"><perl><![CDATA[
cins $PApp::module || "default";
]]></perl></module>

zeigt die Seite an. Da ein leerer Name in der Datenbank (die Startseite hat den leeren String als Namen) nicht sonderlich nützlich ist, ersetzt || "default" ihn durch ‘default’.

Wie man sieht, kann man in 100 Zeilen einiges erreichen, ohne sich den Raum für Verbesserungen zu verbauen. In der Online-Version dieses Artikels findet man ein Beispiel-Stylesheet und einige Ideen, wie man das System weiter ausbauen kann.

Marc Lehmann
ist Mitglied im GCC Steering Committee und Leiter der Entwicklungsabteilung der nethype GmbH. Er hackt, gerne, viel und ausführlich Perl.

[1] Henning Behme; XML-Programmierung; Mutabor; XSLT-Tutorial I-III; iX 1/2001, S. 167; iX 2/2001, S. 142; iX 3/2001, S. 167

Mehr Infos

iX-TRACT

  • Mit dem Perl-Modul PApp lässt sich ein eigenes Content-Management-System aufbauen. Voraussetzung ist die Perl-Entwicklerversion.
  • Eine MySQL-Datenbank speichert Webseiten im XML-Format und die zugehörigen XSLT-Stylesheets.
  • Die Webseiten können eingebetteten Perl-Code enthalten und beliebige Perl-Funktionen benutzen, etwa zur Abfrage von Datenbanken.