掲示板(URL自動変換/連続改行・スペース削除)
#! /usr/local/bin/perl
$FILE = './dat/board.dat'; #書き込み保存ファイル
$MAX = 100; #保存件数
$VIEW = 10; #1ページの表示件数
$TEXTMAX = 1000; #本文文字数制限(全角)
$NAMEMAX = 10; #名前文字数制限(全角)
$TITLEMAX = 20; #題名文字数制限(全角)
$PASSWORD = '1234'; #削除用パスワード
$time = time;
loadForm(); #フォームデータ取り込み
loadData(); #記事データ読み込み
print qq(Content-type: text/html; charset=UTF-8\n\n);
print <<END; #HTML出力
<!DOCTYPE html>
<html>
<head>
<title>掲示板</title>
<style type="text/css">
<!--
body { padding:10px 5%; background:#eef }
hr { display:none }
form { margin:5px 0; padding:5px 0; border-bottom:3px solid #f60 }
table { margin:20px 5%; width:90%; border:1px solid #963 }
th { background:#fc9; text-align:left }
td { padding:10px; background:#fff }
h1 { margin:0px; font-size:22px; border-bottom:3px solid #f60 }
textarea { width:80% }
.small { font-size:12px; text-align:right }
.err { padding:10px 0; color:#f33; font-weight:bolder; text-align:center;
border-bottom:3px solid #f60 }
//-->
</style>
<script language="JavaScript">
<!--
function check() { //空欄・文字数チェック
for(i = 0 ; i < document.form.length ; i++) {
with(document.form.elements[i]) {
if(value == "") {
alert("すべての項目を入力してください!!");
focus();
return false;
}
if(name == "text" && value.length > $TEXTMAX) {
alert("文字数オーバーです!!(" + value.length + "文字)");
focus();
return false;
}
}
}
}
//-->
</script>
</head>
<body>
<h1>掲 示 板</h1>
<hr>
<form action="$ENV{'SCRIPT_NAME'}" method="POST" name="form" onSubmit="return check()">
名前:<input type="text" name="name" size="50" maxlength="$NAMEMAX" value="$FORM{'name'}"><br>
<br>
題名:<input type="text" name="title" size="50" maxlength="$TITLEMAX"><br>
<br>
本文:<textarea name="text" rows="10" cols="70"></textarea><br>
<small>※$TEXTMAX字以内 ※HTML使用不可(ホームページアドレス・メールアドレスはリンクに自動変換)</small><br>
<br>
<input type="hidden" name="mode" value="write">
<input type="submit" value=" 書き込み "><br>
</form>
<hr>
END
if($ERR) { #エラーチェック
print <<" END";
<div class="err">【エラー】$ERR!!</div>
<hr>
END
}
printData(); #記事出力
print <<END; #HTML出力
</body>
</html>
END
exit;
#==============================================================================記事出力
sub printData
{
my $next = $FORM{'next'} + $VIEW;
if($next > @DATA) {
$next = @DATA;
}
print qq(<form action="$ENV{'SCRIPT_NAME'}" method="POST">\n);
for($i = $FORM{'next'} ; $i < $next ; ++$i) {
my ($tm, $name, $title, $text, $agent, $addr) = split(/\t/, $DATA[$i]);
my ($sec, $min, $hour, $date, $mon, $year, $day) = localtime($tm);
if($title eq "") {
$title = "無題";
}
# $text =~ s/(http:\/\/[\w\.\/\-\~\?\=\+\%(&)]+)/<a href="$1">$1<\/a>/g; #URL変換
# $text =~ s/([\w\.\-]+@[\w\.\-]+)/<a href="mailto:$1">$1<\/a>/g; #メールアドレス変換
print qq(<table cellspacing="0">);
print qq(<tr>);
printf qq(<th><input type="radio" name="no" value="%d">【$title】</th>), $i;
printf qq(<th class="small">$name [%02d/%02d %02d:%02d]</th>), ++$mon, $date, $hour, $min;
print qq(</tr>);
# if($FORM{'pass'} eq $PASSWORD) {
# print qq(<tr>);
# print qq(<th colspan="2" class="small">$agent<br>$addr</th>);
# print qq(</tr>);
# }
print qq(<tr>);
print qq(<td colspan="2">$text</td>);
print qq(</tr>);
print qq(</table>\n);
print qq(<hr>\n);
}
print qq(<div align="right">\n);
print qq(<input type="hidden" name="mode" value="delete">\n);
print qq(<input type="hidden" name="next" value="$FORM{'next'}">\n);
print qq(<input type="password" name="pass" value="$FORM{'pass'}" size="4">\n);
print qq(<input type="submit" value=" 削 除 ">\n);
print qq(</div>\n);
print qq(</form>\n);
print qq(<hr>\n);
print qq(<center>\n);
if($FORM{'next'}) { #前ページへのリンク
printf qq(<a href="$ENV{'SCRIPT_NAME'}?next=%d">≪前ページ</a>\n), $FORM{'next'} - $VIEW;
}
else {
print qq(≪前ページ\n);
}
for($i = 0 ; $i < @DATA ; $i += $VIEW) { #ページへのリンク
if($i == $FORM{'next'}) {
printf qq(%d\n), $i / $VIEW + 1;
}
else {
printf qq(<a href="$ENV{'SCRIPT_NAME'}?next=$i">%d</a>\n), $i / $VIEW + 1;
}
}
if($next < @DATA) { #次ページへのリンク
print qq(<a href="$ENV{'SCRIPT_NAME'}?next=$next">次ページ≫</a>\n);
}
else {
print qq(次ページ≫\n);
}
print qq(</center>\n);
}
#==============================================================================読み込み・書き込み・削除
sub loadData
{
open(FILE, "<$FILE") or ($ERR = "記事ファイルが開けません" and return); #データ読み込み
eval{ flock(FILE, 1) };
@DATA = <FILE>;
close FILE;
if($FORM{'mode'}) {
if($FORM{'mode'} eq 'write') { #データ追加
my ($tm, $name, $title, $text) = split(/\t/, $DATA[0]);
if($FORM{'text'} eq "") {
$ERR = "本文が空白";
return;
}
if(length($FORM{'text'}) > $TEXTMAX * 2
or length($FORM{'name'}) > $NAMEMAX * 2
or length($FORM{'title'}) > $TITLEMAX * 2 ){
$ERR = "文字数オーバー";
return;
}
if($FORM{'text'} eq $text) {
$ERR = "書き込み済み";
return;
}
unshift @DATA, "$time\t$FORM{'name'}\t$FORM{'title'}\t$FORM{'text'}\t"
. "$ENV{'HTTP_USER_AGENT'}\t$ENV{'REMOTE_ADDR'}\t\n";
while(@DATA > $MAX) {
pop @DATA;
}
}
elsif($FORM{'mode'} eq 'delete') { #データ削除
if($FORM{'no'} eq "") {
return;
}
if($FORM{'pass'} ne $PASSWORD) {
$ERR = "削除パスワードが違います";
return;
}
splice @DATA, $FORM{'no'}, 1;
}
#サンプルにつき書き込み停止
#open(FILE, ">$FILE"); #データ書き込み
#eval{ flock(FILE, 2) };
#print FILE @DATA;
#close FILE;
}
}
#==============================================================================フォームデータ取り込み
sub loadForm
{
my ($query, $pair);
if($ENV{'REQUEST_METHOD'} eq 'POST') {
read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
}
else {
$query = $ENV{'QUERY_STRING'};
}
foreach $pair (split(/&/, $query)) {
my ($key, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg;
$value =~ s/&/&/g;
$value =~ s/</</g;
$value =~ s/>/>/g;
$value =~ s/\x81\x40/ /g; #全角スペースを半角スペースに変換
$value =~ s/(\s*\x0D\x0A\s*)+/<br>/g; #改行を<br>に変換(前後のスペース・連続改行削除)
$value =~ s/\s+/ /g; #連続スペースをスペース1文字に変換
if($value eq "<br>" or $value eq " ") { #改行・スペースのみの場合 削除
$value = "";
}
if(substr($value, 0, 4) eq "<br>") { #先頭が改行の場合 削除
$value =~ s/<br>//;
}
$FORM{$key} = $value;
}
}
〔 実行する 〕