多機能掲示板Upgrade

 予てからご希望のあった、レスが付いた時にその表示場所をトップに移動させる機能ですが、ようやくちょっと時間が出来たので、既存のスクリプトを改造する事が出来ました。とは言っても、記事書き込みの際の処理に、この部分を追加しただけなんですけどね(^^ゞ

 スクリプトの内容は、ここの第17章の「多機能掲示板(修正版)」をベースにしていますので、以下に今回の変更部分のみ解説を加えたいと思います。

追伸・・・後日、他にも細かな修正を加えました。書き込み時のエラー表示とか、ページ移動のボタン追加とか。書き込み記事表示の際のグループ分けも、従来の水平線使用からテーブル表示に変更!

*注意…このスクリプトはPHP4.2.0以降のスーパーグローバル変数に対応したものになっていません。ご注意下さい。

<?php
//===================初期設定=====================
//時間の取得
$now = date("Y/m/d H:i:s");
$data_file = "./
g_book7.dat"; //データファイル名
$data_max = 100; //データ最大記録数
$page_view = 10; //1ページの記事表示件数
$homegif = "./home.gif"; //ホームページへのリンク用画像
$passwd = "777"; //管理者用パスワード
?>
初期設定をファイルの先頭に移動しました。

データ保存用ファイルの名前も「g_book7.dat」に変更。
<HTML>
<HEAD>
<TITLE>
多機能掲示板Upgrade</TITLE>
<STYLE TYPE="text/css">
<!--
:link {
Color : blue ;
Text-Decoration : None
}
:active {
Color : blue ;
Text-Decoration : None
}
:visited {
Color : blue ;
Text-Decoration : None
}
A:hover {
Color : blue ;
Text-Decoration : Underline
}
-->
</STYLE>

</HEAD>
<BODY BGCOLOR="#ffffee">
<FORM ACTION="<?php echo $PHP_SELF; ?>" METHOD="POST" NAME="line">
<P><INPUT TYPE="HIDDEN" NAME="action" SIZE="-1" VALUE="regist"></P>
<P ALIGN="CENTER"><FONT SIZE="6" COLOR="#000099">
多機能掲示板Upgrade</FONT></P>
<P ALIGN="CENTER"><FONT SIZE="2" COLOR="red">お名前とコメントは必ず書き込んで下さい。エラーになります。記事の最大記録数は
<?php echo $data_max; ?>件です。</FONT></P>
<BLOCKQUOTE>
<P>
<TABLE BORDER="0" WIDTH="88%">
<TR>
<TD WIDTH="12%" ALIGN="RIGHT">
<P ALIGN="RIGHT">お名前
</TD>
<TD WIDTH="88%"><INPUT TYPE="TEXT" NAME="name" SIZE="15"></TD>
</TR>
<TR>
<TD WIDTH="12%" ALIGN="RIGHT">
<P ALIGN="RIGHT">E-mail
</TD>
<TD WIDTH="88%"><INPUT TYPE="TEXT" NAME="email" SIZE="25"></TD>
</TR>
<TR>
<TD WIDTH="12%" ALIGN="RIGHT">
<P ALIGN="RIGHT">HomePage
</TD>
<TD WIDTH="88%"><INPUT TYPE="TEXT" NAME="hp" SIZE="40"></TD>
</TR>
<TR>
<TD WIDTH="12%" ALIGN="RIGHT">
<P ALIGN="RIGHT">題目
</TD>
<TD WIDTH="88%"><INPUT TYPE="TEXT" NAME="subject" SIZE="40"></TD>
</TR>
<TR>
<TD WIDTH="12%" ALIGN="RIGHT">
<P ALIGN="RIGHT">題目色
</TD>
<TD WIDTH="88%">
<INPUT TYPE="RADIO" NAME="scolor" VALUE="blue" checked><B><FONT COLOR="blue">青</FONT></B>
 <INPUT TYPE="RADIO" NAME="scolor" VALUE="red"><B><FONT COLOR="red">赤</FONT></B>
 <INPUT TYPE="RADIO" NAME="scolor" VALUE="green"><B><FONT COLOR="green">緑</FONT></B>
 <INPUT TYPE="RADIO" NAME="scolor" VALUE="#996600"><B><FONT COLOR="#996600">茶</FONT></B>
 <INPUT TYPE="RADIO" NAME="scolor" VALUE="#660099"><B><FONT COLOR="#660099">紫</FONT></B>
</TD>
</TR>
<TR>
<TD WIDTH="12%" ALIGN="RIGHT">
<P ALIGN="RIGHT">コメント
</TD>
<TD WIDTH="88%"><TEXTAREA NAME="comment" ROWS="4" COLS="60"></TEXTAREA></TD>
</TR>
<TR>
<TD WIDTH="12%">
<P ALIGN="RIGHT">削除キー
</TD>
<TD WIDTH="88%"><INPUT TYPE="PASSWORD" NAME="pass" SIZE="7"><FONT SIZE="2" COLOR="#CC0000"> *半角英数4文字以上推奨。削除キーを設定すると、いつでもご自分の記事を削除できます。</FONT></TD>
</TR>
<TR>
<TD WIDTH="12%">&nbsp;</TD>
<TD WIDTH="88%">
<?php
if ($flags == 'return') {
 print "<input type=submit value=[" . $return . "]に返信>\n";
 print "<input type=hidden name=ress value=$code>\n";
} else {
 print "<INPUT TYPE=SUBMIT VALUE=書き込み> <INPUT TYPE=RESET VALUE=リセット>\n";
}
?>
</TD>
</TR>
</TABLE>
</FORM>
題名を変更!

修正版スクリプトのファイル名は、「
g_book7.php」となります。

記事の最大記録数の表示は、初期設定から呼び出すようにしました。

又、リンク表示等の文字色の設定を追加しました。
<?php
//===============データの削除==================
if ($action == "delete") {
 if(($delcode != "") && ($password != "")){
  $delfile = file($data_file);
  $fp = fopen($data_file, "w");
  for($i=0; $i<sizeof($delfile); $i++){
   list($dcode,$dress,$dname,$demail,$dhp,$dsubject,$dscolor,$dcomment,$dpass,$dnow,$dhost,$dagent) = split( ",", $delfile[$i]);
   if (($password == $dpass) || ($password == $passwd)) {
    if ($delcode != $dcode) {
     fputs($fp,$delfile[$i]);
    }
   } else {
    fputs($fp,$delfile[$i]);
   }
  }
  fclose($fp);
 }
}
//===============インライン関数===================
function inline_link($link){
 $link = ereg_replace("(https?|ftp|news)(://[[:alnum:]\+\$\;\?\.%,!#~*/:@&=_-]+)","<a href=\"\\1\\2\" target=\"_blank\">\\1\\2</a>",$link);
 return $link;
}
従来と同じ。
//===============データの書き込み=================
if ($action == "regist") {
 if (($name != "") && ($comment != "")) {
  
$code = time(); //アクセス時の秒数
  //ホスト名を取得
  $host = getenv("REMOTE_HOST");
  $addr = getenv("REMOTE_ADDR");
  if($host == "" || $host ==$addr){
   $host = gethostbyaddr($addr);
  }
  //訪問者のブラウザ名を取得
  $agent = getenv("HTTP_USER_AGENT");
  //ここから書き込みデータの調整
  $name = htmlspecialchars($name); //特殊文字のHTMLエントリへの変換
  $email = htmlspecialchars($email);
  $hp = htmlspecialchars($hp);
  $subject = htmlspecialchars($subject);
  $comment = htmlspecialchars($comment);
  $comment = nl2br($comment); //HTML改行文字の挿入
  $comment = str_replace("\r", "", $comment); //文字列の置換
  $comment = str_replace("\n", "", $comment);
  //ログファイルの区切文字(",")と区別するために文字コード(&#44)に書き換える。
  $name = str_replace(",", "&#44;",$name);
  $email = str_replace(",", "",$email);
  $hp = str_replace(",", "",$hp);
  $subject = str_replace(",", "&#44;",$subject);
  $comment = str_replace(",", "&#44;",$comment);
  //インラインリンクを実現(URLのみ)
  $comment = inline_link($comment);
  //配列要素を文字列により連結
  $input_msg = implode(",", array($code,$ress,$name,$email,$hp,$subject,$scolor,$comment,$pass,$now,$host,$agent));
  $message = file($data_file);
  $fp = fopen($data_file, "w");
  rewind($fp);
  fputs($fp, "$input_msg\n");
  //最大記録数の調整
  if(
($data_max * 1.2) < sizeof($message))
   $msg_num = $data_max - 1;
  else
   $msg_num = sizeof($message);
  for($i = 0; $i < $msg_num; $i++){
   fputs($fp, $message[$i]);
  }
  fclose($fp);
  unset($message);
記事番号に、アクセス時の秒数を使うようにしました。それぞれに常に固有の記事番号を得るためです。

従来の通し番号だと、エラーが出てしまったもので…(^^ゞ

又、従来の不要な文字処理を削除しました。

尚、書き込み記事の最大記録数の調整部分ですが、初期設定値の1.2倍を超えた場合に処理を実行するよう変更しました。この方が効率がいいもので(^^ゞ

他は従来と同じです。
  //返信があった時の親記事の移動
  if($ress){
   //親記事を配列に格納後、削除
   $sfile = file($data_file);
   $fp = fopen($data_file, "w");
   for($i=0; $i<sizeof($sfile); $i++){
    list($scode,$sress,$sname,$semail,$shp,$ssubject,$sscolor,$scomment,$spass,$snow,$shost,$sagent) = split( ",", $sfile[$i]);
    if($ress == $scode){
     $CHOICE = $sfile[$i];
     $sfile[$i] = "";
    }
    fputs($fp,$sfile[$i]);
   }
   fclose($fp);
   unset($sfile);
   //引き抜いた親記事をファイルの先頭に書き込み
   $message2 = file($data_file);
   $fp = fopen($data_file, "w");
   rewind($fp);

   fputs($fp, "$CHOICE");
   for($i = 0; $i < sizeof($message2); $i++){
    fputs($fp, $message2[$i]);
   }
   fclose($fp);
   unset($message2);
  }
■今回の追加部分

変数
$ressに返信すべき親記事の記事番号が格納されていたら、以下の処理を行います。

まずログファイルを開いて、書き込み記事を
for文で一つずつチェックし、変数$ressの値と変数$scode(親記事の記事番号)の値が等しければ、その親記事の内容を変数$CHOICEに書き込んで、ログファイル中のその親記事を一旦削除します。

次に、再びログファイルを開き、先ほど抜き取った親記事の内容を格納した変数
$CHOICEの値を、ログファイルの先頭に書き込み、元のログファイルの内容も追加挿入し、ログファイルを閉じます。

これで返信のあった親記事の場所が、ログファイルの先頭に移動し、記事の一覧表示も修正されます。
 }else{
  print "<B>※必須項目が書き込まれていません。やり直して下さい。</B><A HREF=JavaScript:history.back(); onMouseOver=this.style.color='red' onMouseOut=this.style.color='blue'>[一つ前のページへ]</A>\n";
  exit;
 }
書き込みの際に必須項目が抜けていた場合、エラー表示を行うようにしました。
}
//親記事だけを抜き出す
$message = file($data_file);
for($i=0; $i<sizeof($message); $i++){
 list($rcode,$rress,$rname,$remail,$rhp,$rsubject,$rscolor,$rcomment,$rpass,$rnow,$rhost,$ragent) = split( ",", $message[$i]);
 if($rress == ""){
  $PARENT[] = $message[$i];
 }
}
従来と同じ。
//==================HTML表示======================
//1ページ当たりの表示件数の調整
$msg_count = count($PARENT);
if($pline == "")
 $p_line = 0;
else
 $p_line = $pline;
$end_data = $msg_count - 1;
$page_end = $p_line + ($page_view - 1);
if($page_end >= $end_data)
 $page_end = $end_data;
for($i=$p_line; $i<=$page_end; $i++){
 list($code2,$ress2,$name2,$email2,$hp2,$subject2,$scolor2,$comment2,$pass2,$now2,$host2,$agent2) = split(",", $PARENT[$i]);
 //親記事の表示
 
print "<table border=1 bordercolor='green' width=88%>\n";
 print "<tr><td>\n";

 print "<form method=POST action=$PHP_SELF>\n";
 print "<input type=radio name=delcode value=$code2>\n";
 print "<font size=4 color=$scolor2><b>$subject2</b></font>\n";
 if($email2 != "")
  print " <a href=mailto:$email2><b>$name2</b></a>\n";
 else
  print " <b>$name2</b>\n";
 if($hp2 != ""){
  print " <a href=$hp2 target=_top><img src=$homegif border=0></a>\n";
 }
 print " <font size=2 color='green'>$now2</font>\n";
 print "<input type=hidden name=action value=delete>\n";
 print "<font size=2>パスワード:</font><input type=password size=7 name=password>\n";
 print "<input type=submit value=削除>\n";
 print "</form>\n";
 print "<blockquote>$comment2<br><br>\n";
 print "<form method=POST action=$PHP_SELF>\n";
 print "<input type=submit value=返信>\n";
 print "<input type=hidden name=code value=" . $code2 . ">\n";
 print "<input type=hidden name=return value=" . $subject2 . ">\n";
 print "<input type=hidden name=flags value=return>\n";
 print "<font size=2>[$host2] $agent2</font>\n";
 print "</form></blockquote>\n";
 //返信記事の表示
 for($j=0; $j<sizeof($message); $j++){
  list($code3,$ress3,$name3,$email3,$hp3,$subject3,$scolor3,$comment3,$pass3,$now3,$host3,$agent3) = split(",", $message[$j]);
  if ($code2 == $ress3) {
   print "<blockquote>\n";
   print "<form method=POST action=$PHP_SELF>\n";
   print "<input type=radio name=delcode value=$code3>\n";
   print "RE:<font color=$scolor3><b>$subject3</b></font>\n";
   if ($email3 != "") {
    print " <a href=mailto:$email3><strong>$name3</strong></a>\n";
   } else { print " <strong>$name3</strong>\n";
   }
   if($hp3 != ""){
    print " <a href=$hp3 target=_top><img src=$homegif border=0></a>\n";
   }
   print " <font size=2 color='green'>$now3</font>\n";
   print "<input type=hidden name=action value=delete>\n";
   print "<font size=2>パスワード:</font><input type=password size=7 name=password>\n";
   print "<input type=submit value=削除>\n";
   print "<blockquote>$comment3<br><br>\n";
   print "<font size=2>[$host3] $agent3</font></blockquote>\n";
   print "</form></blockquote>\n";
  }
 }
 
print "</td></tr></table><br>\n";
}
unset($message);
?>
書き込み記事のグループ分けに従来は水平線を使用していましたが、変更ついでにテーブル表示に変えました。この方が見た目がスッキリすると思います。

他は従来と同じ。
<P>
<TABLE BORDER="0" WIDTH="88%">
 <TR>
  <TD WIDTH="50%">
<?php
if($page_end >= $page_view){
 $page_count = floor($page_end / $page_view);
 $prev_line = ($page_count - 1) * $page_view;
 print "<form method=Post action=$PHP_SELF>\n";
 print "<input type=hidden name=pline value=$prev_line>\n";
 print "<input type=submit value=前のページ>\n";
 print "</form>\n";
}else{
 print "&nbsp;\n";
}
?>
  </TD>
  <TD WIDTH="50%"><P ALIGN="RIGHT">
<?php
$next_line = $page_end + 1;
if($page_end != $end_data){
 print "<form method=Post action=$PHP_SELF>\n";
 print "<input type=hidden name=pline value=$next_line>\n";
 print "<input type=submit value=次のページ>\n";
 print "</form>\n";
}else{
 print "&nbsp;\n";
}
?>
  </TD>
 </TR>
</TABLE>
「前のページ」ボタンも表示するよう変更しました。

表示位置の調整のため、テーブルの中に入れています。

ページ移動の為の値を求める際には、下の
floor関数を使っています。求め方は、コード内容から推察して下さい。

★int floor ( float number )関数・・・引数 number の小数点以下を切り捨てた値を返します。
</BLOCKQUOTE>
</BODY>
</HTML>
従来と同じ。

 *上の内容をこのまま複写しても動きません。何故なら、表示を見やすくする為コード中に全角スペースが入っているからです。実際にお使いになる時は、この点を修正してからサーバにアップして下さい。

HOME