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