ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/docs/papp.sdf
Revision: 1.6
Committed: Tue Feb 6 02:00:05 2001 UTC (25 years, 8 months ago) by root
Branch: MAIN
Changes since 1.5: +3 -3 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     Benutzerauthentifizierung über Zertifikate ist auch im Einsatz (mit dem
446     C<ssl>-Modul von PApp).
447 root 1.1
448 root 1.2 H2: Session/User-tracking
449 root 1.1
450 root 1.2 Persistente Variablen erfordern Session-Tracking. Benutzerabhängige
451     Einstellungen (Preferences) darüber hinaus auch User-Tracking. Ersteres
452     geschieht mit einer Session-ID, die (auf verschiedene Arten) in die
453     URL-kodiert wird und die alle notwendigen Daten enthält, um die
454     gewünschte Seite komplett zu erzeugen. Darüber hinaus wird (optional)
455     ein Cookie benutzt, das aber z.Zt. nur zum Identifizieren des Benutzers
456     dient, da ich Session-Definitionen per Cookie als Unsinn erachte. Das
457     Cookie wird auch nur einmal am Tag gesetzt, so daß man auch mit der
458     Browser-Einstellung "vor Cookies warnen" arbeiten kann.
459 root 1.1
460 root 1.2 Eine Anwendung wird darüber informiert, ob eine neue Session begonnen
461     wurde oder ob sich ein bisher unbekannter Benutzer angemeldet hat, so
462     daß man Benutzereinstellungen o.ä. initialisieren kann. Dies nützt
463     PApp selbst, um sich z.B. die Sprache des Benutzers zu merken oder um das
464     User-Cookie nur einmal täglich zu setzen.
465 root 1.1
466 root 1.2 Eine "Sitzung" ist übrigens ein Baum (nicht nur in PApp), der mit dem
467     ersten "Hit" als Wurzel beginnt. Dadurch ist es unter anderem möglich,
468     die Anzahl der "Reloads" einer Seite zu bestimmen. Dies ist nützlich,
469     um potentiell gefährliche Aktionen (z.B. Löschen von Datenbanken,
470     abschicken von E-mails) nur einmal auszuführen.
471 root 1.1
472 root 1.2 H2: Benutzerverwaltung
473 root 1.1
474 root 1.2 Da PApp Benutzer (mehr oder weniger) eindeutig identifizieren muss,
475     implementiert es intern eine eigene Benutzerverwaltung inklusive einer
476     Unix-artigen Rechtevergabe (es gibt allerdings keinen Super-User im
477     Unix-Sinne). In vielen Anwendungen kann man diese gleich mitbenutzen, da
478     eine eindeutige Benutzer-ID automatisch vergeben wird.
479 root 1.1
480 root 1.2 !block verbatim
481     Sie sind wahrscheinlich Benutzer Nummer <?$userid:><br/>
482     #if auth_p
483     Und ausserdem haben sie sich authentifiziert.<br/>
484     # if access_p "admin"
485     Oh, und "admin"-Rechte besitzen Sie auch noch! Meine Güte!!<br/>
486     # endif
487     #endif
488 root 1.1 !endblock
489    
490 root 1.2 H2: Geschwindigkeit und Skalierbarkeit
491 root 1.1
492 root 1.2 Geschwindigkeit war bei der Implementierung von PApp das zweitwichtigste
493     Ziel (Korrektheit ist das wichtigste ;). Als Marke dient mir ein
494     Pentium-II 266Mhz-Rechner, auf dem auch komplexe Seiten mit mindestens 15
495     Hits/Sekunde dargestellt werden, bzw. der Vergleich mit einem ähnlichen
496     C<Apache::Registry>-Skript.
497 root 1.1
498 root 1.2 Das State-Management von PApp verschlingt pro Seite 1-3 Datenbankzugriffe,
499     die die Zeit bei weitem dominieren (andere Features wie I18n sind im
500     Prinzip kostenlos). Da komplexe Seiten im Allgemeinen wesentlich mehr und
501     kompliziertere Zugriffe enthalten bzw. man ja irgendwie seine Daten
502     speichern muss, ist dies in der Praxis selten eine Einschränkung
503     (Ausnahmen gab und gibt es immer). Andere PApp-Features (z.B. im
504     SQL-Bereich) bringen häufig sogar eine Steigerung der Geschwindigkeit.
505 root 1.1
506 root 1.2 Da ich bisher keine Seite fabrizieren konnte, die die 15 Zugriffe/s-Marke
507     unterschritten hätte, glaube ich noch etwas Spielraum für noch mehr
508     Features zu haben. Wer sich übrigens fragt, woher diese 15 Hits/s
509     kommen: 15 Hits ist die Marke, die einen wirklich grossen Server von den
510     99.99% der restlichen Welt unterscheidet. Hat man auf seinem Server 15
511     oder mehr Zugriffe pro Sekunde kann man sich meistens auch eine schnellere
512     Datenbank oder mehrere Rechner leisten: PApp skaliert problemlos auf
513     mehrere Maschinen.
514 root 1.1
515 root 1.2 H2: Ideen, Ideen: z.B. "tied forms"
516 root 1.1
517     Ich habe es schon angesprochen: Persistenz von fast allem, zusammen mit
518     den Möglichkeiten von Perl können einen schon auf Ideen bringen, z.B.
519     auf eine Bibliothek (editform), die HTML-Formularfelder an Perl-Refrenzen
520     bindet.
521    
522     Nun ist es an der Zeit, einmal eine Referenz auf eine SQL-Tabelle zu
523     erzeugen:
524    
525     !block perl
526     <:
527     my $row = new PApp::DataRef 'DB_row',
528     table => "user",
529     where => [id => $userid],
530     delay => 1,
531     autocommit => 1;
532    
533     # pre-set name
534     $row->{name} ||= "<username>";
535    
536     ef_begin;
537     :><br>__"ID:" <?ef_string \$row->{id} , 5:><:
538     :><br>__"Name:" <?ef_string \$row->{name}, 20:><:
539     ef_submit __"Update";
540     ef_end;
541     :>
542     !endblock
543    
544     Die Referenz auf die Zeile steht in C<$row> un verhält sich wie eine
545     Referenz auf einen herkömmlichen Perl-Hash. Das C<autocommit> sorgt
546     dafür, dass die geänderten Daten automatisch zurückgeschrieben werden
547     sobald das C<$row>-Objekt gelöscht wird (ausser Scope geht, C<DESTROY>ed
548     wird). Sie ist lokal (C<my>!) gespeichert, da aber die C<ef_>-Funktionen
549     die Referenz persistent speichern, wird das Objekt erst auf der nächsten
550     Seite (also nachdem die Ergebnisse hineingeschrieben wurden!) zerstört,
551     bzw. die Daten geschrieben. Etwas kompliziert, aber zum Benutzen muss man
552     die Details ja auch nicht kennen.
553    
554     Nun macht das Beispiel keine Fehlerüberprüfung, meistens das
555     schwierigste. Mit PApp kein Problem. Zuerst schalten wir das C<autocommit>
556     ab, damit die Daten nicht "aus Versehen" geschrieben werden. Ausserdem
557     übergeben wir die Referenz als Argument an die "nächste" Seite.
558    
559     !block perl
560     <:
561     my $row = $A{row}
562     || new PApp::DataRef 'DB_row',
563     table => "user",
564     where => [id => $userid],
565     delay => 1,
566     autocommit => 0;
567    
568     ef_begin -row => $row; # Daten als Argument übergeben
569     !endblock
570    
571     Nun brauchen wir ein Flag, das uns sagt, ob der Datensatz fehlerhaft ist
572     und schon können wir loslegen:
573    
574     !block perl
575     my $err = 0; # Daten fehlerhaft?
576    
577     :><br/>__"ID:" <?ef_string \$row->{id} , 5:><:
578     #if $row->{id} !~ /^(\d+)$/
579     <error>__"The ID must be an integer!"</error>
580     <:$err++:>
581     #endif
582     :><br/>__"Name:" <?ef_string \$row->{name}, 20:><:
583     #if 2 > length $row->{name}
584     <error>__"The Name must contain at least two characters!"</error>
585     <:$err++:>
586     #endif
587     ef_submit __"Update";
588     ef_end;
589     !endblock
590    
591     Die Daten schreibt man dann bei Bedarf in die Datenbank:
592    
593     !block perl
594     #if $row->dirty
595     # if $err
596     <error>__"The entered Data is invalid, please surrender or die (or correct it ;)</error>
597     # else
598     <:$row->flush:>
599     __"The record has been updated."
600     # endif
601     #endif
602     :>
603     !endblock
604    
605     H2: "Web-Widgets"
606    
607 root 1.2 Eine relativ neue "Neuerung" in PApp ist die Einführung von
608     Web-Widgets. Wie so vieles sind das keine speziellen Objekte, sondern eine
609     Art der Programmierung, die zwar schon immer möglich warm aber an die man
610     nicht sofort denkt.
611    
612     Statt einer ganzen Applikation, die "ganze Seiten" (z.B. ganze
613     HTML-Seiten) ausgibt, schreibt man eine Applikation, die nur noch
614     Teilseiten ausgibt, die man in größere Applikationenn einbaut. Dies ist
615     nicht das gleiche wie ein "include": Der Namensraum (globale Variablen,
616     state-keys, also Session-Variablen und Preferences) einer solchen
617     eingebetteten Applikation ist getrennt von der einbettenden Anwendung und
618     bildet eine Art "Unternamensraum".
619    
620     Derlei eingebettete Anwendungen sind vollkommen autark, haben ihren
621     eigenen Zustand ("aktuelle Seite") und können ihre eigenen Formulare,
622     Links etc. erzeugen die mit anderen Applikationen nicht kollidieren. In
623     einer Anwendung verwende ich dasselbe Forum-Element, um "Web Chat",
624     "Kleinanzeigen" und eine "News!"-Seite zu implementieren - jedesmal eine
625     leicht andere Konfiguration aber derselbe Code.
626    
627     Da PApp-Applikationen im Prinzip nur grosse Statemaschinen sind (das ist
628     eine Einschränkung der verwendeten Protokolle, d.h. bis Perl effektive
629     und schnelle continuations bekommt ;), kann man sie auch einfach in andere
630     einbauen. Sie können sich gegenseitig beeinflussen, treten sich aber
631     nicht auf die Füsse.
632    
633 root 1.3 Also im Prinzip genauso wie ein "Widget" in X11 oder gtk+.
634 root 1.2
635     H2: Logging/Protokollierung
636    
637     PApp protokolliert wesentlich mehr, als man benötigt, legal wäre, und
638     speicherbar wäre. Da PApp jederzeit in der Lage sein muss, eine ältere
639     Seite zu regenerieren ("Back & Reload"), speichert es pro Seite die
640     persistenten Variablen (ca. 300-900 Byte, je nach Applikation, das ist,
641     wie gesagt, auch eine tolle Sache zum Debuggen).
642    
643     Aber irgendwann müssen diese Daten wieder weg bzw. statistische Daten
644     her - nichts ist interessanter, als Zugriffsmuster, Voreinstellungen oder
645     ähnliches, an dem man ablesen kann, welche Dinge beliebt sind und welche
646     nicht (klar, man kann auch ganz andere Sachen auswerten, aber das ist
647     nicht mein Problem).
648    
649     Beim Aufräumen spielt PApp die einzelnen Zugriffe noch einmal
650     durch. Statt jedoch Seiten zu erzeugen gibt PApp den einzelnen
651     Applikationen die Möglichkeit, statistische Daten zu sammeln. Etwas
652     undokumentierter (wenn es da Abstufungen gibt) ist die Möglichkeit,
653     globale Daten zu sammeln, aber es geht...
654 root 1.1
655 root 1.2 H1: Eine einfache Anwendung mit PApp
656 root 1.1
657 root 1.3 Jetzt kommen wir zur eigentlichen Frage: Wie sieht so ein PApp-Programm
658     aus? Nun, meistens so:
659 root 1.1
660 root 1.3 H2: Das Hauptprogramm
661 root 1.1
662 root 1.3 !block verbatim
663     <package name="demo"> <domain lang="en">
664 root 1.1
665 root 1.3 <database dsn="DBI:mysql:demodb"/>
666 root 1.1
667 root 1.3 <translate fields="project.name place.name" lang="de"/>
668     <translate fields="project.description" lang="*" style="auto"/>
669 root 1.1
670 root 1.3 <import src="macro/util"/>
671 root 1.1
672 root 1.3 <include src="demo/somepages"/>
673    
674     </domain> </package>
675     !endblock
676    
677     Hmm... das ist also erstmal XML mit furchtbar vielen neuen
678     Elementen. Normalerweise kopiert man sich einfach ein anderes Programm
679     und schreibt nicht alles neu. Gut: zuersteinmal das C<package>-Element:
680     damit wird - genau wie in perl - ein neuer Namensraum erzeugt, der sowohl
681     normale Package-Variablen als auch State-Variablen enthält. Nicht jedes
682     Programm muss einen eigenen Namensraum öffnen.
683    
684     Das nächste Element, C<domain>, aktiviert die Übersetzungen: Alle
685     markierten Texte werden unter einem Namen, der C<domain>
686     gebündelt. Beispielsweise befinden sich alle Texte des PApp-Systems
687     selbst in der "papp"-Domain. Da im Beispiel kein Domainname angegeben
688     wird, nimmt PApp den Namen des umschleissenden C<package>-Elements, d.h.
689     wir definieren hier eine Übersetzungsdomain "demo".
690    
691     Das nächste Element deklariert die Standarddatenbank: Wird die
692     Datenbankhandle bei SQL-Abfragen weggelassen, wird diese Datenbank
693     benutzt. Normalerweise deklariert man Datenbanken aber nicht im Quellcode
694     sondern beim Einrichten der Anwendung im Konfigurationsmenü.
695    
696     Zu den nächsten beiden Elementen muss ich etwas über das Programm
697     erklären, das ich hier entwickeln möchte: Das Demo-Programm sollte
698     kurz sein und die wichtigsten Features von PApp zeigen. Es wird aus drei
699     (Web-) Seiten bestehen, eine Art Hauptseite mit einem Menü und zwei
700     Unterseiten, auf denen man ein Projekt auswählen kann (ein Projekt ist
701     ein einfacher Datensatz in der Datenbank, der den Projektnamen, den
702     Ort und die Beschreibung enthält). Auf der dritten Seite kann man die
703     einzelnen Felder eines Projektes ansehen und ändern.
704    
705     Als besonders überflüssiger Schnickschnack sollen die Projektdaten
706     übersetzbar sein, d.h. in der Sprache des Benutzers angezeigt werden. Das
707     ist nicht sehr wirklichkeitsnah, gibt dafür aber ein sehr simples
708     Beispiel ab.
709    
710     Da die Projektdaten in der Datenbank gespeichert sind, kann man
711     sie nicht einfach mit C<__"xxx"> markieren. sondern braucht ein
712     C<translate>-Element. Wo PApp die Übersetzungen herbekommt ist relativ
713     egal, man muss PApp nur sagen, wie es an die Texte herankommt und in
714     welcher Sprache sie sind.
715    
716     Das erste C<translate>-Element sagt, daß die Spalten C<name> in der
717     Tabelle <project> und die Spalte C<name> in der Tabelle C<place> in
718     Deutsch sind und dementsprechend in andere Sprachen zu übersetzen sind.
719    
720     Das zweite C<translate>-Element ist schon komplizierter: Nicht alle
721     Projektbeschreibungen sind in derselben Sprache (behaupte ich einfach
722     mal), weshalb als Sprache C<*> angegeben wird. Das bedeutet lediglich,
723     dass {{alle}} Übersetzer diese Texte übersetzen müssen. Wenn der
724     Englisch-Deutsch-Übersetzer also auf einen Deutschen Text stößt, muss
725     er ihn einfach überspringen. Wäre die Spalte als "de" (oder "deu")
726     markiert, würde er sie garnicht erst zu Gesicht bekommen.
727    
728     Da Beschreibungen ausserdem sehr lang sein können, erhält man mit
729     C<style="auto"> die Möglichkeit, die C<__"xxx">-Syntax auch in
730     den Beschreibungen zu verwenden. Dies kann man auch für einfache
731     Textbausteine mißbrauchen...
732    
733     Das darauffolgende C<import>-Element ist wieder etwas einfacher: es
734     entspricht mehr oder weniger der C<use>-Anweisung in Perl (in genau die
735     wird es auch übersetzt), bezieht sich aber auf PApp-Dateien. Damit
736     kann man Funktionen aus anderen (PApp-) Namensräumen importieren. Im
737     vorliegenden Fall importiere ich einfach mal das C<macro/util>-Paket, da
738     sich immer wieder grosser Beliebtheit erfreut.
739    
740     Das letzte Element tut zur Abwechslung genau das, was es sagt: es
741     fügt (logisch gesehen) eine andere Datei an dieser Stelle ein. Die
742     Entscheidung, die folgenden Seiten in eine andere Datei auszulagern lohnt
743     sich normalerweise nur für grössere Programme, aber so bleibt das
744     Beispiel klein.
745    
746     Bis jetzt tut sich noch nichts. Ändern wir das:
747    
748     H2: Das erste PApp-Modul
749    
750     Einzelne Zustände (z.B. Webseiten) werden bei PApp "Module" genannt, wohl
751     einzig um die Anwender zu verwirren. Diese Module werden meist in andere
752     Elemente (C<domain>, C<package>, C<style> etc..) verpackt, die auf die
753     Sete auf verschiedenste Weise einwirken.
754    
755     Ausführbaren Code kann man grundsätzlich auf zwei verschiedene Weisen
756     angeben: Normaler Perl-Code und Perl-Code gemischt mit Text (also wie bei
757     anderen embedded-Dialekten):
758 root 1.1
759 root 1.3 !block verbatim
760     <module name=""><phtml><![CDATA[
761     <html> <title>
762     __"PApp - demo", <?localtime:>
763     </title>
764    
765 root 1.4 <?slink "Deutsch", "/lang" => "de":>
766 root 1.3 <?slink "English", SURL_SET_LANG, 'en':>
767    
768     <p><?slink __"To the editor", "editor":>
769     <p><?slink __"Edit project 1", "editor", projectid => 1:>
770    
771     </html>
772     ]]></phtml></module>
773     !endblock
774    
775     Zuerst zum C<module>-Element: Jedes Modul besitzt einen eindeutigen
776     Namen. Das Standardmodul (das angezeigt wird, wenn nichts spezielles
777     ausgewählt ist, also sozusagen die Startseite) ist das Modul mit dem
778     leeren Namen: C<"">.
779    
780     Das Ergebnis eines Moduls sind die Ausgaben, die darin gemacht werden
781     (z.B. mit C<printf> oder dem PApp-C<echo>). Die Ausgabe des obersten
782     Moduls (im Baum) wird an den Browser geschickt. Für Ausgabe braucht man
783     Perl und das habe ich in ein C<phtml>-Element gepackt (für verbatimen
784     Perl-code würde man ein C<perl>- oder C<xperl>-Element verwenden, für
785     Perl gemischt mit XML gibt es noch C<pxml>).
786    
787     Innerhalb des C<phtml>-Elementes darf nur eine Zeichenkette stehen, die
788     ausgegeben wird. Da wir innerhalb der Zeichenkette Perl einbetten wollen
789     und die Modus-Umschalter {{kein}} gültiges XML sind, muss de rInhalt
790     geschützt werden, in diesem Fall mit einem C<CDATA> (die Verwending von
791     Abkürzungen in VI o.ä. empfiehlt sich ;)
792    
793     Da dies das oberste Modul ist und wir (noch) kein Stylesheet verwenden,
794     müssen wir eine ganz HTML-Seite ausgeben. Als Titel nehmen wir den Text
795     C<PApp-demo>, der übersetzt werden muss sowie, weil es so schön ist, die
796     aktuelle Uhrzeit. Dies geschieht mit einem {{C:<?}}: Der Ausdruck (hier
797     C<localtime>) wird in einem Skalaren Kontext ausgewertet und das Ergebnis
798     ausgegeben.
799 root 1.1
800 root 1.3 Die nächste Code-Zeile ist interessanter:
801 root 1.1
802 root 1.3 !block verbatim
803 root 1.4 <?slink "Deutsch", "/lang" => "de":>
804 root 1.3 !endblock
805 root 1.1
806 root 1.3 C<slink> ist PApps Art, eine Hypertext-Referenz (C<A>-Element) zu
807     erzeugen. C<slink> erwartet als erstes Argument den Inhalt des Verweises
808     (der Text, den der Benutzer "anklicken" muss) und daraufhin die
809     sogenannten C<surl>-Argumente, die bei vielen PApp-Funktionen angegeben
810 root 1.4 werden können. C<surl>-Argumente sind im wesentlich "Name => Wert"-Paare,
811     die dem Zielmodul als Argumente übergeben werden können. Im Beispiel
812     ist das die Variable C</lang> (der Slash am Anfang markiert eien globale
813     Variable), die auf den Wert "de" gesetzt wird. Das Ergebnis ist, dass
814     Textmeldungen nun auf Deutsch übersetzt werden.
815 root 1.1
816 root 1.4 !block verbatim
817     <?slink "English", SURL_SET_LANG, 'en':>
818     !endblock
819 root 1.1
820 root 1.4 Neben Argumenten kann man auch bestimmte "Cookies" in die
821     C<surl>-Argumente einbauen. C<SURL_SET_LANG> z.B. setzt die Sprache auf
822     den folgenden Wert und speichert ausserdem die Voreinstellungen ab,
823     d.h. die Sprachwahl ist nun permanent. Neben C<SURL_SET_LANG> gibt es
824     noch eine ganze Reihe weitere "Cookies" wie z.B. C<SURL_EXEC>, mit dem
825     man bei Auswahl des Links eine Unterroutine aufrufen lassen kann oder
826     C<SURL_STYLE_GET>, mit dem man den Stil der URL einmalig überschreiben
827     kann. Die nächste Zeile bringt eine weitere Erweiterung:
828 root 1.1
829 root 1.4 !block verbatim
830     <p><?slink __"To the editor", "editor":>
831     !endblock
832 root 1.1
833 root 1.4 Bisher wurde kein {{Ziel}} für den Link angegeben. In solchen Fällen
834     nimmt PApp die aktuelle Seite (das aktuelle Modul) als Ziel, d.h. sie wird
835     einfach neu geladen (bei Sprachnänderungen etc. ja gewünscht). Möchte
836     man ein anderes Modul ansteuern gibt man einfach den Namen des Moduls an,
837     z.B. C<"editor">. Ein "Klick" auf den Link (oder das Laden der URL, wie
838     auch immer das geschieht) ruft dann das C<editor>-Modul auf.
839    
840     Das "Ziel" muss auch nicht immer ein Modulname sein, es geht auch
841     komplizierter: C<"admin,user/edit,group/"> z.B. verzweigt auf das
842     C<admin>-Modul und gleichzeitig in einem eingebetteten Widget C<user> auf
843     das Modul C<edit>, während es im Widget C<group> auf das Hauptmodul (das
844     mit dem leeren String als Namen) schaltet. Aber das braucht man wirklich
845     nur in grossen Projekten ;)
846 root 1.1
847 root 1.4 Ziel und Argumente kann man natürlich kombinieren:
848 root 1.1
849 root 1.4 !block verbatim
850     <p><?slink __"Edit project 1", "editor", projectid => 1:>
851     !endblock
852 root 1.1
853 root 1.4 Hier wird dieselbe Seite (C<editor>) angesteuert, dieses mal wird jedoch
854     ein Argument übergeben. Beim Aufruf wird dieses Argument in den State
855     geschrieben, steht dem C<editor>-Modul also als C<$S{projectid}> zur
856     Verfügung. Manchmal möchte man einfach nur ein Argument übergeben,
857     dass nicht im Satte endet, d.h. nicht persistent ist. In diesem Fall
858     kann man vor den Namen ein '-' stellen, der Wert landet dann nicht in
859     C<%S> sondern in C<%A> und wird nach dem Aufruf weggeworfen. Eien dritte
860     Möglichkeit ist es, eine Referenz auf einen skalar anzugeben (der
861     natürlich persistent sein muss, sonst existiert er beim Aufruf nicht
862     mehr).
863    
864     Die Werte dürfen beliebige Perl-Referenzen sein, solange sie
865     serialisierbar sind. In PApp gehören Code-Referenzen und (PApp-)
866     Datenbankhandles übrigens zu den serialisierbaren Datentypen, wenn man
867     etwas Vorsicht walten lässt.
868    
869     Der erste Link auf C<editor> soll, da er kein Projekt auswählt, eien
870     Liste aller Projekte zeigen während der zweite ein spezielles Projekt
871     (das hoffentlich existiert) anzeigen soll. Damit wäre die erste Seite
872     erklärt, schreiten wir zum C<editor>-Modul:
873    
874     H2: Das C<editor>-Modul
875    
876     Ich gebe zu, ich habe etwas gemogelt. Natürlich macht es normalerweise
877     mehr Sinn, zwei getrennte Seiten (z.B. C<project_list> und
878     C<project_edit>) für das Listen und edieren zu benutzen. Aber dann hätte
879     ich keinen Grund, ein weiteres Stückchen "syntactic sugar" zu zeigen,
880     Präprozessorkommandos:
881    
882     !block perl
883     <module name="editor">
884     <state keys="projectid" local="yes"/>
885     <phtml><![CDATA[
886     <:header:>
887    
888     #if defined $S{projectid}
889     ... edit the specific project
890     #else
891     ... show a list of projects
892     #endif
893 root 1.1
894 root 1.4 <:footer:>
895     ]]></phtml></module>
896     !endblock
897 root 1.1
898 root 1.4 Das C<module>-Element kennen wir ja schon. Diesmal deklariert es das
899     C<editor>-Modul. Das nächste Element C<state> ist schon interessanter: hier
900     markiert es bestimmte State-Keys (hier: C<projectid>) als C<local>, d.h. lokal zu allen Seiten, die
901     diese Variable so markieren. Da das Hauptmodul die C<projectid> {{nicht}} als lokal markiert wird
902     sie beim "Klick" auf das Hautpmodul automatisch gelöscht. Hätte man stattdessen geschrieben:
903 root 1.1
904 root 1.4 !block perl
905     <state keys="projectid bgcolour" preferences="yes"/>
906     !endblock
907 root 1.1
908 root 1.4 hätte man die beiden State-Keys als Voreinstellungswerte markiert, d.h.
909     beim nächsten Aufruf würde der Benutzer das gleiche Projekt sehen (und
910     eventuell die gleiche Hintergrundfarbe, je nachdem, was C<bgcolour>
911     bedeutet).
912    
913     Die beiden "Tags", {{C:<:header:>}} und {{C:<:footer:>}}, sind eigentlich
914     nur getarnte Funktionsaufrufe: In den Tagen vor XSLT habe ich meistens auf
915     diese Weise ein Standard-Layout erstellt. C<header> könnte man z.B. so definieren:
916    
917     !block perl
918     <macro name="header"><phtml><![CDATA[
919     <html>
920     <head><title>__"Hallole"</title></head>
921     <body>
922     ]]></phtml></macro>
923     !endblock
924 root 1.1
925 root 1.4 C<macro> definiert eine ganz normale Perl-Funktion, sie kann Argumente
926     annehmen und Werte zurückliefern. Der einzige Unterschied ist, dass man
927     Perl-Funktionen auf diese Weise in "phtml"-Syntax schreiben kann. Um
928     einzelne Seiten gegen unberechtigen Zugriff zu schützen (und um noch mehr
929     abzuschweifen) habe ich früher folgendes gemacht:
930    
931     !block perl
932     <macro name="page(&amp;)" args="$body"><phtml><![CDATA[
933     <html> <body>
934     #if access_p "project_editor"
935     <:&$body:> <!-- eigentliche seite anzeigen -->
936     #else
937     __"You need to login yourself first"<p>
938     <:loginbox:> <!-- loginbox anzeigen -->
939     #endif
940     </body> </html>
941     ]]></phtml></macro>
942    
943     <module name="secure_page"><phtml><![CDATA[
944     <:page {:>
945     <h1>__"Hallo"</h1>
946     <:}:>
947     ]]></phtml></module>
948     !endblock
949 root 1.1
950 root 1.4 Gut, zurück zum Thema: Die Aufgabe ist klar: ist eine C<projectid>
951     gegeben, soll das entsprechende Projekt ediert werden, sonst soll eine
952     Liste von Projekten zur Auswahl angeboten werden.
953    
954     Das könnte man zwar mit einem C<if> lösen, es geht aber auch anders:
955    
956     !block perl
957     #if <perl-ausdruck>
958     ...
959     #elif <perl-ausdruck>
960     ...
961     #else
962     ...
963     #endif
964     !endblock
965 root 1.1
966 root 1.4 Das ist fast so schön wie C... *hüstel*. Jedenfalls wird es in ein
967     hundsnormales Perl-if/elsif/else/endif umgesetzt. Die beiden Teile "edit
968     the specific project" und "show a list of projects" werden sofort gefüllt:
969    
970     H3: "show a list of projects"
971    
972     Zuerst die Liste der Projekte. Ah, wir benötigen SQL. Nun, das ist
973     einfach, wir brauchen nur "jede Menge" HTML auf das Problem zu werfen:
974    
975     !block perl
976     <table><tr><th>__"Project"<th>__"Budget"
977     <:
978     my $st =
979     sql_exec \my($id, $name, $budget),
980     "select id, name, budget
981     from project";
982    
983     while ($st->fetch) {
984     :><tr><td>
985     <?slink gettext$name,
986     projectid => $id?>
987     <td>$budget
988     <:
989     }
990     :>
991     </table>
992     !endblock
993 root 1.1
994 root 1.4 Dies erzeugt eine einfache HTML-Tabelle mit den beiden
995     Spaltenüberschriften "Project" und "Budget", natürlich übersetzbar. Der
996     Aufruf von C<sql_exec> tut drei Dinge:
997    
998     ^ C<prepare>, falls notwendig (C<prepare>te Statements werden gecached).
999     & C<bind_columns>, auf die drei Referenzen C<\$id>, C<\$name> und C<\$budget>.
1000     & C<execute>, um die Abfrage zu starten.
1001    
1002     Nun müssen wir nur noch in einer Schleife über die Zeilen iterieren
1003     (mit dem alten DBI-Bekannten C<fetch>) und TR/TD-Zeilen ausgeben. In
1004     der Schleife kann man sehr schön sehen, wie man zwischen Perl und HTML
1005     umschalten kann. Keine Angst, daran gewöhnt man sich sehr schnell, vor
1006     allem mit Syntax-Hilighting (ein weiterer Grund, auf vim umzusteigen).
1007    
1008     Das einzig Komplizierte ist der Aufruf von C<slink>, der einen C<A
1009     HREF>-Link auf das C<editor>-Modul (also auf die aktuelle Seite) erzeugt
1010     und diesem gleich noch die aktuelle Projekt-ID mitgibt. C<gettext> ist
1011     der Runtime-Teil der C<__>-Funktion, d.h. die Funktion, die von C<__> zur
1012     Laufzeit aufgerufen wird. Sie {{übersetzt}} das Argument, {{markiert}} es
1013     aber nicht.
1014    
1015     Das Ergebnis ist ein Link, der beim Aufruf die Seite neu lädt, nur mit
1016     dem Unterschied, dass diesmal eine Projekt-ID verfügbar ist also nicht
1017     mehr die Liste angezeigt wird.
1018    
1019     H3: "edit the specific project"
1020 root 1.1
1021 root 1.4 Der Rest ist nun relativ einfach: Mit C<DataRef> eine Referenz erzeugen,
1022     mit C<editform> ein Formular, der Rest geht von alleine:
1023 root 1.1
1024 root 1.4 !block perl
1025     <:ef_begin:>
1026 root 1.1
1027 root 1.4 <:my $row = new PApp::DataRef 'DB_row',
1028 root 1.1 table => "project",
1029     where => [id => $S{projectid}]:>
1030    
1031 root 1.4 <p>__"Name": <:ef_string \$row->{name}, 40:>
1032     <p>__"Place:" <:ef_relation \$row->{place},
1033     "id, name from place order by 2",
1034     0 => __"unknown":>
1035     <p>__"Budget": <:ef_string \$row->{budget}, 8:>
1036     <p>__"Description": <:ef_text \$row->{description}, 60, 10:>
1037 root 1.1
1038 root 1.4 <:ef_constant \$row->{user}, $userid:>
1039 root 1.1
1040 root 1.4 <:ef_submit __"Update":>
1041     <:ef_end:>
1042     !endblock
1043 root 1.1
1044 root 1.4 C<new PApp::DataRef> erzeugt eine Zeilenreferenz auf die Zeile mit
1045     dem gewünschten Projekt, C<ef_begin> und C<ef_end> umschliessen ein
1046     Fomular, C<ef_string> erzeugt ein Textfeld in der gewünschten Breite
1047     während C<ef_text> ein C<textarea>-Element erzeugt. C<ef_submit> sollte
1048     selbsterklärend sein.
1049    
1050     Habe ich was vergessen? Oh ja, der Ort ist ja in einer seperaten Tabelle,
1051     in C<project> wird ja nur die ID des Ortes gespeichert. Für solche
1052     Relationen gibt es bei C<editform> das C<ef_relation>-"Element". Man
1053     übergibt einen SQL-Ausdruck, der zwei Spalten (ID und Name) liefert
1054     und optional ein paar weieter Paare (z.B. kann man auch "unbekannt"
1055     angeben). Als Ergebnis erhält man ein C<select>-Element in HTML.
1056    
1057     Als letztes wird ein Formularfeld noch mit einer Konstanten (die der
1058     Benutzer nicht ändern kann) gefüllt, in diesem Fall wird das Feld
1059     C<user> auf die aktuelle C<userid> gesetzt, so weiss man immer, wer den
1060     Datensatz zuletzt verändert hat.
1061    
1062     Wer Formulare in anderen Sprachen (WML...) benötigt, kann sich seien
1063     eigenen Elemente basteln: C<editform> ist es grundsätzlich egal, wie die
1064     Synatx ist, solange das Schnittstellenmodul von PApp die Daten dekodieren
1065     kann.
1066    
1067     H2: Aufgemotzt hält besser
1068    
1069     Bis jetzt war alles noch langweilige Grundlagen. Vor allem ist
1070     Standard-HTML doch soo langweilig: die Tabellen sind nicht farbig
1071     hinterlegt, um das Lesen zu erleichtern. Und die C<header>- und
1072     C<footer>-Methode ist auch nicht gerade ein flexibles Layout-Werkzeug.
1073    
1074 root 1.6 Deshalb{{}}: XSLT muss her ("eXtensible StyLesheet Transformations"). Und weil
1075 root 1.4 ich so anspruchsvoll bin, gleich zwei Stylesheets: eins ohne aufwendige
1076     Grafik zum benutzen und eins mit vielen farbigen Elementen zum verkaufen
1077     ("bunt haben wollen").
1078    
1079     Zuerst müssen wir das Stylesheet laden:
1080    
1081     !block perl
1082     <perl><![CDATA[
1083     $stylesheet[0] = $papp->load_stylesheet("demo/demo1");
1084     $stylesheet[1] = $papp->load_stylesheet("demo/demo2");
1085     ]]></perl>
1086     !endblock perl
1087    
1088     Naja, zuerst müsste man es schreiben oder besser klauen. Ausserdem muss
1089     man es nicht zuerst laden, aber darüber gehe ich einfach mal hinweg...
1090    
1091     Das C<perl>-Element ist übrigens {{nicht}} in einem Modul: Perl-Code,
1092     der nicht in ein Modul gesteckt wird, wird beim Laden der Applikation
1093     ausgeführt, d.h. ganz ähnlich wie ein Perl-Modul. Zu diesem Zeitpunkt
1094     kann man aufwendige Initialisierungen machen bzw. Dateien nachladen, die
1095     man später braucht.
1096    
1097     Wie wendet man diese "Stylesheets" nun an? Ganz einfach, man packt alle
1098     Module, die "gestyled" werdne sollen, in ein C<style>-Element:
1099    
1100     !block perl
1101     <style apply="output" expr="$stylesheet[ $S{style} ]">
1102    
1103     <module name=""><phtml><![CDATA[
1104     <p/><?slink __"To the edito...
1105     <p/><?slink __"Edit projec...
1106     ]]></phtml></module>
1107 root 1.1
1108 root 1.4 ...
1109     </style>
1110     !endblock perl
1111    
1112     Das C<apply> bezieht sich auf den Zeitpunkt, zu dem das Stylesheet
1113     angewendet werden soll. C<apply="output"> bestimmt, dass das Stylesheet
1114     kurz vor der Ausgabe angewendet werden soll. Normalerweise würde man nun
1115     den Dateinamen des Stylesheets mit C<src="pfad"> angeben, da wir aber
1116     zwischen zwei Stylesheets hin- und herschalten wollen, geben wir mit
1117     C<expr> einen Perl-Ausdruck an, der ein Stylesheet-Objekt als Ergebnis
1118     haben (sollte). Das muss {{natürlich}} auch kein XSLT-Stylesheet sein,
1119     aber wem sage ich das...
1120    
1121     Das Modul ist übrigens (bis auf die durch "..." angedeuteten Lücken)
1122     vollständig, d.h. der Kopf mit Titel und Sprachumschalter (sowie
1123 root 1.5 Stylesheet-Umschalter) wird durch das Stylesheet hinzugefügt.
1124 root 1.1
1125 root 1.5 <<<bilder?>>>
1126     <<<xslt-quelltexte im anhang?>>>
1127 root 1.1
1128 root 1.5 H1: Tinychat - ein kleiner Chat in 20 Zeilen
1129    
1130     Zum Schluss noch ein sehr einfaches Beispiel: C<macro/tinychat.papp> ist
1131     ein sehr einfaches Chat-Fenster: Es zeigt die letzten fünf Eingabezeilen
1132     an, gefolgt von einer Eingabebox. Der Chat-Inhalt ist Systemweit, d.h. auf
1133     allen Servern in einem PApp-System ist immer derselbe Text, man kann diese
1134     "Chatbox" also in beliebige Programme einbauen.
1135    
1136     !block perl
1137     <macro name="tinychat*()"><pxml><![CDATA[
1138     #if $A{tinychat_submit} && $P{input} && !reload_p
1139     <:
1140     lockenv {
1141     my $r = getenv "TINYCHAT";
1142     shift @$r while @$r > 5;
1143     push @$r, escape_html sprintf "%s (%s): %s",
1144     username || "<ANON$userid>", $P{input};
1145     setenv "TINYCHAT", $r;
1146     };
1147     :>
1148     #endif
1149     <:
1150     my $r = getenv "TINYCHAT";
1151     echo map "<tt>$_</tt><br />", @$r;
1152     :>
1153     <br />
1154     <?sform -tinychat_submit => 1:>__"Chat: "<?textfield "input":><?endform:>
1155     ]]></pxml></macro>
1156     !endblock
1157    
1158     Zuallererst ist C<tinychat> nur eine Funktion, teilt sich also den
1159     Namensraum mit dem Aufrufer. Tinychat ist so winzig und so schlecht
1160     konfigurierbar, dass das Sinn macht... Sehen wir uns mal den Teil an, der
1161     die Ausgabe erledigt:
1162    
1163     !block perl
1164     <:
1165     my $r = getenv "TINYCHAT";
1166     echo map "<tt>$_</tt><br />", @$r;
1167     :>
1168     !endblock
1169    
1170     Die Texte sind in einer "Environment"-Variable gespeichert. Diese
1171     Variablen sind global für das gesamte PApp-System und könenn auch
1172     ausserhalb von PApp abgefragt bzw. verändert werden. Das ganze ist also
1173     eher ein System zur asynchronen Kommunikation. Neben normalen Strings kann
1174     man alles darin ablegen, was irgendwie serialisierbar ist, insbesondere
1175     eine Array-Referenz, in der die einzelnen Zeilen sind.
1176    
1177     Zur Ausgabe wird also lediglich die Variable C<PAPP_TINYCHAT> ausgelesen
1178     und die einzelnen Zeilen in ein "<tt>zeile</tt><br />" gepackt.
1179    
1180     Fehlt noch das Eingabefeld:
1181    
1182     !block perl
1183     <?sform -tinychat_submit => 1:>
1184     __"Chat: "
1185     <?textfield "input":>
1186     <?endform:>
1187     !endblock
1188    
1189     Hier wird kein C<editform> benutzt: Der Overhead ist im Vergleich
1190     zum Gewinn (es gibt keinen) zu gross. Die Funktion C<sform>
1191     (die auch von C<ef_begin> benutzt wird) gibt das einleitende
1192     C<FORM>-Tag aus. Dann folgt der Text C<Chat:> und ein ganz normales
1193     HTML-C<INPUT>-Element. C<endform> schleisslich gibt ein "</FORM>" aus und
1194     existiert eigentlich nur aus Symmetrie.
1195    
1196     Das Eingabefeld heisst C<input>. Was passiert, wenn wir sonst noch
1197     ein C<input>-Feld haben und diese andere Feld submittet wird? Kein
1198 root 1.6 Problem{{}}: Wir übergeben C<sform> einfach ein Argument, das nur dazu
1199     dient, die Information "Tinychat-Formular ist gemeint" zu übertragen.
1200 root 1.5
1201     Ganz nebenbei: C<sform> ist - wie sehr viele PApp-Funktionen - sehr
1202     einfach definiert:
1203    
1204     !block perl
1205     PApp::HTML::_tag "form", { method => 'GET', action => &surl };
1206     !endblock
1207    
1208     Nun zum Teil, der die Eingabezeile nach dem Abschicken hinzufügt:
1209    
1210     !block perl
1211     #if $A{tinychat_submit} && $P{input} && !reload_p
1212     <:
1213     lockenv {
1214     my $r = getenv "TINYCHAT";
1215     shift @$r while @$r > 5;
1216     push @$r, escape_html sprintf "%s (%s): %s",
1217     username || "<ANON$userid>", $P{input};
1218     setenv "TINYCHAT", $r;
1219     };
1220     :>
1221     #endif
1222     !endblock
1223    
1224     Die erste Zeile testet drei Dinge:
1225    
1226     ^ Es muss "unser" Formular sein, das erkennt man daran, dass der Parameter
1227     C<tinychat_submit> logisch wahr ist.
1228     & Die Eingabe sollte nicht leer sein (o.k. "0" ist auch nicht erlaubt)
1229     & Die Seite soltle nicht das Ergebnis eines "Reloads" sein.
1230    
1231     Der letzte Punkt bedarf einer Erklärung: PApp weiss, wie oft eine
1232     Seite angefordert wurde und teilt dies über die Funktino C<reload_p>
1233     mit, die die Anzahl der Seitenaufrufe für {{dieselbe}} Seite minus
1234     eins zurückliefert. Ist diese Zahl ungleich null, wurde der Code schon
1235     ausgeführt.
1236    
1237     Das eigentliche hinzufügen ist Standard: Variable holen, alte Zeilen
1238     rauslöschen, neue Zeile hinzufügen, Variable auf neuen Wert setzen.
1239    
1240     Die Funktion C<lockenv>, in die die Manipulation eingeschlossen ist,
1241     schützt das Programm gegen gleichzeitige Modifikationen anderer
1242     Webserver, d.h. die Operation wird atomar. C<escape_html> quoted das
1243     Argument. Die Funktion C<username> (aus dem C<macro/admin>-Paket) liefert
1244     den Namen des Benutzers, falls dieser einen besitzt. Ansonsten nimmt
1245     Tinychat C<ANON> + die numerische User-ID, die jedem Benutzer zugeteilt
1246     wird.
1247    
1248     <<<bild?>>>
1249    
1250     H1: Die Nachteile
1251    
1252     Bei allen Vorteilen, es gibt auch Nachteile... die packe ich ans Ende und
1253     fasse mich auch gerne sehr kurz:
1254    
1255     H2: Die Lizenz (Oder doch ein Vorteil?)
1256    
1257     Tja, PApp war doch tatsächlich mal GPL, und zwar zu einer Zeit, zu der
1258     wir es praktisch nur als CGI-Krücke verwendet haben. Inzwischen hat
1259     sich PApp gemausert und wurde zu einem unserer Standbeine. Als eine
1260     andere Firma versuchte, uns mit unserem Produkt Konkurrenz bei unseren
1261     Kunden zu machen ("wir können da einfach ein paar dutzend Programmierer
1262     dransetzen"), mussten wir leider handeln.
1263    
1264     Die "PApp Public License" ist so ähnlich wie die MySQL Public License,
1265     d.h. wer sie privat einsetzt (bzw. für die Forschung und Lehre oder
1266     für eine not-for-profit-Organisation), darf PApp weiterhin kostenlos
1267     nutzen. Wer kräftig Kohle damit macht, muss uns einen Teil davon abgeben
1268     (die eigentliche Lizenz ist etwas länger ;).
1269    
1270     Langfristig ist geplant, PApp wieder in GPL oder besser zu
1271     überführen. Mittelfristig muss man damit Leben ;)
1272    
1273     H2: Die Abhängigkeiten
1274    
1275     Zur Zeit gibt es keine Perl-Release, die annähernd mit UTF8
1276     zurechtkommt. Z.Zt. (d.h. buchstäblich in dieser Minute) benötigt
1277     PApp perl-5.7.0-DEVEL7952 oder ein paar hundert Patches davor oder
1278     danach. Demnächst wird auf DEVEL8xxx umgestellt (z.Zt. gibt es einige
1279     Bugs, die dies verhindern). PApp funktioniert zwar wunderbar, aber eben
1280     nur, wenn die restlichen Komponenten aufeinander abgestimmt sind.
1281    
1282     H2: MySQL
1283    
1284     Das Grundsystem von PApp benötigt zwar kein MySQL, einige Module
1285     (z.B. C<PApp::Env>) dagegen (aus Geschwindigkeitsgründen) schon.
1286     Möchte man also eine einfache Installation und alle Features empfiehlt
1287     sich MySQL, zumindest für die PApp-interne Datenbank selbst. Ansonsten
1288     arbeitet PApp mit allen Datenbanken, die ein DBI-Interface aufweisen, ohne
1289     Probleme.
1290    
1291     H2: Geschmackssache
1292    
1293     PApp ist etwas sehr persönliches - ich habe meine eigenen Vorstellungen
1294     davon, wie Web/CGI etc. funktionieren sollte, darin verwirklicht. Es
1295     hat meine Motivation (die sich mit "nie wieder CGI" zusammenfassen
1296     liess) gewaltig gesteigert - Ein einfaches aber dennoch komplettes
1297     Content-Management-System kann man in weniger als 500 Zeilen hinlegen -
1298     für mich ein wichtiger Faktor, denn ich hasse nichts mehr, als das Rad
1299     jedesmal neu erfinden zu müssen.
1300    
1301     Da ich bekannt bin für meinen etwas merkwürdigen Geschmack (sagt man
1302     mir) muss das {{wie}} nicht unbedingt jedem gefallen.
1303    
1304     A1: Referenzen
1305 root 1.2
1306 root 1.1