📄 ch18.htm
字号:
foreach ( @body ) { print
":: $_"; }<BR>
print "</TEXTAREA></PRE>\n"
,<BR>
"<INPUT TYPE=SUBMIT NAME=\"submit\" VALUE=\"Submit
this Article\">\n" ,<BR>
"<INPUT TYPE=RESET NAME=clear Value=\"Clear this
form\">\n" , "</FORM>\n";<BR>
}<BR>
}<BR>
<BR>
sub read_article {<BR>
<BR>
local ($post_id);<BR>
<BR>
($post_id) = @_;<BR>
<BR>
open(POST,"$post_id.post");<BR>
&read_header;<BR>
@body = <POST>;<BR>
close(POST);<BR>
<BR>
}<BR>
<BR>
sub read_header {<BR>
<BR>
chop($author = <POST>);<BR>
($discard,$author) = split(/\t/,$author);<BR>
chop($email = <POST>);<BR>
($discard,$email) = split(/\t/,$email);<BR>
chop($subject = <POST>);<BR>
($discard,$subject) = split(/\t/,$subject);
<BR>
chop($to = <POST>);<BR>
($discard,$to) = split(/\t/,$to);<BR>
chop($time = <POST>);<BR>
($discard,$time) = split(/\t/,$time);<BR>
<BR>
}<BR>
<BR>
sub print_posting {<BR>
<BR>
print "<HR>\n";<BR>
<BR>
&read_article($prefix);<BR>
<BR>
if ( $email ne '' ) {<BR>
print "<A HREF=mailto:$email>$author</A>
";<BR>
} else {<BR>
print "$author ";
<BR>
}<BR>
print "on <I>$time</I> said:\n";
<BR>
print "<H2>$subject</H2>\n";
<BR>
<BR>
foreach $line ( @body ) {<BR>
$line =~ s/\n/<BR>\n/g;
<BR>
print $line;<BR>
}<BR>
print "<BR><HR>\n";<BR>
<BR>
}<BR>
<BR>
sub print_footer {<BR>
<BR>
print "</BODY></HTML>\n";
<BR>
<BR>
}</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
My general philosophy when programming is to find the sneakiest
way of doing something. I try to use the tools of a language to
do something that likely no one ever thought of doing before.
I do this to avoid real work.
<P>
There are a great number of studied and well understood data structures
in computer science that people use to get jobs done. When you
need a solution, you go to your books or your source code libraries
and invoke them as prescribed. Unless absolutely necessary, I'm
too much of a loner to plug in someone else's code, and I'm too
lazy to recode a classical solution from the ground up. Instead,
I come up with a hack.
<P>
My hack for this program is to name data files so that the postings
are automatically threaded for me, more or less. A top-level posting
(one with no parent) will be named XXXXX-post, where the <I>X</I>s
are digits. The program assigns 00000 to the first post, and each
subsequent first-level post is given the value of the most recent
post plus 1. A reply to this will be XXXXX.XXXXX-post. A reply
to this would be XXXXX.XXXXX.XXXXX-post, and so on. I get these
in the correct order by the Perl command
<BLOCKQUOTE>
<TT><FONT FACE="Courier">@posts = 'ls -1r *-post';</FONT></TT>
</BLOCKQUOTE>
<P>
The automatic ASCII collating of the <TT><FONT FACE="Courier">ls</FONT></TT>
command will arrange the postings in order of oldest to newest,
given the numbering scheme I described in the preceding paragraph.
The <TT><FONT FACE="Courier">-1r</FONT></TT> flag reverses the
ordering so that the newest postings will be on the top of the
list. The program keeps a count of how many dots (<TT><FONT FACE="Courier">.</FONT></TT>)
there are within a file's name, and this acts as the basis for
determining which level of the "family tree" a posting
rests on.
<P>
The bistate nature of this discussion forum is controlled by the
<TT><FONT FACE="Courier">QUERY_STRING</FONT></TT> environment
variable. Though all posting-oriented forum information is passed
to the program through the form using the <TT><FONT FACE="Courier">POST</FONT></TT>
method, I'm "manually" post-pending a query string to
the URL that invokes the chat room. If this string is empty, the
CGI program knows that it is being called directly and that it
should show its "entry-level" face. If there is a string
there, it displays the posting that corresponds to that string
and the family of postings that surround that posting. This string
is put to use quite simply. Called <TT><FONT FACE="Courier">$prefix</FONT></TT>,
the place where it is most important is
<BLOCKQUOTE>
<TT><FONT FACE="Courier">@posts = 'ls -1r $prefix*-post';</FONT></TT>
</BLOCKQUOTE>
<P>
This is the form of the line that is actually in use within the
program. With this simple trick, the CGI program will focus in
on only those posts that are relevant to the user's needs.
<H2><A NAME="DiscussionForumAdministration"><FONT SIZE=5 COLOR=#FF0000>Discussion
Forum Administration</FONT></A></H2>
<P>
In computer programming, even common sense questions have to be
answered explicitly: once postings are entered into the discussion
forum, how do they get removed? Do they ever get removed?
<P>
The answer to the second question is yes-of course they get removed.
A discussion forum with several hundred thousand messages in it
is just about as worthless as one with no messages at all. The
problem then becomes finding a good way to accomplish these posting
removals. I'll outline a few popular options.
<H3><A NAME="RemovePostingbyDate">Remove Posting by Date</A></H3>
<P>
This is a very popular option because it allows a very natural
organization: old postings go, and new ones stay. There are three
ways to program this feature: manually controlled by the discussion
forum administrator, automatically by the discussion forum, or
through an auxiliary program as a <TT><FONT FACE="Courier">cron</FONT></TT>
job.
<P>
The <TT><FONT FACE="Courier">cron</FONT></TT> command is a UNIX
daemon that runs other commands according to time-based rules
found within the various crontab files. These rules tell <TT><FONT FACE="Courier">cron</FONT></TT>
which command to run, how, and when. If the <TT><FONT FACE="Courier">cron</FONT></TT>
method is used to clean out old postings within the discussion
forum, the solution isn't strictly CGI. The discussion forum programmer
would be responsible for writing a crontab file that details how
<TT><FONT FACE="Courier">cron</FONT></TT> should run another program,
written to clean out the discussion forum.
<P>
If the job of cleaning out old postings is given to the discussion
forum CGI itself, the strategy would be that whenever the discussion
forum was activated, it would scan through its postings and remove
ones that fit some programmed criteria of age.
<H3><A NAME="RemoveThreadbyDate">Remove Thread by Date</A></H3>
<P>
This option for removing articles in a discussion forum will look
for the first (and therefore oldest) file in a thread and will
delete all postings in that thread. The mechanisms that invoke
this option are identical to those for removing individual postings
by date.
<H3><A NAME="RemovePostingbyAuthor">Remove Posting by Author</A>
</H3>
<P>
This is a handy feature to have in the event that a discussion
forum is graced by a less than graceful individual. Programming
this sort of removal is highly dependent upon the programming
language being used and the overall organization of the discussion
forum.
<H3><A NAME="RemoveIndividualPostings">Remove Individual Postings</A>
</H3>
<P>
This option can be the easiest to program but requires the most
effort on the part of the discussion forum administrator. The
only consideration with this option is how the program will deal
with threads left tattered by the procedure. In discussion forum
setups based on data files and dynamic generation of lists, this
problem wouldn't normally be too great. With discussion forums
that deal with complete HTML files, the easy part is removing
the data file. The hard part is hunting through all the other
HTML files for references to that file and killing those references.
Re-threading the discussion forum might be appropriate or needed.
<H3><A NAME="RemoveIndividualThreads">Remove Individual Threads</A>
</H3>
<P>
This option is the same as the preceding but focuses on whole
threads rather than individual postings.
<P>
The following code, Listing 18.2, will remove postings from forum.cgi
by threads, authors, dates, and individual postings. I have not
included any automatic date removal features.
<HR>
<BLOCKQUOTE>
<B>Listing 18.2. <FONT SIZE=2 FACE="MCPdigital-B">admin.cgi</FONT>-The
Discussion Forum administration program that accompanies <FONT SIZE=2 FACE="MCPdigital-B">forum.cgi</FONT>.
<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">#!/usr/bin/perl<BR>
<BR>
#<BR>
# This program was written by Richard Dice of Anadas Software
Development<BR>
# as part of the Sams Net "CGI Programming Unleashed"
book. The author<BR>
# intends this code to be used for instructional purposes and
not for<BR>
# resale or commercial gain.<BR>
#<BR>
# Any questions or comments regarding this program are welcome. You
<BR>
# may contact the author by Internet email: rdice@anadas.com<BR>
#<BR>
<BR>
#<BR>
# the timelocal.pl library is needed for access to the &timegm()
subroutine,<BR>
# which I use in the sorting of dates<BR>
#<BR>
require "timelocal.pl";<BR>
<BR>
# -- Configuration Variables --<BR>
$prog_url = 'http://www.anadas.com/cgiunleashed/discussion/admin.cgi';
<BR>
$forum_url = 'http://www.anadas.com/cgiunleashed/discussion/forum.cgi';
<BR>
<BR>
# -- start of GET/POST method handler --<BR>
<BR>
# puts all POST query information the variable $input_line<BR>
read(STDIN, $input_line, $ENV{CONTENT_LENGTH});<BR>
<BR>
# replace all '+' coded spaces with real spaces<BR>
$input_line =~ tr/+/ /;<BR>
<BR>
# creates array of all data files in $input_line from & separated
info<BR>
@fields = split(/\&/,$input_line);<BR>
undef($input_line); # free up memory<BR>
<BR>
#<BR>
# decodes hex info for each name/value pair and places pairs in
<BR>
# %input associative array<BR>
#<BR>
foreach $i (0 .. $#fields) {<BR>
($name,$value) = split(/=/,$fields[$i]);<BR>
$name =~ s/%(..)/pack("c",hex($1))/ge;
<BR>
$value =~ s/%(..)/pack("c",hex($1))/ge;
<BR>
$input{$name} = $value;<BR>
}<BR>
<BR>
# --- end of GET/POST method handler ---<BR>
<BR>
#<BR>
# should this page be accessed with form data and having a refering
URL<BR>
# different from $prog_url, someone is attempting to post data
from<BR>
# an invalid form -- exit the program with error message<BR>
#<BR>
if ( defined(%input) || ($ENV{HTTP_REFERER} ne $prog_url) ) {
<BR>
&referer_error;<BR>
}<BR>
<BR>
#<BR>
# Program will switch on the query string. Also, the
program is being<BR>
# invoked via a form submission, then actual deleting of postings
needs<BR>
# to be done and not just displaying the option which queries
for which<BR>
# postings to delete<BR>
#<BR>
$mode = $ENV{QUERY_STRING};<BR>
if ( !defined($input{'method'}) ) {<BR>
&query_remove if ( ($mode eq 'posts') ||
($mode eq 'thread') );<BR>
&query_date_remove if $mode eq 'date';<BR>
&query_author_remove if $mode eq 'author';
<BR>
} else {<BR>
&remove_posts if $input{'method'} eq 'posts';
<BR>
&date_remove if $input{'method'} eq 'date';
<BR>
&thread_remove if $input{'method'} eq 'thread';
<BR>
&author_remove if $input{'method'} eq 'author';
<BR>
}<BR>
<BR>
#<BR>
# if no query string nor $input{'method'} is found in the invoking
of this<BR>
# page, present an intro page which supplies a menu of options
<BR>
#<BR>
&intro_page;<BR>
<BR>
exit 0;<BR>
<BR>
#<BR>
# display this page if an invalid form submission is being made
<BR>
#<BR>
sub referer_error {<BR>
<BR>
print <<END;<BR>
<HTML><HEAD><TITLE>Refering URL Error!</TITLE></HEAD>
<BR>
<BODY><BR>
<P><BR>
The form which was submitted to this CGI program did not originate
with<BR>
this program. This is forbidden.<BR>
<P><BR>
<A HREF=$prog_url>Return to the Discussion Forum</A>
<BR>
</BODY></HTML><BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -