| Tripletail documentation | Contained in the Tripletail distribution. |
Tripletail::TagCheck - HTMLのタグのチェック
my $tc = $TL->newTagCheck;
$tc->setAllowTag(':BR;SMALL;STRONG');
my $checked_html = $tc->check('<font size="+7">foo</font><small>bar</small>');
if ($check_html eq 'foo<small>bar</small>') {
# true
}
HTML のタグのチェックを行い、不必要なタグを削除する。
$checker = $TL->newTagCheck
Tripletail::TagCheck オブジェクトを作成。
$checked_html = $checker->check($html)
渡されたHTMLを処理し、その結果を返す。
$checker->setTagBreak('line')
'none', 'line', 'block'が指定可能。デフォルトは'line'。 タグを自動で閉じるかどうかを設定する。
自動で閉じない。
行末、または文字列の末尾で閉じる。
改行が二つ続いた位置、または文字列の末尾で閉じる。
$checker->setAutoLink(1)
テキスト中に含まれるURLを自動で<a href="...">でリンクにするかどうか。 0の場合、リンクにしない。 1の場合、リンクにする。
デフォルトは1。
$checker->setAllowTag(':HR:BR;STRONG')
使用を許可するタグを指定する。ここで指定されなかったタグは許可されていないもの として、"<"と">"を"<"と">"にエンコードする。書式は次の通り。
":TAG"または";TAG"で一つのタグを表す。 このような指定を任意の個数だけ繋げる事が出来る。
":"で指定されたタグは、その閉じタグの存在が禁止される。 禁止された閉じタグは削除される。
";"で指定されたタグは、その閉じタグの存在が要求される。 閉じタグが存在しない場合は、setTagBreakで指定された方法に従って 閉じタグが追加される。
";A(HREF,TARGET)"のように"(...)"で属性の種類を制限可能。 列挙しなかった属性は削除される。
";TR[TD,TH]"のように"[...]"で子要素の種類を制限可能。 列挙しなかった子要素はエスケープされる。また、このようにして子要素の種類を 制限した場合は、要素がテキストを持つ事も禁止される。禁止されたテキストは 削除される。";TR[TD,TH,*]"のように要素名として"*"を指定すると、子要素としての テキストが禁止されない。
";TD{none}"のように"{...}"でsetTagBreakの指定を部分的に上書き可能。 このようにしてTagBreakが例外指定された要素については、setTagBreakでの設定が 適用されない。
上記"(...)", "[...]", "{...}"のオプションは任意の順序で同時に指定する事が可能。 但し同じ種類のオプションを一つのタグに対し複数個指定する事は出来ない。
デフォルト値は次の通り:
":HR:BR;S;STRONG;I;U;EM;A(HREF,TARGET,NAME)"
$checker->addAllowTag('!EMBED;TABLE[TR];TR[TD,TH];TD{none};TH{none}')
既存のタグの許可情報が消されない事を除き、setAllowTagと同様。 また、このメソッドでのみ意味のある指定方式として、 "!TAG"のように特定のタグを改めて禁止する事が出来る。
$checker->setATarget('_blank')
a要素のtarget属性を書換えるかどうか。undefを指定すると書換えが行われない。 setATargetを実行しない状態では'_blank'として設定されている。
TagCheck は入力される文字列が HTML の文法的に正しい(well-formed である) 事を前提にしています。 出力は、文法的に正しく解釈しようとした結果で作られるため、 入力したタグの形と変わることがあります。
Copyright 2006 YMIRLINK Inc.
This framework is free software; you can redistribute it and/or modify it under the same terms as Perl itself
このフレームワークはフリーソフトウェアです。あなたは Perl と同じライセンスの 元で再配布及び変更を行うことが出来ます。
Address bug reports and comments to: tl@tripletail.jp
HP : http://tripletail.jp/
| Tripletail documentation | Contained in the Tripletail distribution. |
# ----------------------------------------------------------------------------- # Tripletail::TagCheck - HTMLã®ã¿ã°ã®ãã§ã㯠# ----------------------------------------------------------------------------- package Tripletail::TagCheck; use strict; use warnings; use Tripletail; 1; sub _new { my $pkg = shift; my $this = bless {} => $pkg; $this->{target} = undef; $this->{allowed} = {}; # {è¦ç´ å => Tripletail::TagCheck::TagInfo} $this->{autolink} = undef; $this->{tagbreak} = undef; $this->setATarget('_blank'); $this->setAllowTag(qq{ :HR :BR ;B ;S ;STRONG ;I ;U ;EM ;A(HREF,TARGET,NAME) }); $this->setAutoLink(1); $this->setTagBreak('line'); $this; } sub setATarget { my $this = shift; my $target = shift; if(ref($target)) { die __PACKAGE__."#setATarget: arg[1] is a reference. [$target] (第1弿°ããªãã¡ã¬ã³ã¹ã§ã)\n"; } $this->{target} = $target; $this; } sub setAllowTag { my $this = shift; my $list = shift; if(!defined($list)) { die __PACKAGE__."#setAllowTag: arg[1] is not defined. (第1弿°ãæå®ããã¦ãã¾ãã)\n"; } elsif(ref($list)) { die __PACKAGE__."#setAllowTag: arg[1] is a reference. [$list] (第1弿°ããªãã¡ã¬ã³ã¹ã§ã)\n"; } %{$this->{allowed}} = (); $this->addAllowTag($list); } sub addAllowTag { my $this = shift; my $list = shift; if(!defined($list)) { die __PACKAGE__."#addAllowTag: arg[1] is not defined. (第1弿°ãæå®ããã¦ãã¾ãã)\n"; } elsif(ref($list)) { die __PACKAGE__."#addAllowTag: arg[1] is a reference. [$list] (第1弿°ããªãã¡ã¬ã³ã¹ã§ã)\n"; } while($list =~ s/([:;!])(\w+)([^:;\s]*)//) { my $type = $1; my $tag = lc($2); my $opt = lc($3); if($type eq '!') { # åé¤ delete $this->{allowed}{$tag}; next; } my $info = $this->{allowed}{$tag} = Tripletail::TagCheck::TagInfo->new($tag); if($type eq ':') { $info->mustBeEmpty(1); } if($opt =~ s/\((.*?)\)//) { # å¯è½ãªå±æ§ã®ãªã¹ã $info->setAllowedAttributes(split /,/, $1); } if($opt =~ s/\[(.*?)\]//) { # å¯è½ãªåè¦ç´ ã®ãªã¹ã $info->textIsAllowed(0); $info->setAllowedChildren(split /,/, $1); } if($opt =~ s/\{(.*?)\}//) { # tag breakã®ãªã¼ãã¼ã©ã¤ã $info->tagbreak($1); } } $this; } sub setAutoLink { my $this = shift; my $flag = shift; if(ref($flag)) { die __PACKAGE__."#setAutoLink: arg[1] is a reference. [$flag] (第1弿°ããªãã¡ã¬ã³ã¹ã§ã)\n"; } $this->{autolink} = $flag; $this; } sub setTagBreak { my $this = shift; my $type = shift; if(!defined($type)) { die __PACKAGE__."#setTagBreak: arg[1] is not defined. (第1弿°ãæå®ããã¦ãã¾ãã)\n"; } elsif(ref($type)) { die __PACKAGE__."#setTagBreak: arg[1] is a reference. [$type] (第1弿°ããªãã¡ã¬ã³ã¹ã§ã)\n"; } elsif($type ne 'line' && $type ne 'block' && $type ne 'none') { die __PACKAGE__."#setTagBreak: invalid tag-break type: [$type] (TagBreakã®æå®ã䏿£ã§ã)\n"; } $this->{tagbreak} = $type; $this; } sub check { my $this = shift; my $html = shift; if(!defined($html)) { die __PACKAGE__."#check: arg[1] is not defined. (第1弿°ãæå®ããã¦ãã¾ãã)\n"; } elsif(ref($html)) { die __PACKAGE__."#check: arg[1] is a reference. [$html] (第1弿°ããªãã¡ã¬ã³ã¹ã§ã)\n"; } my $filter = $TL->newHtmlFilter( interest => [qr/.+/], # å ¨ã¦ã®ã¿ã°ã«èå³ããã filter_text => 1, ); $filter->set($html); my $open_stack = []; # éããã¾ã¾ã«ãªã£ã¦ããã¿ã°ã®ã¹ã¿ã㯠while(my ($context, $elem) = $filter->next) { if($elem->isText) { if($elem->str =~ m/\r?\n\r?\n/) { # æ¹è¡ãäºã¤ç¶ãã¦ããã®ã§ããã®ä½ç½®ã§ # blockã¾ãã¯lineã§tagbreakããã¿ã°ãéããã my $old = $elem->str; $old =~ s/(\r?\n\r?\n)/$this->__break(['block', 'line'] => $open_stack) . $1/e; $elem->str($old); } elsif($elem->str =~ m/\r?\n/) { # æ¹è¡ãããã®ã§ããã®ä½ç½®ã§lineã§tagbreakããã¿ã°ãéããã my $old = $elem->str; $old =~ s/(\r?\n)/$this->__break(['line'] => $open_stack) . $1/e; $elem->str($old); } # èªåãªã³ã¯ãæå¹ã«ãªã£ã¦ãããªãèªåãªã³ã¯å®è¡ã if($this->{autolink}) { if(@$open_stack && $this->{allowed}{lc($open_stack->[-1]->name)} && !$this->{allowed}{lc($open_stack->[-1]->name)}->isAllowedChild('a')) { # 親ã¿ã°ãAã¿ã°ã®åå¨ã許ãã¦ããªãã®ã§ãèªåãªã³ã¯ããªãã } else { $elem->str($this->__autoLink($elem->str)); } } # 親ã¿ã°ãããã¹ãã®åå¨ã許ãã¦ããªããã°ããããæ¶ãã if(@$open_stack && $this->{allowed}{lc($open_stack->[-1]->name)} && !$this->{allowed}{lc($open_stack->[-1]->name)}->textIsAllowed) { $context->delete; } } elsif($elem->isElement) { $elem->name =~ m!^(/?)(.+)$!; my $close = $1; my $name = lc($2); my $taginfo = $this->{allowed}{$name}; my $forbidden; if(!$taginfo) { # ãã®ã¿ã°ã¯ãããã許ããã¦ããªãã $forbidden = 1; } elsif($close && @$open_stack && lc($open_stack->[-1]->name) eq $name) { # èªåã®éãã¿ã° } elsif(@$open_stack && $this->{allowed}{lc($open_stack->[-1]->name)} && !$this->{allowed}{lc($open_stack->[-1]->name)}->isAllowedChild($name)) { # 親ã¿ã°ããã®ã¿ã°ã許ãã¦ããªãã $forbidden = 1; } if(!$forbidden && $close) { if($taginfo->mustBeEmpty) { # éãã¿ã°ã®åå¨ã許ããã¦ããªã $context->delete; $this->__close($name => $open_stack); next; } elsif(!grep {lc($_->name) eq $name} @$open_stack) { # 対å¿ããéå§ã¿ã°ãåå¨ããªãã $forbidden = 1; } else { # 対å¿ããæè¿ã®éå§ã¿ã°ãéããããäºã«ãã $this->__close($name => $open_stack); } } if($forbidden) { # 許ããã¦ããªãã¿ã°ã¯ã¨ã¹ã±ã¼ãããã # ä½ã親ã¿ã°ãããã¹ãã®åå¨ã許ãã¦ããå ´åã®ã¿ã $context->delete; if(@$open_stack && $this->{allowed}{lc($open_stack->[-1]->name)} && !$this->{allowed}{lc($open_stack->[-1]->name)}->textIsAllowed) { # 許ãã¦ããªã } else { $context->add($TL->escapeTag($elem->toStr)); } next; } # ãã®è¦ç´ ã®åå¨ã許ããã¦ãããªããããã¸æ¥ã¦ããã if(!$close) { # 屿§ã®ãã§ã㯠foreach my $attrkey ($elem->attrList) { if(!$taginfo->isAllowedAttribute($attrkey)) { # ãã®å±æ§ã¯è¨±ããã¦ããªãã®ã§æ¶ãã $elem->attr($attrkey => undef); } } # Aã¿ã°ã®å ´åã®ç¹å¥å¦çãtargetæå®ãããã°ãããè¨å®ããã if(lc($elem->name) eq 'a' && defined($this->{target})) { $elem->attr(target => $this->{target}); } # éããã¹ãã¿ã°ãªãã¹ã¿ãã¯ã«ããã·ã¥ if(!$taginfo->mustBeEmpty and !($elem->end and $elem->end eq '/')) { push @$open_stack, $elem; } } } } my $endtag = $this->__break(['block', 'line'] => $open_stack); $filter->toStr . $endtag; } sub __close { my $this = shift; my $name = shift; my $stack = shift; for(my $i = @$stack-1; $i >= 0; $i--) { if(lc($stack->[$i]->name) eq $name) { splice @$stack, $i, 1; last; } } } sub __break { my $this = shift; my $types = shift; my $stack = shift; my $result = ''; for(my $i = @$stack-1; $i >= 0; $i--) { my $taginfo = $this->{allowed}{lc($stack->[$i]->name)}; my $tagbreak = $taginfo ? $taginfo->tagbreak : undef; if(!$tagbreak) { $tagbreak = $this->{tagbreak}; } if(grep {$_ eq $tagbreak} @$types) { # å®éã«éãã $result = sprintf '%s</%s>', $result, $stack->[$i]->name; splice @$stack, $i, 1; } } $result; } sub __autoLink { my $this = shift; my $str = shift; $str =~ s{((?:https?|ftp|mailto)://[\x21-\x7e]+)}{ if(defined(my $target = $this->{target})) { sprintf '<a href="%s" target="%s">%s</a>', $TL->escapeTag($1), $target, $TL->escapeTag($1); } else { sprintf '<a href="%s">%s</a>', $TL->escapeTag($1), $TL->escapeTag($1); } }eg; $str; } package Tripletail::TagCheck::TagInfo; sub new { my $class = shift; my $tag = shift; my $this = bless {} => $class; $this->{tag} = $tag; $this->{must_be_empty} = undef; $this->{allowed_attributes} = undef; # 屿§å => 1 $this->{allowed_children} = undef; # è¦ç´ å => 1 $this->{is_text_allowed} = 1; $this->{tagbreak} = undef; # undefã§ãªããã°tag breakããªã¼ãã¼ã©ã¤ã $this; } sub mustBeEmpty { my $this = shift; if(@_) { $this->{must_be_empty} = shift; } $this->{must_be_empty}; } sub setAllowedAttributes { my $this = shift; $this->{allowed_attributes} = { map {$_ => 1} @_ }; $this; } sub isAllowedAttribute { my $this = shift; my $attr = shift; my $allow_attr = $this->{allowed_attributes}; if($allow_attr) { $allow_attr->{$attr}; } else { 1; } } sub setAllowedChildren { my $this = shift; $this->{allowed_children} = { map {$_ => 1} @_ }; if($this->{allowed_children}{'*'}) { $this->{is_text_allowed} = 1; delete $this->{allowed_children}; } $this; } sub isAllowedChild { my $this = shift; my $elem = shift; my $child = $this->{allowed_children}; if($child) { $child->{$elem}; } else { 1; } } sub textIsAllowed { my $this = shift; if(@_) { $this->{is_text_allowed} = shift; } $this->{is_text_allowed}; } sub tagbreak { my $this = shift; if(@_) { $this->{tagbreak} = shift; } $this->{tagbreak}; } __END__