このページは福井県立大学の田中求之が2006年1月まで運用していた Mac のサーバ運用に関する会議室 「Web Scripter's Meeting」の記録です。情報が古くなっている可能性がありますのでご注意ください。

掲示板でのメッセージ表示の制限プログラムはどこに?

発言者:柊 甘奈
( Date Friday, June 04, 1999 09:19:34 )


お世話になっております。
プログラム内部の話などよろしいでしょうか?
Macでサーバを構築し、そこにMacPerlを導入して稼動させています。
ここの掲示板でお世話になりました。
その節はありがとうございました。

そこで掲示板の作成をしているのですが、
どうにも表示制限のプログラムがわかりません。
最新の10メッセージ表示し、それ以前のものを前のページとする、という
最低条件のシステムです。

掲示板の状況とプログラムですが、
フレームを使用して、「登録フォームHTML」「書き込みCGI」「表示CGI」
これが主要ファイルです。
そのCGIソースですが、

書き込みプログラム「guestbook.cgi」
#Copyright Forest Co.,Ltd.

require 'jcode.pl';
require 'cgi-lib.pl';

#ログ用のファイル名とここの"guestlog"の名前を同じにする。
$log_file = 'guestlog1';
$log_file2 = 'guestlog2';

#登録完了のHTMLがあるURLを指定
$query_file = 'guest3.html';
$view_file = 'view.cgi';

#現在時刻設定
  wtime();
&ReadParse;
$name = $in{"NAME"};
$addr = $in{"ADDR"};
$home = $in{"HOME"};
$mess = $in{"MESS"};

if($name eq '' || $mess eq '')
{
  print "Content-type: text/html\n\n";
  print "<HTML>\n";
  print "<HEAD>\n";
  print "<META HTTP-EQUIV=\"Content-type\" CONTENT=\"text/html; charset=x-sjis\">\n";
  print "<TITLE>WORNING!!</TITLE>\n";
  print "</HEAD>\n";
  print "<BODY bgcolor=\"#808080\">\n";
  print "<FONT SIZE=6 COLOR=#FF0000>名前、メッセージは必ず入力して下さい。</FONT>\n";
    print "</BODY></HTML>\n";
    exit;
}
else
{
  &jcode'convert(*name, "sjis");
  &jcode'convert(*madd, "sjis");
  &jcode'convert(*subject, "sjis");
  &jcode'convert(*mess, "sjis");
 

 $cnt_file = 'cnt';
 $cnt_file2 ='cnt2';
 open(FH," $cnt_file");
 $cnt =<FH>;
 cl          甘&甘&F..         甘&甘&002.tォxt02     TXT ?&テ&ε&xFINDER  DAT"甘&甘&Gク薤SOURCEFRK甘&甘&H
Ty*g-噛[8=蠑称未~1    ?テ&テ&?テ&(           002.txt02レaTEXTttxtウ|^正|^~002     TXT 
名称未設定#Pレ8タTEXTttxtA\ウ|]楜|]欅:ーシ称未~1    any key
IO      YSMSDOS   SYSAサ\~顱Uェ< 10)
  {
    $mon =  "0$mon";
  }
  if($mday < 10)
  {
    $mday =  "0$mday";
  }
  if($sec < 10)
  {
    $sec =  "0$sec";
  }
  if($min < 10)
  {
    $min =  "0$min";
  }
  if($hour < 10)
  {
    $hour = "0$hour";
  }
  $ltime = $year . '年' . $mon . '月' . $mday . '日' . $hour . '時' . $min . '分';
}

#一時書き込みソート用ログファイルの消去
open(DEA,">$log_file2");
close(DEA);
#一時書き込みソート用ログファイルに最新メッセージをセーブ
  open(LOGA,">>$log_file2");
     $value = "$cnt,$name,$ltime,$addr,$home,$mess\n";
  print LOGA $v?ェョヌエ湖レ9oツKウj」捩シウ唾チj恬ケセサン蝙サネォサヌースウN]R1'  +a=yチ汕ウ」Xa`猿激囎圻ァェエエ?}、隔凵oーウホヤノヤマフマヒツホノチヒホタホマナ鱆ン?粹玻ルレユ鉤珞鰛ヤワムリ獪ヨ衲レ袍ユレヤヒヤソミレニクソキウソクエセカ」ォ}ZピN蛍`~r[?`xkQ賜\息。豫ォ肇ァ墾杪ig79nq「犂。蠻!H)+ Ng:
