A Zope

A Zope DTML script nyelve

A Zope-t többek közt a saját script nyelve, a DTML (Document Template Markup Language) teszi igazán erőteljes eszközzé. A DTML elsősorban HTML fájlok generálására használják Zope objektumokból (text rendering), de itt nyílik lehetőség a Z SQL objektum révén adatbázis-lekérdezésekre is. A DTML nyelvi elemei a HTML-hez hasonló, un. Tag szintaktikát követik, így könnyen tanulható, és a szokásosak mellett olyan korszerű nyelvi elemeket is tartalmaz, mint például a kivétel-kezelés.

Szintaktika

A DTML Tag-eket három szintaktika szerint írhatjuk le: létezik dokument-template, szerver oldali szintaktika, valamint a Python kiterjesztett sztring formátuma is.
A három szintaktikát így adjuk meg:

A DTML Tag szintaktikája a megszokott:

<dtml-tag név attribútum="érték1" attribútum="érték" ... >

A Tag neve már meghatározza a Tag típusát is. A nevet általában egy vagy több a taghoz tartozó attribútum követi, amely meghatározza, hogy hol találhatóak a tag adatai és hogy kell azokat beilleszteni. Néha az attribútum értékek el is hagyhatóak, illetve az értékeket nem kell idézőjelbe tenni, ha az nem tartalmaz szóközt, tabot, új-sor karaktert, vagy kettős idézőjelet. Például

<dtml-var date fmt=Date> <dtml-var name="standard_html_header"> <dtml-with subfolder>

Az egyik leggyakrabban használt tag, a dtml-var tag. A var tagot változók értékeinek szövegbe való behelyettesítésére használják. Például legyen az a feladat, hogy egy input_name nevű változóban tárolt üdvözlő szöveget akarunk megjeleníteni. Ezt a következőképp tehetjük meg:

Hello <dtml-var name="input_name" capitalize>!

A fenti példa két attribútumot használ, ezek a name és a capitalize. A name attribútum egy hivatkozás valamely WWW adatkérésre, vagy változóra vagy Zope objektumok tulajdonságaira. Mivel a name attribútum használata igen gyakori, ezért létezik egy rövidített forma is, ahol a name -et nem kell kiírni:

Hello <dtml-var input_name capitalize>!

A rövidített forma használatakor a tag első attribútuma a name értéke is egyben, és ilyenkor nem tesszük ezt idézőjelbe. Hasonló rövidítés létezik az expr attribútumra is. Az expr attribútumot számításokra, kifejezések kiértékelésére használják:

<dtml-if expr="age > 18">

Ez a name attribútumhoz teljesen hasonlóan így rövidíthető:

<dtml-if "age > 18">

A capitalize egy jó példa egy érték nélküli attribútumra: hatására a behelyettesítésre kerülő szöveg első betűje nagybetű lesz.
A dtml-var tag egyszerű tag, (singleton), hisz nem tartalmaz más tagot. Összetett tagokat <dtml-tagnév> <\dtml-tagnév> közé kell írni.
Ilyen például a dtml-if tag:

<dtml-if input_name> !-- ha az input_name nem üres sztring Hello <dtml-var input_name>. </dtml-if>

vagy else ugyanez ággal

<dtml-if input_name> Hello <dtml-var input_name>. <dtml-else> Have we been introduced? <dtml-endif>

Bővített Python sztring szintaktika

Ezt a szintaktikát két Python osztály, a DocumentTemplate.String és a DocumentTemplate.File használja eredetileg. Így lehet a sztringek megjelenését szabályozni formázó karakterekkel a '[' , ']' blokkjelölő karakterek között. A szövegbeillesztést a formázó karakterek mellett még kiegészítő attribútumokkal is szabályozhatjuk, például:

%(date fmt=DayOfWeek upper)s

Ennek hatására a date változó egy egyéni, 'DayOfWeek' formátumban fog megjelenni úgy, hogy minden kisbetű átkonvertálódik nagybetűvé A Document Template sztringek formázásra egy kibővített Python szintaktikát használnak. Ennek formája igen egyszerű:

%(név)x

