このWikiForumを提供しているWikiEngine。 Perlで実装されていて、ページの部分編集、RCSによる複数世代のバージョン管理等、いたって高機能。
FreeBSDのportsにも、japanese/wikickerとして加えられています。
- NaneyOrgWiki: WiKicker - WiKicker 公式サイト
Tips
robots.txt for Query String
サイトを外部からも閲覧できるようにしていると、検索エンジンのロボット(クローラー)が根こそぎコンテンツを持っていこうとします。 あまり、持っていっても意味がないと思われるページ*1は、robots.txtに書いておいて持っていかないよう、お願いしておいた方が無難*2。 ちょうど、WiKickerの場合、Wikiの編集画面など、閲覧者には二次的な内容のコンテンツは、URIにクエリ文字列が付く形を とっています。従って、robots.txtに、
User-agent: * Disallow: /wiki?
としておけば、メジャーなクローラー*3なら、それを汲み取って、クエリ文字列が続くURIにはアクセスしてきません。
あるいは、編集画面へのアクセスだけを拒否するには、
User-agent: * Disallow: /wiki?edit=
としておけばOK。
.htaccess
※セキュリティ上の都合で、内容の一部を改変している場合があります。
<Files wiki>
SetHandler speedycgi-script
# SetHandler cgi-script
</Files>
####
RewriteEngine on
RewriteCond %{QUERY_STRING} (?:diff|edit|name|rlog|search)=
RewriteCond %{HTTP_COOKIE} !WiKicker=
RewriteRule ^wiki$ - [F,L]
RewriteCond %{REQUEST_METHOD} POST
RewriteCond %{HTTP_REFERER} !^(?:http://www\.xdelta\.net/wiki(?:[/|\?].+))?$
RewriteCond %{HTTP_COOKIE} !WiKicker=
RewriteRule ^wiki$ - [F,L]
RewriteRule ^wiki/(.+)/$ /wiki/$1.html [R,L]
RewriteRule ^wiki/(?:.+?/)?favicon\.ico$ /favicon.ico [L]
RewriteRule ^wiki/.+\.(?i:gif|ico|jpe?g|png)$ - [F,L]
一番下は、変なリクエストをしてくる一部Webブラウザ用。ディレクトリごとにfavicon.icoを取得しようとしたり、ディレクトリの解釈にバグがあると思われる。
ついでに、管理人がスパムと認識しているクローラーを拒否する設定例のいくつか
RewriteCond %{HTTP_USER_AGENT} ^User-Agent:
RewriteRule ^.*$ - [F,L]
# 全信協スパム
RewriteCond %{HTTP_USER_AGENT} "^Mozilla/4.0 \(compatible; MSIE 6\.0; Windows 98\)$"
RewriteCond %{SERVER_PROTOCOL} ^HTTP/1\.0$
RewriteCond %{HTTP_REFERER} ^$
RewriteRule ^.*$ - [F,L]
# 不正なリファラ
RewriteCond %{HTTP_REFERER} ^http://www\.xdelta\.net/wiki/(?:.*/)?$
RewriteRule ^wiki(?:[/?].*)?$ - [F,L]
# some referer spam
RewriteRule ^wiki/\+(?:.*?-\+-.*)-\+\.html$ - [F,L]
RewriteCond %{HTTP_REFERER} "black-?jack|casino|gambling|poker|roulette|texas-hold-?em" [OR]
RewriteCond %{HTTP_REFERER} "loans" [OR]
RewriteCond %{HTTP_REFERER} "diet-pill||phentermine|via[gq]rr?a"
RewriteRule ^.*$ - [F,L]
中段は、いわゆる全信協スパムクローラー*4と呼ばれるものを意図して弾く設定。アクセス元には触れてないので、このままの設定だと、もしかすると無関係な方も拒否されるかも知れないので、十分注意する必要あり。
最近は、たどって来たページに対して自動的にリンクを張るページを対象に、宣伝目的のHTTP-REFERERを送信してくるリファラスパムと呼ばれるものも増えている*5ので、これも、.htaccess等で弾いておいた方が無難。
スパム対策
可能なかぎり.htaccess等を駆使してなるべくCGIが起動される前に、スパム目的と思しきクライアントは弾きたいものであるが、すり抜けてスパム投稿してきた場合、厄介である*6。
しかし、WiKickerには、あらかじめ書き込み禁止ワードを設定する機能が付いており、これによってある程度、不本意な書き込みを制限することができる。
書き込み禁止パターンファイルの行頭にqrで始まる行があれば、Perl正規表現qr//*7であると見なされ、例えば、
qr/black[-\s]?jack|casino|gambling|poker|roulette|texas[-\s]hold[-\s]?em/i qr/loans/i qr/diet[-\s]pill|phentermine|via[gq]rr?a/i
のように、書き込み禁止パターンファイル内で、正規表現を使用することもできる。
Perl 5.6.2→5.8.7移行メモ
FreeBSDのportsでインストールされるPerlのデフォルトのバージョンも、v5.8.7になったので、 そろそろ、Webサーバーの方のPerlもv5.6からアップグレードしなくてはならないかなと思い、試みてみました。
ところがそのまま移行しただけでは、WiKickerの方が、
Byte order is not compatible at blib/lib/Storable.pm
と言われて起動できない。
いろいろ調べたところ、FreeBSDのPerlは、v5.8から64bit Intengerでビルドされるよう*8*9なので、Storableモジュールのデータの互換性がとれないのが原因らしい。
(wikicker.database.directory)/info/basic 以下のファイルを削除すれば、解決するようだけど、そうすると、各ページのサマリや、更新履歴が空白になってしまう…。
で、解決策として、古いPerl上で、Data::Dumperを使って該当データをPerlのソースコードにダンプし、それを新しい方のPerlで読み込んで、そのStorableで書き戻すというやり方*10を試みてみました。
まず、
#!/usr/local/bin/perl5.6.2 -w
use strict;
use Storable qw(thaw);
use Data::Dumper;
$Data::Dumper::Indent = 0; # 出力に改行をはさまない
$Data::Dumper::Useqq = 1; # エスケープして出力
my $db_dir = '/SOURCE_DATABASE_DIR/info/basic'; ### 適宜修正
opendir(DIR, $db_dir) || die "can't open dir $db_dir.\n";
while (my $key = readdir(DIR)) {
next if ($key =~ m/^(?:\.){1,2}$/);
my $value;
if (open(FILE, "$db_dir/$key")) {
binmode(FILE);
local $/;
$value = <FILE>;
close(FILE);
} else {
die "can't read file $key.\n";
}
my $record = \%{thaw($value)};
print Data::Dumper->Dump( [ $key, $record ],
[ qw(key record) ] ) . "\n";
}
close(DIR);
をdb-dump.plという名前で保存して、古いPerl上で、
% perl5.6 db-dump.pl > dump.txt
と実行し、dump.txtを作成する。
そして、新しい方のPerlで、
#!/usr/local/bin/perl5.8.7 -w
use strict;
use Storable qw(freeze);
my $output_dir = '/NEW_DATABASE_DIR/info/basic'; ### 適宜修正
while (defined(my $line = <STDIN>)) {
my($key, $record);
eval $line;
die "dame\n" if $@;
my $frozen = freeze($record);
if (open(FILE, "> $output_dir/$key")) {
binmode(FILE);
local $/;
print FILE $frozen;
close(FILE);
} else {
die "can't write file.\n";
}
}
をdb-restore.plという名前で保存し、
% cat dump.txt | perl5.8.7 db-restore.pl
と実行すればOK。指定したディレクトリに復元されます。これで、サマリ等も見れるようになりました。
あと、IPC::ShareLiteを使用して、RecentLogの機能を有効にしたい場合は、通常のままだとPerlがコアダンプしてしまう不具合があるので、lang/perl5.8を-DWITHOUT_PERL_MALLOC付きでビルドするか、もしくは、
でのパッチをdevel/p5-IPC-ShareLiteに適用する必要があります*11。
*8そういえば、Perl 5.8では「$M = 0x100000000」はOKなのに、5.6上では「Integer overflow in hexadecimal number at 」とか怒られた。「$M = 0xFFFFFFFF + 0x00000001」と小分けにすると大丈夫なようだが?
*9DON's Diaryに書いてあったけど、lang/perl5.8を「WITHOUT_PERL_64BITINT」付きでビルドすると、従来のデータとの互換性が保てるかも知れない。実際のところ、このオプションを付けてビルドしたら、上記Perl 5.6と5.8との間のStorableを使用したキャッシュファイルの互換性の問題は解決した(データの変換を要せずに、ちゃんとサマリ等も表示できる。もっとも、出来ると言うだけであって、根本的な解決とは思えないが…。それに特定のアプリケーションのために、ビルド方法を変更すると、他のアプリケーションで問題が発生してくる可能性もあるし。やっぱり、標準ビルドの5.8対応を考えた方が無難)。
*10「$Storable::interwork_56_64bit = 1」とすると、Perl 5.6形式のデータも読めるそうだが、そうすると、「Out of memory during "large" request for 2147487744 bytes, total sbrk() is 901120 bytes at ../../lib/Storable.pm」とか怒られたので。
*11現時点でのp5-IPC-ShareLite-0.09のバージョンの場合。
データベースから直接、Google Sitemapを生成
WiKickerのデータベースファイルから、直接、Google Sitemapファイルを生成するスクリプトを書いてみました*12。現在は、当該CGIスクリプトにアクセスがあるとgzip圧縮されたXMLファイルを生成する仕様で、定時にcronでcurlを起動してSitemapファイルを生成することを想定しています。
PageRanking
上記サイトマップを元に各コンテンツのPageRankを調べたい時には、
curl -s http://www.somedomain.com/wiki-sitemap.xml.gz | gzip -dc | \ perl -nle 'm"<loc>(.+?)</loc>" && print $1' | gprank.pl | sort
とする。興味本位に、検索エンジンのインデックス状況を知りたい時には、結構便利。
- gprank.pl - 詳細はGoogle/PageRank参照。
*12クローラーの巡回に頼らずに、新規ページや更新部分を伝えることができるという試みには賛同するので。
参照しているページ自体へのAutomaticリンクを取り除く
以下のJavaScriptをフッタファイルに書いておけば、新しめのWebブラウザなら、取り除けると思います。
var ShortNameAttribute = "[[value:ShortNameAttribute]]";
function removeSameLinksAsThePage(element) {
if (! element.hasChildNodes)
return;
for (var i = 0; i < element.childNodes.length; i ++) {
var e = element.childNodes[i];
if (e.nodeType == 1 && e.tagName.match(/^A$/i)
&& e.getAttribute('class') == 'auto') {
if (e.hasChildNodes && e.firstChild.nodeType == 3
&& e.firstChild.nodeValue == ShortNameAttribute) {
element.replaceChild(document.createTextNode(ShortNameAttribute), e);
}
} else {
removeSameLinksAsThePage(e);
}
}
}
if (document.getElementById && document.createElement) {
var elements = document.getElementsByTagName('div');
for (var i = 0; elements.length > i; i ++) {
if (elements[i].getAttribute('class') == 'main') {
removeSameLinksAsThePage(elements[i]);
}
}
}
その他、備忘用
- WiKickerでのInterWikiを定義するページ名は、InterWikiDefinitionでなければならない模様。それ以外のページ内で定義しても、(wikicker.database.directory)/cache/interwiki/以下が更新されない。
- [[anchor:ANCHOR]]などとしてアンカー文字を指定せず、空の<a name="ANCHOR"></a>をたくさん生成すると、SpeedyCGI使用下において、サーバが応答を返さなくなり、プロセスだけが残ってしまう場合がある。SpeedyCGIを使わずに、PerlでCGIを起動する場合に置いては、このような問題は発生しないので、おそらく変数の初期化が関係しているのだと思われる。
- 入り組んだリストや見出しは、3段階まで使える。それより多いと、3段階以降の部分は単なる文字と認識される。
- memcachedを使っていると、raw viewの挙動が変な時がある。memcachedを再起動すれば直る。
- SpeedyCGIを使っていると、RssPage等のviewがエラーになる。サイトのporopertiesファイルに、view.rss: WiKicker::CGI::View::XML::Rss等と明示しておく必要がある。
文頭に「:」(コロン)で始まる行では、定義リストを作成する。すなわち、
:定義語:定義の説明
は、
<dl> <dt>定義語</dt> <dd>定義の説明</dd> </dl>
のように展開される。しかし、定義語の部分に、「:」(コロン)を使用することはできない。 例えば、定義語の箇所に、[[anchor:アンカー]]を挿入しようとしても、ブラケット内に含まれる「:」に邪魔されて、意図した通りに機能しないので、便宜上、dd部分に挿入する必要がある。