Creating a simple CGI message board
The HTML Templates
The Bulletin Board Script
Displaying the Message List
Displaying Messages
Adding New Messages
Adding Replies
Expiring Messages
The Complete Bulletin Board Script
Expiring Messages
You could set up a working bulletin board with the four subroutines you have created. However, eventually, your bulletin board will contain messages that are very old. Of course, you could always delete the message files yourself, but then you would also need to modify the list files, removing the header information for any message files you deleted. Handling the expiration of messages is a job better left to your bulletin board script. It can easily check messages for expiration, removing all reference to them in the list files when deleting them. It can also regularly check for expiration, keeping your bulletin board fresh for everyone who uses it.
The Expires subroutine you develop in this section checks the date when each message file was last modified. Because the message files are only modified when they are created, this is the date the file was created. If the number of days since the file was created is greater than a number you specify, the Expires subroutine deletes the message file and removes the header information from the list file, unless the header information is for the main message (the first message in the list file) and there are headers for replies in the same file. If the main message is the only file in the list file, and its message file has expired or no longer exists (if it had previously expired but the header was not removed because of replies), the list file is also deleted.
Your Expires subroutine is similar in structure to the Display_Message_Lists subroutine. The Expires subroutine starts by looping over all of the message list files, opening each one and reading in all the lines containing message and reply header information. If there are any reply headers, an inner loop breaks apart each reply header, checking whether the corresponding message file has expired. If it has, the message file is deleted and the reply header is removed from the file. Once the inner loop has completed, the header for the main message is split apart, and the corresponding message file is checked for expiration (or to see whether it even exists).
As in the Display_Message_Lists subroutine, you use for loops for both the inner and outer loop. The outer loop needs to be executed once for every list file. So, you once again open the count.dat file in the message-lists subdirectory and use that value as the upper bounds for your outer for loop. The following four lines of Perl code open the count.dat file, retrieve the number that was last used for a list file name, and start the outer loop:
open(LISTS,"$list_count") || die "Content-type: text/html\n\nCannot open list
count!";
$num_lists = <LISTS>;
close(LISTS);
for ($i=1; $i<=$num_lists; $i++) {
Once inside the body of the outer loop, you need to open the list file and read in all the contents of the file. To do so, you use the following three lines of Perl, which are identical to the lines you used to open and read in the contents of the list file in the Display_Message_Lists subroutine:
open(LIST, "$list_dir/$i") || next; @list_contents = <LIST>; close(LIST);
Next, you need to initialize a new array, one that you did not use in the Display_Message_Lists subroutine. You do this with the Perl statement
@new_list_contents = ($list_contents[Ø]);
This new array stores the contents for the new list file that replaces the old list file. Because you will remove header lines from the @list_contents array when the message file for the reply expires, you need to create a new list file that reflects these deletions. To do this, you use the @new_list_contents array. In the preceding line, you initialize the array to contain only the header for the main message. During the inner loop that you will develop in a moment, you append to the @new_list_contents array the header information for all replies that have not expired.
You now need to begin the inner loop, which will loop over the reply header lines in the @list_contents array. First, you should check whether there are any reply headers. Because the first element in the @list_contents array always contains the header information for the main message, you can check for the existence of any replies by checking the size of the @list_contents array. If the length of the @list_contents array is greater than 1, it contains reply headers. So, you can surround your inner for loop with an if conditional that checks the length of the @list_contents array. The following lines of Perl contain the if conditional and the inner for loop for the Expires subroutine:
if (@list_contents > 1) {
for ($j=1; $j<@list_contents; $j++) {
($name, $subject, $message_date, $message_file) =
split(/::/, $list_contents[$j]);
chop $message_file;
if (-M "$message_dir/$message_file" > $expires) {
unlink "$message_dir/$message_file";
} else {
push(@new_list_contents, $list_contents[$j]);
}
}
open(LIST, ">$list_dir/$i") || die "Content-type: text/html\n\nCannot open list
file for output!";
print LIST @new_list_contents;
close(LIST);
}
The first several lines should look familiar. They are identical to lines from the Display_Message_Lists subroutine. But the second if statement is different. The conditional expression for this if statement checks whether the number of days since the message file (which you just got from the splitting the header information in the previous lines) was last modified is greater than the value in the $expires variable. The -M is a special Perl file operator that, when given a file name as a parameter, returns the number of days since the file was last modified. The $expires variable is a global variable that you set at the beginning of your bulletin board script. For this example, you will set this variable to 90. So, when the Expires subroutine is called, it deletes any files and header lines for messages and replies that are over 90 days old. By modifying the value you assign to the $expires variable, you can control how long messages remain on your bulletin board.
If the conditional expression in the inner if statement evaluates to true--meaning the file is older than 90 days--the message file is deleted with the Perl unlink statement. If the file is not older than 90 day, the header information is placed in the last element of the @new_list_contents array. This if statement is evaluated for each of the reply headers in the @list_contents array. After the for loop has finished, you have the new contents for your list file stored in the @new_list_contents array. So, the next few lines after the for loop open the list file for output and replace the old list contents with the new list contents.
At this point in the Expires subroutine, you have only expired replies to the main message. You have not yet checked whether the main message itself has expired. This is left until last so you already know whether there are still any unexpired replies. If not, and the main message file has expired, you will also delete the message list file, because you no longer have any messages or replies for that message list.
To finish the Expires subroutine, you need to check the message file for the main message for expiration. Before doing so, you need to split apart the header information for the main message. You do this with the following lines of Perl code:
($name, $subject, $message_date, $message_file) = split(/::/, $list_contents[Ø]); chop $message_file;
Next, you should check whether there are any replies. Remember that you placed any unexpired reply headers in the @new_list_contents array. Also remember that you initialized the @new_list_contents array with the header information for the main message before you entered the inner loop. Because you already placed at least one element in the @new_list_contents array, if the length is not equal to 1, you know there are reply headers in the @new_list_contents array. Otherwise, there is just the header information for the main message. You use the following if...else block to check the main message file for expiration:
if (@new_list_contents == 1) {
if (-e "$message_dir/$message_file") {
unlink "$list_dir/$i", "$message_dir/$message_file"
if (-M "$message_dir/$message_file" > $expires);
} else {
unlink "$list_dir/$i";
}
} else {
unlink "$message_dir/$message_file"
if ( (-e "$message_dir/$message_file") &&
(-M "$message_dir/$message_file" > $expires) );
}
The preceding if...else block uses a conditional that is true when the @new_list_contents array only contains the header information for the main message. That is, the lines of code under the if portion of the if...else are only executed when there are no unexpired reply headers, whereas the else portion is only executed when there are unexpired reply headers.
The lines of code within the if portion contain another if...else statement block. This if...else uses the -e Perl file operator, which evaluates to true when the specified file (the main message file in this case) exists. You need to check whether the file exists because it could have already expired and been deleted. Remember that the header information for the main message remains as long as there are reply headers in the same file. So, even if the main message file has already been deleted, the main message header information could still exist. This if...else block checks whether the main message file still exists. If it does, the statements under the if portion of the if...else block are executed. These lines use the Perl unlink statement to remove both the main message file and list file if the main message file has expired. Keep in mind that these statements are only executed if there are no replies. So, if the main message file is expired, you no longer need the message list file either. The else portion of this inner if...else block is executed only when the main message file doesn't exist. Again, there are no replies left, so you can delete the message list file.
The lines of code under the else portion of the outer if...else portion are only executed when there is still header information for replies. In this case, you only need to delete the main message file if it has expired. Whether or not you delete the main message file, you still keep the message list file because it contains the headers for replies. You use the Perl unlink statement to delete the message file if it both exists and is expired.
You now have all the code you need for the Expires subroutine. Listing 7 puts together all the code you have developed. The only new lines are the subroutine and local variable declarations.
sub Expires {
local (@lists, @list_contents, @new_list_contents,
$num_lists, $i, $j, $name, $subject, $message_date,
$message_file, $header, $replies);
open(LISTS,"$list_count") || die "Content-type: text/html\n\nCannot open list
count!";
$num_lists = <LISTS>;
close(LISTS);
for ($i=1; $i<=$num_lists; $i++) {
# Open the message list file. If it cannot be
# opened, assume it has been deleted and go
# to the next message list.
# Windows users need to change the string "$list_dir/$i" to
# "$list_dir\\$i"
open(LIST, "$list_dir/$i") || next;
@list_contents = <LIST>;
close(LIST);
# Start the creation of the new header file list by placing the main message
in
# the first element of the @new_list_contents array
@new_list_contents = ($list_contents[Ø]);
# If the header file only contains 1 message, there are no replies.
if (@list_contents > 1) {
# The message list contains replies, so check to
# see if they have expired.
for ($j=1; $j<@list_contents; $j++) {
# Split each message header from the message list file.
($name, $subject, $message_date, $message_file) =
split(/::/, $list_contents[$j]);
chop $message_file;
# This checks to see if the associated message file has expired. If so
# the message file is deleted.
# Windows users need to change the "$message_dir/$message_file" strings
# below to "$message_dir\\$message_file"
if (-M "$message_dir/$message_file" > $expires) {
unlink "$message_dir/$message_file";
} else {
# The message file has not expired, so add the header line to
# the new contents of the header file stored in the @new_list_contents
array.
push(@new_list_contents, $list_contents[$j]);
}
}
# Create the new header file.
# Windows users need to change the string "$list_dir/$i" to
# "$list_dir\\$i"
open(LIST, ">$list_dir/$i") || die "Content-type: text/html\n\nCannot open
list file for output!";
print LIST @new_list_contents;
close(LIST);
}
# Split each message header from the message list file.
($name, $subject, $message_date, $message_file) =
split(/::/, $list_contents[Ø]);
chop $message_file;
# Check to see if the main message has expired.
if (@new_list_contents == 1) {
# The list only contains the original message (no replies)
# Windows users need to change the string "$message_dir/$message_file" to
# "$message_dir\\$message_file"
if (-e "$message_dir/$message_file") {
# If the message has expired delete the message file
# and the list file.
# Windows users need to change the "$list_dir/$i" and
# "$message_dir/$message_file" strings below to
# "$list_dir\\$i" and $message_dir\\$message_file"
unlink "$list_dir/$i", "$message_dir/$message_file"
if (-M "$message_dir/$message_file" > $expires);
} else {
# The original message file does not exist, so delete
# the list.
# Windows users need to change the string "$list_dir/$i"
# to "$list_dir\\$i"
unlink "$list_dir/$i";
}
} else {
# The list contains the original message and replies. Delete only the main
# message file if it exists and has expired.
# Windows users need to change the "$message_dir/$message_file" strings
# below to "$message_dir\\$message_file"
unlink "$message_dir/$message_file"
if ( (-e "$message_dir/$message_file") &&
(-M "$message_dir/$message_file" > $expires) );
}
}
}
|