Page Template を使う
Zope Book (バージョン2.7対応版)の "Using Page Templates"の翻訳です。Zope内蔵のテンプレート言語であるPage Template(ページ・テンプレート,ZPT)について,概要と機能,および簡単な利用方法をまとめた文書です。
この文書について
この文書は、Zope Book 最新版の "Using Page Templates" を日本語に翻訳したものです。デザイナー、技術者をふくめ、Page Templateについて学びたい方にとって有益な文書です。また、Ploneの見栄えを定義している「スキン」はCSSなどを除いた多くの部分が Page Template で記述されています。Plone を学ぶためにも有用な文書となるでしょう。
なお、原文は以下のURLにあります。
http://www.plope.com/Books/2_7Edition/ZPT.stx
Page Templateを使う
Page Template(ページテンプレート) はWebページを生成するためのツールです。 Page Templateを使うと、Zope のWebアプリケーションで利用する動的なWebページを作成する際、プログラマとデザイナーが協調作業しやすくなります。 デザイナーは慣れ親しんだオーサリングツールをそのまま使ってページをメンテナンスすることができますし、アプリケーションに必要なページ内のロジックを壊さずに済みます。
この章では Page Template の基本機能について学びます。サイト上に動的なウェブページを作る時に、Page Template をどのように利用すればよいかについて解説します。 次の節では、スクリプトと Page Template を使って Zope の Web アプリケーションを構築するための実践的な例題を取り扱っています。 「Page Templateの高度な使い方(未訳)」というタイトルの章では、高度な Page Template の機能について学びます。
Page Template が目指すのは、デザイナーとプログラマーの強調作業を容易にすることです。 デザイナーは DreamweaverやGoLiveのようなWYSIWYG HTML エディターを使ってテンプレートを作成でき、プログラマーはテンプレートをアプリケーションに組み込めるように機能を追加できます。 必要なら、その後デザイナーはテンプレートを HTML エディターで 読み込み直して 、HTMLの構造やデザインを変更することもできます。 プログラマーが行った変更を壊さないために適切な手順を踏めば、デザイナーがアプリケションを壊してしまうようなことはありません。
Page Template は3つの原則を採用してデザイナーとプログラマーの協調作業を容易にしています。 ::
- HTML編集ツールと親和性がある
- 見た目と実際の表示が「ほとんど」同じである
- コードは、構造上のロジックを除き、テンプレートから追い出す
Page Templateは生成されるWebページの「ひな形」のようなものです。そのため、多くの HTML ツールで読み込むことができます。
Zope Page Template と DTML
ページテンプレートを記述するためにはTemplate Attribute Language (TAL) を利用します。 一方、DTML Methods, DTML Documents と SQL Methods はDocument Template Markup Language (DTML) を利用しています。 Zope にはなぜ2つの異なるテンプレート言語があるのか、疑問に思われるかもしれません。
第一には、歴史的な理由があります。Page Template は比較的新しい技術です。 Zope 2.5 が Page Template を採用した最初のリリースでした。 Page Template が向いている分野であるにも関わらず、いまだに多くのZope プロダクトやHow ToでDTML が利用されています。 また、DTML を好んで使う人もいます。
第二に、 DTML と Page Template にはそれぞれ異なる長所と短所があります。 Page Template は HTML のデザイナーのために作られました。 HTMLにDTML を埋め込んで動的なページを作ると、結果として HTMLのコードはたいてい HTML ツールで読み込むことができなくなり、Zope の外では使いにくくなります。 一方、Page Template の仕様は、プレゼンテーション、ロジック、コンテンツ(データ)の分離を促進します。 プレゼンテーション、ロジック、コンテンツを分離することにより、コンテンツ管理と、Webサイト開発のスケーラビリティが増大します。 そして、 Page Templateでは「名前(name)」検索がずっと明快になります。 DTMLの名前空間のモデルには非常にたくさんのマジックがあるのとは対照的です。
しかし、DTML にも利点はあります。HTML/XML 以外のもの、動的に生成されるメールの本文や SQLのクエリのようなものを扱うには、 DTML が向いているでしょう。
DTML には "将来性がない"というわけではありません。特定の処理を実現する際には、DTML も学んだ方がよいでしょう。
HTML Page Template
Page Template は2つのモードで編集できます。HTML モードとXML モードです。 XML モードの使い方については、この章の最後で 解説します。 多くの場合、デフォルトでもあるHTML モードを使えば十分でしょう。
HTML Mode で編集作業をするには、 Content-Type を text/html に設定してください。
HTML は XML に準拠していないので、テンプレート言語として拡張することはできません。 Page Templateをレンダリングして出力されるHTMLは正しい(validな) HTML となりますが、Page Templateのソースコード自体は正しい HTML あるいは XML とはなりません。
しかし、 Template Attribute Language (TAL) はTALの記述を HTML タグの中に巧妙に隠すように作られているので、 多くの HTML 編集ツールではPage Template のソースを問題なく読み込めるはずですし、 TAL の記述を無視してくれるはずです。
すでにご存知かもしれませんが、 XHTML は HTML を XML に準拠するように変更したものであり、今日広く使われています。 いずれにせよ、Page Template を使えば HTML も XHTML も全く問題なく生成できます。 HTML Mode ではXMLの文法に従った(well-formed) XML を要求しませんが、このモードを XHTML 用に使ってもまったく問題ありません。
Page Templateの仕組み
Page Template は Template Attribute Language (TAL) を使って記述します。 TAL は特別な「タグ属性」で構成されています。 たとえば、ページに動的にヘッドラインを埋め込みたいときは以下のようにします。:
<h1 tal:content="context/title">Sample Page Title</h1>
tal:content という属性が TAL の文です。
TAL 文は XML の名前空間(tal: の箇所) を持っているので、ほとんどのHTML編集ツールでエラーなしに読み込めるはずですし、TAL 文が削除されることもありません。
WYSIWYG エディタやWebブラウザーで読み込んだ際も、テンプレートの構造や見た目には変化はありません。
content という名前は h1 タグに囲まれた文字列を置き換えることを意味し、 context/title という値は、タグ内に挿入する文字列を決めるための「式」となっています。
context/title で指定された要素が "Susan Jones Home Page" という文字列を返したとすると、生成された HTML の一部はこのようになります。 :
<h1>Susan Jones Home Page</h1>
名前が tal: ではじまるタグ属性からなるすべての TAL 文と、全てのTAL 文には、対応する「値(value)」があります。
TAL 文の「値」は引用符の中に記述します。
TAL の詳細については、付録 C の「Zope Page Templateリファレンス(未訳)」 を参照してください。
WYSIWYG ツールを使っている HTML デザイナーからずれば、例にある動的な「見出し」は理解することができる HTML に見えるでしょうし、使っている編集ツールでは、普通の見出しとして表示されます。 つまり、Page Template は編集ツールと「親和性」があるといえるわけです。
また、この例は、"見た目と実際の表示がほとんど同じである" という原則を示しています。 テンプレートをHTMLエディタで見ると、ヘッドラインのテキストは動的なヘッドラインのテキストの「置き換え」として機能します。 テンプレートには、生成されたドキュメントがどう見えるかという「例」が埋め込まれているわけです。
このテンプレートを Zope 上に保存して閲覧すると、Zope は"Sample Page Title" という文字列を context/title からひかれる文字列に置き換え、ダミーの文字列を動的な文字列に置き換えます。
この場合、 context/title はテンプレートが適用されているオブジェクトのタイトルに置き換えられます。
置き換えは、テンプレートを閲覧する際に動的に実行されます。
TALには、タグ全体、タグで囲まれた文字列、属性の一部を置き換えるためのテンプレート文があります。 また、タグを何度か繰り返したり、完全に省略したりといったことも可能ですし、 他のテンプレートの一部を部品として埋め込んだり、簡単なエラー処理を指定することもできます。 このような TAL の機能を使い、ドキュメントの「構造」を生成してゆくのです。
TAL はさまざまな機能を持っていますが、サブルーチンやクラスを作ったり、複雑な制御を行ったり、複雑なアルゴリズムを単純に表記するするといったことは Page Template ではできません。 こうした用途には、Python Scriptやアプリケーションやフレームワークに組み込まれた機能を使ってください。 Page Template 言語は、強力さや汎用性を 意図的に備えない ように設計されています。 ページのレイアウトに関係のないビジネスロジックを扱う仕組みを持っているZopeのようなフレームワーク上で利用されることを前提として、Page Templateは開発されました。
たとえば、テンプレート言語は、項目ごとに1つの「列」を生成し、各列に説明、量、価格などをテキストとして挿入して、請求書のページをレンダリングするのには便利です。 データベースに請求書の記録を追加したり、クレジットカードの決済システムとの間で処理をするのに使うものではありません。
Page Template を作成する
ページをデザインする時は、Zope 管理画面(ZMI)ではなくFTPやWebDAV を使ってPage Template をリモート編集することになるでしょう。 その場合は、「高度な使い方 (未訳)」の章にある "FTP や WebDAVを使ってリモート編集する" を参照してください。 この章にある例については、ZMI を使って編集をした方が簡単でしょう。
Zope 管理画面に管理者権限でログインするには、Webブラウザを使います。
Zope のルートフォルダに template_test という名前の作業用フォルダ(Folder)を作成します。このフォルダに入り、 管理画面の右上にあるメニューから Page Template を選択します(決してDTML Method や DTML
Document は選択しないでください。次の例は Page Template でのみ機能します)。
追加フォームの Id フィールドに simple_page とタイプし、 Add and Edit ボタンを押します。すると新しい Page Template の編集ページが表示されるはずです。
タイトルは空で、デフォルトのテンプレートの本文は編集用のフォーム に表示されています。
それでは、単純な動的ページを作成してみましょう。 Title フィールドに a Simple Page という文字列をタイプします。次に、テンプレートの本文を次のように編集します:
<html>
<body>
<p>
This is <b tal:content="template/title">the Title</b>.
</p>
</body>
</html>
そして Save Changes ボタンを押します。 Zope は変更が保存されたと確認するメッセージを表示するはずです。
エラーメッセージか表示されたら、例を正しくタイプしたかを確認し、もう一度保存してください。外部エディタ(External Editor) を使っている場合は、Page Template Diagnostics で始まる HTML のコメントがテンプレート本文に追加され、何かおかしかったことを知らせます。 コメントとして埋め込まれたエラーを削除する必要はありません。エラーが訂正されれば、消えてしまいます。
次にTest タブをクリックします。"This is a Simple Page." と上部に表示されたページが見えるはずです。タイトルは太字で表示されていることに注目してください。
tal:content ステートメントが bold タグで囲まれた部分(content)を入れ替えているためです。
次に、Webブラウザの戻るボタンを押して編集画面に戻ります。 Content-Type フィールドの下にある Browse HTML source リンクをクリックします。 すると、レンダリングされていない状態のテンプレートが見えるでしょう。 "This is the Title." と見えているはずです。 太字のテキストは動的なタイトル・テキストの代替の役目を果たしています。 例をさらに編集するために、もう一度戻るボタンを押します。
Edit タブにはまだ説明していない設定が2つあります。 Content-Type フィールドではページのコンテント・タイプを指定できます。この章で後ほど触れますが、この値を変更すると、 Page Template を XML モードに変更できます。 Expand macros with editing の 使用方法についてはこの章の "Macros" の節で説明します。
TALES 式
例題の Page Template にある「式」"template/title" はpath 式です。これはもっとも一般的な「式」です。 このほかにも TAL Expression Syntax (TALES) 規格により定義された「式」があります。
TALES の詳細は 「Zope Page Templates リファレンス(未訳)」 を参照してください.
Path 式
template/title というpath 式はテンプレートの title 属性を取得します。
そのほかによく使われる path 式には次のようなものがあります。 ::
- 'context/objectValues': テンプレートが呼び出されているフォルダにあるサブ・オブジェクトのシーケンス(リスト)
- 'request/URL': Webページが表示している URL
- 'user/getUserName': 認証ユーザのログイン名
一つ前の章で Python Script で利用できる context 変数と API 関数 objectValues についてすでに学びました。
ほかの2つの例は、単にパターンを示すために紹介しました。
詳細は後ほど解説します。
この例が何を返すか確かめるには、次の行を Page Template にコピーし、Test タブをクリックします。
context/objectValues が返すシーケンス(リスト)を利用するためには、追加の処理を記述する必要があります。
詳細については、後ほど解説します。 :
<p tal:content="context/objectValues"></p>
<p tal:content="request/URL"></p>
<p tal:content="user/getUserName"></p>
Path 式 はまず変数名で始めます。 利用可能な変数名は、Page Template にデフォルトで束縛されている、 context, request, user のようなオブジェクトや、あるいは TAL を使って Page Template 内で定義した変数です。 here は context のエイリアスで、古い定義なのですが、現在でもよく使われています。
request や user のような「組み込み変数」については「Page Templates の高度な使い方(未訳)」で詳しく解説しています。 「高度な使い方(未訳)」では、変数を独自に定義する方法についても解説しています。
Path 式に指定した変数が期待通りの値を返すなら、それで終了です。
そうでないなら、スラッシュ(/) に続けて、サブ・オブジェクト、あるいは属性の名前を追加します。
求めている値を得るためには、いくつかのサブオブジェクトを経由しないとならないかもしれません。
Python 式
経験則からいって、ロジックを表記するために Python が必要なら、コードを分割してスクリプトにすることをお勧めします。 とはいえ、Zope はプロトタイプにも優れたツールでもあるので、たった一行のコードのためにスクリプトを書くのは生産性を著しく下げることになります。 既存のプロダクトでも、 'Python 式' はよく利用されていますので、Python 式について学んでみましょう。 この章の最初の例を思い出してみましょう。 :
<h1 tal:content="context/title">Sample Page Title</h1>
Python 式 を使って書き直してみます。 :
<h1 tal:content="python: context.getProperty('title')">Sample Page
Title</h1>
TAL では Path 式 がデフォルトなので、 Path 式以外の式を記述するためには、ほかの型の式であることを示す型プリフィックスが必要となります。
python: という型プリフィックスの式が(この例の場合は)上の例の path 式と同じような動作をします。
しかし、例にあるような Python 式を記述するためには、 title がコンテクスト・オブジェクトのプロパティであることと、プロパティにアクセスする方法を知ってる必要があります。
Python 式は title というプロパティに Path 式と異なった方法でアクセスしています。Python 式は一般に「柔軟性」がありますが、「明示的ではない」と言えるでしょう。
Path 式では実行できない処理がいくつかあります。 Path 式で実行できない処理の代表例は、次のように値を比較することです。 :
"python: variable1 == variable2"
あるいは、Path 式ではメソッドに引数を渡すこともできません。以下が例です。 :
"python: context.objectValues(['Folder'])"
Path 式で実行できない処理をするためには、Python 式を利用してください。
TAL アトリビュート
Page Templates は表示のためのサンプル文字列で構成されており、ページの部品となります。 TAL 文は、サンプル文字列を動的に変換する方法を定義します。 使われている TAL 属性に従って、サンプル文字列や属性を動的な値に置き換えます。 あるいは、動的な値を使ってサンプル文字列の要素を削除したり、繰り返します。
文字列を挿入する
"simple_page" テンプレートでは、bold タグで tal:content ステートメントを使いました。
「テスト」すると、 Zope は HTML の bold で囲まれたエレメントをテンプレートのタイトルに置き換えました。
HTMLのタグで囲まれたエレメント全体を置き換えたいのであれば、この例のように簡単です。
では、エレメント内にあるいくつかの単語だけを置き換えたいとしたらどうすればいいでしょうか。
ある文字列の中に動的なテキストを設置するには、 'span'タグを追加して、'tal:replace'を使うのが常套手段です。 たとえば、次の行をサンプルに追加してみます。 :
<p>The URL is
<span tal:replace="request/URL">
http://www.example.com</span>.</p>
span タグは見た目には影響を与えず、構造上のタグにすぎないので、 エディターやブラウザーでソースを見ると、"The URL is http://www.example.com." というようになっています。
しかし、レンダリングされた結果は以下のようになるでしょう。 :
The URL is http://localhost:8080/template_test/simple_page.
レンダリングされたもののソースコードを見ると、 span タグは削除されています。
tal:replace と tal:content の違いを確かめるには、Page Template を作成して、本文に次のような行を記入してください。 :
<b tal:content="template/title"></b>
<b tal:content="request/URL"></b>
<b tal:content="user/getUserName"></b>
<b tal:replace="template/title"></b>
<b tal:replace="request/URL"></b>
<b tal:replace="user/getUserName"></b>
TAL 属性を使い、「レンダリング時に削除されるエレメント」を追加する方法はほかに2つあります。 :
<p>The URL is
<span tal:content="request/URL" tal:omit-tag="">
http://www.example.com</span>.</p>
... この手法は例題とは違う状況で活用すると便利です。後ほど詳しく解説します。 また 、:
<p>The URL is
<tal:span tal:content="request/URL">
http://www.example.com</tal:span>.</p>
HTML のエレメントと tal:replace や tal:omit-tag を組み合わせてかなりのことができますが、エレメントが TAL 属性を追加するためだけに使われている場合、 TAL エレメントの方を好む人もいます。
TAL は属性言語なので、 tal:span のようなエレメントは定義できませんが、完全な XML 名前空間を使っているので、エレメントには好きな名前を使えます。
こうしたエレメントは、Page Template がレンダリングされる際には、暗黙に削除されます。
HTML上で span や div のようなエレメントを使えない場所にエレメントを追加する場合は、tal:loop, tal:case や tal:span のような tal 属性を使うと便利です。
ブラウザーやエディターがこれらのタグは無視するとすれば、HTMLのエレメントを追加するより tal 属性を追加した方がデザイナーにとってはうれしいはずです。
構造を繰り返す
シンプルな3行のコードからはじめましょう。 :
<p tal:repeat="number python: range(4)" tal:content="number">
999
</p>
number は 繰り返し変数 で、 range(4) は '[0, 1, 2, 3]' を返すPython 式' です。
このコードがレンダリングされると、repeat ステートメントはシーケンス(リスト)の各値の paragraph エレメントを繰り返し、変数 number を現在のシーケンス(リスト)の値に置き換えます。
したがって、レンダリングされたページは例にある数字の 999 は表示せず、シーケンス(リスト)にある数を含めた4つのパラグラフを表示します。
より実践的には、もっと複雑なシーケンス(リスト)を扱うこととなります。
次の例では、オブジェクト(への参照)のシーケンス(リスト)を利用する方法を示しています。
simple_page テンプレートに「アイテムリスト」を追加して改良してみましょう。
テンプレートと同じ Folder にあるオブジェクトのシーケンス(リスト)を使います。
各オブジェクトが行を持ち、id, meta-type,title をそれぞれの列に持つテーブルを作ります。
次の行を、サンプルのテンプレートの最後に追加してください。 :
<table border="1" width="100%">
<tr>
<th>Id</th>
<th>Meta-Type</th>
<th>Title</th>
</tr>
<tr tal:repeat="item context/objectValues">
<td tal:content="item/getId">Id</td>
<td tal:content="item/meta_type">Meta-Type</td>
<td tal:content="item/title">Title</td>
</tr>
</table>
テーブルの行にある tal:repeat 文は、 「この context の中にある各アイテムに対して繰り返す」 ということを意味します。
repeat 構文はシーケンス(リスト)にあるオブジェクトをitem 変数 (「繰り返し変数」と呼びます)に、一度にひとつずつ割り当て、この変数を使って行のコピーを作成します。
各行のitem/getId の値は、その行にあるオブジェクトの Id となります。
item/meta_type, item/title も同様に、オブジェクトが持つ値を返します。
繰り返し変数の名前は、文字ではじまり、文字、数字とアンダースコア(_)のみで構成されていれば、
好きな名前が使えます("item" は
「例」として使っています)。 繰り返し変数 は repeat タグの中でのみ利用(ローカル変数)できます。 tr タグで囲まれた上位、あるいは下位で使うと、
エラーになります。
現在繰り返しているものの情報を得るために、繰り返し変数の名前を使うこともできます。 「Page Template の高度な使い方」
をご覧ください。ページを表示すると、テンプレートと同じフォルダにあるすべてのオブジェクトがシーケンス(リスト)されていることが分かるでしょう。
フォルダでオブジェクトを追加・削除すると、ページが動的に変わるはずです。
条件によって処理を振り分ける
Page Templates を使えば、環境変数を調べて、場合によってテキストを動的に「挿入し分ける」ことができます。 以下の例では、クッキーに応じて情報を表示しています。 :
<p tal:condition="request/cookies/verbose | nothing">
Here's the extra information you requested.
</p>
verbose というクッキーが設定されている場合のみ、表示にパラグラムが出ます。
request/cookies/verbose | nothing という表記は
verbose という名前のクッキーが設定されている場合のみ、真になります。
こうした表記は
「Page Template の高度な使い方(未訳)」の章で詳しく学びます。
tal:statement 式を使うと条件をチェックを実行できます。
tal:condition 式は表記が「真(true)」の場合は、
タグとタグに囲まれた文字列をそのままにします。「偽(false)」の場合は、削除します。
Zope は数字の0、空の文字列、空のシーケンス(リスト)、組み込み変数
nothing などを「偽値」としています。
0 以外の数字や何かの含まれた文字列(スペース - 空白でも)を含め、それ以外のほとんどすべてのオブジェクトは「真」となります。
その他の condition の典型的な使い方は、シーケンス(リスト)を繰り返す前に、空であるかどうかをテストすることです。 例えば、一つ前のセクションで一連のオブジェクトを走査して、 テーブルを出力する方法について触れました。 オブジェクトのシーケンス(リスト)が空の場合に、テーブルの出力を抑制するためには次のようにします。
効果を認識するために、まず、コンテクストのフォルダにある
Folder オブジェクトだけを表示するよう、例題に少しだけ変更を加えましょう。
context/objectValues というようなpath 式 では引数を渡すことができないので、まず、 context.objectValues()
という Python 式 に変更し、objectValues メソッドがサブフォルダのみを返すよう、引数を追加します。 :
<tr tal:repeat="item python: context.objectValues(['Folder'])">
これまでに template_test フォルダにサブフォルダを追加していない場合、Test タブを押せば、ヘッダのみのテーブルが表示されるはずです。
これを避けるために、テーブルタグに tal:condition ステートメントを追加します。
完成版のテーブルは次のようになります。 :
<table tal:condition="python: context.objectValues(['Folder'])"
border="1" width="100%">
<tr>
<th>Id</th>
<th>Meta-Type</th>
<th>Title</th>
</tr>
<tr tal:repeat="item python: context.objectValues(['Folder'])">
<td tal:content="item/getId">Id</td>
<td tal:content="item/meta_type">Meta-Type</td>
<td tal:content="item/title">Title</td>
</tr>
</table>
Test tab again.
サブフォルダのシーケンス(リスト)が空の場合、 condition は「偽」になり、テーブル全体が省略されます。 Test タブをもう一度押すことで確認できます。
simple_page テンプレートのある template_test フォルダに
1, 2, 3 という名前の3つのフォルダを追加します。
simple_page を再度表示し、Test タブを押してレンダリングされた出力をみます。
すると、ちょうど下のようなテーブルが見えるでしょう。 :
Id Meta-Type Title
1 Folder
2 Folder
3 Folder
アトリビュート領域を書き換える
すべてではないにせよ、テンプレートにシーケンス(リスト)されているオブジェクトの多くには、そのオブジェクトの種類を表示するためのアイコンへのパスを含んだ icon 属性が含まれています。
このアイコンを meta-type カラムで表示するには、このパスを
img タグの src 属性に挿入する必要があります。
上の例の meta-type カラムにあるテーブルのセルを次のように編集します :
<td><img src="/misc_/OFSP/File_icon.gif"
tal:attributes="src item/icon" />
<span tal:replace="item/meta_type">Meta-Type</span></td>
tal:attributes 文は img タグの src 属性を
item/icon の値に置き換えます。
テンプレートにある src="/misc_/OFSP/File_icon.gif" 属性は「代替」として機能します。
テーブルのセルにある tal:content 属性を span タグにある
tal:replace 文に入れ替えたことに注意してください。
この変更により、 画像とテキストの両方をテーブルセルに表示できます。
XML Page Template
Page Template で XML を作成するには、 HTML を作成する時とほとんど同じ手順を踏みます。
content-type フィールドを text/xml か XML にふさわしい
content-type に設定することで、 XML モードに切り替わります。
XML モードでは、"ルーズな" マークアップは決して許されません。Zope はテンプレートが XMLの文法に従った (well-formed な) XML であることを前提にしています。Zope
は XML を出力するために、TAL と METAL の XML 名前空間の宣言も要求します。たとえば、XHTML を出力したければ、 html タグの名前空間の
宣言で次のようなもの設置します。 :
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
XML テンプレートのソースを閲覧するには、 source.html ではな
く source.xml にします。
デバッグとテスト
Zope は Page Template にある問題を見つけ、修正すべき点を示してくれます。 Zope は問題点を2回 に分けて知らせてくれます。 : Page Template を編集したときと Page Template を表示(view)しているときです。 Zope は Page Template を編集しているときと表示しているときで異なるタイプのエラーを捕捉して表示します。
Zope がエラーを発見したときに Page Template に挿入する問題解決用のコメントを見たことがあるかもしれません。これらのコメントは、テンプレートを編集中に Zope が見つけた問題を教えてくれます。編集中にZope が見つける問題はたいてい TAL ステートメントのエラーです。 たとえば :
<!-- Page Template Diagnostics
Compilation failed
TAL.TALDefs.TALError: bad TAL attribute: 'contents', at line 10, column 1
-->
この エラーメッセージを見ると、テンプレートの10行目で tal:content
とすべきところを誤って tal:contents と間違ったのだと分かります。
ほかのエラーメッセージはテンプレートの式や マクロのエラーを教えてくれます。
Zope 管理画面で Page Template を編集している場合、こういったエラーメッセージを見つけるのは簡単です。というのも、Page Template を保存する際に、管理画面のページの "Error" ヘッダーに表示されるからです。しかし、WebDAV や FTP を使っていると、これらのメッセージを見逃しやすいでしょう。たとえば、Zope 上に FTPでテンプレートを保存した場合、問題点を教えてくれる FTP エラーは表示されません。 実際、エラーメッセージを見るには、Zope 上でテンプレートをリロードしないとなりません。FTP や WebDAV を使っている場合は、 エラーメッセージが含まれているかどうか確認するためにも、テンプレートをリロードしてみることをお勧めします。
エラーメッセージに気づかずに問題のあるテンプレートをレンダリングしようとすると、次のようなメッセージが表示されます :
Error Type: PTRuntimeError
Error Value: Page Template hello.html has errors.
テンプレートをリロードし、エラーメッセージを確認するように即するシグナルです。
編集時のエラーメッセージ以外にも、Page Template を 表示 (view) すると、いつもの Zope のエラーが表示されることがあります。 これらの問題はたいていテンプレートの表記上の問題によるものです。たとえば、表記が変数を解決できないとエラーになります。 :
Error Type: KeyError
Error Value: 'unicorn'
このメッセージは unicorn 変数が見つからないことを教えてくれます。問題点を発見しやすくするために、 Zope はトレースバックに環境変数の情報を含めます。情報は (Zope のルートフォルダにある) error_log から得られます。トレースバックには、エラーが発生した場所の情報と、環境変数の情報が保存されています。 :
URL: /sandbox/demo
Line 1, Column 14
Expression: standard:'context/unicorn'
Names:
{'container': <Folder instance at 019AC4D0>,
'context': <Application instance at 01736F78>,
'default': <Products.PageTemplates.TALES.Default instance at 0x012F9D00>,
...
'root': <Application instance at 01736F78>,
'template': <ZopePageTemplate at /sandbox/demo>,
'traverse_subpath': [],
'user': admin}
この情報はちょっと暗号のようですが、少しよく調べれば、何が悪かったのか理解できるでしょう。この場合、 'context'変数は
"Application instance" であることがわかります。つまり、トップレベルの Zope フォルダです(root 変数も同じ "Application instance" であることに気をつけてください)
おそらく、unicorn プロパティのあるフォルダーにテンプレートを適用したかったけれども、テンプレートを呼び出したルートには、該当するプロパティが設定されていなかったのが問題なのでしょう。
マクロ
これまで、各Webページに動的な振る舞いを追加するためには Page Template をどう使えばいいのかを見てきました。
例えば、Page Template を使うと、一貫した見た目のサイトを作ることができます。 ページの内容に関係なく、標準ヘッダー、サイドバー、フッター、そして(あるいは)ページの要素を設置することができます。
macro(マクロ)を使えば、ページ全体でプレゼンテーションの要素を再利用できます。マクロはほかのページで再利用できるページの部分を定義します。マクロはページ全体でも、ヘッダーやフッターといったページのほんの一部でもかまいません。Page Template に複数のマクロを定義すれば、ほかの Page Template で利用できます。
マクロ ( Macro ) を使う
TAL ステートメントに似たタグ属性を使ってマクロを定義できます。 マクロのタグ属性は Macro Expansion Tag Attribute Language(METAL) といいます。 これが、マクロを定義する例です :
<p metal:define-macro="copyright">
Copyright 2001, <em>Foo, Bar, and Associates</em> Inc.
</p>
metal:define-macro 構文が "copyright" というマクロを定義します。マクロは (p の閉じタグまでにあるすべての要素を含む)
'p'要素で構成されています。
Page Template で定義されているマクロはテンプレートの macros 属性に保存されます。これらを、定義されている Page Template の macros 属性を通じて、これらを参照することで、ほかの Page Template からもマクロを使えます。 たとえば、 "master_page"という名前の Page Template に copyright マクロがあるとします。 別の Page Template からは、次のようにして copyright マクロを使います。 :
<hr />
<b metal:use-macro="container/master_page/macros/copyright">
Macro goes here
</b>
この Page Template では、 b 要素は Zope がページをレンダリングする際にマクロに置き換わります。 :
<hr />
<p>
Copyright 2001, <em>Foo, Bar, and Associates</em> Inc.
</p>
(copyright を持っているPage Templateが変更されるなどして) マクロを変更すると、このマクロを使ったすべての Page Templateに自動的に変更が反映されます。
metal:use-macro 構文を使う場合、path 式
によってマクロを選択していることに注目してください。
metal:use-macro 構文は構文中のエレメントを名前の付けられたマクロに入れ替えます。
マクロの詳細
metal:define-macro ステートメントと metal:use-macro ステートメントは非常にシンプルです。しかし、これらを使う際に覚えておくべき事柄がいくつかあります。
Page Template 内で定義されているマクロの名前は「一意」である必要があります。 テンプレート内で複数のマクロを定義してもかまいませんが、これらはみな異なった名前である必要があります。
普通、マクロは path 式を使って metal:use-macro のマクロを指定する。しかし、マクロを返しさえすれば、好きな型の式 を使えます。たとえば :
<p metal:use-macro="python:context.getMacro()">
Replaced with a dynamically determined macro,
which is located by the getMacro script.
</p>
この場合、path 式は getMacro スクリプトで動的に定義されたマクロを返します。マクロを locate する際に Python
式 を使うことでテンプレートでどのマクロを使うかを動的に変えることができます。
metal:use-macro では、 default 変数を使えます。 :
<p metal:use-macro="default">
This content remains - no macro is used
</p>
結果は tal:content と tal:replace で default を使った場合と同じです。タグにある "default" のコンテントはレンダリングされても変わりません。場合によってマクロを使う、あるいは、存在しない場合にデフォルトのコンテントを使うようにする必要があるときに便利です。
metal:use-macro で nothing 変数を使おうとすると、エラーになります。nothing はマクロではないからです。
Zope はテンプレートをレンダリングするときになって初めてマクロを扱います。 そのあとで、ZopeはTAL式を評価します。たとえば、このマクロを考えて見ましょう。:
<p metal:define-macro="title"
tal:content="template/title">
template's title
</p>
このマクロを使うと、マクロが定義されているテンプレートのタイトルではなく 、マクロが使われているテンプレートのタイトルが挿入されます。つまり、マクロを使うということは、マクロのテキストをテンプレートにコピーし、そのテンプレートをレンダリングするようなもの です。
Page Template の Edit タブにある Expand macros when editing オプションをチェックすると、使っているマクロすべてがテンプレートのソースに展開されます。WYSIWYG の編集ツールではなく、ZMI で編集している場合、編集時にはマクロは展開しない方が便利です 新規に作成されたテンプレートの場合、マクロを展開しないのがデフォルトの設定です。 一方で、WYSIWYG ツールの場合には、完全なページを編集できるように、マクロを展開した方が都合がよい場合が多いでしょう。この場合、ページを編集する前に、 Expand macros... チェックボックスをチェックします。
スロット ( Slot ) を使う
マクロを使う際、その一部を「継承」できればより便利でしょう。 テンプレートを使う際に埋めるマクロに slots を定義することでマクロの一部を継承できます。たとえば、 サイドバーのマクロを考えてみましょう。 :
<div metal:define-macro="sidebar">
Links
<ul>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/support">Support</a></li>
<li><a href="/contact">Contact Us</a></li>
</ul>
</div>
このマクロでもいいのですが、いくつかのページのサイドバーにもう少し情報を追加したいとします。スロットを使うのがひとつの方法です。 :
<div metal:define-macro="sidebar">
Links
<ul>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/support">Support</a></li>
<li><a href="/contact">Contact Us</a></li>
</ul>
<span metal:define-slot="additional_info"></span>
</div>
このマクロを使う際、スロットを埋めるかどうかをを選択できます。以下のようにします。 :
<p metal:use-macro="container/master.html/macros/sidebar">
<b metal:fill-slot="additional_info">
Make sure to check out our <a href="/specials">specials</a>.
</b>
</p>
このテンプレートがレンダリングされると、このサイドバーにはスロットとして用意したいくつかの情報が含まれます :
<div>
Links
<ul>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/support">Support</a></li>
<li><a href="/contact">Contact Us</a></li>
</ul>
<b>
Make sure to check out our <a href="/specials">specials</a>.
</b>
</div>
スロットを定義している span エレメントが スロットを埋めている
b エレメントにどう置き換わっているかに注意してください。
デフォルトのデザインをカスタマイズする
スロットの一般的な使い道は、カスタマイズ可能なデフォルトのデザインを提供することです。一つ前のセクションにおけるスロットの例では、スロットは単に空の span エレメントを定義しただけでした。しかし、スロットの定義で、デフォルトのデザインを提供することもできます。たとえば、次のような修正版のサイドバーマクロを考えて見ましょう :
<div metal:define-macro="sidebar">
<div metal:define-slot="links">
Links
<ul>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/support">Support</a></li>
<li><a href="/contact">Contact Us</a></li>
</ul>
</div>
<span metal:define-slot="additional_info"></span>
</div>
これで、サイドバー全体をカスタマイズできます。サイドバーのリンクを再定義するには、 links スロットを埋めます。しかし、links
スロットを埋めなければ、デフォルトのリンクがスロット内部に現れます。
スロットの中にスロットを定義することで、このテクニックをさらに活用することもできます。こうすれば、デフォルトのデザインを細かく継承できます。 スロット内でスロットを定義したサイドバーマクロはこのようになります。 :
<div metal:define-macro="sidebar">
<div metal:define-slot="links">
Links
<ul>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/support">Support</a></li>
<li><a href="/contact">Contact Us</a></li>
<span metal:define-slot="additional_links"></span>
</ul>
</div>
<span metal:define-slot="additional_info"></span>
</div>
サイドバーの links をカスタマイズしたければ、 links スロットを埋めて完全にリンクを継承するか、additional_links スロットを埋めて、デフォルトのリンクに続けていくつかさらにリンクを挿入できます。
METAL と TAL を組み合わせる
同じエレメント中で METAL ステートメントと TAL ステートメントを混ぜることができます。たとえば、 :
<ul metal:define-macro="links"
tal:repeat="link context/getLinks">
<li>
<a href="link url"
tal:attributes="href link/url"
tal:content="link/name">link name</a>
</li>
</ul>
この場合、getLinks はリンク・オブジェクトを集める(架空の)スクリプトで、おそらく Catalog のクエリーを使っているでしょう。
METAL ステートメントは TAL ステートメントよりも先に評価されるのでコンフリクトはありません。この例は、スロットを使わずにマクロをカスタマイズしているという点でも興味深いといえます。マクロはリンクを決定するために getLinks スクリプトを呼び出しています。したがって、サイト内の別の場所で getLinks を再定義することで、サイトのリンクをカスタマイズできます。
サイトの異なる場所のルック&フィールをカスタマイズする最良の方法を見つけ出す作業はいつも簡単というわけではありません。 一般に、プレゼンテーションの要素を継承するにはスロットを使い、コンテンツを動的に出力するにはスクリプトを使うべきです。リンクの例では、リンクがコンテンツなのかプレゼンテーションなのかを議論する余地があるかもしれません。 スクリプトを使えばもっと柔軟に解決できるでしょう。特に、サイトがリンクのコンテント・オブジェクトを含んでいる場合はそうです。
ページ全体を定義するマクロ
マクロは、複数のページ間で共有する部品を部分として利用できるだけでなく、ページ全体を定義するのにも活用できます。ページ全体を定義するマクロを作るにはスロットを活用します。 ページ全体を定義するマクロの例はこのようになります。 :
<html metal:define-macro="page">
<head>
<title tal:content="context/title">The title</title>
</head>
<body>
<h1 metal:define-slot="headline"
tal:content="context/title">title</h1>
<p metal:define-slot="body">
This is the body.
</p>
<span metal:define-slot="footer">
<p>Copyright 2001 Fluffy Enterprises</p>
</span>
</body>
</html>
このマクロでは headline, body, footer の3つのスロットが定義されています。 ヘッドラインの内容を動的に決めるために、
headline スロットで TAL 文が使われていることに注意してください。
このようにすれば、ひとつのマクロをさまざまなタイプのコンテンツや、サイト上の異なる場所で利用できます。 たとえば、ニュース・アイテム用のテンプレートがこのマクロを使ったときの例は以下のようになるでしょう。 :
<html metal:use-macro="container/master.html/macros/page">
<h1 metal:fill-slot="headline">
Press Release:
<span tal:replace="context/getHeadline">Headline</span>
</h1>
<p metal:fill-slot="body"
tal:content="context/getBody">
News item body goes here
</p>
</html>
このテンプレートは "Press Release" という文字列を埋め込むために
headline スロットを再定義し、現在のオブジェクトで
getHeadline メソッドを呼び出しています。
現在のオブジェクトで getBody メソッドを呼び出すために、 body
スロットも再定義しています。
このアプローチはとても強力で、page マクロを変更すると、プレスリリース・テンプレートも自動的に更新されます。
たとえば、ページの本文をテーブルにおいたり、左にサイドバーを追加したりすると、プレス・リリースのテンプレートには、自動的に追加した表示用の部品が埋め込まれます。
これは、ページの見た目を制御する上で、 DTML でよく見かける
standard_html_header, standard_html_footer でヘッダとフッタを埋め込む方法よりもずっと柔軟な解決策です。
実際、Zope には head スロットと body スロットのあるページ全体のマクロを含んだ、standard_template.pt という名前のページテンプレートがルートフォルダについてきます。
このマクロを使うには、テンプレートを以下のようにします。 :
<html metal:use-macro="context/standard_template.pt/macros/page">
<div metal:fill-slot="body">
<h1 tal:content="context/title">Title</h1>
<p tal:content="context/getBody">Body text goes here</p>
</div>
</html>
standard_template.py マクロの利用方法は、ページ全体を定義したのマクロの利用方法とよく似ています。些細なことですが、唯一指摘しておきたいのは、マクロを特定するために使われるパスです。
この例では、パスは context で始まっています。
つまり、 Zope はテンプレートが使われているオブジェクト( context )から、獲得を使って、standard_template.pt オブジェクトを探しているということです。
このようにすれば、あちこちにカスタマイズした standard_template.pt オブジェクトを設置して、テンプレートの見た目を変更できるようになります。
context だけでなく、root, container から、マクロのパスを始めることもできます。
コンテンツとテンプレートを組み合わせる
一般に、Zope はコンテンツ、プレゼンテーション、ロジックのコンポーネントをサポートしています。 Page Template はプレゼンテーションのコンポーネントであり、コンテンツ・コンポーネントを表示するのに利用できます。
Zope にはいくつかのコンテンツ・コンポーネントが付属しています: ZSQL メソッド、ファイル, 画像。DTML Document および Method は純粋にコンテンツ・コンポーネントというわけではありません。コンテンツを持ち、DTML コードを実行するからです。 テキスト質のコンテンツには File が使えます。 ファイルサイズが 64k 以下で、テキストを含んでいるなら、ファイルのコンテンツを編集できるからです。 しかし、File オブジェクトはかなり基本的なので、必要とされる機能やメタデータがすべて用意されていないかもしれません。
Zope の Content Management Framework (CMF) は豊富なコンテンツ・コンポーネントを提供することで、この問題を解決しています。 CMF は Zope でコンテンツ管理をするために作られたフレームワークです。 ワークフロー、スキン、コンテンツオブジェクトを含むあらゆる機能を提供します。 CMF は Page Template を多用しています。 Zope の将来のリリースではCMFに影響を受けた技術が含まれることになるでしょう。