Skip to content

Text::Xslate::Manual::Cookbook.ja

gardejo edited this page Nov 18, 2011 · 1 revision

Text::Xslate::Manual::Cookbook.ja

Text::Xslate::Manual::Cookbookの日本語訳です。

Text::Xslateのバージョン1.5007から訳出しました。

名称

Text::Xslate::Manual::Cookbook - Xslateテンプレートの料理法

説明

このXslateクックブックは、Xslateの機能を紹介するレシピです。

レシピ

HTMLフォームを管理する方法

HTMLフォームの管理は、ウェブアプリケーションにおける重要な関心事です。 テンプレートを用いて自分自身でどうにかするよりも、HTMLフォームを管理するためのモジュールを使うとよいでしょう。このセクションでは、FillInFormとHTMLフォームビルダーという、二つの基本的な解決法を提案します。

いずれの解決法も、 セキュリティーホール を作り込みやすい mark_rawフィルター をテンプレート内で使うべきではありません。 その代わりに、 Text::Xslate からエクスポートできる mark_raw関数 をアプリケーションコードで呼び出すことを考慮すべきです。

FillInFormを使う

HTMLフォームを管理するための一つの解決法は、ブロックフィルター文法を用いて、FillInFormモジュールを使うことです。

HTML::FillInForm を使ったコードの例です:

#!perl -w
use strict;
use Text::Xslate qw(html_builder);

use HTML::FillInForm; # HTML::FillInForm::Liteでもよいです

sub fillinform {
    my($q) = @_;
    my $fif = HTML::FillInForm->new();
    return html_builder {
        my($html) = @_;
        return $fif->fill(\$html, $q);
    };
}

my $tx = Text::Xslate->new(
    function => {
        fillinform => \&fillinform,
    },
);

my %vars = (
    q => { foo => "<埋め込まれた値>" },
);
print $tx->render_string(<<'T', \%vars);
FillInForm:
: block form | fillinform($q) -> {
<form>
<input type="text" name="foo" />
</form>
: }
T

出力はこうなります:

FillInForm:
<form>
<input type="text" name="foo" value="&lt;埋め込まれた値&gt;" />
</form>

HTML::FillInForm::Litefillinform 関数を提供しているので、さらに簡素にすることができます:

use HTML::FillInForm qw(fillinform);

my $tx = Text::Xslate->new(
    function => { fillinform => html_builder(\&fillinform) },
);

詳細については、HTML::FillInFormHTML::FillInForm::Liteもご覧ください。

HTMLフォームビルダーを使う

HTMLフォームを管理するためのもう一つの解決法は、フォームビルダーを使うことです。 この場合、HTMLパーツで mark_raw() を適用するだけで済みます。

HTML::Shakanを使ったPSGIアプリケーションの例です:

#!psgi
use strict;
use warnings;
use Text::Xslate qw(mark_raw);
use HTML::Shakan;
use Plack::Request;

my $tx = Text::Xslate->new();

sub app {
    my($env) = @_;
    my $req  = Plack::Request->new($env);

    my $shakan = HTML::Shakan->new(
        request => $req,
        fields  => [ TextField(name => 'name', label => 'あなたの名前: ') ],
    );

    my $res = $req->new_response(200);

    # テンプレート内ではなく、ここでmark_rawしてください
    my $form = mark_raw($shakan->render());
    $res->body( $tx->render_string(<<'T', { form => $form }) );
<!doctype html>
<html>
<head><title>フォームの構築</title></head>
<body>
<form>
<p>
フォーム:<br />
<: $form :>
</p>
</body>
</html>
T
    return $res->finalize();

}

return \&app;

出力はこうなります:

<!doctype html>
<html>
<head><title>フォームの構築</title></head>
<body>
<form>
<p>
フォーム:<br />
<label for="id_name">あなたの名前:</label>
<input id="id_name" name="name" type="text" value="&lt;Xslate&gt;" />
</p>
</body>
</html>

詳細については、HTML::Shakanもご覧ください。

Kolon文法でTemplate ToolkitのWRAPPER機能を使用する方法

WRAPPER ディレクティブのスーパーセットである、テンプレートの重ね掛け(template cascading)を使ってください。

wrapper.tx:

<div class="wrapper">
block content -> { }
</div>

content.tx

: cascade wrapper

: override content -> {
    Hello, world!
: }

出力はこうなります:

<div class="wrapper">
    Hello, world!
</div>

テンプレートの重ね掛け

Xslateは、ブロックモディファイヤーによってテンプレートを拡張することができる、 テンプレートの重ね掛け(template cascading) をサポートしています。 これは伝統的なテンプレートのインクルード(包摂)のようなものですが、より強力なものとなっています。

