Приглашаем посетить
db_search
#!/usr/local/bin/perl
# Name: db_search.cgi
#
# Version: 5.02
#
# Last Modified: 01-23-97
#
# Copyright Information: This application was written by Selena Sol
# (selena@eff.org, http://www.eff.org/~erict) and Gunther Birznieks
# (birzniek@hlsun.redcross.org) having been inspired by
# countless other Perl authors. Feel free to copy, cite, reference,
# sample, borrow, resell or plagiarize the contents. However, if you
# don't mind, please let me know where it goes so that I can at least
# watch and take part in the development of the memes. Information wants
# to be free, support public domain freware. Donations are appreciated
# and will be spent on further upgrades and other public domain scripts.
#######################################################################
# Flush the Perl Buffer. #
#######################################################################
# The script begins by telling the Perl interpreter that
# it should continuously flush its buffer so that text
# from this script is sent directly to the Web Browser.
# We do this to streamline debugging and make sure that
# the script operates with the flow we want it to.
$| = 1;
# Also, send out the http header early for easy debugging
# and so that the server will not time us out if we take a
# while to process.
print "Content-type: text/html\n\n";
#######################################################################
# Read and Parse Form Data #
#######################################################################
# Next, the ReadParse subroutine in cgi-lib.pl is used to
# read the incoming form data. However, the subroutine is
# sent "form_data" as a parameter so that the associative
# array of form keys/values comes back with a descriptinve
# name rather than just %in.
&require_supporting_libraries (__FILE__, __LINE__,
"./Library/cgi-lib.pl",
"./Library/db-lib.pl",
"./Library/auth-lib.pl");
&ReadParse(*form_data);
#######################################################################
# Load Supporting Files #
#######################################################################
# Once it has read the incoming form data, the script
# will be able to determine which setup file it should
# use to process the incoming form data.
#
# Perhaps a bit of explanation is in order.
#
# Whenever you run this application, you must pass to it
# the name of the setup file which it will use to process
# the search request.
#
# This variable will provide the name of the file which
# this script will use to define all of the customizable
# aspects of its operation. For example, the setup file
# defines what is contained in the database and which
# fields should be displayed to the user.
#
# The reason for this is that this one script can handle
# an infinite amount of databases.
#
# Each database has a corresponding setup file which defines
# how the script performs. The logic (and programming)
# remains the same for all db's. All that changes are
# the variables and subroutines in the setup files. This
# makes it very easy for you to quickly generate diverse
# databases with the one backend.
#
# The script first takes the value of "setup_file"
# coming in from the form (which cgi-lib.pl has already
# parsed into the %form_data associative array) and
# assigns it to the variable $setup_file.
#
# So how do you get this information to the script?
#
# There are two ways to do that. Firstly, you can encode
# the information into the URL if you are executing this
# script directly from a hyperlink.
#
# For example, you might use the following hyperlink to
# direct the script to access address_book.setup:
#
# http://www.you.com/cgi/db_search.cgi?setup_file=address_book.setup
#
# You can also send this information as a hidden field in
# an HTML form using something like the following.
#
# <INPUT TYPE = "hidden" NAME = "setup_file"
# VALUE = "[NAME OF SETUP FILE]">
#
# For example, the following code would define a setup
# file called address_book.setup:
#
# <INPUT TYPE = "hidden" NAME = "setup_file"
# VALUE = "address_book.setup">
#
# You might also create a select box so that the user can
# choose from a number of databases dynamically:
#
# <SELECT NAME = "setup_file">
# <OPTION VALUE = "address_book.setup">Address Book
# <OPTION VALUE = "customer_list.setup">Customers
# </SELECT>
#
# The script uses the subroutine require_supporting_libraries
# documented later in this script to actually load the
# setup file and all of its configuration options.
#
# Once the setup file has been loaded, the script also
# uses the require_supporting_libraries subroutine to
# load the mail library which we will use to send email to
# the form administrator.
$setup_file = $form_data{'setup_file'};
&require_supporting_libraries (__FILE__, __LINE__,
"./Setup_/files/010/$setup_file");
#######################################################################
# Perform Authentication #
#######################################################################
if ($should_i_authenticate eq "yes")
{
$session_file = "$form_data{'session_file'}";
($session_file, $session_username, $session_group,$session_first_name,
$session_last_name, $session_email) =
&GetSessionInfo($session_file, $this_script_url, *form_data);
}
#######################################################################
# Display Search Screen #
#######################################################################
# There are really only two things required of this
# script. Firstly, it will have to give the user a form
# in which she can submit her search criteria and
# secondly, it must process her search request.
#
# The way the script knows which task it is being asked to
# perform depends on a special HTML form submit button.
# When the user clicks on the submit button "Submit the
# Search Request" the script will know that it is supposed
# to search.
#
# Of course you can have the button say anything you want,
# the important thing is that you have the submit button
# on the bottom of your frontend form which looks like
# this:
#
# <INPUT TYPE = "submit" NAME = "submit_search"
# VALUE = "[INSERT YOUR OWN LABEL HERE]">
#
# The other thing this script does, as we said, is create
# the search form on which the above button appears. The
# script does this by accessing the output_html_query_form
# subroutine in the setup file which basically prints out
# an HTML form for you which you can create or customize
# as you desire.
#
# Thus, the following routine asks, "if thre is no value
# for the submit button, that means I am not be asked to
# do a search...therefore, I must be being asked to
# display the search form!" It does so and then quits.
#
# By the way, you can bypass this form generated script
# and use your own so long as you have the correct submit
# button on your form. You can also hardcode searches by
# including the submit_search parameter as URl encoded
# data. Maybe something like the following:
#
# http://www.you.com/cgi/db_manager.cgi?setup_file=customer_list.setup&submit_search=yes&lname=B
#
# This would theoretically access a setup file called
# customer_list.setup and would search the database set in
# that setup file for the lname field for all names
# starting with B.
if ($form_data{'submit_search'} eq "")
{
&output_html_query_form;
exit;
}
#######################################################################
# Search the Database #
#######################################################################
# As we said, the other thing that you can do with this
# script is to actually search the database which is
# defined in the setup file.
#
# The script begins by sending out the beginning of the
# HTML response to the client defined in
# search_results_header which is located in the setup
# file.
#
# Then we are ready to begin returning search results.
#
# Most of the work for search is done by db-lib.pl.
# The script access the submit_query subroutine passing it
# an array database_rows by reference (which means that
# the subroutine is going to fill that array with
# database rows which were matches to the users search
# criteria directly and not pass it back when it is done),
#
# It will also expect to be returned a total row count of
# successful hits.
#
# The working of the search are covered in depth in
# db-lib.pl so go there if you are still confused.
else
{
# Before we go in and search however, we format any
# incoming sort_by information. We'll discuss the sorting
# algorithm in just a minute. However, I want to note
# here that there are two ways to define a field by which
# this script will sort the returned database rows. You
# can set a default row in the setup file by setting
# $index_of_field_to_be_sorted_by equal to the index of
# the field that you want sorted by. Thus, you may just
# want to sort automatically by last name and not even
# give the user the option to sort by another row.
#
# On the other hand, you might want to allow the user to
# choose which field the returned rows are sorted by. If
# this is the case, you need to add another form variable
# to your HTML interface. This variable MUST be called
# "sort_by" and will usually be in the form of a
# select box such as the following:
#
# <TH>Sort by which field</TH>
# <TD><SELECT NAME = "sort_by">
# <OPTION VALUE = "0">First Name
# <OPTION SELECTED VALUE = "1">Last Name
# <OPTION VALUE = "2">Email
# <OPTION VALUE = "9">Age
# </SELECT></TD>
#
# If you allow the user to define which field to sort on,
# then this information will override the information in
# the setup file using the following it test.
#
# Remember that arrays start counting from zero so the
# first filed in your dataabse has an index value of 0,
# not 1
if ($form_data{'sort_by'} ne "")
{
$index_of_field_to_be_sorted_by = $form_data{'sort_by'};
}
# okay, now display the header and grab our lisdt of
# database rows using &submit_query in db-lib.pl. Notice
# that you need to redefine
# $index_of_field_to_be_sorted_by "before" you display the
# header because the header displays the hidden form field
# which will carry that data throughout further
# self-referencing screens.
&search_results_header;
($total_row_count) = &submit_query(*database_rows);
# Now here is where the real fun comes in. We want to
# sort the database rows that are displayed to the user.
# The process of this is fairly simple. For every
# database row contained in @database_rows, we are going
# to grab the value of the field defined as the field to
# be sorted by and append that value to the very begining
# of the line (so that the field will be repeated twice.)
# Then you sort the rows (sort will sort on the first
# characters first which is why you need to append the
# sortable field to the front.) Then, finally, you remove
# the appended field so that the database rows are as they
# began, but in a sorted order.
#
# Thus, if you were sorting by last name and you had the
# following database rows ($row) in the @database_rows
# array:
#
# Eric|Tachibanaerict@eff.org
# Selena|Sol|selena@eff.org
# Gunther|Birznieks|birzniek@hlsun.redcross.org
#
# The script would then take each row and append the last
# name field to the front like so:
#
# Tachibana|Eric|Tachibana|erict@eff.org
# Sol|Selena|Sol|selena@eff.org
# Birznieks|Gunther|Birznieks|birzniek@hlsun.redcross.org
foreach $row (@database_rows)
{
@row = split (/\|/, $row);
$sortable_field = $row[$index_of_field_to_be_sorted_by];
unshift (@row, $sortable_field);
$new_row = join ("\|", @row);
push (@new_rows, $new_row);
}
# Once we have the rows reformatted as above, we are ready
# to sort them. First however, we erase the contents of
# @database_rows since we are going to want to recreate
# that array with the sorted rows from @new_rows in just a
# moment.
@database_rows = ();
# Then we are ready to sort...guess what the result is:
#
# Birznieks|Gunther|Birznieks|birzniek@hlsun.redcross.org
# Sol|Selena|Sol|selena@eff.org
# Tachibana|Eric|Tachibana|erict@eff.org
@sorted_rows = sort (@new_rows);
# Next, we need remove that first sortable field so that
# we have the following:
#
# Gunther|Birznieks|birzniek@hlsun.redcross.org
# Selena|Sol|selena@eff.org
# Eric|Tachibana|erict@eff.org
#
# Look! They are now sorted by last name! By the way, if
# you sort by a field with numbers, remember that
# computers sort with their own funky rules. That is, if
# you don't put a 0 before the nuber 1, it will sort after
# 9 but alphabetical sorting should be just fine.
foreach $sorted_row (@sorted_rows)
{
@row = split (/\|/, $sorted_row);
$sorted_field = shift (@row);
$old_but_sorted_row = join ("\|", @row);
push (@database_rows, $old_but_sorted_row);
}
# now that we have sorted the rows, lets figure out how to
# display them all.
#
# The reason that we wanted to get the $total_row_count
# back from the search libraries is so that we can then
# check to make sure that if their search returned no hits
# we can let them know rather than just sending them a
# blank screen.
if ($total_row_count < 1)
{
&no_hits_message;
exit;
}
# So what exactly do we show the user if their search did
# turn up some hits. Well, that depends on 1) how many
# rows were returned from the database as scoring matches
# to their search criteria, 2) how many rows we have
# defined in the setup file to allow them to see and 3)
# how many rows they have already seen.
#
# Let me expound. Let's assume that we have set
# $max_rows_returned to 2 in the setup file and that their
# search turned up 11 hits which have just been sorted.
#
# The first screen that they should see should say, "You
# scored 10 hits and I have been instructed to show you
# two at a time". It should then display the first two
# sorted rows and then provide a button which says "See
# next 2 hits". When the user clicks on that button, she
# should then get the next two sorted rows. The script
# needs to remember that she already saw the first two
# rows as well as remembering that it should only show her
# two at a time.
#
# Finally, the script will have gone through all the rows
# up to 9 and 10. The final trick is that it must then
# tell her that she can click the button to see the next 1
# hi(t)...no "s" on the end of that....the script has to
# know some grammar rules.
#
# So first, we will collect any incoming information about
# the hits that the client has seen so far. This
# information will be stored in a hidden inpout field
# called "hits_seen" which must accompany every submit
# button that promises to show "x more hits". Note that
# the first time around, there will be no new_hits_seen
# value coming in from the form since the user will not
# have seen any hits yet.
$hits_seen = $form_data{'new_hits_seen'};
# Now the script will go through and remove from
# @database_rows all of the rows that have already been
# seen by shifting off (remove from the front of
# the array) one element up to the value of $hits_seen
for ($i = 1;$i <= $hits_seen;$i++)
{
$seen_row = shift (@database_rows);
}
# Then we need to remove all of the rows from
# @database_rows that will not be shown to the user quite
# yet because we are only allowed to display
# max_rows_returned at any one time.
#
# To do this we will first figure out how many elements
# are left in the array. Then, we will pop out (remove
# from the end of the list) all of the extra rows.
$length_of_database_rows = @database_rows;
for ($i = $length_of_database_rows-1;$i >= $max_rows_returned;$i--)
{
$extra_row = pop (@database_rows);
}
# Now we will reset $new_hits_seen so that we can
# incorporate this value in the hidden form tag generated
# by &search_results_footer so that the next time we come
# to this routine, we can give the user the next set of
# hits.
$new_hits_seen = $hits_seen + $max_rows_returned;
# Now we will actually send the results of the search to
# the user as well as send the HTML footer. And that,
# they say, is that.
&search_results_body;
&search_results_footer;
}
#######################################################################
# Require Supporting Libraries. #
#######################################################################
# require_supporting_libraries is used to read in some of
# the supporting files that this script will take
# advantage of.
#
# require_supporting_libraries takes a list of arguments
# beginning with the current filename, the current line
# number and continuing with the list of files which must
# be required using the following syntax:
#
# &require_supporting_libraries (__FILE__, __LINE__,
# "file1", "file2",
# "file3"...);
#
# Note: __FILE__ and __LINE__ are special Perl variables
# which contain the current filename and line number
# respectively. We'll continually use these two variables
# throughout the rest of this script in order to generate
# useful error messages.
sub require_supporting_libraries
{
# The incoming file and line arguments are split into
# the local variables $file and $line while the file list
# is assigned to the local list array @require_files.
#
# $require_file which will just be a temporary holder
# variable for our foreach processing is also defined as a
# local variable.
local ($file, $line, @require_files) = @_;
local ($require_file);
# Next, the script checks to see if every file in the
# @require_files list array exists (-e) and is readable by
# it (-r). If so, the script goes ahead and requires it.
foreach $require_file (@require_files)
{
if (-e "$require_file" && -r "$require_file")
{
require "$require_file";
}
# If not, the scripts sends back an error message that
# will help the admin isolate the problem with the script.
else
{
print "Content-type: text/html\n\n";
print "I am sorry but I was unable to require $require_file at line
$line in $file. Would you please make sure that you have the
path correct and that the permissions are set so that I have
read access? Thank you.";
exit;
}
} # End of foreach $require_file (@require_files)
} # End of sub require_supporting_libraries