+  跳イィホニキ醵」ルヒ汎チサウ迷タオタト乂「征ヌV?K?I\ユLsロ>jラ aレ;`蹌eラNイ_m丗U:f翼、エ峡キゥムレ・ノヤ諒ヤ佩ヤ面ホ估ネ伴セx鉛?ェ板ヤ懲ネ淘セ\|・?カ「ァネ増セ洵ネォウフ?ォhl罫~沺ッケZZp=je界YTk虚\wh腹xェァl亠6dァsイチ」゙トヘケヘツヌ裝。タヌbュKmチQk絞?C+ Z06oMP砧gァ操コ「u搦\jl¢藷ィ、漫撈ォィ涕イ荘薄岳這eュエ惱ヌケテテサヘヘスハハソヌハセセスウヤホヌ釵レ?裃

表示するプログラムファイル「view.cgi」

#Copyright Forest Co.,Ltd.

#ログ用のファイル名とここの"guestlog"の名前を同じにする。
$log_file = 'guestlog1';

#タイトル文字を設定します。
$title = '訪問者リスト';

#背景色を設定します。
$bg_color = '#ffffff';

#フォント色を設定します。
$font_color = '#000000';

#戻るファイルを指定します。
$back_file = 'http://interjet/internal/index.htm';

print "Content-type: text/html\n\n";
print "<HTML>\n";
print "<HEAD>\n";
print "<META HTTP-EQUIV=\"Content-type\" CONTENT=\"text/html; charset=x-sjis\">\n";
print "<TITLE>bbs</TITLE>\n";
print "</HEAD>\n";
print "<BODY BGCOLOR=$bg_color>\n";
print "<FONT COLOR=$font_color>\n";

dispelements();

print "<HR>\n";
print "<CENTER>\n";
print "</CENTER>\n";
print "</FONT>\n";
print "</BODY>\n";
print "</HTML>\n";

sub dispelements
{
  open(FPA,"$log_file");
  @lines = <FPA>;
  close(FPA);

  foreach $line (@lines)
  {
    print "<HR>\n";
    ($cnt,$name, $ltime,$addr, $home, $mess) = split(/,/, $line);
  print "<B><FONT SIZE =4 COLOR = #000080>$cnt</FONT></B>\n";
    if ($addr eq "")
    {
      print " 名前:<B><FONT SIZE =6 COLOR=#ff4040>$name</FONT></B>\n";
    }
           else
    {
      print " 名前:<B><FONT SIZE =6 COLOR=#ff4040><A HREF=\"mailto:$addr\">$name</A></font></B> \n";
    }
    if ($home ne "")
    {
      print "<p></p>\n";
    }
print "$ltime<BR>\n";
    print "<P>$mess<BR>\n";
  }
}

です。
わかりにくいですかね?
これのどこにどんな命令で書き加えれば良いのでしょうか?

よろしくお願いいたします。


柊 甘奈 さんからのコメント
( Friday, June 04, 1999 09:20:46 )

おかしいですね。
プログラムの一部が文字化けしていますね。
どういうことでしょうか?

田中求之 さんからのコメント
( Friday, June 04, 1999 12:22:05 )

>プログラムの一部が文字化けしていますね。
>どういうことでしょうか?

プログラム中にエスケープ文字が入っていた可能性があります。ま、文字化けのことは
あまりに気にしないで、肝心のプログラムのことに話を絞りましょう。

私は Perl のことはよくわからないので、具体的なプログラムではなく、手順(アルゴリ
ズム)のレベルで話をしたいと思います。

現在の掲示板は、書き込まれた発言をログファイルに書き込んでおき、それを表示する
ようになっているわけですよね。

ログファイルは各発言が1行になっていて、表示の際は、行ごとに読み込んで、
それを split で項目ごとに分割し、HTML を組み立て、ブラウザへ返す、と。

で、分割表示の話しに入る前に、以下の点を確認させてください。


文字化けもあってよくわからないのですが、ログファイルの内部では、新しい発言が
ファイルの先頭に来るようになっているのですか? それとも、ファイルの末尾に追記
されるようになっているのですか?

柊 甘奈 さんからのコメント
( Friday, June 04, 1999 13:08:49 )

早速、田中先生からのレスありがとうございます。

>文字化けもあってよくわからないのですが、ログファイルの内部では、新しい発言が
>ファイルの先頭に来るようになっているのですか? それとも、ファイルの末尾に追記
>されるようになっているのですか?

そうです。
最新のメッセージが一番上にくるようにプログラムしてあります。

そうです。
ログファイルを二つ用意して、やっておりますし、
一行ごとに1メッセージという書き出しになっています。

御返事楽しみに待ってます。


田中求之 さんからのコメント
( Friday, June 04, 1999 18:04:12 )

>最新のメッセージが一番上にくるようにプログラムしてあります。

でしたら、まず最新の10発言だけを表示するというのは簡単にできますよね。

表示用の CGI で、ファイルを読み込んでから行ごとに表示している部分、つまり

  foreach $line (@lines)

のループの部分を、@line すべての項に対して行うのではなく、最初の10行だけ
表示処理を行うようにすればよいことになります。

そして、残りの発言に関しては、別のページで表示するようにするわけですが、
その場合、CGI の呼び出しの際に search argument で何番目の発言から表示
すればよいかを指定できるという仕様にしておくのが簡単で良いと思います。

つまり、view.cgi を単純に呼び出したときには、最新の10の発言、また、
view.cgi?11 (search argument に 11 を渡す)と、11番目から20番目まで
の10発言を表示するという具合です。そして、発言に残りがあるときには、自
動的にリンクを生成して埋め込むようにしておけば、いいでしょう。

search argument は Perl の場合、環境変数で受け取れるようになっているはず
です。


具体的な Perl のスクリプトは私には書けませんので、やや漠然としたコメント
ですが、参考になれば幸いです。

柊 甘奈 さんからのコメント
( Tuesday, June 08, 1999 15:19:02 )

>でしたら、まず最新の10発言だけを表示するというのは簡単にできますよね。

ごめんなさい。
そこがわからないのです。
どうしたら、10発言だけ表示というものができるのでしょうか?

よろしくお願いいたします。

田中求之 さんからのコメント
( Tuesday, June 08, 1999 19:51:25 )

>どうしたら、10発言だけ表示というものができるのでしょうか?

配列の先頭から10こだけを表示すればいいわけですから、

   foreach $line (@lines)
   {
   }

の部分を

   for($i=0; $i <=10; $i++)
   {
   }

というループに変えて、$line[$i] に対して表示処理を行うようにする
わけですが…

そういう構文の話のことでしょうか??

柊 甘奈 さんからのコメント
( Thursday, June 10, 1999 08:59:04 )

基本なところからありがとうございます。
10発言だけ表示のプログラムを組んでみましたところ、
最新の情報(一番上にくるはずのメッセージ)が飛ばされて
一つ後のメッセージから10表示されるのですけど、
これはどういうことなのでしょうか?

こんな初歩的なことを聞いてスミマセン。
HTMLとJavaScriptを触ったことがある程度ですから......
よろしければ、御教授ください。

表示プログラムは次の通りです。

#Copyright Forest Co.,Ltd.

#ログ用のファイル名とここの"guestlog"の名前を同じにする。
$log_file = 'guestlog1';

#タイトル文字を設定します。
$title = '訪問者リスト';

#背景色を設定します。
$bg_color = '#ffffff';

#フォント色を設定します。
$font_color = '#000000';

print "Content-type: text/html\n\n";
print "<HTML>\n";
print "<HEAD>\n";
print "<META HTTP-EQUIV=\"Content-type\" CONTENT=\"text/html; charset=x-sjis\">\n";
print "<TITLE>bbs</TITLE>\n";
print "</HEAD>\n";
print "<BODY BGCOLOR=$bg_color>\n";
print "<FONT COLOR=$font_color>\n";

dispelements();

print "<CENTER>\n";
print "</CENTER>\n";
print "</FONT>\n";
print "</BODY>\n";
print "</HTML>\n";

sub dispelements
{
  open(FPA,"$log_file");
  $line = <FPA>;

  for($i=1; $i<=10; $i++)
  {
  ($cnt,$name,$ltime,$addr,$home,$mess) = split(/,/,<FPA>);
  
  print "<B><FONT SIZE =4 COLOR = #000080>$cnt</FONT></B>\n";
    if ($addr eq "")
    {
      print " 名前:<B><FONT SIZE =6 COLOR=#ff4040>$name</FONT></B>\n";
    }
           else
    {
      print " 名前:<B><FONT SIZE =6 COLOR=#ff4040><A HREF=\"mailto:$addr\">$name</A></font></B> \n";
    }
    if ($home ne "")
    {
      print "<p></p>\n";
    }
print "$ltime<BR>\n";
    print "<P>$mess<BR>\n";
    print "<HR>\n";
  }
  close(FPA);

}

柊 甘奈 さんからのコメント
( Thursday, June 10, 1999 09:02:39 )

上の私の説明が
わかりにくいかもしれませんので補足させていただきます。

実際は20のメッセージがあり、
その中で最新のメッセージから10発言を表示したい。

つまり、20、19、18〜......11と表示したいのですが、
実際、このプログラムだと、
19、18、17〜.......10と表示されるのです。
確かに10発言の表示はされているのですけど、
最新のものではないの。
これはどういうことでしょうか?

柊 甘奈 さんからのコメント
( Thursday, June 10, 1999 09:13:18 )

更に追加でごめんなさい。
それと、実際のメッセージが10もない場合、
その数分だけしか表示しない、となるとどういう条件にすれば良いのでしょうか?

そんなに一気に聞かないほうがいいですかね?
頭、パンクしちゃうかも.....( ̄口 ̄;)

田中求之 さんからのコメント
( Thursday, June 10, 1999 11:30:36 )

>最新の情報(一番上にくるはずのメッセージ)が飛ばされて
>一つ後のメッセージから10表示されるのですけど、

配列の先頭の Index は1ではなくて、0ですので、

for($i=1; $i<=10; $i++)

の部分を

for($i=0; $i<10; $i++)

にすればいいのでは?

>実際のメッセージが10もない場合、
>その数分だけしか表示しない

配列の数(行数)と 10 との min をとって、その回数だけループで回せばいいわけです。


… Perl に詳しい方、フォローしてねぇ〜

柊 甘奈 さんからのコメント
( Thursday, June 10, 1999 13:21:34 )

>for($i=0; $i<10; $i++)
変更をしたのですが上手くいきません。(T-T)

田中さまはPerlは専門外なそうで、
でも応えてくれるなんて嬉しいです。

最初に読み取り部分が最新のメッセージの部分じゃないんですね。
for文とってみて1つだけ表示してみても
やっぱり、最新の一つ後のメッセージが表示されました。
foreachの時は上手くいったのになんででしょう.....

田中求之 さんからのコメント
( Thursday, June 10, 1999 14:46:04 )

どうせわかんない Perl ですので (^_^;; あてずっぽですが

  open(FPA,"$log_file");
  $line = <FPA>;

  for($i=1; $i<=10; $i++)
  {
  ($cot,$name,$ltime,$addr,$home,$mess) = split(/,/,<FPA>);

の部分を

  open(FPA,"$log_file");
  @line = <FPA>;

  for($i=0; $i<10; $i++)
  {
  ($cot,$name,$ltime,$addr,$home,$mess) = split(/,/,$line[$i]);

にしたらうまくいきませんか?

柊 甘奈 さんからのコメント
( Thursday, June 10, 1999 15:08:36 )

できました!できました!!できました!!!
ちゃんと、最新のメッセージから10発言だけが表示されるようになりました。
教えてもらいながらでも一つ一つ各部署のプログラムが
正常の動くようになると嬉しいものですネ。

さっそく本題にいきます。(やっとです。スミマセン。)
この10までの制限された以外のメッセージを表示する、
掲示板や検索エンジンによくある機能の「次のページへ」というものを作りたいのです。
どうすればいいんでしょうか?
もしかして、少し考えればわかるようなことなのでしょうか?

田中求之 さんからのコメント
( Thursday, June 10, 1999 17:35:15 )

>掲示板や検索エンジンによくある機能の「次のページへ」というものを作りたいのです。
>どうすればいいんでしょうか?

他の方からフォローが無いようなので…

理屈は簡単ですが、Perl でどのように説明したらいいのか調べないといけませんので
今夜までまってください。自宅に戻れば Perl の本はありますので。

なお、CGI を作成し運用するには、その CGI のプログラムに関する知識はどうしても
必要になりますので、Perl に関する知識を身に付けておいてくださいね。自分でも
訳がわからないものを動かしているというのは、サーバ管理者としては褒められた
ものではありませんので。

田中求之 さんからのコメント
( Thursday, June 10, 1999 22:44:46 )

>理屈は簡単ですが、Perl でどのように説明したらいいのか調べないといけませんので
>今夜までまってください。

まず原理ですが、search_argsment (QUERY_STRING) を使って、何番目から表示するかを
指定できるようにします。そして、必要に応じて next / prev のリンクを生成するよう
にします。

で、Perl でのやりかたですが、main の冒頭部分で、

$start_counter = $ENV{"QUERY_STRING"}
if ($start_counter == "") {
  $start_counter = 0
}
$end_counter = $start_counter + 10

というようなスクリプトで、環境変数 QUERY_STRING を $start_counter に入れます。
(空だった場合には 0 を入れます)。また、これに10を加えたものを $end_counter
に入れます。

そして、この2つの変数を dispelements() にパラメータで渡してください。


dispelements() の中の表示のための処理の部分を

  open(FPA,"$log_file");
  @line = <FPA>;

  if ($end_counter > $#line) {
    $end_counter = $#line
  }

  for($i=$start_counter; $i<$end_counter; $i++)
  {
  ($cnt,$name,$ltime,$addr,$home,$mess) = split(/,/,$line[$i]);


というように、$start_counter 行目から $end_counter -1 行目まで表示するように変更します。

こうしておけば、表示用の CGI 、これを view.acgi とするなら、

http://you.host/view.acgi

だと最初から10行が、

http://your.host/view.acgi?10

だと11行目から10行が表示されることになります。ですから、ページを生成する中で、
上記のような URL のリンクを生成するようにするわけです。

なお、上記の Perl のスクリプトは、あくまでも「こういう感じになるはず」というもの
であって、実際に動くかどうかは知りませんので、その辺は、ご自分で Perl を学習しな
がら、動くものに仕上げてくださいね。

田中求之 さんからのコメント
( Friday, June 11, 1999 12:19:18 )

>そして、この2つの変数を dispelements() にパラメータで渡してください。

Perl って、変数は明示的に宣言しないかぎりグローバルなんでしたっけ?
だったら、パラメータ使う必要はないですが。

柊 甘奈 さんからのコメント
( Friday, June 11, 1999 16:53:36 )

ありがとうございます。
まんま、使ってもちょっとの補正でプログラムが上手く動きました。
私なりにセンセーが教えてくださった部分の理念を理解したつもりなのですが、
「次のページ」のリンクの表示非表示の変数の作り方がよくわかりません。
表示非表示とは、最後のページ(もう最初の発言まで戻されてる状態)で
次にメッセージはもうないから、「次のページ」というリンクを表示させないというものですが、

現存する変数で試みたのですが頭が追い付いてません。(TwT)
私なりに一生懸命考えたつもりなんですけど......

#Copyright Forest Co.,Ltd.

#ログ用のファイル名とここの"guestlog"の名前を同じにする。
$log_file = 'guestlog1';


#タイトル文字を設定します。
$title = '訪問者リスト';

#背景色を設定します。
$bg_color = '#ffffff';

#フォント色を設定します。
$font_color = '#000000';

#戻るファイルを指定します。
$back_file = 'http://interjet/internal/index.htm';

$start_counter = $ENV{'QUERY_STRING'};
if ($start_counter == '') {
  $start_counter = 0;
}
$end_counter = $start_counter + 10;

print "Content-type: text/html\n\n";
print "<HTML>\n";
print "<HEAD>\n";
print "<META HTTP-EQUIV=\"Content-type\" CONTENT=\"text/html; charset=x-sjis\">\n";
print "<TITLE>bbs</TITLE>\n";
print "</HEAD>\n";
print "<BODY BGCOLOR=$bg_color>\n";
print "<FONT COLOR=$font_color>\n";

dispelements();

print "<CENTER>\n";

#その状況での変数値を調べるのに特設しました。
print "$start_counter\n";
print "$end_counter\n";
print "$cnt\n";
print "$ENV\n";

#ここのよくわからないのです。
unless(($cnt = $cnt + 9) < 10){
print "<a href='view.cgi?$end_counter'  target='_self'>次のページへ</a>\n";
print "$cnt\n";
$cnt = $cnt - 9;
print "$cnt\n";
}

print "</CENTER>\n";
print "</FONT>\n";
print "</BODY>\n";
print "</HTML>\n";

sub dispelements
{
  open(FPA,"$log_file");
  @line = <FPA>;

 if ($end_counter > $#line) {
    $end_counter = $#line
  }

  for($i=$start_counter; $i<$end_counter; $i++)
  {
  ($cnt,$name,$ltime,$addr,$home,$mess) = split(/,/,$line[$i]);
  
  print "<B><FONT SIZE =4 COLOR = #000080>$cnt</FONT></B>\n";
    if ($addr eq "")
    {
      print " <B><FONT SIZE =6 COLOR=#ff4040>$name</FONT></B>\n";
    }
           else
    {
      print " <B><FONT SIZE =6 COLOR=#ff4040><A HREF=\"mailto:$addr\">$name</A></font></B> \n";
    }
    if ($home ne "")
    {
      print "<p></p>\n";
    }
print "$ltime<BR>\n";
print "$cnt\n";
    print "<P>$mess<BR>\n";
    print "<HR>\n";
  }
  close(FPA);

}

田中求之 さんからのコメント
( Friday, June 11, 1999 17:30:46 )

>最後のページ(もう最初の発言まで戻されてる状態)で
>次にメッセージはもうないから、「次のページ」というリンクを表示させない

$end_counter と @line の配列の数を比較して、$end_counter の方が小さければ、
まだ表示していないデータ(行)があると判断できますので、

if ($end_counter < $#line) {
  print "<a href='view.cgi?$end_counter'  target='_self'>次のページへ</a>\n";
}

とすればよいはずです。

きむら さんからのコメント
( Friday, June 11, 1999 21:14:09 )

すばらしいフォローです。私はなかなかそこまでできないので、微力ながら(^^;)

> Perl って、変数は明示的に宣言しないかぎりグローバルなんでしたっけ?

ふつうはそのとおりです。

柊 甘奈 さんからのコメント
( Monday, June 14, 1999 17:11:45 )

田中先生、きむら様、ありがとうございます。
改ページのプログラムできました。

田中先生はPerlのこと詳しく知らないのに
ここまで助言してくださってありがとうございます。
私もPerlのこともっと勉強します。

ところで、Perlのことかどうかわからないのですが、
よく、ブラウザ上で掲示板などの設定を変更するタイプのものがありますよね?
それも掲示板のCGIファイルのなかに組み込むのですか?
それとも別物のプログラムとして考えるのですか?

田中求之 さんからのコメント
( Monday, June 14, 1999 23:21:20 )

>田中先生はPerlのこと詳しく知らないのに
>ここまで助言してくださってありがとうございます。

いえいえ、少しはお役に立って何よりです。わたしも Perl の勉強をさ
せてもらいました。

… Perl はやっぱ自分には合わないなぁと実感しました (^_^;;