ahol a név a behelyettesítendő érték neve és az 'x' egy formátum specifikáció, mint például a '12.2d'. Olyan blokkok bevezetésére, mint az 'if' vagy az 'in' blokk, vagy egy blokk folytatása, például az 'else' blokk, a '[' szolgál a formátum specifikációban. Lezárni a ']' karakterrel lehet a formázó blokkot. Erre egy példa:

%(if input_name)[ Hello %(var input_name size=16 etc="...")s. %(elif nick_name)[ Hi %(var nick_name capitalize)s. %(else)[ Have we been introduced? %(/if)]

A %(név)x forma tulajdonképpen a %(var név)x rövidített formája. A legtöbb esetben a 'var' elhagyható, azonban ki kell írni, ha

%(var expr="foo+1")s

Gyakran használt attribútumok

Az előbbiekben két olyan attribútumot láthattunk, amit a legtöbb DTML tag használ: ezek a name és az expr. Mindkettő olyan adatok azonosítására, illetve kiszámítására szolgál, amiket az adott tag használ.

A name attribútum: adatok név szerinti hivatkozására szolgál. Speciális, hisz létezik az előzőekben már taglalt rövidített formája is. Amikor a name attribútum értékét kikeresi a rendszer, azonnal értelmezi is azt, ha egy mód van rá. Ha ez az érték egy Zope Documentum vagy egy Python dokumentum template, akkor ez előbb behelyettesítésre kerül, és úgy adódik át a name-t használó tagnek. Például a legtöbb Zope dokumentum a következő dtml-var taggel kezdődik:

<dtml-var standard_html_header>

ahol is a standard_html_header egy olyan Zope dokumentum, ami egy sima HTML-t állít elő, ami aztán majd minden oldal tetejére beillesztődik. Amikor a fentebbi var taget használjuk, a standard_html_header DTML file feldolgozásra kerül, és az eredmény kerül be a szóban forgó dokumentumba. Ha a name értéke egy argumentum nélküli függvény, akkor a függvény eredménye kerül átadásra a tagnek. Ha a name attribútumot a dtml-if, dtml-elif, dtml-unless, dtml-in vagy a dtml-with tag használja, akkor a name-hez tartozó érték szép magyar szóval cache-elődik, ami jelentősen meggyorsítja a hivatkozást. Ez rendkívül hasznos akkor, ha valami nagy számításigényű függvényt használunk, pl:

<dtml-if reallyExpensiveFunction> <dtml-var reallyExpensiveFunction> </dtml-if>

Itt a dtml-var tag már a reallyExpensiveFunction cache-elt értékét használja. Megjegyzésként meg kell említeni, hogy az olyan tageknél, ahol lehetőség van új változók bevezetésére, és ha ez megegyezik a cache-elt változó nevével, akkor a cache-ben lévő érték felülíródhat.
A dtml-var tagnak sok egyéb attribútuma van, pl formázáshoz, ezek megtalálhatóak az eredeti DTML User's Guide -ban.

Az expr attribútum: akár egész bonyolult kifejezések kiértékelésére használatos. Az attribútumhoz tartozó kifejezést idézőjelek ("") közé kell rakni. Épp ezért a kifejezés belsejében idézőjel nem szerepelhet.

A kifejezések szintaktikája

A kifejezések szintaktikája megegyezik a Python-ban használatossal, ami meg hasonló az olyan közkedvelt nyelvek szintaktikájához, mint a C vagy a Java.
Álljon itt néhány példa:

Kifejezés Magyarázat
x*2*3 Numerikus kifejezés
func(a, b) Függvényhívás
obj.title Az obj title nevű argumentumát adja vissza
obj.meth(a, b) Meghívja az obj meth nevű metódusát a, b argumentumokkal
(age < 12 or age > 65) and status == 'student' Egy logikai (igaz / hamis) teszt
REQUEST['HTTP_REFER'] Kikeres egy értéket a REQUEST-ből a 'HTTP_REFER' kulcs alapján

Változók kikeresése

A kikeresett változók nem "hívódnak meg" azonnal, ahogy a name esetében: Az 1. táblázat kifejezéseiben szereplő nevek közűl az x, func, a, b, obj, sge és a status változó nevek, míg a meth és a title nevek objektumok attribútumait jelölik. A változó neveknek betűvel kell kezdődniük, és csak betűket, számokat illetve aláhúzást tartalmazhatnak.
Az ettől eltérő nevű változókat egy speciális változó, az _ segítségével lehet elérni. A _ speciális változóról a következőkben lesz szó

Feltételhez kötött szöveg beillesztés: a dtml-if tag

A Zope lehetővé teszi, hogy szövegeket csak bizonyos adatok függvényében illesszünk be. Erre szolgál az if tag, aminek is az alábbi négy formája létezik:

A dtml-if és a dtml-elif csak a name és az expr attribútumokat támogatják, míg a dtml-else -nek nincs argumentuma.
A dtml-unless /dtml-unless páros abban tér el a dtml-if -től, hogy a szöveg akkor helyettesítődik be, ha az expr attribútumhoz tartozó kifejezés értéke hamis.

<dtml-unless input_name> Nem adta meg a nevét. </dtml-unless>

Iterált beillesztés: a dtml-in tag

Sokszor van szükség értéklisták beillesztésére, például egy ZSQL metódus eredményének az iteratív beillesztésére.

A dtml-in tagot használják objektum sorozatok beillesztésére. Például egy munkavállalói könyvtár lista előállítása DTML-el igen egyszerű. Ebben a példában az employees dolgozók egy listáját szolgáltatja, vagy úgy, hogy maga a lista, vagy egy függvény, pl egy Zope SQL method, ami előállítja a listát. Minden dolgozónak van neve és telefonszáma: ezeket az adatokat dtml-var tagokkal érjük el. A dtml-in tag sort attribútuma a dolgozókat névsorba rendezi.

<table> <tr><th>Név</th><th>Telefon szám</th></tr> <dtml-in employees sort=name> <tr> <td><dtml-var name></td> <td><dtml-var phone></td> </tr> </dtml-in> </table>

A példában, ha nem lenne egy dolgozó sem, akkor egy üres tábla jelenne meg. Ezt elkerülendő, használhatjuk a dtml-in tagot a dtml-if -fel kombinálva:

<dtml-if employees> <table> <tr><th>Név</th><th>Telefon szám</th></tr> <dtml-in employees sort=name> <tr> <td><dtml-var name></td> <td><dtml-var phone></td> </tr> </dtml-in> </table> <dtml-else> Sajnos nincs egyetlen dolgozónk se. </dtml-if>

De nem muszáj a dtml-if -et használni, hisz a dtml-in taghez is tartozhat dtml-else tag, ami pontosan akkor illesztődik be, ha a feldolgozandó sorozat üres. A következő példa hatása megegyezik az előzőével, a bonyolultságát mégis megnöveli az a tény, hogy a táblázatok fejének és aljának kiíratását be kellett tenni a dtml-in blokkon belülre. Mi több: a fejet és a táblázat alját aszerint kell beilleszteni, hogy az épp aktuális elem az első, az utolsó, vagy egy közbülső elem. Erre két változót használunk, ezek a sequence-start és a sequence-end.

<dtml-in employees sort=name> <dtml-if sequence-start> <table> <tr><th>Név</th><th>Telefon szám</th></tr> </dtml-if> <tr> <td><dtml-var name></td> <td><dtml-var phone></td> </tr> <dtml-if sequence-end> </table> </dtml-if> <dtml-else> Sajnos nincs egyetlen dolgozónk se. </dtml-in>

Amikor egy dtml-in name attribútumát egy dtml-if tagen belül használjuk, akkor az csak egyszer számítódik ki, hiszen a dtml-if cache-eli a name-hez tartozó értéket.

A legtöbb esetben célszerű a dtml-in taget a dtml-if -en belül használni. Egy esetben lehet jobb megoldás a dtml-in -hez tartozó dtml-else -t használni:
Ha a sorozatot az expr attribútummal állítjuk elő, és a számítás nagyon költséges. A dtml-else használata dtml-in -en belül megakadályozza, hogy egy kifejezést kétszer definiáljunk és számítsunk ki.

A dtml-in által definiált változók

Amikor a dtml-in taget használjuk szöveg beillesztésre, akkor tulajdonképp a sorozat minden tagjáról egy másolat készül, és ez kerül beillesztésre. A dtml-in tag belsejében definiálva van néhány, csak a belső tagek által elérhető változó.
Ezek :

Ezek részletes leírása megtalálható az eredeti DTML User's Guide-ban.

Objektumok tulajdonságainak egyszerűsített elérése a dtml-with taggel:

A dtml-with /dtml-with blokk hasonló a Delphi with obj do end blokkjához. Itt is a dtml-with blokk elején megadott objektum tulajdonságait érhetjük el egyszerűbb hivatkozással a blokkon belül. Például ha egy Document Template egy mappát jelenít meg, akkor ennek az almappáját a következőképp jeleníthetjük meg dtml-with taggel:

<dtml-with subfolder> <dtml-var title> </dtml-with>

Itt is, hasonlóan a Delphihez, először a title-t mint subfolder. A title-t keresi a rendszer, csak aztán keresi a többi változó között, ha a subfolder objektumnak nem lenne ilyen nevű változója.
A dtml-with tagnek van egy attribútuma, ez az only. Ez azt írja elő, hogy a blokkban szereplő változókat, metódusokat csak az adott objektum tulajdonságai között keresse. Ezzel elkerülhetők az öröklésből adódó problémák, például:

<dtml-with REQUEST only> <dtml-unless id> Az id nincs specifikálva. <dtml-/unless> <dtml-/with>

Ugyanez az only attribútum nélkül a környezet id-jét adná, ami ebben a példában hibás lenne.

Hibák jelzése: a dtml-raise tag

Sokszor szükség van a változók - főleg az input változók - leellenőrzésére, validálására. Ha egy változó értéke hibás, jó lenne hibaüzenet generálni. A DTML erre lehetőséget ad a dtml-raise tag segítségével, amit dtml-if -el kombinálva használnak. A dtml-raise attribútuma a hiba típusát írja le. Csak úgy, mint a standard name attribútum esetében, ez is elhagyható. A hiba típus egy rövid, a hibára utaló név. Van néhány standard hibatípus is, mint pl. az "Unauthorized" vagy a "Redirect", amelyek mindketten HTTP hibák.
Íme egy példa a dtml-raise használatára:

<dtml-if "egyenleg >= folyoszamla"> <dtml-call "folyoSzamla(szamla)"> Az átutalás megtörtént a <dtml-var szamla> számláról. <dtml-else> <dtml-raise type="Insufficient funds"> Nincs a <dtml-account> számlán az átutalás fedezetéhez szükséges pénz.<p> </dtml-raise> </dtml-if>

A dtml-raise tag egy Zope hibát vált ki. Ezeknek a hibáknak van egy nagyon fontos mellékhatása, mégpedig az, hogy sx adott WEB kérés által okozott változásokat törli. Ez azért van így, mert a rendszer mindig egy tranzakciót feltételez a hívás mögött, s ennek korrekt kezelése érdekében szükség van az előbb említett mechanizmusra.

Kivételek kezelése: a dtml-try tag

Ha már hibát (illetve kivételt) lehet kiváltani, akkor azt le is kéne kezelni. Erre van a Zope -ban a dtml-try dtml-except dtml-else dtml-finally blokkstruktúra, ami hasonló más nyelvek kivételkezeléséhez. Ennek szintaktikája:

<dtml-try> <dtml-except hiba még_egy_hiba> <dtml-except még_egu_újabb_hiba> <dtml-except> <dtml-else> <dtml-finally> </dtml-try>

Az első dtml-except blokk az utána megnevezett hibákat kezeli le. Ha a dtml-except után nem írunk nevet, akkor az összes hibát lekezeli az adott blokkal. A dtml-else ág akkor fut le, ha a dtml-try blokkon belül nem történt kivétel. A dtml-except blokk a Python osztály alapú kivételeivel dolgozik, tehát ha egy adott nevű kivétel szerepel a dtml-except tag után, akkor az a kivétel összes alosztályát is lekezeli. Például ha ArithmaticError a tag után írva, akkor az összes ebből származtatott kivételt, például a ZeroDivisonError -t is le tudja kezelni az adott blokk.
A dtml-else ágban fellépő esetleges kivételeket már nem kezelik le a szóban forgó dtml-except blokkok.

A dtml-try blokkon belül speciális változók is rendelkezésre állnak:

A dtml-finally ág kivételtől függetlenül mindig lefut. Ezt a blokkot tipikusan változók felszabadítására, takarításra használják. Ha a dtml-try blokkban dtml-return fordul elő, a dtml-finally blokk akkor is lefut, az ő outputja nem jelenik meg, egyszerűen törlődik.
Fontos megjegyezni, hogy ha a dtml-finally blokkban kivétel történik, akkor az előző kivétel adatai törlődnek. Ebből az következik, hogy ha egy return taget használtunk a dtml-try blokkban, és a dtml-finally blokkban is kivétel történik, akkor a dtml-try blokk visszaadott értéke elvész.

Megjegyzések: a dtml-comment tag

Megjegyzéseket a dtml forrásba a
<dtml-comment>
</dtml-comment>
blokk közé tehetünk. Nem túlságosan meglepő módon, ezen blokkon belül szereplő sorok nem kerülnek beillesztésre, vagy értelmezésre.

Értékek visszaadása: a dtml-return tag

A dtml-return tag szolgál egy DTML metódus által kiszámított érték visszaadására. A dtml-return tag attribútumai csak a standard name és az expr attribútumok lehetnek.
Példa:

blah blah <dtml-return "1">

Ha az előbbi DTML metódus lefut, akkor az 1 szám kerül visszaadásra, a "blah" szöveg nem. Egy másik példa:

<dtml-in objectIds> <dtml-return sequence-item> </dtml-in> blah

Itt, ha léteznek objektum azonosítók, akkor az első ilyennel tér vissza a metódus, ha nem, akkor pedig a "blah" szöveget adja vissza.

Levélküldés: a dtml-sendmail tag

A dtml-sendmail tag segítségével SMTP protokollon keresztül e-mail-t küldhetünk. Ez a tag, a többi taggel ellentétben nem generál outputot. A dtml-sendmail tagnek jó pár adatra szüksége van, ennek megfelelően sok attribútummal rendelkezik, amik közül én csak a fontosabbakat említem meg. Minimálisan kell neki, hogy létezzen egy Zope MailHost objektum, vagy egy STMP host címét kell megadni az smtphost attribútummal. A címzeteket, a feladót, és a tárgyat meg kell adni, de ez történhet a dtml-sendmail attribútumai segítségével is, de lehetnek ezen információk az üzenet "fejlécében", első soraiban is.
Erre két példa:

<dtml-var standard_html_header> <dtml-sendmail smtphost="gator.digicool.com"> To: Ügyfélszolgálat <<dtml-var support>> From: Web adatlap <<dtml-var feedback>> Subject: <dtml-var subject> <dtml-var body> </dtml-sendmail> Köszönjük az észrevételt! <dtml-var standard_html_footer>

A WEB adatlap, ami az észrevételeket összegyűjti a felhasználóktól, és elküldi az ügyfélszolgálatnak:

<dtml-var standard_html_header> <H2>Ide írhatja le az észrevételeit!</H2> <form action=SendFeedback> Subject: <input type=text name=subject size=40> <br> <textarea name=body rows=10 cols=50> </textarea><br> <input type=submit value="Elküld"> </form> <dtml-var standard_html_footer>

A dtml-sendmail taget követő szöveg generálására használhatjuk a dtml tageket is, mint ahogy azt az előző példából is láthatttuk.

Melléklet küldése: a dtml-mime tag

A dtml-mime tag a dtml-sendmail taggel együtt használható e-mail melléklet küldésére. A dtml-mime tag automatikusan multipart/mixed -re állítja az üzenet tartalmának típusát. Sok, különféle adat csatolható egy egyszerű üzenethez egy, vagy több dtml-boundary tag segítségével.
Például:

<dtml-var standard_hmtl_header> <dtml-sendmail smtphost=gator.digicool.com> From: zope@digicool.com To: <dtml-var who> <dtml-mime type=text/plain encode=7bit> Itt küldjük az éves kimutatást. <dtml-boundary type=application/octet-stream disposition=attachment encode=base64><dtml-var "eves_kimutatas"></dtml-mime> </dtml-sendmail> Az üzenet a melléklettel együtt el lett küldve. <dtml-var standard_hmtl_footer>

A dtml-boundary tagnek három attribútuma van (type, disposition, encode), mivel is a melléklet MIME fejlécét adhatjuk meg. Az alapértelmezett kódolási érték 7bit.