この機構はテンプレートの継承(template inheritance)とも呼ばれます。

Text::Xslate.ja/"Template cascading"もご覧ください。

インクルードパスへ__DATA__セクションをマップする方法

Data::Section::Simple と、 $file_name => $content のマッピングを含むハッシュリファレンスを受け容れる、 new()path オプションを使ってください。

use Text::Xslate;
use Data::Section::Simple;

my $vpath = Data::Section::Simple->new()->get_data_section();
my $tx = Text::Xslate->new(
    path => [$vpath],
);

print $tx->render('child.tx');

__DATA__
@@ base.tx
<html>
<body><: block body -> { :>default body<: } :></body>
</html>
@@ child.tx
: cascade base;
: override body -> {
child body
: } # endblock body

この機能はText::MicroTemplate::DataSectionから着想したものであり、Mojoliciousから引用したものです。

Data::Section::SimpleText::MicroTemplate::DataSectionMojoliciousもご覧ください。

XSSにならないように、JavaScriptのセクションへデータ入れ込む方法

XslateがエスケープするのはHTMLのメタ文字だけなので、 <script> ... </script> セクションでデータを入力する場合、JavaScriptのメタ文字はあなた自身でエスケープしなければなりません。

JSON モジュールは、"</script>"のようないくつかのメタ文字をエスケープしないため、ふさわしくありません。

XSSを避けてJavaScriptのエスケープを安全に行うための、実績あるユーティリティーを使うとよいでしょう。 JavaScript::Value::Escapeが、この目的のための一助になります。

my $tx = Text::Xslate->new(
    module => ['JavaScript::Value::Escape' => [qw(js)]],
    );

my %params = (
    user_input => '</script><script>alert("XSS")</script>',
    );

print $tx->render_string(<<'T', \%params);
<script>
document.write('<: $user_input | html | js :>');
var user_input = '<: $user_input | js :>';
</script>
T

さらに複雑な場合については、セキュリティーの専門家に助言を求めた方がよいでしょう。

XSSにならないように、構造化されたテキストをHTMLに入れ込む方法

構造化されたテキストを安全な方法で解析するための、銀の弾丸はありません。 それをしたい場合には、セキュリティーの専門家に助言を求めた方がよいでしょう。

いくつかのCPANモジュールがその助けになります。【訳注:helpのlが脱字】一例として、String::Filterをご覧ください。

テンプレートでローカライズを管理する方法

_() を含むどんな関数でも 登録することができます。それには特別な技巧は必要ありません。

例えば:

use I18N::Handle;
# I18N::Handleは、グローバル名前空間に対して、ロケールの関数である"_"をインストールします。
# ( *_ シンボルがグローバルであることを忘れないでください)
I18N::Handle->new( ... )->speak('zh_tw');

my $tx = Text::Xslate->new(
    function => {
        _ => \&_,
    },
);

そして、テンプレートでは以下のように記します:

<: _('Hello %1', $john ) :>

I18N::HandleApp::I18Nもご覧ください。

fork()する前にテンプレートを読み込む方法

Preforkモデルのアプリケーションでテンプレートを読み込むことは良いアイデアです。 与えられたパスにあるすべてのテンプレートを読み込む例を示します:

use File::Find;

my $path = ...;
my $tx = Text::Xslate->new(
    path      => [$path],
    cache_dir =>  $path,
);

# 事前読み込みファイル
find sub {
    if(/\.tx$/) {
        my $file = $File::Find::name;
        $file =~ s/\Q$path\E .//xsm; # パス名の是正
        $tx->load_file($file);
    }
}, $path;

# forkして描画する……

事前処理用のテンプレートを実現する方法

パースする前にコンテンツに対して処理を行うために、Text::Xslateの slurp_template() をオーバーライドすることができます。

{
    package MyTemplate;
    use parent qw(Text::Xslate);

    sub slurp_template {
        my($self, $input_layer, $fullpath) = @_;

        my $content = $self->SUPER::slurp_template(
                        $input_layer,
                        $fullpath,
        );
        ### ここに $content に対する処理を書きます ###
        return $content;
    }
}

第一引数の $self はText::Xslateのインスタンスで、第二引数は input_layer オプションの引数(デフォルトでは :utf8 )で、第三引数はロードするテンプレートファイルのフルパスです。

slurp_templateテキスト文字列バイト文字列 のいずれかを返すでしょう。

このフックは、テンプレートの内容に対して事前処理を行うために提供されていることに気を付けてください。つまりメモリー上にテンプレートを読み込むためには、 SUPER::slurp_template 呼び出す必要があります。

あわせて読みたい

Text::Xslate.ja

Text::Xslate::Manual.ja

Text::Xslate::Manual::FAQ.ja