記事削除機能の実装

 ここでは、前に紹介した超簡単掲示板に記事削除機能を実装する方法についてご説明しましょう。これがないと、書き込み記事の管理が面倒ですからねー(^^ゞ

変更部分

 記録用ファイルに書き込まれた記事を削除する為には、まず該当記事を指定できる印が必要です。その為に記事の書き込み処理内で記事番号を取得する処理を施しています。記事番号を変数$codeに格納して、それを利用します。

 又、誰もが勝手に記事を削除出来たのではいけませんので、当然パスワードも設定出来るようにしなくては行けません。これには、管理者用のパスワード「$passwd」と、記事の書き込み者が自由に設定できるパスワード「$pass」とが必要でしょう。管理者用のパスワードはスクリプト文の最初に指定してやります。一方、記事の書き込み者用のパスワードは、記事の書き込みフォームにテキストボックスで設定出来るようにしてやります。

 更に処理内容が「記事削除」と「記事書き込み」と「HTML表示」との3つになり、これを一箇所で処理分岐してしまうと処理の流れがスムーズでなくなります。又、この方法では「記事削除」と「記事書き込み」処理の最後にそれぞれリロード処理を施さなくてはならなくなるので、全体の流れを更に遅くしてしまいます。そこで、「記事削除」と「記事書き込み」の2つの処理を「if ... elsif ...」で条件分岐し、次の「HTML表示」処理はそのままの流れで処理するようにしました。こうすることで、スクリプト全体の処理速度が数段向上します。

記事削除処理

 この処理の流れは、フォームから送られて来た命令(action)が記事削除(delete)なら、まず記事番号を確認します。記事番号が一致したらパスワードの符合を確認し、もし一致すれば該当番号の記事を(行単位で)削除すると言うものです。

実際のソース

 では実際のソースを見てみましょう。該当の変更箇所は色を変えて示しています。よく注意してご覧になって下さい。

#!/usr/bin/perl
# ↑あなたが加入しているプロバイダの「perl」言語が使用できるパスを指定します。
# 一般的には「#!/usr/local/bin/perl」か「#!/user/bin/perl」で大丈夫。
require '../jcode.pl';
$bbs_title = '簡単掲示板';
$datafile = 'keijiban2.txt';
$cgi_name = 'keijiban2.cgi'; #ここで使うCGIスクリプトの名前
$max = 50;
$passwd = '****'; #管理者用パスワード
*記事削除機能の為の管理者パスワードを設定しています。これでどの記事でも管理者権限で削除できるようになります。
#==============初期設定が必要なのはここまでです=================================
($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900;
$mon = sprintf("%02d", $mon + 1);
$day = sprintf("%02d", $day);
$hour = sprintf("%02d", $hour);
$min = sprintf("%02d", $min);
$date_now = "$year年$mon月$day日 $hour時$min分";
if ($ENV{'REQUEST_METHOD'} eq "POST") {
read(STDIN, $QUERY_DATA, $ENV{'CONTENT_LENGTH'});
} else { $QUERY_DATA = $ENV{'QUERY_STRING'}; }
@pairs = split(/&/,$QUERY_DATA);
foreach $pair (@pairs) {
 ($name, $value) = split(/=/, $pair);
 $value =~ tr/+/ /;
 $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
 $value =~ s/</&lt;/g;
 $value =~ s/>/&gt;/g;
 $value =~ s/\n//g;
 $value =~ s/\,//g;
 &jcode'convert(*value,'sjis');
 $FORM{$name} = $value;
}
if (!open(NOTE,"$datafile")) { &error(bad_file); }
@DATA = <NOTE>;
close(NOTE);
if ($FORM{'action'} eq 'delete') {
 if ($FORM{'password'} eq '') { &error(not_pass); }
 if ($FORM{'code'} < 1) { &error(bad_code); }
 $i = 0; $match = 0;
 foreach $line (@DATA) {
  ($date,
$code,$name,$email,$HP,$subject,$comment,$pass,) = split(/\,/,$line);
  if ($FORM{'code'} == $code) {
   if ($FORM{'password'} eq $pass || $FORM{'password'} eq $passwd) {
    splice(@DATA,$i,1);
    $match = 1; last;
   }
  }
  $i++;
 }
 if ($match) {
  if (!open(NOTE,">$datafile")) { &error(bad_file); }
  print NOTE @DATA;
  close(NOTE);
 }
*記事削除の為に変数$codeと$passとを追加しています。尚、変数$passの後にコンマ「,」があることに注意して下さい。これがないと、次のパスワードの一致を確認する処理で変数$passを認識する事が出来なくなります。split関数との兼ね合いですかね…。尚、「\,」はエスケープシーケンスでコンマ「,」の事です。

*処理の流れとしては、まず「$i=0; $match=0;」と言う風に変数の初期化を行い、次にデータ@DATAを一行ずつ各項目に分けながら、記事番号の一致とパスワードのチェックを行い、それぞれ条件が合致すればsplice関数で該当番号の記事を一行単位で削除しています。

*削除処理の中で記事の削除がなされた場合、変数$match=1となり次の条件「if($match)」に合致するようになるので、データファイル$datafileを書き込みモードで開き、記事削除後の@DATAをデータファイルのハンドルNOTEに書き込み閉じます。
} elsif ($FORM{'action'} eq 'regist') {
 if ($FORM{'name'} eq '') { &error(bad_name); }
 if ($FORM{'comment'} eq '') { &error(bad_comment); }
 $count = @DATA;
 if ($count > $max) { pop (@DATA); }
 if ($count < 1) {
  $new_code = 1;
 } else {
  ($date,
$code,$name,$email,$HP,$subject,$comment,$pass,) = split(/\,/,$DATA[0]);
  $new_code = $code + 1;
 }

 $value = "$date_now\,$new_code\,$FORM{'name'}\,$FORM{'email'}\,$FORM{'HP'}\,$FORM{'subject'}\,$FORM{'comment'}\,$FORM{'pass'}\n";
 unshift(@DATA,$value);
 if (!open(NOTE,">$datafile")) { &error(bad_file); }
 print NOTE @DATA;
 close(NOTE);
}
*記事番号$codeをデータに書き込む処理を施します。もしデータがまだなければ「if($count < 1)」変数$new_code=1となります。既にデータがある場合は、先頭のデータ「$DATA[0]」の記事番号に1をプラスします「$new_code = $code + 1;」。「\,」はエスケープシーケンスでコンマ「,」の事です。
print "Content-type: text/html\n\n";
print "<html><head>\n";
print "<title>" . $bbs_title . "</title></head>\n";
print "<body bgcolor=#FFFFDD text=#000000 link=#FF0000 vlink=#FF0000>\n";
print "<form action=$cgi_name method=POST>\n";
print "<input type=hidden name=action value=regist>\n";
print "<center><font size=6>" . $bbs_title . "</font></center>\n";
print "<center><font color='red'>お名前とコメントは、必ずご記入下さい。尚、記事の最大記録数は50件です。</font></center>\n";
print "<table border=0 cellspacing=1>\n";
print "<tr><td align=right>お名前</td>\n";
print "<td><input type=text size=29 name=name></td></tr>\n";
print "<tr><td align=right>E-mail</td>\n";
print "<td><input type=text size=29 name=email></td></tr>\n";
print "<tr><td align=right>HomePage</td>\n";
print "<td><input type=text size=50 name=HP></td></tr>\n";
print "<tr><td align=right>題名</td>\n";
print "<td><input type=text size=50 name=subject></td></tr>\n";
print "<tr><td align=right>コメント</td>\n";
print "<td><textarea name=comment rows=4 cols=60></textarea></td></tr>\n";
print "<tr><td align=right>削除キー</td>\n";
print "<td><input type=password size=10 name=pass>\n";
print "<font size=-1 color='red'> *英数8文字以上推奨</font></td></tr>\n";

print "<tr><td></td><td><input type=submit value=書き込み>";
print " <input type=reset value=取り消し></td></tr>\n";
print "</table></form>\n";
*記事の書き込みフォーム内に、パスワードを書き込む欄を設けます。ここで「name=pass」になっている事に注意!
print "<form action=$cgi_name method=POST>\n";
print "<input type=hidden name=action value=delete>\n";

foreach $line (@DATA) {
 ($date,$code,$name,$email,$HP,$subject,$comment,$pass,) = split(/\,/,$line);
 $comment =~ s/\r/<br>/g;
 print "<hr>\n";
 print "<input type=radio name=code value=$code>\n";
 print "<font color='blue' size=4><b>$subject</b></font>\n";
 if ($email ne "") {
  print "<a href=mailto:$email><strong> $name</strong></a>\n";
 } else { print "<strong> $name</strong>\n"; }
 if ($HP ne "") {
  print "<a href=$HP target=_top> HomePage</a>\n";
 }
 print "<font size=-1> $date</font>\n";
 print "<blockquote>$comment</blockquote>\n";
}
print "<hr>\n";
print "削除用パスワード:<input type=password size=10 name=password>";
print "<input type=submit value=削除>\n";

print "</form></body></html>\n";
exit;
*データの一覧表示部分を<form>タグでくくっています。これは削除機能を持たせる為に、各書き込み記事の先頭に記事番号を表すラジオボタンを付けていることによります。もし、最下欄に削除用の記事番号を書き込むテキストボックスを設けた場合は、ここのように全体を<form>タグでくくる必要はありません。下の該当部分だけを<form>タグでくくれば大丈夫でしょう。
#================================エラー処理ルーチン=============================
sub error {
$error = $_[0];
if ($error eq "bad_file") { $msg = 'ファイルのオープン、入出力に失敗しました。'; }
elsif ($error eq "bad_name") { $msg = 'お名前が記入されていません。'; }
elsif ($error eq "bad_comment") { $msg = 'コメントが記入されていません。'; }
elsif ($error eq "not_pass") { $msg = 'パスワードが記入されていません。'; }
elsif ($error eq "bad_code") { $msg = '削除記事番号が不正です。'; }

else { $msg = '原因不明のエラーで処理を継続できません。'; }
print "Content-type: text/html\n\n";
print "<html><head><title>" . $bbs_title . "</title></head>\n";
print "<body bgcolor=#FFFFDD text=#000000 link=#FF0000 vlink=#FF0000>\n";
print "<center><h2>エラーです!</h2>\n";
print "やり直して下さい。<hr>\n";
print "<b>" . $msg . "</b></center>\n";
print "</body></html>\n";
exit;
}
*エラー表示の項目が増えたので、メッセージも増やしています。

記事削除処理部分の詳解

 記事削除処理は新たに追加した部分なので、やはり別途詳しく解説して置きたいと思います。では、どうぞ_(._.)_

if ($FORM{'action'} eq 'delete') { 連想配列$FORM{'action'}のvalueが「delete」ならば、の意味です。
if ($FORM{'password'} eq '') { &error(not_pass); } eq」は「…ならば」の意味。反対は「ne」。「''」はデータが空である事を意味します。つまり、削除用パスワードの欄に、何も書き込まれていない場合のエラー処理を行っている訳です。
if ($FORM{'code'} < 1) { &error(bad_code); } ここも上と同じエラー処理です。「$FORM{'code'} < 1」と言うのは、記事のラジオボタンにチェックを入れていない事を意味します。
$i = 0; $match = 0; 変数の初期化を行っています。
foreach $line (@DATA) { foreach関数はもうお馴染みですね(^。^)
($date,$code,$name,$email,$HP,$subject,$comment,$pass,) = split(/\,/,$line); split関数も説明不要でしょう!データをコンマ「,」部分で分割して返しています。
if ($FORM{'code'} == $code) { 各記事の頭にあるラジオボタンにチェックを入れれば、この条件式が成り立ちます。
if ($FORM{'password'} eq $pass || $FORM{'password'} eq $passwd) { 削除用パスワードの欄に書き込まれたパスワード「$FORM{'password'}」が、訪問者が記事書き込みフォーム内に書き込んだ削除キー「$pass」に等しいか、又は、管理者用パスワード「$passwd」に等しければ、と言う意味。「||」は「または」、「&&」が「且」。
splice(@DATA,$i,1); ★splice関数splice(配列変数, 開始位置, 要素数)」・・・配列変数の開始位置から指定した要素数だけ削除し、削除された要素数を返します。
$match = 1; last; ◆last文・・・ループの実行を直ちに中断し、ループから抜けます。ここでは、記事の削除がなされると、変数$match=1にして直ちにループから抜け出しています。
}  
}  
$i++; 「$i++」は「$i=$i+1」の意味。「$i」はループ処理用変数。
}  
if ($match) { 条件式でこのように変数のみを書いた場合は、変数$matchが真「1」であれば、と言う意味になる。つまりここでは、上の処理の流れから、記事が削除された場合の事後処理を行っている。
if (!open(NOTE,">$datafile")) { &error(bad_file); } !」は否定を意味する。ここは、データファイルが開けなかった時のエラー処理を表している。
print NOTE @DATA; データファイルを書き込みモードで開く事が出来たら、記事削除後の@DATAの内容をファイルハンドルNOTEに書き込んでいる。
close(NOTE);  
}  

超簡単フォームメール 前へ

HOME

次へ 訪問者の情報をゲット!