Приглашаем посетить
Толстой (tolstoy-lit.ru)

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