ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/docs/papp.sdf
Revision: 1.8
Committed: Sun Feb 25 21:28:42 2001 UTC (25 years, 6 months ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +238 -147 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 !init OPT_STYLE="paper"
2    
3     !define DOC_NAME "PApp - Perl-Anwendungen für die Zukunft des WWW"
4     !define DOC_AUTHOR "Marc Lehmann <pcg@goof.com>"
5     !build_title
6    
7 root 1.2 H1: PApp - Wie beschreibt man ein Chamäleon?
8 root 1.1
9     PApp ist schwer zu beschreiben, da es sich nicht auf ein spezielles
10 root 1.8 Problem spezialisiert, sondern möglichst eine offene Universallösung sein
11 root 1.1 will. Unter anderem ist PApp:
12    
13 root 1.8 - ein Embedded-Perl-Dialekt. Es gibt verschiedene Formen: Zum einen den
14     "literal programming style", der nicht XML-konform ist, sondern beliebige
15     Daten ausgeben kann und viele Möglichkeiten des Einbettens von Perl in
16     XML. Zum anderen einen Standard-XML-Dialekt, der eine Mischung aus beidem
17     darstellt.
18    
19     - eine Bibliothek (aus vielen Perl-Modulen), die das Arbeiten in einer
20     CGI-artigen Umgebung erlauben. Die Umgebung hält sich mehr an Apache, ist
21     jedoch unabhängig davon, ob die PApp-Anwendung in einer CGI-Umgebung,
22     unter mod_perl oder (z.B.) unter CGI::SpeedyCGI ausgeführt wird.
23 root 1.1
24 root 1.8 - eine Bibliothek, die das Arbeiten mit HTML, XML, SQL und anderen,
25     häufig benutzten Umständlichkeiten vereinfacht.
26 root 1.1
27 root 1.8 - eine grosse "State-Maschine", die technisch getrennte Programmteile
28 root 1.1 (z.B. CGI) logisch zu {{einer}} Applikation zusammenfasst.
29    
30 root 1.8 - ein XSLT-Stylesheet-Prozessor. Damit kann man Layout und Inhalt
31 root 1.1 voneinander trennen. Oder auch Ausgabeformat (PDF/HTML/XML etc.) vom
32 root 1.8 Layout. Oder vom Inhalt... Oder der Benutzersprache...
33 root 1.1
34     H2: Warum PApp geschrieben wurde
35    
36     Grosse und kleine Applikationen für das WWW zu erstellen ist sehr
37 root 1.8 arbeitsaufwendig. Session-Variablen kennt Perl (zum Glück ;) nicht,
38     Session- und Usertracking natürlich auch nicht. Es gibt sehr gute
39     Module, die einem einen Teil der Arbeit abnehmen, aber es ist immer sehr
40     aufwendig und umständlich. Dies führt zu unübersichtlichen Programmen,
41     die noch dazu auf viele Dateien aufgeteilt sind, die nicht unbedingt der
42     inneren Logik des Programmes entsprechen. Auch Sicherheitsfehler (wie das
43     Übergeben von sicherheitsrelevanten Daten über {{hidden-fields}} oder
44     ähnliches) passieren schnell, schliesslich muss man sich bei jeder Seite
45     überlegen, wie man seine Daten weiterreicht.
46 root 1.1
47 root 1.5 Datenbankabfragen, das Einbauen des Layouts usw. sind sehr
48 root 1.1 umständlich. Internationalisierung existiert praktisch nicht.
49    
50     PApp vereinfacht alle diese Probleme, indem es sie weitestgehend
51     automatisiert.
52    
53     H1: Grundlegende PApp-Features oder "Nie wieder CGI"
54    
55 root 1.2 Zunächst möchte ich einige der vielen Features von PApp vorstellen
56     (natürlich die wichtigsten ;). Wem das zu langweilig ist und erstmal
57     eine richtige PApp-Applikation sehen will, sollte zum nächsten Abschnitt
58     gehen, "Eine einfache Anwendung mit PApp".
59    
60     H2: Applikationen statt Einzelseitenverwaltungsmonstren
61 root 1.1
62     Bei CGI oder anderen Schnittstellen ist die Sicht auf die Anwendung
63     protokollbedingt seitenbasiert - jede "logische" Seite ist eine Datei
64     oder eine Fallunterscheidung innerhalb der Datei - gemeinsame Komponenten
65 root 1.8 müssen in Bibliotheken (oder auch einfachen "Include"-Dateien; das ist im
66     Prinzip das gleiche) abgelegt werden, die auf jeder Seite erneut geladen,
67     konfiguriert und eingebaut werden müssen. Perl-Programme funktionieren
68     jedoch anders, da gibt es keine Zustände, die beim Anklicken wechseln,
69 root 1.1 sondern ein lineares Programm. Dies läßt sich zwar auch mit PApp nicht
70 root 1.8 verwirklichen, aber es tut sein Bestes, diese Einschränkungen aufzuheben,
71 root 1.1 indem es ganze Anwendungen bearbeitet: Ob mehrere Seiten in einer Datei
72     oder eine Seite auf mehrere Dateien verteilt wird ist in PApp egal.
73    
74 root 1.8 H2: Development/Maintainance: Aktivposten statt Bremsklötzen
75 root 1.1
76 root 1.2 H3: Entwicklungshilfe
77 root 1.1
78 root 1.8 Programme müssen erst entwickelt werden. Dabei werden Fehler
79     gemacht. Daran ändert PApp nichts. Da bei PApp sehr viele Komponenten
80     zusammenwirken, können die Fehler entsprechend komplex sein. Eine gute
81     Unterstützung bei der Fehlersuche ist also wichtig.
82 root 1.1
83 root 1.8 Dies fängt damit an, dass die Zeilennummern des Quelltextes auch
84     nach Kompilieren von Perl/XML zu reinem Perl oder z.B. in ein XSLT
85 root 1.2 erhalten bleiben. Sollte ein Fehler auftreten, so hat man sofort Zugriff
86 root 1.8 auf die Quelldateien, einen kompletten Backtrace und natürlich die
87 root 1.2 State-Daten. Das Exception-System von PApp ist einfach zu benutzen (C<die>
88     genügt, will man es schöner haben benutzt man C<fancydie> ;) und
89 root 1.8 beliebig erweiterbar.
90 root 1.1
91 root 1.2 Im Betrieb will man natürlich keine ausführlichen Fehlermeldungen,
92 root 1.8 womöglich komplett mit Passwort der Datenbank usf... Deshalb
93     protokolliert PApp alle Fehler in eine SQL-Datenbank zusammen mit der
94     State-ID, so dass nur die Fehlerkategorie erscheint (plus ein nettes
95     Textfeld, in dem der Benutzer zusätzliche Informationen eingeben
96     kann). Solange die Seite nur wiederholbare Aktionen enthält, kann der
97     Entwickler den Fehler jederzeit reproduzieren.
98 root 1.1
99 root 1.2 H3: Datenbanken
100 root 1.1
101 root 1.2 Wie soll man ohne auskommen? Natürlich garnicht. In Perl gibt es ein ganz
102 root 1.8 wunderbares System (DBI!), um mehr oder weniger genormt auf SQL-Datenbanken
103 root 1.2 zugreifen zu können. Jetzt ist "nacktes" DBI recht umständlich,
104 root 1.8 zumindest für mich, für den vier Aufrufe pro SQL-Befehl entschieden
105     zu viel sind. Vor allem, wenn man diese aus effizienzgründen über das
106     Programm verstreuen muss (C<prepare> und C<execute>).
107 root 1.1
108 root 1.2 In PApp erledigt man die meisten Aufgaben mit der C<sql_exec>-Funktion
109     (den Rest erledigt man mit C<sql_fetch/fetchall/exists/insertid>). Der
110 root 1.8 folgende Aufruf beispielsweise ersetzt C<prepare>, C<bind_params>,
111     C<execute> und C<bind_columns>:
112 root 1.1
113     !block perl
114 root 1.2 my $st = sql_exec \my($p, $w),
115     "select purzel, wurzel from table
116     where name like ?",
117     $S{search};
118    
119     while ($st->fetch) {
120     echo "$p, $w\n";
121     }
122 root 1.1 !endblock
123    
124 root 1.2 Der besondere Clou: Die SQL-Statements werden gecached, d.h. im
125     Allgemeinen nur ein einziges mal C<prepare>d. Bei Datenbanken, die einiges
126     an Aufwand in diesem Schritt investieren (Stichwort Query-Optimizer)
127     ist das ein grosses Plus. Sogar bei Datenbanken, die dies nicht tun
128 root 1.8 (z.B. MySQL) steigt die Geschwindigkeit merklich. Zudem stehen C<prepare>
129     und das C<execute> nicht mehr weit auseinander (Initialisierung
130     vs. Benutzung!).
131 root 1.1
132 root 1.2 Bei Datenbanken, die eine Art "insertid" unterstützen (zum Glück fast
133     alle), kann man diese nach einem C<INSERT> gleich mit abfragen:
134 root 1.1
135     !block perl
136 root 1.2 my $id = sql_insertid
137     sql_exec "insert into tiere (name) values (?)",
138     "hase";
139 root 1.1 !endblock
140    
141 root 1.8 Den Database-Handle habe ich übrigens nicht vergessen: Wenn man
142     keinen angibt, wird ein Default-Handle benutzt. Geschickterweise wird
143 root 1.2 dieser von PApp selbst gesetzt, so dass man sich im Normalfall noch
144     nicht einmal um die Datenbankverbindung kümmern muss (weil diese auf
145 root 1.8 dem Produktionssystem meistens andere Passwörter benötigt als auf
146     dem Entwicklungssystem, stellt man diese geschickterweise bei der
147     Konfiguration der Applikation ein).
148 root 1.1
149 root 1.8 <<<admin.png>>>
150 root 1.1
151 root 1.2 H2: Trennung von Quelltext und Sprache
152 root 1.1
153 root 1.2 H3: XML
154 root 1.1
155 root 1.2 XML wird in mehreren Teilen von PApp unterstützt bzw. gefordert: Der
156     Standard-Dialekt von PApp ist in XML geschrieben, d.h. um "normale"
157     PApp-Applikationen zu schreiben muss man sich XML bedienen. Die Ausgabe
158     von PApp (meistens Text) ist beliebig, sofern man keine Stylesheets
159     benutzt. Tut man das, muss die Ausgabe natürlich in XML-Syntax sein. Und
160     last-not-least gibt es ein PApp-Modul, mit dem man beliebige XML-Fragmente
161     mit eingebettetem Perl-Quelltext bearbeiten/ausführen/anzeigen kann.
162 root 1.1
163 root 1.2 Die Entscheidung, XML so zentral einzusetzen, fiel mir nicht
164     leicht: Meiner Meinung nach ist XML wunderbar dafür geeignet,
165     Daten innerhalb von Programmen zu verarbeiten und vor allem
166     auszutauschen. Toll ist, dass Menschen XML notfalls lesen und auch
167     schreiben können. Ansonsten ist doch die Mächtigkeit von SGML (oder
168     etwas anderem) vorzuziehen.
169 root 1.1
170 root 1.2 XML jedoch hat bestechende Vorteile: HTML ist eine XML-Applikation (und
171     HTML ist wichtig ;); es existiert eine breite Unterstützung für XML;
172     es gibt bestehende Standards für Stylesheets, Layout, Metadaten und
173     mehr. Schliesslich ist XML auch noch sehr schnell. Nun ja.
174 root 1.1
175 root 1.8 Das hat mich trotzdem nicht davon abgehalten, noch etwas
176 root 1.2 draufzusetzen: Fast alle Dokumente können in einem "Meta-Format" mit
177     eingebettetem Perl-Quelltext geschrieben werden. Die Standard-Syntax
178     dafür bedient sich vier sog. "Modus-Umschalter", die jeweils vom
179     "Verbatim" in den "Perl"-Modus bzw. zurück schalten:
180 root 1.1
181     !block verbatim
182 root 1.2 <: schalte von HTML in Perl um
183     <? wie <:, füge jedoch das Ergebnis in die Ausgabe ein
184    
185     :> schalte in den HTML-Modus
186     ?> schalte in den interpolierten HMTL-Modus
187 root 1.1 !endblock
188    
189 root 1.8 Eine HTML-Seite kann man z.B. so schreiben:
190 root 1.1
191 root 1.2 !block perl
192     <h1>Ein bisschen HTML</h1>
193     <: echo "mit eingebautem perl" :>
194     <: print "so gehts auch, ist aber unendlich wenig langsamer ;)" :>
195     <?"So gehts am schnellsten":>
196     <:for my $text (qw(Noch eine komische Methode)) {
197     ?>$text&#160;<:
198     }:>
199     !endblock
200 root 1.1
201 root 1.2 Die Modus-Umschalter verhalten sich dabei wie eine Statement-Grenze in Perl (also C<;>).
202 root 1.1
203 root 1.2 H3: I18n
204 root 1.1
205     I18n steht kurz für Internationalisierung: Das englische Wort
206     {{Internationalization}} hat 18 Buchstaben zwischen dem 'I' und dem
207     'n', und da das Wort für den Durchschnittsamerikaner viel zu lang und
208     kompliziert ist, hat man diese 18 Buchstaben einfach durch "18" ersetzt.
209    
210     In Kontext von PApp bedeutet dies, dass beinahe überall Textmeldungen
211     übersetzt werden können (wer das C<gettext>-Paket kennt, mit dem
212     üblicherweise C-Programme internationalisiert werden, wird sich fast
213     sofort Zuhause fühlen). Hier ist ein Beispiel:
214    
215     !block verbatim
216     <h1>__"Contents"</h1>
217     <:for (1..10) {:>
218     <?slink sprintf(__"Chapter %d", $i++),
219     "view_chapter", chapterid => $i:>
220     <:}:>
221     !endblock
222    
223     Wie man sieht, kann man C<__"text"> sowohl im
224     HTML/XML/wasauchimmer-Quelltext benutzen, als auch auf Perl-Ebene
225 root 1.8 aufrufen. Die C<__>-Funktion erledigt dabei zwei Aufgaben: Zunächst
226     werden damit Textkonstanten zum übersetzen {{markiert}}. Zur Laufzeit
227     wird dann in der Übersetzungstabelle nachgesehen und der (evt.)
228     übersetzte String zurückgeliefert.
229 root 1.1
230 root 1.3 Wenn man Daten z.B. aus einer Datenbank in eine Variable holt, darf man
231     diese natürlich {{nicht}} markieren, da sie ja nur zur Laufzeit einen
232     String {{enthält}}, selbst aber keine Textkonstante ist. In solchen
233     Fällen verwendet man C<gettext>:
234 root 1.1
235     !block perl
236     my $st = sql_exec \my($id, $name), "select id, name from table";
237    
238     while ($st->fetch) {
239     echo "<li>", gettext$name, "</li><br/>";
240     }
241     !endblock
242    
243     In welche Sprache jeweils übersetzt wird, entscheidet bei HTTP die
244     Einstellung des Browsers, wobei man zweckmässigerweise ein Menü
245     anbietet, in dem der Benutzer dies überschreiben kann:
246    
247     !block perl
248     <?slink "I want English", SURL_SET_LANG => "en":>
249     <?slink "Deutsch willich!", SURL_SET_LANG => "de":>
250     !endblock
251    
252     Auch die Spracheinstellung ist eine Preferences-Variable. Im Gegensatz
253 root 1.8 zum C<gettext>-Paket müssen die Quelltexte nicht alle in einer Sprache
254     sein. Bei C<gettext> ist das auch weniger ein Problem, da der Quellcode
255 root 1.1 eines Programms meistens in einer (natürlichen) Sprache geschrieben
256     wurde, in PApp kann man aber Seiten dynamisch einfügen oder Datenbanken
257     übersetzen. Da diese meist nicht vom selben Team geschrieben werden, ist
258     es nützlich, dort unterschiedliche Sprachen verwenden zu können.
259    
260 root 1.8 <<<poedit1.png>>>
261     <<<poedit2.png>>>
262 root 1.1
263 root 1.2 H3: Unicode / Zeichensatzunabhängigkeit
264 root 1.1
265     Intern unterstützt PApp genau zwei Datentypen (genau wie Perl
266 root 1.8 selbst): {{Binärdaten}} und {{Text}}. Binärdaten verwendet man
267     üblicherweise für Bilder, Datei-Downloads und ähnliches. Diese Daten
268 root 1.1 werden von PApp nicht angerührt.
269    
270     Ganz anders Text: Perl arbeitet intern mit Unicode, das z.Zt. entweder in
271     ISO-8859-1 oder UTF-8 gespeichert wird. Dieser interne Zeichensatz ist
272     völlig unabhängig von der Ausgabe, d.h. der Text wird bei der Ausgabe
273     automatisch in den gewünschten Zeichensatz kodiert (das geschieht wieder
274     Browser/Benutzer/Programmabhängig wobei von den gebräuchlichen Browsern
275     nur Netscape und Lynx die entsprechenden Header liefern). Umgekehrt werden
276     Formulardaten u.ä. automatisch in Unicode gewandelt, d.h. in C<%P> steht
277     Unicode auch wenn der Browser ein Formular in ISO-2022-JP zurückgeschickt
278     hat.
279    
280     Ein JPEG-Bild kann man z.B. so ausgeben:
281    
282     !block perl
283     content_type "image/jpeg", undef;
284     $gd->jpeg(80);
285     !endblock
286    
287     Während man eine japanische Benutzerin vielleicht mit ISO-2022-JP
288     zufriedenstellen kann:
289    
290     !block perl
291     content_type "text/html", "iso-2022-jp";
292     echo __"Hoffentlich war der Übersetzer fleissig";
293     !endblock
294    
295 root 1.2 H2: Trennung von Daten und Layout/Protokoll
296 root 1.1
297 root 1.2 Sprache und Quelltext trennt PApp ja schon. Jetzt muss man noch das Layout
298     vom eigentliche Programm bzw. das Protokoll von den eigentlichen Daten
299     trennen.
300    
301     Mit XSLT-Stylesheets geht das. Und mehr: "Browser XYZ hat ein Problem? Ich
302     fix' es im Stylesheet und vergesse es dann einfach." (z.B. sollte man
303     leere HTML-Tabellenzellen für Netscape lieber mit einem C<&nbsp;>
304     füllen). "Es soll WML (oder CHTML) sein? Ich weiss zwar nicht, wozu
305     das ganze WML-Zeugs sinnvoll sein soll, aber dann mache ich halt' ein
306     Stylesheet dafür." Und eine der wichtigsten Anwendungen: "Wir brauchen
307     eine Version zum drucken. Dafür wandeln wir unser XML-Dokument in XSL" (und dann
308     nach Latex, PDF...).
309    
310     Das bedeutet, dass man - Planung natürlich vorausgesetzt - einen hohen
311     Grad an Protokollunabhängigkeit erreichen kann, ohne den Quelltext zu
312     sehr mit Layout-Entscheidungen o.ä. belasten zu müssen.
313    
314     Leider gibt es noch keine Web-Designer, die XSLT statt HTML/CSS liefern,
315     aber (meine) Vision des Webs der Zukunft sieht so aus: Der Server liefert
316     XML zusammen mit einem XSLT (vom Designer), welches XSL erzeugt. Dieses
317     wird dann angezeigt/gedruckt etc.a.. Metadaten (mit dem etwas besseren
318     Nachfolger von RDF) gestatten dann Fragen wie: "Wer hat dieses Dokument
319     geschrieben?", "Wozu dient es?" aber auch: "Wieso ist das Ding so bunt?".
320    
321     H2: Trennung von Funktionalität und Ausführungsumgebung
322    
323 root 1.8 Plattform-Unabhängigkeit: ein toller Begriff. Was bedeutet er? Nicht
324 root 1.2 viel. Bei PApp bedeutet es, dass auf Unix-Abhängigkeiten möglichst
325     verzichtet wurde und sich PApp nicht an einen bestimmten Server
326 root 1.8 (z.B. Apache) bindet. In der Praxis kann man als Platform mod_perl oder
327     CGI verwenden und für Unfälle wie Windoze schere ich mich einen Dreck
328     (d.h. ich habe momentan nicht vor, für proprietäre Schnittstellen
329     Module zu schreiben bzw. habe PApp nur mit den beschriebenen Plattformen
330     getetst). Aber auch andere Umgebungen wie Text-Interfaces sind denkbar: Da
331     PApp der Applikation immer eine gleichbleibende Umgebung anbietet, muss
332     man lediglich ein Schnittstellenmodul schreiben.
333 root 1.2
334     H2: Persistente Variablen für Sessions und User
335    
336     PApp kümmert sich automatisch um persistente Variablen. So kann man
337     automatisch Variablen erzeugen, die über eine gesamte Sitzung (die mit
338     dem Aufruf der ersten URL beginnt und dann einen Baum bildet) persistent
339     sind. Das geht so einfach, dass man spezielle Hilfsmittel braucht, um
340     nicht an jeder Stelle der Sitzung Zugriff auf beinahe alles zu besitzen.
341    
342     Persistente Variablen gibt es in verschiedenen Geschmäckern. Es gibt:
343    
344     - Session-Variablen (sogenannte {{state keys}}), die über eine gesamte
345     Sitzung erhalten bleiben. Diese werden im Hash C<%S> gespeichert. Man kann
346     beliebige Werte darin ablegen (solange C<Storable> diese serialisieren
347 root 1.8 kann). Meistens geschieht dies durch Anklicken eines Links, weniger
348 root 1.2 häufig direkt.
349    
350     - Benutzerabhängige Variablen (die auch über Sitzungsgrenzen hinaus
351     erhalten bleiben und deshalb {{preferences items}}, Voreinstellungen,
352     heissen). Auch diese befinden sich in C<%S>, von wo sie automatisch in die
353     Benutzerdatenbank wandern bzw, von dort gelesen werden.
354 root 1.1
355 root 1.2 - Lokale Variablen ({{local keys}}) zu nur innerhalb einer Seite oder
356     einer Gruppe von Seiten Bedeutung haben. Diese befinden sich ebenfalls in
357     C<%S> und werden automatisch daraus entfernt.
358 root 1.1
359 root 1.2 - oder beliebige Kombinationen davon.
360 root 1.1
361 root 1.2 Ein Beispiel für eine Sitzungsvariable ist die Information, ob der
362     Benutzer sich schon angemeldet/eingeloggt hat oder ob er bestimmte
363 root 1.8 Zugriffsrechte besitzt. Eine benutzerabhängige Variable ist z.B. die
364 root 1.2 Sprache, die der Benutzer zuletzt ausgewählt hat (global) oder die
365     Anzahl der Tabellenzeilen, die der Benutzer gerne auf einer bestimmten
366     Seite angezeigt haben möchte. Eine lokale Variable ist z.B. ein
367     Datenbank-Objekt, das über mehrere Seiten (z.B. einer Transaktion)
368     gebraucht wird und danach seine Gültigkeit verliert.
369 root 1.1
370 root 1.2 Dadurch ergeben sich mehr Möglichkeiten, als man erwarten
371     würde: Unabhängige Komponenten (z.B. Werbebanner oder Foren) sind auf
372     normale Weise nur sehr schwer zu programmieren, da sie immer Hilfe vom
373 root 1.8 "Hauptprogramm" erfordern, wenn es um die Parameterübergabe geht. In PApp
374 root 1.2 benutzt man einfach persistente Variablen.
375 root 1.1
376 root 1.2 Ein Beispiel für eine etwas ungewöhnlichere Komponente ist die
377     {{editform}}-Bibliothek. Mit ihr kann man HTML-Formulare erstellen, die
378     sich direkt an eine bestimmte Variable binden:
379 root 1.1
380 root 1.2 !block perl
381     ef_begin;
382     ef_string \$S{search};
383     ef_end;
384     !endblock
385 root 1.1
386 root 1.8 Dieses Beispiel stammt von einer Seite, die Objekte aus einer Datenbank
387 root 1.2 anzeigt und sich dabei auf solche beschränkt, die ein Suchwort
388     enthalten. Das Formular bindet die State-Variable C<search> an ein
389     HTML-Textfeld. Bei der Ausgabe wird der aktuelle Wert von C<$S{search}>
390     benutzt. Ändert der Benutzer den Text und schickt das Formular ab wird
391     die Seite neu aufgebaut - mit einem anderen Wert in C<$S{search}>. Da
392 root 1.8 C<editform> beliebige Perl-Referenzen akzeptiert und man in PApp auch
393     Referenzen auf Dateien, SQL-Spalten etc... erzeugen kann, werden viele
394 root 1.2 Formulare zum Kinderspiel.
395 root 1.1
396 root 1.2 Zusätzlich zu den Sitzungsvariablen gibt es noch sogenannte
397     {{Argumente}}, die nur eine Seite lang "persistent" sind, d.h. bei einem
398     Link auf eine andere Seite übergeben werden:
399 root 1.1
400 root 1.2 !block perl
401     echo slink "Klicken Sie hier", -argument1 => "wert1", var2 => "wert2";
402 root 1.1 !endblock
403    
404 root 1.2 Wenn man diesem Link folgt, steht der "wert1" im Hash C<%A> bzw. "wert2"
405     im Hash C<%S>:
406 root 1.1
407     !block perl
408 root 1.2 printf "Das Argument argument1 ist %s, die State-Variable var2 ist %s",
409     $A{argument1}, $S{var2};
410 root 1.1 !endblock
411    
412 root 1.2 Werte, die von "Aussen" (z.B. GET-Request in CGI) kommen, stehen, um die
413     Verwirrung komplett zu machen, in C<%P>. Als Faustregel gilt: was in C<%S>
414     und C<%A> steht ist sicher (bzw. man hat es selbst dorthin gepackt), was
415     in C<%P> steht ist unsicher und muss erst gefiltert/geprüft werden.
416    
417     H2: Sicherheit
418    
419     In vielen CGI-Programmen (aber nicht nur dort) wird der Fehler
420     begangen, sensitive Daten in {{hidden}}-Feldern in einem Formular zu
421     "verstecken" oder sie in die URL zu kodieren um die Parameterübergabe zu
422     realisieren. Dies ist in PApp nicht nötig, da der persistente 'State' in
423     einer Datenbank gespeichert wird und nur der (mit 256 Bit verschlüsselte)
424     Zeiger darauf zum Client übertragen wird. Dadurch werden sensitive Daten
425     gar nicht erst zum Client übertragen, was natürlich auch Bandbreite
426     spart. Sollte der Schlüssel einmal bekannt werden kann man zwar auf
427     andere Sessions zugreifen, die Daten jedoch immer noch nicht verändern
428     (dieser Fall tritt beispielsweise auch auf, wenn ein kaputtes Proxy
429     zwischen Server und Client sitzt).
430    
431     Eine typische PApp-URL sieht übrigens so aus (Applikation "kis", Modul
432     "abteilungswahl"):
433 root 1.1
434 root 1.2 !block verbatim
435     /kis/abteilungswahl/NIlRSJNDIfP3AHfVjxLF5F
436     !endblock
437 root 1.1
438 root 1.2 Da eine "Seite" in PApp aus einem Modulbaum besteht, geht es auch
439     komplizierter:
440 root 1.1
441 root 1.2 !block verbatim
442 root 1.8 /exec/admin/+admin=poedit+poedit=view--?papp=eTDgL.I-lj9O9lTO3wznTk
443 root 1.2 !endblock
444 root 1.1
445 root 1.2 Zusammen mit einem sicheren Transportprotokoll (z.B. SSL, wobei die
446     meisten Browser nur ungenügend oder garnicht gegenüber Attacken
447     schützen, nur so am Rande ;) schützt man sich damit gegen alle Seiten.
448 root 1.7
449     Benutzerauthentifizierung über Zertifikate ist auch im Einsatz: Mit
450     dem C<ssl>-Modul von Stefan Traby gibt es seit neuestem eine komplette,
451     transparente SSL-Integration in das PApp User-Management: Nach
452     einem "<:ssl_user_needed:>" hat man beispielsweise eine garantierte
453     SSL-Session mit User-Zertifikat und einem eindeutigen PApp-user. Das
454     CA-Management-Tool ist in Vorbereitung.
455    
456 root 1.8 <<<janman.png>>>
457 root 1.1
458 root 1.2 H2: Session/User-tracking
459 root 1.1
460 root 1.2 Persistente Variablen erfordern Session-Tracking. Benutzerabhängige
461     Einstellungen (Preferences) darüber hinaus auch User-Tracking. Ersteres
462     geschieht mit einer Session-ID, die (auf verschiedene Arten) in die
463     URL-kodiert wird und die alle notwendigen Daten enthält, um die
464     gewünschte Seite komplett zu erzeugen. Darüber hinaus wird (optional)
465 root 1.8 ein Cookie benutzt, das aber z.Zt. nur zum identifizieren des Benutzers
466     dient, da ich Session-Definitionen über Cookie als Unsinn erachte. Das
467     Cookie wird auch nur einmal am Tag gesetzt, so dass man auch mit der
468 root 1.2 Browser-Einstellung "vor Cookies warnen" arbeiten kann.
469 root 1.1
470 root 1.2 Eine Anwendung wird darüber informiert, ob eine neue Session begonnen
471 root 1.8 wurde oder ob sich ein bisher unbekannter Benutzer angemeldet hat, so dass
472     man Benutzereinstellungen o.ä. initialisieren kann. Dies nützt PApp
473     selbst, um sich z.B. die gewünschte Sprache des Benutzers zu merken oder
474     um das User-Cookie nur einmal täglich zu setzen.
475 root 1.1
476 root 1.2 Eine "Sitzung" ist übrigens ein Baum (nicht nur in PApp), der mit dem
477     ersten "Hit" als Wurzel beginnt. Dadurch ist es unter anderem möglich,
478     die Anzahl der "Reloads" einer Seite zu bestimmen. Dies ist nützlich,
479     um potentiell gefährliche Aktionen (z.B. Löschen von Datenbanken,
480     abschicken von E-mails) nur einmal auszuführen.
481 root 1.1
482 root 1.2 H2: Benutzerverwaltung
483 root 1.1
484 root 1.2 Da PApp Benutzer (mehr oder weniger) eindeutig identifizieren muss,
485     implementiert es intern eine eigene Benutzerverwaltung inklusive einer
486     Unix-artigen Rechtevergabe (es gibt allerdings keinen Super-User im
487 root 1.8 Unix-Sinne). In vielen Anwendungen kann man diese gleich mitbenutzen, da
488 root 1.2 eine eindeutige Benutzer-ID automatisch vergeben wird.
489 root 1.1
490 root 1.2 !block verbatim
491     Sie sind wahrscheinlich Benutzer Nummer <?$userid:><br/>
492     #if auth_p
493     Und ausserdem haben sie sich authentifiziert.<br/>
494     # if access_p "admin"
495     Oh, und "admin"-Rechte besitzen Sie auch noch! Meine Güte!!<br/>
496     # endif
497     #endif
498 root 1.1 !endblock
499    
500 root 1.2 H2: Geschwindigkeit und Skalierbarkeit
501 root 1.1
502 root 1.2 Geschwindigkeit war bei der Implementierung von PApp das zweitwichtigste
503     Ziel (Korrektheit ist das wichtigste ;). Als Marke dient mir ein
504     Pentium-II 266Mhz-Rechner, auf dem auch komplexe Seiten mit mindestens 15
505     Hits/Sekunde dargestellt werden, bzw. der Vergleich mit einem ähnlichen
506     C<Apache::Registry>-Skript.
507 root 1.1
508 root 1.2 Das State-Management von PApp verschlingt pro Seite 1-3 Datenbankzugriffe,
509     die die Zeit bei weitem dominieren (andere Features wie I18n sind im
510     Prinzip kostenlos). Da komplexe Seiten im Allgemeinen wesentlich mehr und
511     kompliziertere Zugriffe enthalten bzw. man ja irgendwie seine Daten
512     speichern muss, ist dies in der Praxis selten eine Einschränkung
513     (Ausnahmen gab und gibt es immer). Andere PApp-Features (z.B. im
514     SQL-Bereich) bringen häufig sogar eine Steigerung der Geschwindigkeit.
515 root 1.1
516 root 1.2 Da ich bisher keine Seite fabrizieren konnte, die die 15 Zugriffe/s-Marke
517     unterschritten hätte, glaube ich noch etwas Spielraum für noch mehr
518 root 1.8 Features zu haben. Wer sich übrigens fragt, woher diese magische Grenze
519     von 15 Hits/s kommen: 15 Hits ist die Marke, die einen wirklich grossen
520     Server von den 99.99% der restlichen Welt unterscheidet. Hat man auf
521     seinem Server 15 oder mehr Zugriffe pro Sekunde kann man sich meistens
522     auch eine schnellere Datenbank oder mehrere Rechner leisten: PApp skaliert
523     problemlos auf mehrere Maschinen oder SMP-Rechner.
524 root 1.1
525 root 1.2 H2: Ideen, Ideen: z.B. "tied forms"
526 root 1.1
527     Ich habe es schon angesprochen: Persistenz von fast allem, zusammen mit
528     den Möglichkeiten von Perl können einen schon auf Ideen bringen, z.B.
529     auf eine Bibliothek (editform), die HTML-Formularfelder an Perl-Refrenzen
530     bindet.
531    
532     Nun ist es an der Zeit, einmal eine Referenz auf eine SQL-Tabelle zu
533     erzeugen:
534    
535     !block perl
536     <:
537     my $row = new PApp::DataRef 'DB_row',
538     table => "user",
539     where => [id => $userid],
540     delay => 1,
541     autocommit => 1;
542    
543     # pre-set name
544     $row->{name} ||= "<username>";
545    
546     ef_begin;
547     :><br>__"ID:" <?ef_string \$row->{id} , 5:><:
548     :><br>__"Name:" <?ef_string \$row->{name}, 20:><:
549     ef_submit __"Update";
550     ef_end;
551     :>
552     !endblock
553    
554 root 1.8 Die Referenz auf die Zeile steht in C<$row> und verhält sich wie eine
555 root 1.1 Referenz auf einen herkömmlichen Perl-Hash. Das C<autocommit> sorgt
556     dafür, dass die geänderten Daten automatisch zurückgeschrieben werden
557     sobald das C<$row>-Objekt gelöscht wird (ausser Scope geht, C<DESTROY>ed
558     wird). Sie ist lokal (C<my>!) gespeichert, da aber die C<ef_>-Funktionen
559     die Referenz persistent speichern, wird das Objekt erst auf der nächsten
560     Seite (also nachdem die Ergebnisse hineingeschrieben wurden!) zerstört,
561 root 1.8 bzw. die Daten geschrieben. Etwas kompliziert, aber zum benutzen muss man
562 root 1.1 die Details ja auch nicht kennen.
563    
564     Nun macht das Beispiel keine Fehlerüberprüfung, meistens das
565     schwierigste. Mit PApp kein Problem. Zuerst schalten wir das C<autocommit>
566     ab, damit die Daten nicht "aus Versehen" geschrieben werden. Ausserdem
567     übergeben wir die Referenz als Argument an die "nächste" Seite.
568    
569     !block perl
570     <:
571     my $row = $A{row}
572     || new PApp::DataRef 'DB_row',
573     table => "user",
574     where => [id => $userid],
575     delay => 1,
576     autocommit => 0;
577    
578     ef_begin -row => $row; # Daten als Argument übergeben
579     !endblock
580    
581     Nun brauchen wir ein Flag, das uns sagt, ob der Datensatz fehlerhaft ist
582     und schon können wir loslegen:
583    
584     !block perl
585     my $err = 0; # Daten fehlerhaft?
586    
587     :><br/>__"ID:" <?ef_string \$row->{id} , 5:><:
588     #if $row->{id} !~ /^(\d+)$/
589     <error>__"The ID must be an integer!"</error>
590     <:$err++:>
591     #endif
592     :><br/>__"Name:" <?ef_string \$row->{name}, 20:><:
593     #if 2 > length $row->{name}
594     <error>__"The Name must contain at least two characters!"</error>
595     <:$err++:>
596     #endif
597     ef_submit __"Update";
598     ef_end;
599     !endblock
600    
601     Die Daten schreibt man dann bei Bedarf in die Datenbank:
602    
603     !block perl
604     #if $row->dirty
605     # if $err
606     <error>__"The entered Data is invalid, please surrender or die (or correct it ;)</error>
607     # else
608     <:$row->flush:>
609     __"The record has been updated."
610     # endif
611     #endif
612     :>
613     !endblock
614    
615     H2: "Web-Widgets"
616    
617 root 1.2 Eine relativ neue "Neuerung" in PApp ist die Einführung von
618     Web-Widgets. Wie so vieles sind das keine speziellen Objekte, sondern eine
619 root 1.8 Art der Programmierung, die zwar schon immer möglich war, aber an die man
620 root 1.2 nicht sofort denkt.
621    
622     Statt einer ganzen Applikation, die "ganze Seiten" (z.B. ganze
623     HTML-Seiten) ausgibt, schreibt man eine Applikation, die nur noch
624     Teilseiten ausgibt, die man in größere Applikationenn einbaut. Dies ist
625     nicht das gleiche wie ein "include": Der Namensraum (globale Variablen,
626     state-keys, also Session-Variablen und Preferences) einer solchen
627     eingebetteten Applikation ist getrennt von der einbettenden Anwendung und
628     bildet eine Art "Unternamensraum".
629    
630     Derlei eingebettete Anwendungen sind vollkommen autark, haben ihren
631     eigenen Zustand ("aktuelle Seite") und können ihre eigenen Formulare,
632     Links etc. erzeugen die mit anderen Applikationen nicht kollidieren. In
633     einer Anwendung verwende ich dasselbe Forum-Element, um "Web Chat",
634     "Kleinanzeigen" und eine "News!"-Seite zu implementieren - jedesmal eine
635     leicht andere Konfiguration aber derselbe Code.
636    
637     Da PApp-Applikationen im Prinzip nur grosse Statemaschinen sind (das ist
638     eine Einschränkung der verwendeten Protokolle, d.h. bis Perl effektive
639     und schnelle continuations bekommt ;), kann man sie auch einfach in andere
640     einbauen. Sie können sich gegenseitig beeinflussen, treten sich aber
641     nicht auf die Füsse.
642    
643 root 1.3 Also im Prinzip genauso wie ein "Widget" in X11 oder gtk+.
644 root 1.2
645     H2: Logging/Protokollierung
646    
647     PApp protokolliert wesentlich mehr, als man benötigt, legal wäre, und
648     speicherbar wäre. Da PApp jederzeit in der Lage sein muss, eine ältere
649     Seite zu regenerieren ("Back & Reload"), speichert es pro Seite die
650     persistenten Variablen (ca. 300-900 Byte, je nach Applikation, das ist,
651 root 1.8 wie gesagt, auch eine tolle Sache zum debuggen).
652 root 1.2
653     Aber irgendwann müssen diese Daten wieder weg bzw. statistische Daten
654     her - nichts ist interessanter, als Zugriffsmuster, Voreinstellungen oder
655     ähnliches, an dem man ablesen kann, welche Dinge beliebt sind und welche
656     nicht (klar, man kann auch ganz andere Sachen auswerten, aber das ist
657     nicht mein Problem).
658    
659     Beim Aufräumen spielt PApp die einzelnen Zugriffe noch einmal
660     durch. Statt jedoch Seiten zu erzeugen gibt PApp den einzelnen
661     Applikationen die Möglichkeit, statistische Daten zu sammeln. Etwas
662     undokumentierter (wenn es da Abstufungen gibt) ist die Möglichkeit,
663     globale Daten zu sammeln, aber es geht...
664 root 1.1
665 root 1.2 H1: Eine einfache Anwendung mit PApp
666 root 1.1
667 root 1.3 Jetzt kommen wir zur eigentlichen Frage: Wie sieht so ein PApp-Programm
668     aus? Nun, meistens so:
669 root 1.1
670 root 1.3 H2: Das Hauptprogramm
671 root 1.1
672 root 1.3 !block verbatim
673     <package name="demo"> <domain lang="en">
674 root 1.1
675 root 1.3 <database dsn="DBI:mysql:demodb"/>
676 root 1.1
677 root 1.3 <translate fields="project.name place.name" lang="de"/>
678     <translate fields="project.description" lang="*" style="auto"/>
679 root 1.1
680 root 1.3 <import src="macro/util"/>
681 root 1.1
682 root 1.3 <include src="demo/somepages"/>
683    
684     </domain> </package>
685     !endblock
686    
687     Hmm... das ist also erstmal XML mit furchtbar vielen neuen
688     Elementen. Normalerweise kopiert man sich einfach ein anderes Programm
689     und schreibt nicht alles neu. Gut: zuersteinmal das C<package>-Element:
690 root 1.8 damit wird - genau wie in Perl - ein neuer Namensraum erzeugt, der sowohl
691 root 1.3 normale Package-Variablen als auch State-Variablen enthält. Nicht jedes
692     Programm muss einen eigenen Namensraum öffnen.
693    
694     Das nächste Element, C<domain>, aktiviert die Übersetzungen: Alle
695     markierten Texte werden unter einem Namen, der C<domain>
696     gebündelt. Beispielsweise befinden sich alle Texte des PApp-Systems
697     selbst in der "papp"-Domain. Da im Beispiel kein Domainname angegeben
698 root 1.8 wird, nimmt PApp den Namen des umschliessenden C<package>-Elements, d.h.
699 root 1.3 wir definieren hier eine Übersetzungsdomain "demo".
700    
701     Das nächste Element deklariert die Standarddatenbank: Wird die
702     Datenbankhandle bei SQL-Abfragen weggelassen, wird diese Datenbank
703 root 1.8 benutzt. Normalerweise deklariert man Datenbanken aber nicht im Quellcode,
704 root 1.3 sondern beim Einrichten der Anwendung im Konfigurationsmenü.
705    
706     Zu den nächsten beiden Elementen muss ich etwas über das Programm
707     erklären, das ich hier entwickeln möchte: Das Demo-Programm sollte
708     kurz sein und die wichtigsten Features von PApp zeigen. Es wird aus drei
709     (Web-) Seiten bestehen, eine Art Hauptseite mit einem Menü und zwei
710     Unterseiten, auf denen man ein Projekt auswählen kann (ein Projekt ist
711     ein einfacher Datensatz in der Datenbank, der den Projektnamen, den
712     Ort und die Beschreibung enthält). Auf der dritten Seite kann man die
713     einzelnen Felder eines Projektes ansehen und ändern.
714    
715     Als besonders überflüssiger Schnickschnack sollen die Projektdaten
716     übersetzbar sein, d.h. in der Sprache des Benutzers angezeigt werden. Das
717     ist nicht sehr wirklichkeitsnah, gibt dafür aber ein sehr simples
718     Beispiel ab.
719    
720     Da die Projektdaten in der Datenbank gespeichert sind, kann man
721 root 1.8 sie nicht einfach mit C<__"xxx"> markieren, sondern braucht ein
722     C<translate>-Element. Wo PApp die Übersetzungen herbekommt, ist relativ
723 root 1.3 egal, man muss PApp nur sagen, wie es an die Texte herankommt und in
724     welcher Sprache sie sind.
725    
726 root 1.8 Das erste C<translate>-Element sagt, dass die Spalten C<name> in der
727 root 1.3 Tabelle <project> und die Spalte C<name> in der Tabelle C<place> in
728     Deutsch sind und dementsprechend in andere Sprachen zu übersetzen sind.
729    
730     Das zweite C<translate>-Element ist schon komplizierter: Nicht alle
731     Projektbeschreibungen sind in derselben Sprache (behaupte ich einfach
732     mal), weshalb als Sprache C<*> angegeben wird. Das bedeutet lediglich,
733     dass {{alle}} Übersetzer diese Texte übersetzen müssen. Wenn der
734     Englisch-Deutsch-Übersetzer also auf einen Deutschen Text stößt, muss
735     er ihn einfach überspringen. Wäre die Spalte als "de" (oder "deu")
736     markiert, würde er sie garnicht erst zu Gesicht bekommen.
737    
738     Da Beschreibungen ausserdem sehr lang sein können, erhält man mit
739     C<style="auto"> die Möglichkeit, die C<__"xxx">-Syntax auch in
740     den Beschreibungen zu verwenden. Dies kann man auch für einfache
741     Textbausteine mißbrauchen...
742    
743     Das darauffolgende C<import>-Element ist wieder etwas einfacher: es
744     entspricht mehr oder weniger der C<use>-Anweisung in Perl (in genau die
745     wird es auch übersetzt), bezieht sich aber auf PApp-Dateien. Damit
746     kann man Funktionen aus anderen (PApp-) Namensräumen importieren. Im
747 root 1.8 vorliegenden Fall importiere ich einfach mal das C<macro/util>-Paket, das
748 root 1.3 sich immer wieder grosser Beliebtheit erfreut.
749    
750     Das letzte Element tut zur Abwechslung genau das, was es sagt: es
751     fügt (logisch gesehen) eine andere Datei an dieser Stelle ein. Die
752 root 1.8 Entscheidung, die folgenden Seiten in eine andere Datei auszulagern, lohnt
753 root 1.3 sich normalerweise nur für grössere Programme, aber so bleibt das
754     Beispiel klein.
755    
756     Bis jetzt tut sich noch nichts. Ändern wir das:
757    
758     H2: Das erste PApp-Modul
759    
760     Einzelne Zustände (z.B. Webseiten) werden bei PApp "Module" genannt, wohl
761     einzig um die Anwender zu verwirren. Diese Module werden meist in andere
762     Elemente (C<domain>, C<package>, C<style> etc..) verpackt, die auf die
763     Sete auf verschiedenste Weise einwirken.
764    
765 root 1.8 Ausführbaren Code kann man grundsätzlich in zwei Formen
766 root 1.3 angeben: Normaler Perl-Code und Perl-Code gemischt mit Text (also wie bei
767     anderen embedded-Dialekten):
768 root 1.1
769 root 1.3 !block verbatim
770     <module name=""><phtml><![CDATA[
771     <html> <title>
772     __"PApp - demo", <?localtime:>
773     </title>
774    
775 root 1.4 <?slink "Deutsch", "/lang" => "de":>
776 root 1.3 <?slink "English", SURL_SET_LANG, 'en':>
777    
778     <p><?slink __"To the editor", "editor":>
779     <p><?slink __"Edit project 1", "editor", projectid => 1:>
780    
781     </html>
782     ]]></phtml></module>
783     !endblock
784    
785     Zuerst zum C<module>-Element: Jedes Modul besitzt einen eindeutigen
786     Namen. Das Standardmodul (das angezeigt wird, wenn nichts spezielles
787     ausgewählt ist, also sozusagen die Startseite) ist das Modul mit dem
788     leeren Namen: C<"">.
789    
790     Das Ergebnis eines Moduls sind die Ausgaben, die darin gemacht werden
791     (z.B. mit C<printf> oder dem PApp-C<echo>). Die Ausgabe des obersten
792     Moduls (im Baum) wird an den Browser geschickt. Für Ausgabe braucht man
793     Perl und das habe ich in ein C<phtml>-Element gepackt (für verbatimen
794     Perl-code würde man ein C<perl>- oder C<xperl>-Element verwenden, für
795     Perl gemischt mit XML gibt es noch C<pxml>).
796    
797     Innerhalb des C<phtml>-Elementes darf nur eine Zeichenkette stehen, die
798     ausgegeben wird. Da wir innerhalb der Zeichenkette Perl einbetten wollen
799 root 1.8 und die Modus-Umschalter {{kein}} gültiges XML sind, muss der Inhalt
800     geschützt werden, in diesem Fall mit einem C<CDATA> (die Verwendung von
801     Abkürzungen in VI o.ä. empfiehlt sich ;).
802 root 1.3
803     Da dies das oberste Modul ist und wir (noch) kein Stylesheet verwenden,
804     müssen wir eine ganz HTML-Seite ausgeben. Als Titel nehmen wir den Text
805     C<PApp-demo>, der übersetzt werden muss sowie, weil es so schön ist, die
806     aktuelle Uhrzeit. Dies geschieht mit einem {{C:<?}}: Der Ausdruck (hier
807 root 1.8 C<localtime>) wird in einem skalaren Kontext ausgewertet und das Ergebnis
808 root 1.3 ausgegeben.
809 root 1.1
810 root 1.3 Die nächste Code-Zeile ist interessanter:
811 root 1.1
812 root 1.3 !block verbatim
813 root 1.4 <?slink "Deutsch", "/lang" => "de":>
814 root 1.3 !endblock
815 root 1.1
816 root 1.3 C<slink> ist PApps Art, eine Hypertext-Referenz (C<A>-Element) zu
817     erzeugen. C<slink> erwartet als erstes Argument den Inhalt des Verweises
818     (der Text, den der Benutzer "anklicken" muss) und daraufhin die
819     sogenannten C<surl>-Argumente, die bei vielen PApp-Funktionen angegeben
820 root 1.4 werden können. C<surl>-Argumente sind im wesentlich "Name => Wert"-Paare,
821     die dem Zielmodul als Argumente übergeben werden können. Im Beispiel
822 root 1.8 ist das die Variable C</lang> (der Slash am Anfang markiert eine globale
823 root 1.4 Variable), die auf den Wert "de" gesetzt wird. Das Ergebnis ist, dass
824     Textmeldungen nun auf Deutsch übersetzt werden.
825 root 1.1
826 root 1.4 !block verbatim
827     <?slink "English", SURL_SET_LANG, 'en':>
828     !endblock
829 root 1.1
830 root 1.4 Neben Argumenten kann man auch bestimmte "Cookies" in die
831     C<surl>-Argumente einbauen. C<SURL_SET_LANG> z.B. setzt die Sprache auf
832     den folgenden Wert und speichert ausserdem die Voreinstellungen ab,
833     d.h. die Sprachwahl ist nun permanent. Neben C<SURL_SET_LANG> gibt es
834     noch eine ganze Reihe weitere "Cookies" wie z.B. C<SURL_EXEC>, mit dem
835     man bei Auswahl des Links eine Unterroutine aufrufen lassen kann oder
836     C<SURL_STYLE_GET>, mit dem man den Stil der URL einmalig überschreiben
837     kann. Die nächste Zeile bringt eine weitere Erweiterung:
838 root 1.1
839 root 1.4 !block verbatim
840     <p><?slink __"To the editor", "editor":>
841     !endblock
842 root 1.1
843 root 1.4 Bisher wurde kein {{Ziel}} für den Link angegeben. In solchen Fällen
844     nimmt PApp die aktuelle Seite (das aktuelle Modul) als Ziel, d.h. sie wird
845 root 1.8 einfach neu geladen (bei Sprachänderungen etc. ja gewünscht). Möchte
846     man ein anderes Modul ansteuern, gibt man einfach den Namen des Moduls an,
847 root 1.4 z.B. C<"editor">. Ein "Klick" auf den Link (oder das Laden der URL, wie
848     auch immer das geschieht) ruft dann das C<editor>-Modul auf.
849    
850     Das "Ziel" muss auch nicht immer ein Modulname sein, es geht auch
851     komplizierter: C<"admin,user/edit,group/"> z.B. verzweigt auf das
852     C<admin>-Modul und gleichzeitig in einem eingebetteten Widget C<user> auf
853     das Modul C<edit>, während es im Widget C<group> auf das Hauptmodul (das
854     mit dem leeren String als Namen) schaltet. Aber das braucht man wirklich
855 root 1.8 nur in grossen Projekten ;).
856 root 1.1
857 root 1.4 Ziel und Argumente kann man natürlich kombinieren:
858 root 1.1
859 root 1.4 !block verbatim
860     <p><?slink __"Edit project 1", "editor", projectid => 1:>
861     !endblock
862 root 1.1
863 root 1.4 Hier wird dieselbe Seite (C<editor>) angesteuert, dieses mal wird jedoch
864     ein Argument übergeben. Beim Aufruf wird dieses Argument in den State
865     geschrieben, steht dem C<editor>-Modul also als C<$S{projectid}> zur
866     Verfügung. Manchmal möchte man einfach nur ein Argument übergeben,
867 root 1.8 dass nicht im State endet, d.h. nicht persistent ist. In diesem Fall
868 root 1.4 kann man vor den Namen ein '-' stellen, der Wert landet dann nicht in
869 root 1.8 C<%S> sondern in C<%A> und wird nach dem Aufruf weggeworfen. Eine dritte
870     Möglichkeit ist es, eine Referenz auf einen Skalar anzugeben (der
871 root 1.4 natürlich persistent sein muss, sonst existiert er beim Aufruf nicht
872     mehr).
873    
874     Die Werte dürfen beliebige Perl-Referenzen sein, solange sie
875     serialisierbar sind. In PApp gehören Code-Referenzen und (PApp-)
876     Datenbankhandles übrigens zu den serialisierbaren Datentypen, wenn man
877     etwas Vorsicht walten lässt.
878    
879 root 1.8 Der erste Link auf C<editor> soll, da er kein Projekt auswählt, eine
880 root 1.4 Liste aller Projekte zeigen während der zweite ein spezielles Projekt
881     (das hoffentlich existiert) anzeigen soll. Damit wäre die erste Seite
882     erklärt, schreiten wir zum C<editor>-Modul:
883    
884     H2: Das C<editor>-Modul
885    
886     Ich gebe zu, ich habe etwas gemogelt. Natürlich macht es normalerweise
887     mehr Sinn, zwei getrennte Seiten (z.B. C<project_list> und
888 root 1.8 C<project_edit>) für das Listen und Edieren zu benutzen. Aber dann hätte
889 root 1.4 ich keinen Grund, ein weiteres Stückchen "syntactic sugar" zu zeigen,
890     Präprozessorkommandos:
891    
892     !block perl
893     <module name="editor">
894     <state keys="projectid" local="yes"/>
895     <phtml><![CDATA[
896     <:header:>
897    
898     #if defined $S{projectid}
899     ... edit the specific project
900     #else
901     ... show a list of projects
902     #endif
903 root 1.1
904 root 1.4 <:footer:>
905     ]]></phtml></module>
906     !endblock
907 root 1.1
908 root 1.4 Das C<module>-Element kennen wir ja schon. Diesmal deklariert es das
909 root 1.8 C<editor>-Modul. Das nächste Element C<state> ist schon interessanter:
910     hier markiert es bestimmte State-Keys (hier: C<projectid>) als C<local>,
911     d.h. lokal zu allen Seiten, die diese Variable so markieren. Da das
912     Hauptmodul die C<projectid> {{nicht}} als lokal markiert, wird sie beim
913     "Klick" auf das Hauptmodul automatisch gelöscht. Hätte man stattdessen
914     geschrieben
915 root 1.1
916 root 1.4 !block perl
917     <state keys="projectid bgcolour" preferences="yes"/>
918     !endblock
919 root 1.1
920 root 1.4 hätte man die beiden State-Keys als Voreinstellungswerte markiert, d.h.
921     beim nächsten Aufruf würde der Benutzer das gleiche Projekt sehen (und
922     eventuell die gleiche Hintergrundfarbe, je nachdem, was C<bgcolour>
923     bedeutet).
924    
925     Die beiden "Tags", {{C:<:header:>}} und {{C:<:footer:>}}, sind eigentlich
926     nur getarnte Funktionsaufrufe: In den Tagen vor XSLT habe ich meistens auf
927     diese Weise ein Standard-Layout erstellt. C<header> könnte man z.B. so definieren:
928    
929     !block perl
930     <macro name="header"><phtml><![CDATA[
931     <html>
932     <head><title>__"Hallole"</title></head>
933     <body>
934     ]]></phtml></macro>
935     !endblock
936 root 1.1
937 root 1.4 C<macro> definiert eine ganz normale Perl-Funktion, sie kann Argumente
938     annehmen und Werte zurückliefern. Der einzige Unterschied ist, dass man
939     Perl-Funktionen auf diese Weise in "phtml"-Syntax schreiben kann. Um
940     einzelne Seiten gegen unberechtigen Zugriff zu schützen (und um noch mehr
941     abzuschweifen) habe ich früher folgendes gemacht:
942    
943     !block perl
944     <macro name="page(&amp;)" args="$body"><phtml><![CDATA[
945     <html> <body>
946     #if access_p "project_editor"
947     <:&$body:> <!-- eigentliche seite anzeigen -->
948     #else
949     __"You need to login yourself first"<p>
950     <:loginbox:> <!-- loginbox anzeigen -->
951     #endif
952     </body> </html>
953     ]]></phtml></macro>
954    
955     <module name="secure_page"><phtml><![CDATA[
956     <:page {:>
957     <h1>__"Hallo"</h1>
958     <:}:>
959     ]]></phtml></module>
960     !endblock
961 root 1.1
962 root 1.4 Gut, zurück zum Thema: Die Aufgabe ist klar: ist eine C<projectid>
963     gegeben, soll das entsprechende Projekt ediert werden, sonst soll eine
964     Liste von Projekten zur Auswahl angeboten werden.
965    
966     Das könnte man zwar mit einem C<if> lösen, es geht aber auch anders:
967    
968     !block perl
969     #if <perl-ausdruck>
970     ...
971     #elif <perl-ausdruck>
972     ...
973     #else
974     ...
975     #endif
976     !endblock
977 root 1.1
978 root 1.4 Das ist fast so schön wie C... *hüstel*. Jedenfalls wird es in ein
979     hundsnormales Perl-if/elsif/else/endif umgesetzt. Die beiden Teile "edit
980     the specific project" und "show a list of projects" werden sofort gefüllt:
981    
982     H3: "show a list of projects"
983    
984     Zuerst die Liste der Projekte. Ah, wir benötigen SQL. Nun, das ist
985     einfach, wir brauchen nur "jede Menge" HTML auf das Problem zu werfen:
986    
987     !block perl
988     <table><tr><th>__"Project"<th>__"Budget"
989     <:
990     my $st =
991     sql_exec \my($id, $name, $budget),
992     "select id, name, budget
993     from project";
994    
995     while ($st->fetch) {
996     :><tr><td>
997     <?slink gettext$name,
998     projectid => $id?>
999     <td>$budget
1000     <:
1001     }
1002     :>
1003     </table>
1004     !endblock
1005 root 1.1
1006 root 1.4 Dies erzeugt eine einfache HTML-Tabelle mit den beiden
1007     Spaltenüberschriften "Project" und "Budget", natürlich übersetzbar. Der
1008     Aufruf von C<sql_exec> tut drei Dinge:
1009    
1010     ^ C<prepare>, falls notwendig (C<prepare>te Statements werden gecached).
1011     & C<bind_columns>, auf die drei Referenzen C<\$id>, C<\$name> und C<\$budget>.
1012     & C<execute>, um die Abfrage zu starten.
1013    
1014     Nun müssen wir nur noch in einer Schleife über die Zeilen iterieren
1015     (mit dem alten DBI-Bekannten C<fetch>) und TR/TD-Zeilen ausgeben. In
1016     der Schleife kann man sehr schön sehen, wie man zwischen Perl und HTML
1017     umschalten kann. Keine Angst, daran gewöhnt man sich sehr schnell, vor
1018     allem mit Syntax-Hilighting (ein weiterer Grund, auf vim umzusteigen).
1019    
1020     Das einzig Komplizierte ist der Aufruf von C<slink>, der einen C<A
1021     HREF>-Link auf das C<editor>-Modul (also auf die aktuelle Seite) erzeugt
1022     und diesem gleich noch die aktuelle Projekt-ID mitgibt. C<gettext> ist
1023     der Runtime-Teil der C<__>-Funktion, d.h. die Funktion, die von C<__> zur
1024     Laufzeit aufgerufen wird. Sie {{übersetzt}} das Argument, {{markiert}} es
1025     aber nicht.
1026    
1027     Das Ergebnis ist ein Link, der beim Aufruf die Seite neu lädt, nur mit
1028     dem Unterschied, dass diesmal eine Projekt-ID verfügbar ist also nicht
1029     mehr die Liste angezeigt wird.
1030    
1031     H3: "edit the specific project"
1032 root 1.1
1033 root 1.4 Der Rest ist nun relativ einfach: Mit C<DataRef> eine Referenz erzeugen,
1034     mit C<editform> ein Formular, der Rest geht von alleine:
1035 root 1.1
1036 root 1.4 !block perl
1037     <:ef_begin:>
1038 root 1.1
1039 root 1.4 <:my $row = new PApp::DataRef 'DB_row',
1040 root 1.1 table => "project",
1041     where => [id => $S{projectid}]:>
1042    
1043 root 1.4 <p>__"Name": <:ef_string \$row->{name}, 40:>
1044     <p>__"Place:" <:ef_relation \$row->{place},
1045     "id, name from place order by 2",
1046     0 => __"unknown":>
1047     <p>__"Budget": <:ef_string \$row->{budget}, 8:>
1048     <p>__"Description": <:ef_text \$row->{description}, 60, 10:>
1049 root 1.1
1050 root 1.4 <:ef_constant \$row->{user}, $userid:>
1051 root 1.1
1052 root 1.4 <:ef_submit __"Update":>
1053     <:ef_end:>
1054     !endblock
1055 root 1.1
1056 root 1.4 C<new PApp::DataRef> erzeugt eine Zeilenreferenz auf die Zeile mit
1057     dem gewünschten Projekt, C<ef_begin> und C<ef_end> umschliessen ein
1058 root 1.8 Formular, C<ef_string> erzeugt ein Textfeld in der gewünschten Breite
1059 root 1.4 während C<ef_text> ein C<textarea>-Element erzeugt. C<ef_submit> sollte
1060     selbsterklärend sein.
1061    
1062 root 1.8 Habe ich was vergessen? Oh ja, der Ort ist ja in einer separaten Tabelle,
1063 root 1.4 in C<project> wird ja nur die ID des Ortes gespeichert. Für solche
1064     Relationen gibt es bei C<editform> das C<ef_relation>-"Element". Man
1065     übergibt einen SQL-Ausdruck, der zwei Spalten (ID und Name) liefert
1066 root 1.8 und optional ein paar weitere Paare (z.B. kann man auch "unbekannt"
1067 root 1.4 angeben). Als Ergebnis erhält man ein C<select>-Element in HTML.
1068    
1069     Als letztes wird ein Formularfeld noch mit einer Konstanten (die der
1070     Benutzer nicht ändern kann) gefüllt, in diesem Fall wird das Feld
1071     C<user> auf die aktuelle C<userid> gesetzt, so weiss man immer, wer den
1072     Datensatz zuletzt verändert hat.
1073    
1074     Wer Formulare in anderen Sprachen (WML...) benötigt, kann sich seien
1075     eigenen Elemente basteln: C<editform> ist es grundsätzlich egal, wie die
1076 root 1.8 Syntax ist, solange das Schnittstellenmodul von PApp die Daten dekodieren
1077 root 1.4 kann.
1078    
1079     H2: Aufgemotzt hält besser
1080    
1081     Bis jetzt war alles noch langweilige Grundlagen. Vor allem ist
1082     Standard-HTML doch soo langweilig: die Tabellen sind nicht farbig
1083     hinterlegt, um das Lesen zu erleichtern. Und die C<header>- und
1084     C<footer>-Methode ist auch nicht gerade ein flexibles Layout-Werkzeug.
1085    
1086 root 1.6 Deshalb{{}}: XSLT muss her ("eXtensible StyLesheet Transformations"). Und weil
1087 root 1.4 ich so anspruchsvoll bin, gleich zwei Stylesheets: eins ohne aufwendige
1088     Grafik zum benutzen und eins mit vielen farbigen Elementen zum verkaufen
1089     ("bunt haben wollen").
1090    
1091     Zuerst müssen wir das Stylesheet laden:
1092    
1093     !block perl
1094     <perl><![CDATA[
1095     $stylesheet[0] = $papp->load_stylesheet("demo/demo1");
1096     $stylesheet[1] = $papp->load_stylesheet("demo/demo2");
1097     ]]></perl>
1098     !endblock perl
1099    
1100     Naja, zuerst müsste man es schreiben oder besser klauen. Ausserdem muss
1101     man es nicht zuerst laden, aber darüber gehe ich einfach mal hinweg...
1102    
1103     Das C<perl>-Element ist übrigens {{nicht}} in einem Modul: Perl-Code,
1104     der nicht in ein Modul gesteckt wird, wird beim Laden der Applikation
1105     ausgeführt, d.h. ganz ähnlich wie ein Perl-Modul. Zu diesem Zeitpunkt
1106     kann man aufwendige Initialisierungen machen bzw. Dateien nachladen, die
1107     man später braucht.
1108    
1109     Wie wendet man diese "Stylesheets" nun an? Ganz einfach, man packt alle
1110 root 1.8 Module, die "gestyled" werden sollen, in ein C<style>-Element:
1111 root 1.4
1112     !block perl
1113     <style apply="output" expr="$stylesheet[ $S{style} ]">
1114    
1115     <module name=""><phtml><![CDATA[
1116     <p/><?slink __"To the edito...
1117     <p/><?slink __"Edit projec...
1118     ]]></phtml></module>
1119 root 1.1
1120 root 1.4 ...
1121     </style>
1122     !endblock perl
1123    
1124     Das C<apply> bezieht sich auf den Zeitpunkt, zu dem das Stylesheet
1125     angewendet werden soll. C<apply="output"> bestimmt, dass das Stylesheet
1126     kurz vor der Ausgabe angewendet werden soll. Normalerweise würde man nun
1127     den Dateinamen des Stylesheets mit C<src="pfad"> angeben, da wir aber
1128     zwischen zwei Stylesheets hin- und herschalten wollen, geben wir mit
1129     C<expr> einen Perl-Ausdruck an, der ein Stylesheet-Objekt als Ergebnis
1130     haben (sollte). Das muss {{natürlich}} auch kein XSLT-Stylesheet sein,
1131     aber wem sage ich das...
1132    
1133     Das Modul ist übrigens (bis auf die durch "..." angedeuteten Lücken)
1134     vollständig, d.h. der Kopf mit Titel und Sprachumschalter (sowie
1135 root 1.5 Stylesheet-Umschalter) wird durch das Stylesheet hinzugefügt.
1136 root 1.1
1137 root 1.5 H1: Tinychat - ein kleiner Chat in 20 Zeilen
1138    
1139     Zum Schluss noch ein sehr einfaches Beispiel: C<macro/tinychat.papp> ist
1140     ein sehr einfaches Chat-Fenster: Es zeigt die letzten fünf Eingabezeilen
1141 root 1.8 an, gefolgt von einer Eingabebox. Der Chat-Inhalt ist systemweit, d.h. auf
1142 root 1.5 allen Servern in einem PApp-System ist immer derselbe Text, man kann diese
1143     "Chatbox" also in beliebige Programme einbauen.
1144    
1145     !block perl
1146     <macro name="tinychat*()"><pxml><![CDATA[
1147     #if $A{tinychat_submit} && $P{input} && !reload_p
1148     <:
1149     lockenv {
1150     my $r = getenv "TINYCHAT";
1151     shift @$r while @$r > 5;
1152 root 1.8 push @$r, escape_html sprintf "%s: %s",
1153 root 1.5 username || "<ANON$userid>", $P{input};
1154     setenv "TINYCHAT", $r;
1155     };
1156     :>
1157     #endif
1158     <:
1159     my $r = getenv "TINYCHAT";
1160     echo map "<tt>$_</tt><br />", @$r;
1161     :>
1162     <br />
1163     <?sform -tinychat_submit => 1:>__"Chat: "<?textfield "input":><?endform:>
1164     ]]></pxml></macro>
1165     !endblock
1166    
1167     Zuallererst ist C<tinychat> nur eine Funktion, teilt sich also den
1168     Namensraum mit dem Aufrufer. Tinychat ist so winzig und so schlecht
1169     konfigurierbar, dass das Sinn macht... Sehen wir uns mal den Teil an, der
1170     die Ausgabe erledigt:
1171    
1172     !block perl
1173     <:
1174     my $r = getenv "TINYCHAT";
1175     echo map "<tt>$_</tt><br />", @$r;
1176     :>
1177     !endblock
1178    
1179     Die Texte sind in einer "Environment"-Variable gespeichert. Diese
1180     Variablen sind global für das gesamte PApp-System und könenn auch
1181     ausserhalb von PApp abgefragt bzw. verändert werden. Das ganze ist also
1182     eher ein System zur asynchronen Kommunikation. Neben normalen Strings kann
1183     man alles darin ablegen, was irgendwie serialisierbar ist, insbesondere
1184     eine Array-Referenz, in der die einzelnen Zeilen sind.
1185    
1186     Zur Ausgabe wird also lediglich die Variable C<PAPP_TINYCHAT> ausgelesen
1187     und die einzelnen Zeilen in ein "<tt>zeile</tt><br />" gepackt.
1188    
1189     Fehlt noch das Eingabefeld:
1190    
1191     !block perl
1192     <?sform -tinychat_submit => 1:>
1193     __"Chat: "
1194     <?textfield "input":>
1195     <?endform:>
1196     !endblock
1197    
1198     Hier wird kein C<editform> benutzt: Der Overhead ist im Vergleich
1199     zum Gewinn (es gibt keinen) zu gross. Die Funktion C<sform>
1200     (die auch von C<ef_begin> benutzt wird) gibt das einleitende
1201     C<FORM>-Tag aus. Dann folgt der Text C<Chat:> und ein ganz normales
1202     HTML-C<INPUT>-Element. C<endform> schleisslich gibt ein "</FORM>" aus und
1203     existiert eigentlich nur aus Symmetrie.
1204    
1205     Das Eingabefeld heisst C<input>. Was passiert, wenn wir sonst noch
1206 root 1.8 ein C<input>-Feld haben und dieses andere Feld submittet wird? Kein
1207 root 1.6 Problem{{}}: Wir übergeben C<sform> einfach ein Argument, das nur dazu
1208     dient, die Information "Tinychat-Formular ist gemeint" zu übertragen.
1209 root 1.5
1210     Ganz nebenbei: C<sform> ist - wie sehr viele PApp-Funktionen - sehr
1211     einfach definiert:
1212    
1213     !block perl
1214     PApp::HTML::_tag "form", { method => 'GET', action => &surl };
1215     !endblock
1216    
1217     Nun zum Teil, der die Eingabezeile nach dem Abschicken hinzufügt:
1218    
1219     !block perl
1220     #if $A{tinychat_submit} && $P{input} && !reload_p
1221     <:
1222     lockenv {
1223     my $r = getenv "TINYCHAT";
1224     shift @$r while @$r > 5;
1225     push @$r, escape_html sprintf "%s (%s): %s",
1226     username || "<ANON$userid>", $P{input};
1227     setenv "TINYCHAT", $r;
1228     };
1229     :>
1230     #endif
1231     !endblock
1232    
1233     Die erste Zeile testet drei Dinge:
1234    
1235 root 1.8 ^ Es muss "unser" Formular sein; das erkennt man daran, dass der Parameter
1236 root 1.5 C<tinychat_submit> logisch wahr ist.
1237 root 1.8 & Die Eingabe sollte nicht leer sein (o.k. "0" ist auch nicht erlaubt).
1238     & Die Seite sollte nicht das Ergebnis eines "Reloads" sein.
1239 root 1.5
1240     Der letzte Punkt bedarf einer Erklärung: PApp weiss, wie oft eine
1241 root 1.8 Seite angefordert wurde und teilt dies über die Funktion C<reload_p>
1242 root 1.5 mit, die die Anzahl der Seitenaufrufe für {{dieselbe}} Seite minus
1243     eins zurückliefert. Ist diese Zahl ungleich null, wurde der Code schon
1244     ausgeführt.
1245    
1246     Das eigentliche hinzufügen ist Standard: Variable holen, alte Zeilen
1247     rauslöschen, neue Zeile hinzufügen, Variable auf neuen Wert setzen.
1248    
1249     Die Funktion C<lockenv>, in die die Manipulation eingeschlossen ist,
1250     schützt das Programm gegen gleichzeitige Modifikationen anderer
1251     Webserver, d.h. die Operation wird atomar. C<escape_html> quoted das
1252     Argument. Die Funktion C<username> (aus dem C<macro/admin>-Paket) liefert
1253     den Namen des Benutzers, falls dieser einen besitzt. Ansonsten nimmt
1254     Tinychat C<ANON> + die numerische User-ID, die jedem Benutzer zugeteilt
1255     wird.
1256    
1257     H1: Die Nachteile
1258    
1259     Bei allen Vorteilen, es gibt auch Nachteile... die packe ich ans Ende und
1260     fasse mich auch gerne sehr kurz:
1261    
1262     H2: Die Lizenz (Oder doch ein Vorteil?)
1263    
1264     Tja, PApp war doch tatsächlich mal GPL, und zwar zu einer Zeit, zu der
1265     wir es praktisch nur als CGI-Krücke verwendet haben. Inzwischen hat
1266     sich PApp gemausert und wurde zu einem unserer Standbeine. Als eine
1267     andere Firma versuchte, uns mit unserem Produkt Konkurrenz bei unseren
1268 root 1.8 Kunden zu machen ("wir können da einfach ein paar Dutzend Programmierer
1269 root 1.5 dransetzen"), mussten wir leider handeln.
1270    
1271     Die "PApp Public License" ist so ähnlich wie die MySQL Public License,
1272     d.h. wer sie privat einsetzt (bzw. für die Forschung und Lehre oder
1273     für eine not-for-profit-Organisation), darf PApp weiterhin kostenlos
1274     nutzen. Wer kräftig Kohle damit macht, muss uns einen Teil davon abgeben
1275     (die eigentliche Lizenz ist etwas länger ;).
1276    
1277     Langfristig ist geplant, PApp wieder in GPL oder besser zu
1278     überführen. Mittelfristig muss man damit Leben ;)
1279    
1280     H2: Die Abhängigkeiten
1281    
1282     Zur Zeit gibt es keine Perl-Release, die annähernd mit UTF8
1283     zurechtkommt. Z.Zt. (d.h. buchstäblich in dieser Minute) benötigt
1284     PApp perl-5.7.0-DEVEL7952 oder ein paar hundert Patches davor oder
1285     danach. Demnächst wird auf DEVEL8xxx umgestellt (z.Zt. gibt es einige
1286     Bugs, die dies verhindern). PApp funktioniert zwar wunderbar, aber eben
1287     nur, wenn die restlichen Komponenten aufeinander abgestimmt sind.
1288    
1289     H2: MySQL
1290    
1291     Das Grundsystem von PApp benötigt zwar kein MySQL, einige Module
1292     (z.B. C<PApp::Env>) dagegen (aus Geschwindigkeitsgründen) schon.
1293     Möchte man also eine einfache Installation und alle Features empfiehlt
1294     sich MySQL, zumindest für die PApp-interne Datenbank selbst. Ansonsten
1295     arbeitet PApp mit allen Datenbanken, die ein DBI-Interface aufweisen, ohne
1296     Probleme.
1297    
1298     H2: Geschmackssache
1299    
1300     PApp ist etwas sehr persönliches - ich habe meine eigenen Vorstellungen
1301     davon, wie Web/CGI etc. funktionieren sollte, darin verwirklicht. Es
1302     hat meine Motivation (die sich mit "nie wieder CGI" zusammenfassen
1303     liess) gewaltig gesteigert - Ein einfaches aber dennoch komplettes
1304     Content-Management-System kann man in weniger als 500 Zeilen hinlegen -
1305     für mich ein wichtiger Faktor, denn ich hasse nichts mehr, als das Rad
1306     jedesmal neu erfinden zu müssen.
1307    
1308     Da ich bekannt bin für meinen etwas merkwürdigen Geschmack (sagt man
1309     mir) muss das {{wie}} nicht unbedingt jedem gefallen.
1310    
1311     A1: Referenzen
1312 root 1.8
1313     * {{http://papp.plan9.de/}}. Die PApp-Homepage.
1314    
1315     A1: XSLT-Quelltext "Text-Only-Layout"
1316    
1317     Dies und das folgende XSLT-Stylesheet kratzen nur an der Oberfläche
1318     dessen, was mit XSLT möglich ist.
1319    
1320     !block verbatim
1321     <xsl:stylesheet version="1.0"
1322     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1323     xmlns:papp="http://www.plan9.de/xmlns/papp"
1324     >
1325    
1326     <xsl:output method="html" omit-xml-declaration='yes' media-type="text/html" encoding="utf-8"/>
1327    
1328     <xsl:template match="papp:module">
1329     <html>
1330     <title><xsl:value-of select="@module"/></title>
1331     <body>
1332     <?slink "English", "/lang" => 'en':>
1333     <xsl:text> </xsl:text>
1334     <?slink "Deutsch", "/lang" => "de":>
1335     <br/>
1336     <?slink __"Fancy", style => 1:>
1337    
1338     <hr/>
1339     <xsl:apply-templates/>
1340     <hr/>
1341     <:debugbox:>
1342     </body>
1343     </html>
1344     </xsl:template>
1345    
1346     <xsl:template match="node()|@*">
1347     <xsl:copy>
1348     <xsl:apply-templates/>
1349     </xsl:copy>
1350     </xsl:template>
1351    
1352     </xsl:stylesheet>
1353     !endblock
1354    
1355     A1: XSLT-Quelltext "Superbunt und Superhässlich-Layout"
1356    
1357     !block verbatim
1358     <xsl:stylesheet version="1.0"
1359     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
1360     xmlns:papp="http://www.plan9.de/xmlns/papp"
1361     >
1362    
1363     <xsl:output method="xhtml" omit-xml-declaration='yes' media-type="text/html" encoding="utf-8"/>
1364    
1365     <xsl:template match="papp:module">
1366     <html>
1367     <title><xsl:value-of select="@module"/></title>
1368     <body link="#a000000" alink="#a00000" vlink="#a00000" text="#000000" bgcolor="#ffffb4">
1369     <?slink "English", "/lang" => 'en':>
1370     <xsl:text> </xsl:text>
1371     <?slink "Deutsch", "/lang" => "de":>
1372     <br/>
1373     <?slink __"Plain", style => 0:>
1374     <table bgcolor='#ffff00' border="1"><tr>
1375     #if $PApp::module ne ""
1376     <td><?slink __"[MAIN PAGE]", "":></td>
1377     #endif
1378     #if $PApp::module ne "editor" or $S{projectid}
1379     <td><?slink __"[PROJECTS]", "editor", projectid => undef:></td>
1380     #endif
1381     </tr></table>
1382     <xsl:if test="@module=''">
1383     <h1>__"Demo"</h1>
1384     __"Welcome to our pages..."
1385     </xsl:if>
1386     <table bgcolor="#ffffff" border="5" cellpadding="20"><tr><td>
1387     <xsl:apply-templates/>
1388     </td></tr></table>
1389     <hr/>
1390     <font size="1">Copyright whatever, whenever etc...</font>
1391     </body>
1392     </html>
1393     </xsl:template>
1394    
1395     <xsl:template match="node()|@*">
1396     <xsl:copy>
1397     <xsl:apply-templates/>
1398     </xsl:copy>
1399     </xsl:template>
1400    
1401     </xsl:stylesheet>
1402     !endblock
1403 root 1.2
1404 root 1.1