Perl/CGI研究室 'PERL-LABO'

Perl/CGI研究室 'PERL-LABO' TOPへ
戻る(History.Back)

BBSの研究

研究内容

CGIプログラムの華、BBS=掲示板を作りましょう!

詳細

ここまで、アクセスカウンターとか、似たようなものですがページビューカウンターとかの 研究をしてきました。これらは、いわばウェブサイトの補佐です。 ページビューカウンターなんか、表にまったく出ずに密かに働きます (^^; それに比べて、メールフォームなどはだいぶインタラクティブな感じですが、 やはりCGIプログラムの華は掲示板。そう思うんです。トライしてみましょう!

結果

掲示板に必要なデータ

まずは書き込みのときに入力するデータから考えます。 まず、書き込みの本文ですよね。 これさえあれば掲示板は成り立ちますが、それじゃさすがに寂しいです。 ですから、加えて、書き込みの題名、それから、お名前。 この3つがあればとりあえずOKという感じでしょうか。 よくあるのはこれに加えてメールアドレスとウェブサイトURL。 メールアドレスは、連絡を取りたいときなどには便利だと思いますが、 ウェブ上にメールアドレスを公開すると迷惑メールが届いたりしますよね。 そういったことを意識せずにメールアドレスを入力しちゃったりすると損なので、 メールアドレスはとりあえず無しで。 あと、ウェブサイトURLですが、 このサイトの性格上、ウェブサイトを運営している方の書き込みとかあると 嬉しいので、URLの入力はできるようにしたいです。 ということで、書き込むときのデータとしては 「名前」「URL」「題名」「内容」の4つです。 これらをフォームで入力してもらって、CGIプログラム側で処理しましょう。 投稿フォームからのデータ受け取りはもちろん getformdata.pl でOKです。

続いて、データファイルに保存するときに付け加えるデータを考えます。 まず、書き込みの時間。これはあった方がいいですよね。 それから、IPアドレスとホスト名。これは、なにか嫌な書き込みがあったときに 特定の人の書き込みを禁止しちゃったりするのに使えます。 場合によっては、警察へ提出が必要になったり?! そんなことにはならないと思いますが、とりあえず保存しましょう。 あとは…?それだけかな?では、上で考えたデータに加えて 「書き込み時刻」「IPアドレスとホスト名」をファイルに保存しましょう。

データファイルの仕様

データはファイルに保存します。 どういう形式で保存するか、ですが、例によって <> 区切りで、 1回の書き込みを1行に保存しましょう。 書き込みの内容に改行が含まれていると1行にならないので、 改行はHTMLの改行タグ <br> に変換してから保存するといいですね。

画面に表示する

書き込みを画面に表示するときですが、 最近の書き込みを例えば20件、新しいものが上の方になるように表示するっていうのはどうでしょう。 古いものが上、新しいものが下っていうのが本来読みやすい順序ですが、 ウェブの場合、ページを開くと一番上が最初に表示されるわけで、新しいものが上の方が 見やすかったりしますよね。ですのでそのようにしましょう。 そして投稿フォームは画面の一番上ということで。

こんな感じで…

こんな感じまで決めたら作成開始です。 細かいところは作りながら。 だいたい、CGIプログラムを作るときって、おおまかな動作とかを決めて作り始めて 作りながら細かいところを考えるっていう感じになります。皆さんはどうなんでしょう? あと、掲示板を作るのに必要な知識ですが、 ここまでいろいろ研究してきて、たいした研究はしていませんが、 結構作れそうな気がするんですよね (^^; やってみよう!の精神です。

作成したCGIプログラム

bbs.cgi
#!/usr/bin/perl

require 'getformdata.pl';
require 'stdplab.pl';
require 'lock.pl';

# データファイル名
$logfname = "bbs_log.cgi";

print "Content-type: text/html\n\n";

# フォームデータ取得
%form = plab::getformdata();

# 処理モードを取得
$mode = $form{'mode'};

# モードに従って分岐します
if ($mode eq "write") {
	# 記事の書き込み処理
	writemode();
	exit;
}
#elsif ($mode eq "clear") {
#	# 記事の全削除
#	if (open(FILE, "> $logfname")) {
#		close(FILE);
#	}
#	print "記事を全て削除しました。";
#	exit;
#}

# 以下、記事の閲覧

# HTMLの頭部分出力
htmlhead();

# フォーム他
print << "EOM";
<div class=pv>
<ol>
<li>	掲示板です。是非足跡を残していってください!
<li>	恐縮ですがタグは無効です。入力項目はいずれも省略可です。
<li>	荒らしはやめてください。IPアドレスを記録しています。…念のため (^^;
</ol>
<form method=post action=bbs.cgi>
<input type=hidden name=mode value=write>
<table>
<tr><td>タイトル</td><td><input type=text name=title size=40></td></tr>
<tr><td>お名前</td><td><input type=text name=name size=40></td></tr>
<tr><td>URL</td><td><input type=text name=url size=40></td></tr>
<tr><td>内容</td><td><textarea rows=5 cols=50 name=body></textarea></td></tr>
<tr><td></td><td><input type=submit value="送信"></td></tr>
</table>
</form>
</div>
EOM

# 記事を読み込む
open(FILE, "< $logfname");
@data = <FILE>;
close(FILE);

# 新しい方から20個表示する
$n = @data;
$nkiji = 0;
for ($i = $n - 1; $i >= 0 && $nkiji < 20; --$i, ++$nkiji) {
	($name, $title, $url, $body, $date, $tim, $ip, $host) = split('<>', $data[$i]);
	$no = $i + 1;
	print "<div class=pvh>No.$no ";
	if ($title ne "") { print "<b>$title</b> "; }
	if ($name ne "")  { print "$name "; }
	if ($url ne "")  { print "[<a href=$url target=_blank>WEB</a>] "; }
	print "$date</div>\n";
	print "<br>\n";
	print "<div class=pv>\n";
	print "$body<br>";
	print "</div>\n";
	print "<br>\n";
}

# HTMLの足部分出力
htmlfoot();
exit;


# 投稿された記事をファイルに保存
sub writemode
{
	htmlhead();
	print "<div align=center>";

	# フォームからのデータを取得
	$name  = $form{'name'};
	$title = $form{'title'};
	$url   = $form{'url'};
	$body  = $form{'body'};

	# タグ、改行などを処理
	$name  = replasecontrolchars($name);
	$title = replasecontrolchars($title);
	$url   = replasecontrolchars($url);
	$body  = replasecontrolchars($body);

	# 記事の長さチェック
	$len = length($body);
	if ($len > 1024) {
		print "記事が長すぎます。もう少し短くしてください。<br>";
		print "ブラウザの戻るボタンで戻ってください。<br>";
	}
	else {
		# その他のデータを取得
		$date = plab::getcurrentdatestring();
		$tim  = plab::getcurrenttimestring();
		$ip   = plab::getip();
		$host = plab::gethost();

		# ロック
		$lockdir = $logfname . ".lockdir";
		if (! plab::lock($lockdir)) {
			print "ファイルロックに失敗しちゃいました。<br>";
		}
		else {
			# データ書き込み
			open FILE, ">> $logfname";
			print FILE "$name<>$title<>$url<>$body<>$date<>$tim<>$ip<>$host\n";
			close(FILE);
			plab::unlock($lockdir);
			print "<br>";
			print "記事の投稿は無事に終わりました。<br>";
			print "ありがとうございました。<br>";
		}
	}

	$url = $ENV{'SCRIPT_NAME'};
	print "<br>";
	print "<a href=$url>掲示板に戻る</a><br>";
	print "<br>";

	htmlfoot();
}

# HTMLタグ、改行の処理
sub replasecontrolchars
{
	local $s = $_[0];
	$s =~ s/\r\n/\n/g;
	while (chomp($s)) {
		;
	}
	$s =~ s/</</g;
	$s =~ s/>/>/g;
	$s =~ s/\n/<br>/g;
	return $s;
}

# HTMLの先頭部分を出力
sub htmlhead
{
	print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=Shift_JIS\">\n";
	print "<body style=\"background-color: #F8F8F8;\">\n";
	print "<style type=\"text/css\">\n";
	print "<!--\n";
	print "span.pv { background-color: #5050D0; font-size: 70%; }\n";
	print "div.pvh { border-width: 1 1 1 1; border-style: solid; font-size: 90%;\n";
	print "          background-color: #F0F0F0; line-height: 1.5em; padding: 0 1em 0 1em; }\n";
	print "div.pv  { padding-left: 2em; font-size: 90%; line-height: 1.3em; }\n";
	print "td.pv   { font-size: 90%; }\n";
	print "-->\n";
	print "</style>\n";

	print "<div class=pvh>掲示板 version 1.00</div>";
	print "<br>";
}

# HTMLの後ろ部分を出力
sub htmlfoot
{
	print "<hr size=1 style=\"padding: 0 1em 0 1em;\">";
	print "<div align=right>\n";
	print "© <a href=http://www.perl-labo.org/>PERL-LABO</a>\n";
	print "</div>";
}

実行結果

掲示板です!書き込みお願いします。
※ 宣伝目的の迷惑書き込みが増えてきましたので、閉鎖しました。ごめんなさい。

考察

プログラムの説明

特に新しいことはしていないと思います。 注意しないといけないこととしては、タグの無効化ですね。 タグを使用可にすると、なんかセキュリティ上のトラブルになったりとかするっていう 話を聞いたことがあって、なのでタグは全面的に禁止にしています。 それは、replasecontrolchars 関数で処理しています。次の部分ですね。

sub replasecontrolchars
{
	local $s = $_[0];
	$s =~ s/\r\n/\n/g;
	while (chomp($s)) {
		;
	}
	$s =~ s/</&lt;/g;
	$s =~ s/>/&gt;/g;
	$s =~ s/\n/<br>/g;
	return $s;
}

タグを無効化するには、< と > を &lt;&gt; に置換します。 こうすると、タグがそのまま画面に表示されることになって、タグが無効になります。 あと、この関数は最初に while (chomp($s)) っていうのをしていますが、 これは末尾の改行を全部削除するっていう意味になります。 chomp というのは末尾の改行を削除して、削除した文字数を返すのですが、 どうやら末尾の改行すべてを一度に削除するのではなくて1回の呼び出しで1つずつ削除する みたいなんですね。それで、while でループしています。末尾の改行を全部削除すると 0 が返ってくるので ループが終わります。{ } の中が ; となっていますが、これは 何もしない という意味になります。 この処理の注意点は、先に $s =~ s/\r\n/\n/g; をしておくことですね。 よく分からないのですが、改行がフォームから送られたときは \r\n になっているようなのですが、 Perl では(正確にはLinuxマシンのPerlでは、かも知れません)改行は \n のようです。 ですので、\r\n のままだとうまくいきませんでした。 …注意点はそれくらいかなぁ。

あ、あともう1つ。掲示板への書き込みがあったとき、書き込みが終わりましたっていう画面を 出しています。この画面を出さずに掲示板に戻っても良かったのかも?でも、 書き込んでくれた人にはお礼をしなくちゃ (^^ で、完了画面から 掲示板に戻るのに自分自身のURLが必要なんですが、それを $ENV{'SCRIPT_NAME'} で取得しています。 これは、ページビューカウンターのところででてきたやつなんですが、ここでも使いました。 便利ですよね!

動きました!

なんと、動きました…。 それほど時間もかからず、作ることができました。 新しい知識も、特に必要無かったです。 ここまで研究してきたことは無駄じゃなかった…。 いつのまにか、掲示板が自作できるようになっていました…。 ちょっと感激です管理人。

Perl/CGI研究室 'PERL-LABO' TOPへ
戻る(History.Back)

Copyright (c) 'PERL-LABO' All Rights Reserved.  リンクフリーです。