Diff for /loncom/interface/lonsearchcat.pm between versions 1.125 and 1.136

version 1.125, 2002/06/20 19:43:50 version 1.136, 2002/07/05 18:56:52
Line 67  search (on a server basis) is displayed Line 67  search (on a server basis) is displayed
 ###############################################################################  ###############################################################################
 ###############################################################################  ###############################################################################
   
   ###############################################################################
 ##                                                                           ##  ##                                                                           ##
 ## ORGANIZATION OF THIS PERL MODULE                                          ##  ## ORGANIZATION OF THIS PERL MODULE                                          ##
 ##                                                                           ##  ##                                                                           ##
 ## 1. Modules used by this module                                            ##  ## 1. Modules used by this module                                            ##
 ## 2. Choices for different output views (detailed, summary, xml, etc)       ##  ## 2. Variables used throughout the module                                   ##
 ## 3. BEGIN block (to be run once after compilation)                         ##  ## 3. handler subroutine called via Apache and mod_perl                      ##
 ## 4. Handling routine called via Apache and mod_perl                        ##  ## 4. Other subroutines                                                      ##
 ## 5. Other subroutines                                                      ##  
 ##                                                                           ##  ##                                                                           ##
 ###############################################################################  ###############################################################################
   
Line 101  use Apache::loncommon(); Line 101  use Apache::loncommon();
   
 =over 4  =over 4
   
 =item %hostdomains  
   
 matches host name to host domain  
   
 =item %hostips  
   
 matches host name to host ip  
   
 =item %hitcount  
   
 stores number of hits per host  
   
 =item $closebutton  =item $closebutton
   
 button that closes the search window  button that closes the search window
   
 =item $importbutton  =item $importbutton
   
 button to take the selecte results and go to group sorting  button to take the select results and go to group sorting
   
 =item %hash     =item %hash   
   
Line 137  used in &handler() and is also used in & Line 125  used in &handler() and is also used in &
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
   
 # -- information holders  
 my %hostdomains; # matches host name to host domain  
 my %hostips;     # matches host name to host ip  
 my %hitcount;    # stores number of hits per host  
   
 # -- dynamically rendered interface components  # -- dynamically rendered interface components
 my $closebutton;  # button that closes the search window  my $closebutton;  # button that closes the search window
 my $importbutton; # button to take the selected results and go to group sorting  my $importbutton; # button to take the selected results and go to group sorting
   
 # -- miscellaneous variables  # -- miscellaneous variables
 my $yourself; # allows for quickly limiting to oneself  
 my %hash;     # database hash  my %hash;     # database hash
   my $diropendb = "";    # db file
 # ------------------------------------------ choices for different output views  
 # Detailed Citation View ---> sub detailed_citation_view  
 # Summary View ---> sub summary_view  
 # Fielded Format ---> sub fielded_format_view  
 # XML/SGML ---> sub xml_sgml_view  
   
 #------------------------------------------------------------- global variables  
 my $diropendb = "";  
 my $domain = "";  
   
 # ----------------------------------------------------------------------- BEGIN  
   
 =pod  
   
 =item BEGIN block  
   
 Load %hostdomains and %hostips with data from lonnet.pm.  Only library  
 servers are considered.  
   
 =cut  
   
 BEGIN {  
     foreach (keys (%Apache::lonnet::libserv)) {  
         $hostdomains{$_}=$Apache::lonnet::hostdom{$_};  
         $hostips{$_}=$Apache::lonnet::hostip{$_};  
     }  
 }  
   
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
Line 257  END Line 212  END
 onClick='javascript:select_group()'>  onClick='javascript:select_group()'>
 END  END
     }      }
     $hidden .= <<END;      $hidden .= &make_persistent({ "form.mode"    => $ENV{'form.mode'},
 <input type='hidden' name='mode'    value='$ENV{'form.mode'}'>                                    "form.form"    => $ENV{'form.form'},
 <input type='hidden' name='form'    value='$ENV{'form.form'}'>                                    "form.element" => $ENV{'form.element'},
 <input type='hidden' name='element' value='$ENV{'form.element'}'>                                    "form.date"    => 2 });
 <input type='hidden' name='date' value='2'>  
 END  
     ##      ##
     ##  What are we doing?      ##  What are we doing?
     ##      ##
     if ($ENV{'form.basicsubmit'} eq 'SEARCH') {      my $searchtype;
         # Perform basic search and give results      $searchtype = 'Basic'    if ($ENV{'form.basicsubmit'}    eq 'SEARCH');
  return &basicsearch($r,\%ENV,$hidden);      $searchtype = 'Advanced' if ($ENV{'form.advancedsubmit'} eq 'SEARCH');
     } elsif ($ENV{'form.advancedsubmit'} eq 'SEARCH') {      if ($searchtype) {
         # Perform advanced search and give results          my ($query,$customquery,$customshow,$libraries) = 
  return &advancedsearch($r,\%ENV,$hidden);              (undef,undef,undef,undef);
           if ($searchtype eq 'Basic') {
               $query = &parse_basic_search($r);
           } elsif ($ENV{'form.advancedsubmit'} eq 'SEARCH') {
               ($query,$customquery,$customshow,$libraries) 
                   = &parse_advanced_search($r);
               return OK if (! defined($query));
           }
           # Send query statements over the network to be processed by 
           # either the SQL database or a recursive scheme of 'grep'-like 
           # actions (for custom metadata).
           $r->rflush();
           my $reply=&Apache::lonnet::metadata_query($query,$customquery,
                                                  $customshow,$libraries);
           &output_results($searchtype,$r,$reply,$hidden);
     } elsif ($ENV{'form.reqinterface'} eq 'advanced') {      } elsif ($ENV{'form.reqinterface'} eq 'advanced') {
         # Output the advanced interface          # Output the advanced interface
         $r->print(&advanced_search_form($closebutton,$hidden));          $r->print(&advanced_search_form($closebutton,$hidden));
         return OK;  
     } else {       } else { 
         # Output normal search interface          # Output normal search interface
         $r->print(&basic_search_form($closebutton,$hidden));          $r->print(&basic_search_form($closebutton,$hidden));
Line 318  sub basic_search_form{ Line 284  sub basic_search_form{
 $hidden  $hidden
 <h3>Basic Search</h3>  <h3>Basic Search</h3>
 <p>  <p>
 Enter terms or phrases separated by AND, OR, or NOT then press SEARCH below.  Enter terms or phrases separated by AND, OR, or NOT 
   then press SEARCH below.
 </p>  </p>
 <p>  <p>
 <table>  <table>
Line 334  ENDDOCUMENT Line 301  ENDDOCUMENT
 <p>  <p>
 &nbsp;<input type="submit" name="basicsubmit" value='SEARCH' />&nbsp;  &nbsp;<input type="submit" name="basicsubmit" value='SEARCH' />&nbsp;
 $closebutton  $closebutton
 <!-- basic view selection -->  <!-- view selection -->
 <select name='basicviewselect'>  <select name="viewselect">
 <option value='Detailed Citation View' selected="true">  <option value="Detailed Citation View" selected="true" >Detailed Citation View</option>
 Detailed Citation View</option>  <option value="Summary View"  >Summary View</option>
 <option value='Summary View'>Summary View</option>  <option value="Fielded Format">Fielded Format</option>
 <option value='Fielded Format'>Fielded Format</option>  <option value="XML/SGML"      >XML/SGML</option>
 <option value='XML/SGML'>XML/SGML</option>  
 </select>  </select>
 <!-- end of basic view selection -->  <!-- end of view selection -->
 <input type="button" value="HELP" onClick="openhelp()" />  <input type="button" value="HELP" onClick="openhelp()" />
 </p>  </p>
 </form>  </form>
Line 367  Returns a scalar which holds html for th Line 333  Returns a scalar which holds html for th
   
 sub advanced_search_form{  sub advanced_search_form{
     my ($closebutton,$hidden) = @_;      my ($closebutton,$hidden) = @_;
       my $advanced_buttons = <<"END";
   <p>
   <input type="submit" name="advancedsubmit" value='SEARCH' />
   <input type="reset" name="reset" value='RESET' />
   $closebutton
   <input type="button" value="HELP" onClick="openhelp()" />
   </p>
   END
     my $scrout=<<"ENDHEADER";      my $scrout=<<"ENDHEADER";
 <html>  <html>
 <head>  <head>
Line 381  sub advanced_search_form{ Line 355  sub advanced_search_form{
 </head>  </head>
 <body bgcolor="#FFFFFF">  <body bgcolor="#FFFFFF">
 <img align='right' src='/adm/lonIcons/lonlogos.gif' />  <img align='right' src='/adm/lonIcons/lonlogos.gif' />
 <h1>Search Catalog</h1>  <h1>Advanced Catalog Search</h1>
   <hr />
   Enter terms or phrases separated by search operators 
   such as AND, OR, or NOT.<br />
 <form method="post" action="/adm/searchcat">  <form method="post" action="/adm/searchcat">
   $advanced_buttons
 $hidden  $hidden
 <hr />  <table>
 <h3>Advanced Search</h3>  <tr><td><font color="#800000" face="helvetica"><b>VIEW:</b></font></td>
   <td>
   <!-- view selection -->
   <select name="viewselect" size ="1">
       <option value="Detailed Citation View" selected="true">Detailed Citation View</option>
       <option value="Summary View">Summary View</option>
       <option value="Fielded Format">Fielded Format</option>
       <option value="XML/SGML">XML/SGML</option>
   </select>
   <!-- end of view selection -->
   </td></tr>
 ENDHEADER  ENDHEADER
     $scrout.=&searchphrasefield('Limit by title','title',      $scrout.=&searchphrasefield('title',   'title'   ,$ENV{'form.title'});
  $ENV{'form.title'});      $scrout.=&searchphrasefield('author',  'author'  ,$ENV{'form.author'});
     $scrout.=&searchphrasefield('Limit by author','author',      $scrout.=&searchphrasefield('subject', 'subject' ,$ENV{'form.subject'});
  $ENV{'form.author'});      $scrout.=&searchphrasefield('keywords','keywords',$ENV{'form.keywords'});
     $scrout.=&searchphrasefield('Limit by subject','subject',      $scrout.=&searchphrasefield('URL',     'url'     ,$ENV{'form.url'});
  $ENV{'form.subject'});      $scrout.=&searchphrasefield('notes',   'notes'   ,$ENV{'form.notes'});
     $scrout.=&searchphrasefield('Limit by keywords','keywords',      $scrout.=&searchphrasefield('abstract','abstract',$ENV{'form.abstract'});
  $ENV{'form.keywords'});      # Hack - an empty table row.
     $scrout.=&searchphrasefield('Limit by URL','url',      $scrout.="<tr><td>&nbsp;</td><td>&nbsp;</td></tr>\n";
  $ENV{'form.url'});      $scrout.=&searchphrasefield('file<br />extension','mime',
 #    $scrout.=&searchphrasefield('Limit by version','version',                          $ENV{'form.mime'});
 # $ENV{'form.version'});      $scrout.="<tr><td>&nbsp;</td><td>&nbsp;</td></tr>\n";
     $scrout.=&searchphrasefield('Limit by notes','notes',      $scrout.=&searchphrasefield('publisher<br />owner','owner',
  $ENV{'form.notes'});   $ENV{'form.owner'});
     $scrout.=&searchphrasefield('Limit by abstract','abstract',      $scrout.="</table>\n";
  $ENV{'form.abstract'});      $ENV{'form.category'}='any' unless length($ENV{'form.category'});
     $ENV{'form.mime'}='any' unless length($ENV{'form.mime'});      $scrout.=&selectbox('File Category','category',
     $scrout.=&selectbox('Limit by MIME type','mime',   $ENV{'form.category'},
  $ENV{'form.mime'},   'any','Any category',
  'any','Any type',   undef,
  \&{Apache::loncommon::filedescriptionex},   (&Apache::loncommon::filecategories()));
  (&Apache::loncommon::fileextensions));  
     $ENV{'form.language'}='any' unless length($ENV{'form.language'});      $ENV{'form.language'}='any' unless length($ENV{'form.language'});
       #----------------------------------------------------------------
       # Allow restriction to multiple domains.
       #   I make the crazy assumption that there will never be a domain 'any'.
       #
       $ENV{'form.domains'} = 'any' if (! exists($ENV{'form.domains'}));
       my @allowed_domains = (ref($ENV{'form.domains'}) ? @{$ENV{'form.domains'}} 
                              :  ($ENV{'form.domains'}) );
       my %domain_hash = ();
       foreach (@allowed_domains) {
           $domain_hash{$_}++;
       }
       my @domains =&Apache::loncommon::get_domains();
       # adjust the size of the select box
       my $size = 4;
       my $size = (scalar @domains < ($size - 1) ? scalar @domains + 1 : $size);
       # standalone machines do not get to choose a domain to search.
       if ((scalar @domains) == 1) {
           $scrout .='<input type="hidden" name="domains" value="any" />'."\n";
       } else {
           $scrout.="\n".'<font color="#800000" face="helvetica"><b>'.
               'DOMAINS</b></font><br />'.
                   '<select name="domains" size="'.$size.'" multiple>'."\n".
                       '<option name="any" value="any" '.
                           ($domain_hash{'any'}? 'selected ' :'').
                           '>all domains</option>'."\n";
           foreach my $dom (sort @domains) {
               $scrout.="<option name=\"$dom\" ".
                   ($domain_hash{$dom} ? 'selected ' :'').">$dom</option>\n";
           }
           $scrout.="</select>\n";
       }
       #----------------------------------------------------------------
     $scrout.=&selectbox('Limit by language','language',      $scrout.=&selectbox('Limit by language','language',
  $ENV{'form.language'},'any','Any Language',   $ENV{'form.language'},'any','Any Language',
  \&{Apache::loncommon::languagedescription},   \&{Apache::loncommon::languagedescription},
Line 455  LASTREVISIONDATEEND Line 474  LASTREVISIONDATEEND
  $ENV{'form.lastrevisiondateend_year'},   $ENV{'form.lastrevisiondateend_year'},
  );   );
     $scrout.='</p>';      $scrout.='</p>';
     $scrout.=&searchphrasefield('Limit by publisher/owner','owner',  
  $ENV{'form.owner'});  
     $ENV{'form.copyright'}='any' unless length($ENV{'form.copyright'});      $ENV{'form.copyright'}='any' unless length($ENV{'form.copyright'});
     $scrout.=&selectbox('Limit by copyright/distribution','copyright',      $scrout.=&selectbox('Limit by copyright/distribution','copyright',
  $ENV{'form.copyright'},   $ENV{'form.copyright'},
Line 485  in a fielded listing for each record res Line 502  in a fielded listing for each record res
 CUSTOMSHOW  CUSTOMSHOW
     $scrout.=&simpletextfield('customshow',$ENV{'form.customshow'});      $scrout.=&simpletextfield('customshow',$ENV{'form.customshow'});
     $scrout.=<<ENDDOCUMENT;      $scrout.=<<ENDDOCUMENT;
 <p>  $advanced_buttons
 <input type="submit" name="advancedsubmit" value='SEARCH' />  
 <input type="reset" name="reset" value='RESET' />  
 $closebutton  
 <!-- advance view select -->  
 <select name='advancedviewselect'>  
 <option value='Detailed Citation View' selected="true">  
 Detailed Citation View</option>  
 <option value='Summary View'>Summary View</option>  
 <option value='Fielded Format'>Fielded Format</option>  
 <option value='XML/SGML'>XML/SGML</option>  
 </select>  
 <!-- end of advanced view select -->  
 <input type="button" value="HELP" onClick="openhelp()" />  
 </p>  
 </form>  </form>
 </body>  </body>
 </html>  </html>
Line 515  ENDDOCUMENT Line 518  ENDDOCUMENT
 =item &make_persistent()   =item &make_persistent() 
   
 Returns a scalar which holds the current ENV{'form.*'} values in  Returns a scalar which holds the current ENV{'form.*'} values in
 a 'hidden' html input tag.    a 'hidden' html input tag.  This allows search interface information
   to be somewhat persistent.
   
 =cut  =cut
   
Line 523  a 'hidden' html input tag. Line 527  a 'hidden' html input tag.
 ######################################################################  ######################################################################
   
 sub make_persistent {  sub make_persistent {
       my %save = %{shift()};
     my $persistent='';      my $persistent='';
     foreach (keys %ENV) {      foreach (keys %save) {
  if (/^form\./ && !/submit/) {   if (/^form\./ && !/submit/) {
     my $name=$_;      my $name=$_;
     my $key=$name;              my @values = (ref($save{$name}) ? @{$save{$name}} : ($save{$name}));
     $ENV{$key}=~s/\'//g; # do not mess with html field syntax  
     $name=~s/^form\.//;      $name=~s/^form\.//;
     $persistent.=<<END;              foreach (@values) {
 <input type="hidden" name="$name" value="$ENV{$key}" />                  s/\"/\'/g; # do not mess with html field syntax
                   $persistent.=<<END;
   <input type="hidden" name="$name" value="$_" />
 END  END
               }
         }          }
     }      }
     return $persistent;      return $persistent;
Line 573  is where the $name and $value are used) Line 580  is where the $name and $value are used)
   
 =item &dateboxes()  =item &dateboxes()
   
   Returns html selection form elements for the specification of 
   the day, month, and year.
   
 =item &selectbox()  =item &selectbox()
   
   Returns a scalar containing an html <select> form.  
   
   Inputs: 
   
   =over 4
   
   =item $title 
   
   Printed above the select box, in uppercase.
   
   =item $name 
   
   The name element of the <select> tag.
   
   =item $default 
   
   The default value of the form.  Can be $anyvalue or in @idlist.
   
   =item $anyvalue 
   
   The <option value="..."> used to indicate a default of 
   none of the values.
   
   =item $anytag 
   
   The text associate with $anyvalue above.
   
   =item $functionref 
   
   Each element in @idlist will be passed as a parameter 
   to the function referenced here.  The return value of the function should
   be a scalar description of the items.  If this value is undefined the 
   description of each item in @idlist will be the item name.
   
   =item @idlist 
   
   The items to be selected from.  One of these or $anyvalue will be the 
   value returned by the form element, $ENV{form.$name}.
   
   =back
   
 =back   =back 
   
 =cut  =cut
Line 592  sub simpletextfield { Line 643  sub simpletextfield {
 sub simplecheckbox {  sub simplecheckbox {
     my ($name,$value)=@_;      my ($name,$value)=@_;
     my $checked='';      my $checked='';
     $checked="CHECKED" if $value eq 'on';      $checked="checked" if $value eq 'on';
     return '<input type="checkbox" name="'.$name.'" '. $checked . ' />';      return '<input type="checkbox" name="'.$name.'" '. $checked . ' />';
 }  }
   
 sub searchphrasefield {  sub searchphrasefield {
     my ($title,$name,$value)=@_;      my ($title,$name,$value)=@_;
     my $instruction=<<END;  
 Enter terms or phrases separated by search operators such as AND, OR, or NOT.  
 END  
     my $uctitle=uc($title);      my $uctitle=uc($title);
     return "\n".      return '<tr><td><font color="#800000" face="helvetica">'.
         '<p><font color="#800000" face="helvetica"><b>'.$uctitle.':</b>'.          '<b>'.$uctitle.':&nbsp;</b></font></td><td>'.
         "</FONT> $instruction<br />".&simpletextfield($name,$value,80);                  &simpletextfield($name,$value,50)."</td></tr>\n";
 }  }
   
 sub dateboxes {  sub dateboxes {
Line 652  END Line 700  END
 }  }
   
 sub selectbox {  sub selectbox {
     my ($title,$name,$value,$anyvalue,$anytag,$functionref,@idlist)=@_;      my ($title,$name,$default,$anyvalue,$anytag,$functionref,@idlist)=@_;
       if (! defined($functionref)) { $functionref = sub { $_[0]}; }
     my $uctitle=uc($title);      my $uctitle=uc($title);
     my $selout="\n".'<p><font color="#800000" face="helvetica">'.      my $selout="\n".'<p><font color="#800000" face="helvetica">'.
         '<b>'.$uctitle.':</b></font><br /><select name="'.$name.'">';          '<b>'.$uctitle.': </b></font><select name="'.$name.'">';
     foreach ($anyvalue,@idlist) {      foreach ($anyvalue,@idlist) {
         $selout.='<option value="'.$_.'"';          $selout.='<option value="'.$_.'"';
         if ($_ eq $value and !/^any$/) {          if ($_ eq $default and !/^any$/) {
     $selout.=' selected >'.&{$functionref}($_).'</option>';      $selout.=' selected >'.&{$functionref}($_).'</option>';
  }   }
  elsif ($_ eq $value and /^$anyvalue$/) {   elsif ($_ eq $default and /^$anyvalue$/) {
     $selout.=' selected >'.$anytag.'</option>';      $selout.=' selected >'.$anytag.'</option>';
  }   }
         else {$selout.='>'.&{$functionref}($_).'</option>';}          else {$selout.='>'.&{$functionref}($_).'</option>';}
     }      }
     return $selout.'</select>';      return $selout.'</select></p>';
 }  }
   
 ######################################################################  ######################################################################
Line 674  sub selectbox { Line 723  sub selectbox {
   
 =pod   =pod 
   
 =item &advancedsearch()   =item &parse_advanced_search()
   
   Parse advanced search form and return the following:
   
   =over 4
   
   =item $query Scalar containing an SQL query.
   
   =item $customquery Scalar containing a custom query.
   
   =item $customshow Scalar containing commands to show custom metadata.
   
   =item $libraries_to_query Reference to array of domains to search.
   
   =back
   
 =cut  =cut
   
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
 sub advancedsearch {  sub parse_advanced_search {
     my ($r,$envhash,$hidden)=@_;      my ($r)=@_;
     my %ENV=%{$envhash};  
     my $fillflag=0;      my $fillflag=0;
     # Clean up fields for safety      # Clean up fields for safety
     for my $field ('title','author','subject','keywords','url','version',      for my $field ('title','author','subject','keywords','url','version',
Line 693  sub advancedsearch { Line 755  sub advancedsearch {
    'lastrevisiondatestart_year','lastrevisiondateend_month',     'lastrevisiondatestart_year','lastrevisiondateend_month',
    'lastrevisiondateend_day','lastrevisiondateend_year',     'lastrevisiondateend_day','lastrevisiondateend_year',
    'notes','abstract','mime','language','owner',     'notes','abstract','mime','language','owner',
    'custommetadata','customshow') {     'custommetadata','customshow','category') {
  $ENV{"form.$field"}=~s/[^\w\/\s\(\)\=\-\"\']//g;   $ENV{"form.$field"}=~s/[^\w\/\s\(\)\=\-\"\']//g;
     }      }
     foreach ('mode','form','element') {      foreach ('mode','form','element') {
Line 702  sub advancedsearch { Line 764  sub advancedsearch {
  $ENV{"form.$_"}=&Apache::lonnet::unescape($ENV{"form.$_"});   $ENV{"form.$_"}=&Apache::lonnet::unescape($ENV{"form.$_"});
  $ENV{"form.$_"}=~s/[^\w\/\s\(\)\=\-\"\']//g;   $ENV{"form.$_"}=~s/[^\w\/\s\(\)\=\-\"\']//g;
     }      }
       # Preprocess the category form element.
       if ($ENV{'form.category'} ne 'any') {
           my @extensions = &Apache::loncommon::filecategorytypes
               ($ENV{'form.category'});
           $ENV{'form.mime'} = join ' OR ',@extensions;
       }
     # Check to see if enough information was filled in      # Check to see if enough information was filled in
     for my $field ('title','author','subject','keywords','url','version',      for my $field ('title','author','subject','keywords','url','version',
    'notes','abstract','mime','language','owner',     'notes','abstract','mime','language','owner',
Line 712  sub advancedsearch { Line 780  sub advancedsearch {
     }      }
     unless ($fillflag) {      unless ($fillflag) {
  &output_blank_field_error($r);   &output_blank_field_error($r);
  return OK;   return ;
     }      }
     # Turn the form input into a SQL-based query      # Turn the form input into a SQL-based query
     my $query='';      my $query='';
     my @queries;      my @queries;
     # Evaluate logical expression AND/OR/NOT phrase fields.      # Evaluate logical expression AND/OR/NOT phrase fields.
     foreach my $field ('title','author','subject','notes','abstract','url',      foreach my $field ('title','author','subject','notes','abstract','url',
        'keywords','version','owner') {         'keywords','version','owner','mime') {
  if ($ENV{'form.'.$field}) {   if ($ENV{'form.'.$field}) {
     push @queries,&build_SQL_query($field,$ENV{'form.'.$field});      push @queries,&build_SQL_query($field,$ENV{'form.'.$field});
  }          }
       }
       # I dislike the hack below.
       if ($ENV{'form.category'}) {
           $ENV{'form.mime'}='';
     }      }
     # Evaluate option lists      # Evaluate option lists
     if ($ENV{'form.language'} and $ENV{'form.language'} ne 'any') {      if ($ENV{'form.language'} and $ENV{'form.language'} ne 'any') {
  push @queries,"(language like \"$ENV{'form.language'}\")";   push @queries,"(language like \"$ENV{'form.language'}\")";
     }      }
     if ($ENV{'form.mime'} and $ENV{'form.mime'} ne 'any') {  
  push @queries,"(mime like \"$ENV{'form.mime'}\")";  
     }  
     if ($ENV{'form.copyright'} and $ENV{'form.copyright'} ne 'any') {      if ($ENV{'form.copyright'} and $ENV{'form.copyright'} ne 'any') {
  push @queries,"(copyright like \"$ENV{'form.copyright'}\")";   push @queries,"(copyright like \"$ENV{'form.copyright'}\")";
     }      }
Line 752  sub advancedsearch { Line 821  sub advancedsearch {
     # Test to see if date windows are legitimate      # Test to see if date windows are legitimate
     if ($datequery=~/^Incorrect/) {      if ($datequery=~/^Incorrect/) {
  &output_date_error($r,$datequery);   &output_date_error($r,$datequery);
  return OK;   return ;
     }      }
     elsif ($datequery) {      elsif ($datequery) {
  push @queries,$datequery;   push @queries,$datequery;
     }      }
     # Process form information for custom metadata querying      # Process form information for custom metadata querying
     my $customquery='';      my $customquery=undef;
     if ($ENV{'form.custommetadata'}) {      if ($ENV{'form.custommetadata'}) {
  $customquery=&build_custommetadata_query('custommetadata',   $customquery=&build_custommetadata_query('custommetadata',
       $ENV{'form.custommetadata'});        $ENV{'form.custommetadata'});
     }      }
     my $customshow='';      my $customshow=undef;
     if ($ENV{'form.customshow'}) {      if ($ENV{'form.customshow'}) {
  $customshow=$ENV{'form.customshow'};   $customshow=$ENV{'form.customshow'};
  $customshow=~s/[^\w\s]//g;   $customshow=~s/[^\w\s]//g;
  my @fields=split(/\s+/,$customshow);   my @fields=split(/\s+/,$customshow);
  $customshow=join(" ",@fields);   $customshow=join(" ",@fields);
     }      }
     # Send query statements over the network to be processed by either the SQL      ## ---------------------------------------------------------------
     # database or a recursive scheme of 'grep'-like actions (for custom      ## Deal with restrictions to given domains
     # metadata).      ## 
       my $libraries_to_query = undef;
       # $ENV{'form.domains'} can be either a scalar or an array reference.
       # We need an array.
       my @allowed_domains = (ref($ENV{'form.domains'}) ? @{$ENV{'form.domains'}} 
                              :  ($ENV{'form.domains'}) );
       my %domain_hash = ();
       foreach (@allowed_domains) {
           $domain_hash{$_}++;
       }
       foreach (keys(%Apache::lonnet::libserv)) {
           if ($_ eq 'any') {
               $libraries_to_query = undef;
               last;
           }
           if (exists($domain_hash{$Apache::lonnet::hostdom{$_}})) {
               push @$libraries_to_query,$_;
           }
       }
       #
     if (@queries) {      if (@queries) {
  $query=join(" AND ",@queries);   $query=join(" AND ",@queries);
  $query="select * from metadata where $query";   $query="select * from metadata where $query";
  my $reply; # reply hash reference      } elsif ($customquery) {
  unless ($customquery or $customshow) {          $query = '';
     $reply=&Apache::lonnet::metadata_query($query);  
  }  
  else {  
     $reply=&Apache::lonnet::metadata_query($query,  
    $customquery,$customshow);  
  }  
  &output_results('Advanced',$r,$envhash,$customquery,$reply,$hidden);  
     }  
     elsif ($customquery) {  
  my $reply; # reply hash reference  
  $reply=&Apache::lonnet::metadata_query('',  
        $customquery,$customshow);  
  &output_results('Advanced',$r,$envhash,$customquery,$reply,$hidden);  
     }      }
     # should not get to this point      return ($query,$customquery,$customshow,$libraries_to_query);
     return 'Error.  Should not have gone to this point.';  
 }  }
   
 ######################################################################  ######################################################################
Line 801  sub advancedsearch { Line 875  sub advancedsearch {
   
 =pod   =pod 
   
 =item &basicsearch()   =item &parse_basic_search() 
   
   Parse the basic search form and return a scalar containing an sql query.
   
 =cut  =cut
   
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
 sub basicsearch {  sub parse_basic_search {
     my ($r,$envhash,$hidden)=@_;      my ($r)=@_;
     my %ENV=%{$envhash};  
     # Clean up fields for safety      # Clean up fields for safety
     for my $field ('basicexp') {      for my $field ('basicexp') {
  $ENV{"form.$field"}=~s/[^\w\s\(\)\-]//g;   $ENV{"form.$field"}=~s/[^\w\s\(\)\-]//g;
Line 835  sub basicsearch { Line 910  sub basicsearch {
     $concatarg='title' if $ENV{'form.titleonly'};      $concatarg='title' if $ENV{'form.titleonly'};
   
     $query=&build_SQL_query('concat('.$concatarg.')',$ENV{'form.'.'basicexp'});      $query=&build_SQL_query('concat('.$concatarg.')',$ENV{'form.'.'basicexp'});
       return 'select * from metadata where '.$query;
     # Get reply (either a hash reference to filehandles or bad connection)  
     my $reply=&Apache::lonnet::metadata_query('select * from metadata where '.$query);  
   
     # Output search results  
   
     &output_results('Basic',$r,$envhash,$query,$reply,$hidden);  
   
     return OK;  
 }  }
   
   
Line 854  sub basicsearch { Line 921  sub basicsearch {
   
 =item &build_SQL_query()   =item &build_SQL_query() 
   
   Builds a SQL query string from a logical expression with AND/OR keywords
   using Text::Query and &recursive_SQL_query_builder()
   
 =cut  =cut
   
 ######################################################################  ######################################################################
Line 876  sub build_SQL_query { Line 946  sub build_SQL_query {
   
 =item &build_custommetadata_query()   =item &build_custommetadata_query() 
   
   Constructs a custom metadata query using a rather heinous regular
   expression.
   
 =cut  =cut
   
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
 sub build_custommetadata_query {  sub build_custommetadata_query {
     my ($field_name,$logic_statement)=@_;      my ($field_name,$logic_statement)=@_;
     &Apache::lonnet::logthis("Entered build_custommetadata_query:".  
                              $field_name.':'.$logic_statement);  
     my $q=new Text::Query('abc',      my $q=new Text::Query('abc',
   -parse => 'Text::Query::ParseAdvanced',    -parse => 'Text::Query::ParseAdvanced',
   -build => 'Text::Query::BuildAdvancedString');    -build => 'Text::Query::BuildAdvancedString');
Line 898  sub build_custommetadata_query { Line 969  sub build_custommetadata_query {
                    \*$2\[\^\\<\]?#           *wordtwo[^\<]                     \*$2\[\^\\<\]?#           *wordtwo[^\<]
                    \*\\<\\\/$1\\>?#                        *\<\/wordone\>                     \*\\<\\\/$1\\>?#                        *\<\/wordone\>
                    /g;                     /g;
     &Apache::lonnet::logthis("match expression: ".$matchexp);  
     return $matchexp;      return $matchexp;
 }  }
   
Line 909  sub build_custommetadata_query { Line 979  sub build_custommetadata_query {
   
 =item &recursive_SQL_query_build()   =item &recursive_SQL_query_build() 
   
   Recursively constructs an SQL query.  Takes as input $dkey and $pattern.
   
 =cut  =cut
   
 ######################################################################  ######################################################################
Line 953  sub recursive_SQL_query_build { Line 1025  sub recursive_SQL_query_build {
   
 =item &build_date_queries()   =item &build_date_queries() 
   
   Builds a SQL logic query to check time/date entries.
   Also reports errors (check for /^Incorrect/).
   
 =cut  =cut
   
 ######################################################################  ######################################################################
Line 1023  contacted, etc.) Line 1098  contacted, etc.)
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
 sub output_results {  sub output_results {
   #    &Apache::lonnet::logthis("output_results:".time);
     my $fnum; # search result counter      my $fnum; # search result counter
     my ($mode,$r,$envhash,$query,$replyref,$hidden)=@_;      my ($mode,$r,$replyref,$hidden)=@_;
     my %ENV=%{$envhash};  
     my %rhash=%{$replyref};      my %rhash=%{$replyref};
     my $compiledresult='';      my $compiledresult='';
     my $timeremain=300; # (seconds)      my $timeremain=300; # (seconds)
     my $elapsetime=0;      my $elapsetime=0;
     my $resultflag=0;      my $resultflag=0;
     my $tflag=1;      my $tflag=1;
       ##
       ## Set viewing function
       ##
       my $viewfunction = undef;
       if ($ENV{'form.viewselect'} eq 'Detailed Citation View') {
           $viewfunction = \&detailed_citation_view;
       } elsif ($ENV{'form.viewselect'} eq 'Summary View') {
           $viewfunction = \&summary_view;
       } elsif ($ENV{'form.viewselect'} eq 'Fielded Format') {
           $viewfunction = \&fielded_format_view;
       } elsif ($ENV{'form.viewselect'} eq 'XML/SGML') {
           $viewfunction = \&xml_sgml_view;
       }
       if (!defined($viewfunction)) {
           $r->print("Internal Error - Bad view selected.\n");
           $r->rflush();
           return;
       }
     #      #
     # make query information persistent to allow for subsequent revision      # make query information persistent to allow for subsequent revision
     my $persistent=&make_persistent();      my $persistent=&make_persistent(\%ENV);
     # spit out the generic header      #
     $r->print(&search_results_header());      # Begin producing output
       $r->print(&search_results_header($mode));
     $r->rflush();      $r->rflush();
       #
     # begin showing the cataloged results      # begin showing the cataloged results
     $r->print(<<CATALOGBEGIN);      my $action = "/adm/searchcat";
 </head>      if ($mode eq 'Basic') { 
 <body bgcolor="#ffffff">          $action .= "?reqinterface=basic";
 <img align=right src=/adm/lonIcons/lonlogos.gif>      } elsif ($mode eq 'Advanced') {
 <h1>Search Catalog</h1>          $action .= "?reqinterface=advanced";
 CATALOGBEGIN      }
         $r->print(<<CATALOGCONTROLS);      $r->print(<<CATALOGCONTROLS);
 <form name='results' method="post" action="/adm/searchcat">  <form name='results' method="post" action="$action">
 $hidden  $hidden
 <input type='hidden' name='acts' value='' />  <input type='hidden' name='acts' value='' />
 <input type='button' value='Revise search request'  <input type='button' value='Revise search request'
Line 1055  $importbutton Line 1150  $importbutton
 $closebutton  $closebutton
 $persistent  $persistent
 <hr />  <hr />
 <h3>Search Query</h3>  
 CATALOGCONTROLS  CATALOGCONTROLS
     #      #
     # Remind them what they searched for  
     #  
     if ($mode eq 'Basic') {  
  $r->print('<p><b>Basic search:</b> '.$ENV{'form.basicexp'}.'</p>');  
     } elsif ($mode eq 'Advanced') {  
  $r->print('<p><b>Advanced search</b> '.$query.'</p>');  
     }  
     $r->print('<h3>Search Results</h3>');  
     $r->rflush();  
     #  
     # make the pop-up window for status      # make the pop-up window for status
     #  
     $r->print(&make_popwin(%rhash));      $r->print(&make_popwin(%rhash));
     $r->rflush();      $r->rflush();
     ##      ##
Line 1088  CATALOGCONTROLS Line 1171  CATALOGCONTROLS
       ## (within the foreach loop below).        ## (within the foreach loop below).
       ##        ##
       foreach my $rkey (sort keys %rhash) {        foreach my $rkey (sort keys %rhash) {
   #        &Apache::lonnet::logthis("Server $rkey:".time);
  $servercount++;   $servercount++;
  $tflag=1;  
  $compiledresult='';   $compiledresult='';
  my $hostname=$rkey;  
  my $reply=$rhash{$rkey};   my $reply=$rhash{$rkey};
  my @results;   my @results;
  if ($reply eq 'con_lost') {   if ($reply eq 'con_lost') {
Line 1103  CATALOGCONTROLS Line 1185  CATALOGCONTROLS
     $reply=~/^([\.\w]+)$/;       $reply=~/^([\.\w]+)$/; 
     my $replyfile=$r->dir_config('lonDaemons').'/tmp/'.$1;      my $replyfile=$r->dir_config('lonDaemons').'/tmp/'.$1;
     $reply=~/(.*?)\_/;      $reply=~/(.*?)\_/;
     {              for (my $counter=0;$counter<2;$counter++) {
  my $temp=0;                  if (-e $replyfile && ! -e "$replyfile.end") {
       WLOOP: while (1) {                      &popwin_imgupdate($r,$rkey,"srvhalf.gif");
   if (-e $replyfile && $tflag) {                      &popwin_js($r,'popwin.hc["'.$rkey.'"]='.
       &popwin_imgupdate($r,$rkey,"srvhalf.gif");                                 '"still transferring..."'.';');
       &popwin_js($r,'popwin.hc["'.$rkey.'"]='.                  }
  '"still transferring..."'.';');                  # Are we finished transferring data?
       $tflag=0;                  if (-e "$replyfile.end") {
   }                      $serversleft--;
   if (-e "$replyfile.end") {                      delete $rhash{$rkey};
       $serversleft--;                      if (-s $replyfile) {
                       delete $rhash{$rkey};                          &popwin_imgupdate($r,$rkey,"srvgood.gif");
       if (-s $replyfile) {                          my $fh;
   &popwin_imgupdate($r,$rkey,"srvgood.gif");                          unless ($fh=Apache::File->new($replyfile)){ 
   my $fh=Apache::File->new($replyfile) or                               # Is it really appropriate to die on this error?
       ($r->print('ERROR: file '.                              $r->print('ERROR: file '.
  $replyfile.' cannot be opened') and                                        $replyfile.' cannot be opened');
        return OK);                              return OK;
   @results=<$fh> if $fh;                          }
   $hitcount{$rkey}=@results+0;                          @results=<$fh> if $fh;
   &popwin_js($r,'popwin.hc["'.$rkey.'"]='.                          my $hits =@results;
     $hitcount{$rkey}.';');                          &popwin_js($r,'popwin.hc["'.$rkey.'"]='.
   $hitcountsum+=$hitcount{$rkey};                                     $hits.';');
   &popwin_js($r,'popwin.document.forms.popremain.'.                          $hitcountsum+=$hits;
     'numhits.value='.$hitcountsum.';');                          &popwin_js($r,'popwin.document.forms.popremain.'.
       }                                     'numhits.value='.$hitcountsum.';');
       else {                      } else {
   &popwin_imgupdate($r,$rkey,"srvempty.gif");                          &popwin_imgupdate($r,$rkey,"srvempty.gif");
   &popwin_js($r,'popwin.hc["'.$rkey.'"]=0;');                          &popwin_js($r,'popwin.hc["'.$rkey.'"]=0;');
       }                      }
       last WLOOP;                      last;
   } # end of if ( -e "$replyfile.end")                  } # end of if ( -e "$replyfile.end")
   last WLOOP unless $timeremain;                  last unless $timeremain;
   sleep 1;    # wait for daemons to write files?                  sleep 1;    # wait for daemons to write files?
   $timeremain--;                  $timeremain--;
   $elapsetime++;                  $elapsetime++;
   last WLOOP if ($temp>1);                  &popwin_js($r,"popwin.document.popremain.".
   &popwin_js($r,"popwin.document.popremain.".                             "elapsetime.value=$elapsetime;");
        "elapsetime.value=$elapsetime;");  
   $temp++;  
       }  
     }      }
     &popwin_js($r,'popwin.document.whirly.'.      &popwin_js($r,'popwin.document.whirly.'.
        'src="/adm/lonIcons/lonanimend.gif";');         'src="/adm/lonIcons/lonanimend.gif";');
  } # end of if ($reply eq 'con_lost') else statement   } # end of if ($reply eq 'con_lost') else statement
  my $customshow='';          my %Fields = undef;     # Holds the data to be sent to the various 
  my $extrashow='';                                  # *_view routines.
  my @customfields;          my ($extrashow,$customfields,$customhash) = 
  if ($ENV{'form.customshow'}) {                                      &handle_custom_fields(\@results);
     $customshow=$ENV{'form.customshow'};          my @customfields = @$customfields;
     $customshow=~s/[^\w\s]//g;          my %customhash   = %$customhash;
     my @fields=map {"<font color=\"#008000\">$_:</font><!-- $_ -->"}    untie %hash if (keys %hash);
     split(/\s+/,$customshow);          #
     @customfields=split(/\s+/,$customshow);   if (! tie(%hash,'GDBM_File',$diropendb,&GDBM_WRCREAT,0640)) {
     if ($customshow) {      $r->print('<html><head></head><body>Unable to tie hash to db '.
  $extrashow="<ul><li>".join("</li><li>",@fields)."</li></ul>\n";                        'file</body></html>');
     }          } else {
  }  
  my $customdata='';  
  my %customhash;  
  foreach my $result (@results) {  
     if ($result=~/^(custom\=.*)$/) { # grab all custom metadata  
  my $tmp=$result;  
  $tmp=~s/^custom\=//;  
  my ($k,$v)=map {&Apache::lonnet::unescape($_);  
     } split(/\,/,$tmp);  
  $customhash{$k}=$v;  
     }  
  }  
  if (keys %hash) {  
     untie %hash;  
  }  
  if (tie(%hash,'GDBM_File',$diropendb,&GDBM_WRCREAT,0640)) {  
     if ($ENV{'form.launch'} eq '1') {      if ($ENV{'form.launch'} eq '1') {
  &start_fresh_session();   &start_fresh_session();
     }      }
Line 1183  CATALOGCONTROLS Line 1246  CATALOGCONTROLS
  next if $result=~/^custom\=/;   next if $result=~/^custom\=/;
  chomp $result;   chomp $result;
  next unless $result;   next unless $result;
  my @fields=map                  %Fields = &parse_raw_result($result,$rkey);
  {&Apache::lonnet::unescape($_)}   $Fields{'extrashow'}=$extrashow;
  (split(/\,/,$result));  
  my ($title,$author,$subject,$url,$keywords,$version,  
     $notes,$abstract,$mime,$lang,  
     $creationdate,$lastrevisiondate,$owner,$copyright)=@fields;  
                 unless ($title =~ /^\s*$/ ) { $title='<i>Untitled</i>'; }  
  unless ($ENV{'user.adv'}) {  
     $keywords='<i>- not displayed -</i>';  
     $fields[4]=$keywords;  
     $notes='<i>- not displayed -</i>';  
     $fields[6]=$notes;  
     $abstract='<i>- not displayed -</i>';  
     $fields[7]=$abstract;  
     $subject='<i>- not displayed -</i>';  
     $fields[2]=$subject;  
  }  
   
  my $shortabstract=$abstract;  
  $shortabstract=substr($abstract,0,200).'...' if length($abstract)>200;  
  $fields[7]=$shortabstract;  
  my $shortkeywords=$keywords;  
  $shortkeywords=substr($keywords,0,200).'...' if length($keywords)>200;  
  $fields[4]=$shortkeywords;  
   
  my $extrashow2=$extrashow;  
  if ($extrashow) {   if ($extrashow) {
     foreach my $field (@customfields) {      foreach my $field (@customfields) {
  my $value='';   my $value='';
  if ($customhash{$url}=~/\<${field}[^\>]*\>(.*?)\<\/${field}[^\>]*\>/s) {   $value = $1 if ($customhash{$Fields{'url'}}=~/\<{$field}[^\>]*\>(.*?)\<\/{$field}[^\>]*\>/s);
             $value=$1;                          $Fields{'extrashow'}=~s/\<\!\-\- $field \-\-\>/ $value/g;
  }                      }
         $extrashow2=~s/\<\!\-\- $field \-\-\>/ $value/g;  
             }  
                 }                  }
                   $compiledresult.="\n<p>\n";
         $compiledresult.=<<END if $compiledresult or $servercount!=$servernum;                  if ($ENV{'form.catalogmode'} eq 'interactive') {
 <hr align='left' width='200' noshade />                      my $titleesc=$Fields{'title'};
 END                      $titleesc=~s/\'/\\'/; # '
                 $compiledresult.=<<END;                      $compiledresult.=<<END if ($ENV{'form.catalogmode'} eq 'interactive');
 <p>  
 END  
                if ($ENV{'form.catalogmode'} eq 'interactive') {  
    my $titleesc=$title;  
    $titleesc=~s/\'/\\'/; # '  
   
                    $compiledresult.=<<END if ($ENV{'form.catalogmode'} eq 'interactive');  
 <font size='-1'><INPUT TYPE="button" NAME="returnvalues" VALUE="SELECT"  <font size='-1'><INPUT TYPE="button" NAME="returnvalues" VALUE="SELECT"
 onClick="javascript:select_data('$titleesc','$url')">  onClick="javascript:select_data('$titleesc','$Fields{'url'}')">
 </font>  </font>
 <br />  <br />
 END  END
                 }                  }
                 if ($ENV{'form.catalogmode'} eq 'groupsearch') {                  if ($ENV{'form.catalogmode'} eq 'groupsearch') {
     $fnum+=0;      $fnum+=0;
     $hash{"pre_${fnum}_link"}=$url;      $hash{"pre_${fnum}_link"}=$Fields{'url'};
     $hash{"pre_${fnum}_title"}=$title;      $hash{"pre_${fnum}_title"}=$Fields{'title'};
     $compiledresult.=<<END;      $compiledresult.=<<END;
 <font size='-1'>  <font size='-1'>
 <input type="checkbox" name="returnvalues" value="SELECT"  <input type="checkbox" name="returnvalues" value="SELECT"
Line 1251  END Line 1281  END
 # <input type="hidden" name="url$fnum" value="$url" />  # <input type="hidden" name="url$fnum" value="$url" />
                     $fnum++;                      $fnum++;
  }   }
         my $httphost=$ENV{'HTTP_HOST'};                  # Render the result into html
                   $compiledresult.= &$viewfunction(%Fields, hostname => $rkey );
         my $viewselect;                  if ($compiledresult or $servercount!=$servernum) {
         if ($mode eq 'Basic') {                      $compiledresult.="<hr align='left' width='200' noshade />";
     $viewselect=$ENV{'form.basicviewselect'};                  }
  }  
         elsif ($mode eq 'Advanced') {  
     $viewselect=$ENV{'form.advancedviewselect'};  
  }  
   
         if ($viewselect eq 'Detailed Citation View') {  
     $compiledresult.=&detailed_citation_view(@fields,  
  $hostname,$httphost,  
  $extrashow2);  
  }  
                 elsif ($viewselect eq 'Summary View') {  
     $compiledresult.=&summary_view(@fields,$hostname,$httphost,  
        $extrashow2);  
         }  
                 elsif ($viewselect eq 'Fielded Format') {  
     $compiledresult.=&fielded_format_view(@fields,$hostname,  
       $httphost,$extrashow2);  
         }  
                 elsif ($viewselect eq 'XML/SGML') {  
     $compiledresult.=&xml_sgml_view(@fields,$hostname,$httphost,  
  $extrashow2);  
  }  
       
             }              }
   
             untie %hash;              untie %hash;
         }          }
         else {  
     $r->print('<html><head></head><body>Unable to tie hash to db '.  
   'file</body></html>');  
  }  
  if ($compiledresult) {   if ($compiledresult) {
     $resultflag=1;      $resultflag=1;
               $r->print($compiledresult);
  }   }
         } # End of foreach loop over servers remaining
  $r->print(<<RESULTS);      }   # End of big loop - while($serversleft && $timeremain)
 $compiledresult  
 RESULTS  
         my $percent=sprintf('%3.0f',($servercount/$servernum*100));  
     }  
   }  
     unless ($resultflag) {      unless ($resultflag) {
         $r->print("\nThere were no results that matched your query\n");          $r->print("\nThere were no results that matched your query\n");
     }      }
 #    $r->print('<script type="text/javascript">'.'popwin.close()</script>'."\n"); $r->rflush();       $r->print('<script type="text/javascript">'.'popwin.close()</script>'.
     $r->print(<<RESULTS);                "\n"); 
 </body>      $r->print("</body>\n</html>\n");
 </html>      $r->rflush(); 
 RESULTS      return;
   }
   
   ###########################################################
   ###########################################################
   
   =pod
   
   =item &parse_raw_result()
   
   Takes a line from the file of results and parse it.  Returns a hash 
   with keys for the following fields:
   'title', 'author', 'subject', 'url', 'keywords', 'version', 'notes', 
   'abstract', 'mime', 'lang', 'owner', 'copyright', 'creationdate', 
   'lastrevisiondate'.
   
   In addition, the following tags are set by calling the appropriate 
   lonnet function: 'language', 'cprtag', 'mimetag'.
   
   The 'title' field is set to "Untitled" if the title field is blank.
   
   'abstract' and 'keywords' are truncated to 200 characters.
   
   =cut
   
   ###########################################################
   ###########################################################
   sub parse_raw_result {
       my ($result,$hostname) = @_;
       # Check for a comma - if it is there then we do not need to unescape the
       # string.  There seems to be some kind of problem with some items in
       # the database - the entire string gets sent out unescaped...?
       unless ($result =~ /,/) {
           $result = &Apache::lonnet::unescape($result);
       }
       my @fields=map {
           &Apache::lonnet::unescape($_);
       } (split(/\,/,$result));
       my ($title,$author,$subject,$url,$keywords,$version,
           $notes,$abstract,$mime,$lang,
           $creationdate,$lastrevisiondate,$owner,$copyright)=@fields;
       my %Fields = 
           ( title     => &Apache::lonnet::unescape($title),
             author    => &Apache::lonnet::unescape($author),
             subject   => &Apache::lonnet::unescape($subject),
             url       => &Apache::lonnet::unescape($url),
             keywords  => &Apache::lonnet::unescape($keywords),
             version   => &Apache::lonnet::unescape($version),
             notes     => &Apache::lonnet::unescape($notes),
             abstract  => &Apache::lonnet::unescape($abstract),
             mime      => &Apache::lonnet::unescape($mime),
             lang      => &Apache::lonnet::unescape($lang),
             owner     => &Apache::lonnet::unescape($owner),
             copyright => &Apache::lonnet::unescape($copyright),
             creationdate     => &Apache::lonnet::unescape($creationdate),
             lastrevisiondate => &Apache::lonnet::unescape($lastrevisiondate)
           );
       $Fields{'language'} = 
           &Apache::loncommon::languagedescription($Fields{'lang'});
       $Fields{'copyrighttag'} =
           &Apache::loncommon::copyrightdescription($Fields{'copyright'});
       $Fields{'mimetag'} =
           &Apache::loncommon::filedescription($Fields{'mime'});
       if ($Fields{'author'}=~/^(\s*|error)$/) {
           $Fields{'author'}="Unknown Author";
       }
       # Put spaces in the keyword list, if needed.
       $Fields{'keywords'}=~ s/,([A-z])/, $1/g; 
       if ($Fields{'title'}=~ /^\s*$/ ) { 
           $Fields{'title'}='Untitled'; 
       }
       unless ($ENV{'user.adv'}) {
           $Fields{'keywords'} = '- not displayed -';
           $Fields{'notes'}    = '- not displayed -';
           $Fields{'abstract'} = '- not displayed -';
           $Fields{'subject'}  = '- not displayed -';
       }
       if (length($Fields{'abstract'})>200) {
           $Fields{'abstract'} = 
               substr($Fields{'abstract'},0,200).'...';
       }
       if (length($Fields{'keywords'})>200) {
           $Fields{'keywords'} =
               substr($Fields{'keywords'},0,200).'...';
       }
       return %Fields;
   }
   
   ###########################################################
   ###########################################################
   
   =pod
   
   =item &handle_custom_fields()
   
   =cut
   
   ###########################################################
   ###########################################################
   sub handle_custom_fields {
       my @results = @{shift()};
       my $customshow='';
       my $extrashow='';
       my @customfields;
       if ($ENV{'form.customshow'}) {
           $customshow=$ENV{'form.customshow'};
           $customshow=~s/[^\w\s]//g;
           my @fields=map {
               "<font color=\"#008000\">$_:</font><!-- $_ -->";
           } split(/\s+/,$customshow);
           @customfields=split(/\s+/,$customshow);
           if ($customshow) {
               $extrashow="<ul><li>".join("</li><li>",@fields)."</li></ul>\n";
           }
       }
       my $customdata='';
       my %customhash;
       foreach my $result (@results) {
           if ($result=~/^(custom\=.*)$/) { # grab all custom metadata
               my $tmp=$result;
               $tmp=~s/^custom\=//;
               my ($k,$v)=map {&Apache::lonnet::unescape($_);
                           } split(/\,/,$tmp);
               $customhash{$k}=$v;
           }
       }
       return ($extrashow,\@customfields,\%customhash);
 }  }
   
 ######################################################################  ######################################################################
Line 1314  RESULTS Line 1438  RESULTS
   
 =item &search_results_header  =item &search_results_header
   
   Output the proper html headers and javascript code to deal with different 
   calling modes.
   
   Takes most inputs directly from %ENV, except $mode.  
   
   =over 4
   
   =item $mode is either (at this writing) 'Basic' or 'Advanced'
   
   =back
   
   The following environment variables are checked:
   
   =over 4
   
   =item 'form.catalogmode' 
   
   Checked for 'interactive' and 'groupsearch'.
   
   =item 'form.mode'
   
   Checked for existance & 'edit' mode.
   
   =item 'form.form'
   
   =item 'form.element'
   
   =back
   
 =cut  =cut
   
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
 sub search_results_header {  sub search_results_header {
       my ($mode) = @_;
       $mode = lc($mode);
       my $title;
       if ($mode eq 'advanced') {
           $title = "Advanced Search Results";
       } elsif ($mode eq 'basic') {
           $title = "Basic Search Results";
       }
     my $result = '';      my $result = '';
     # output beginning of search page      # output beginning of search page
     $result.=<<BEGINNING;      $result.=<<BEGINNING;
 <html>  <html>
 <head>  <head>
 <title>The LearningOnline Network with CAPA</title>  <title>$title</title>
 BEGINNING  BEGINNING
     # conditional output of script functions dependent on the mode in      # conditional output of script functions dependent on the mode in
     # which the search was invoked      # which the search was invoked
Line 1407  SCRIPT Line 1568  SCRIPT
     }      }
 </script>  </script>
 SCRIPT  SCRIPT
       $result.=<<END;
   </head>
   <body bgcolor="#ffffff">
   <img align=right src=/adm/lonIcons/lonlogos.gif>
   <h1>$title</h1>
   END
     return $result;      return $result;
 }  }
   
Line 1432  sub make_popwin { Line 1599  sub make_popwin {
     # rows of 10 each.  No longer used to index images.      # rows of 10 each.  No longer used to index images.
     my $sn=1;      my $sn=1;
     foreach my $sk (sort keys %rhash) {      foreach my $sk (sort keys %rhash) {
  # '<a href="  
  $grid.="'<a href=\"";   $grid.="'<a href=\"";
  # javascript:displayinfo('+  
  $grid.="javascript:opener.displayinfo('+";   $grid.="javascript:opener.displayinfo('+";
  # "'"+'key  
  $grid.="\"'\"+'";   $grid.="\"'\"+'";
  $grid.=$sk;   $grid.=$sk;
  my $hc;   my $hc;
  if ($rhash{$sk} eq 'con_lost') {   if ($rhash{$sk} eq 'con_lost') {
     $hc="BAD CONNECTION, CONTACT SYSTEM ADMINISTRATOR ";      $hc="BAD CONNECTION ";
  }   }
  else {   else {
     $hc="'+\"'\"+\"+hc['$sk']+\"+\"'\"+'";      $hc="'+\"'\"+\"+hc['$sk']+\"+\"'\"+'";
     $hcinit.="hc[\"$sk\"]=\"not yet connected...\";";      $hcinit.="hc[\"$sk\"]=\"not yet connected...\";";
  }   }
  $grid.=" hitcount=".$hc;   $grid.=" hitcount=".$hc;
  $grid.=" domain=".$hostdomains{$sk};   $grid.=" domain=".$Apache::lonnet::hostdom{$sk};
  $grid.=" IP=".$hostips{$sk};   $grid.=" IP=".$Apache::lonnet::hostip{$sk};
  # '+"'"+'">'+   # '+"'"+'">'+
  $grid.="'+\"'\"+')\">'+";   $grid.="'+\"'\"+')\">'+";
  $grid.="\n";   $grid.="\n";
  $grid.="'<img border=\"0\" name=\"img_".$hostdomains{$sk}.'_'.$sk."\"".   $grid.="'<img border=\"0\" name=\"img_".$Apache::lonnet::hostdom{$sk}.
     " src=\"/adm/lonIcons/srvnull.gif\" alt=\"".$sk."\" /></a>'+\n";              '_'.$sk."\" src=\"/adm/lonIcons/srvnull.gif\" alt=\"".$sk.
                   "\" /></a>'+\n";
  $grid.="'<br />'+\n" unless $sn%10;   $grid.="'<br />'+\n" unless $sn%10;
         $sn++;          $sn++;
     }      }
Line 1515  ENDPOP Line 1680  ENDPOP
 Output is a HTML-ified string.  Output is a HTML-ified string.
 Input arguments are title, author, subject, url, keywords, version,  Input arguments are title, author, subject, url, keywords, version,
 notes, short abstract, mime, language, creation date,  notes, short abstract, mime, language, creation date,
 last revision date, owner, copyright, hostname, httphost, and  last revision date, owner, copyright, hostname, and
 extra custom metadata to show.  extra custom metadata to show.
   
 =over 4  =over 4
Line 1527  extra custom metadata to show. Line 1692  extra custom metadata to show.
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
 sub detailed_citation_view {  sub detailed_citation_view {
     my ($title,$author,$subject,$url,$keywords,$version,      my %values = @_;
  $notes,$shortabstract,$mime,$lang,  
  $creationdate,$lastrevisiondate,$owner,$copyright,  
  $hostname,$httphost,$extrashow)=@_;  
     my $result=<<END;      my $result=<<END;
 <i>$owner</i>, last revised $lastrevisiondate  <h3><a href="http://$ENV{'HTTP_HOST'}$values{'url'}" 
 <h3><A HREF="http://$httphost$url" TARGET='search_preview'>$title</A></h3>      target='search_preview'>$values{'title'}</a></h3>
 <h3>$author</h3>  
 </p>  
 <p>  <p>
 <b>Subject:</b> $subject<br />  <b>$values{'author'}</b>, <i>$values{'owner'}</i><br />
 <b>Keyword(s):</b> $keywords<br />  
 <b>Notes:</b> $notes<br />  <b>Subject:       </b> $values{'subject'}<br />
 <b>MIME Type:</b>  <b>Keyword(s):    </b> $values{'keywords'}<br />
 END  <b>Notes:         </b> $values{'notes'}<br />
     $result.=&Apache::loncommon::filedescription($mime);  <b>MIME Type:     </b> $values{'mimetag'}<br />
     $result.=<<END;  <b>Language:      </b> $values{'language'}<br />
 <br />  <b>Copyright/Distribution:</b> $values{'cprtag'}<br />
 <b>Language:</b>   
 END  
     $result.=&Apache::loncommon::languagedescription($lang);  
     $result.=<<END;  
 <br />  
 <b>Copyright/Distribution:</b>   
 END  
     $result.=&Apache::loncommon::copyrightdescription($copyright);  
     $result.=<<END;  
 <br />  
 </p>  </p>
 $extrashow  $values{'extrashow'}
 <p>  <p>
 $shortabstract  $values{'shortabstract'}
 </p>  </p>
 END  END
     return $result;      return $result;
Line 1576  END Line 1726  END
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
 sub summary_view {  sub summary_view {
     my ($title,$author,$subject,$url,$keywords,$version,      my %values = @_;
  $notes,$shortabstract,$mime,$lang,  
  $creationdate,$lastrevisiondate,$owner,$copyright,  
  $hostname,$httphost,$extrashow)=@_;  
     my $cprtag=&Apache::loncommon::copyrightdescription($copyright);  
     my $result=<<END;      my $result=<<END;
 <a href="http://$httphost$url" TARGET='search_preview'>$author</a><br />  <a href="http://$ENV{'HTTP_HOST'}$values{'url'}" 
 $title<br />     target='search_preview'>$values{'author'}</a><br />
 $owner -- $lastrevisiondate<br />  $values{'title'}<br />
 $cprtag<br />  $values{'owner'} -- $values{'lastrevisiondate'}<br />
 $extrashow  $values{'copyrighttag'}<br />
   $values{'extrashow'}
 </p>  </p>
 END  END
     return $result;      return $result;
Line 1604  END Line 1751  END
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
 sub fielded_format_view {  sub fielded_format_view {
     my ($title,$author,$subject,$url,$keywords,$version,      my %values = @_;
  $notes,$shortabstract,$mime,$lang,  
  $creationdate,$lastrevisiondate,$owner,$copyright,  
  $hostname,$httphost,$extrashow)=@_;  
     my $mimetag=&Apache::loncommon::filedescription($mime);  
     my $language=&Apache::loncommon::languagedescription($lang);  
     my $cprtag=&Apache::loncommon::copyrightdescription($copyright);  
     my $result=<<END;      my $result=<<END;
 <b>URL: </b> <A HREF="http://$httphost$url" TARGET='search_preview'>$url</A>  <b>URL: </b> <a href="http://$ENV{'HTTP_HOST'}$values{'url'}" 
                 target='search_preview'>$values{'url'}</a>
 <br />  <br />
 <b>Title:</b> $title<br />  <b>Title:</b> $values{'title'}<br />
 <b>Author(s):</b> $author<br />  <b>Author(s):</b> $values{'author'}<br />
 <b>Subject:</b> $subject<br />  <b>Subject:</b> $values{'subject'}<br />
 <b>Keyword(s):</b> $keywords<br />  <b>Keyword(s):</b> $values{'keywords'}<br />
 <b>Notes:</b> $notes<br />  <b>Notes:</b> $values{'notes'}<br />
 <b>MIME Type:</b> $mimetag<br />  <b>MIME Type:</b> $values{'mimetag'}<br />
 <b>Language:</b> $language<br />  <b>Language:</b> $values{'language'}<br />
 <b>Creation Date:</b> $creationdate<br />  <b>Creation Date:</b> $values{'creationdate'}<br />
 <b>Last Revision Date:</b> $lastrevisiondate<br />  <b>Last Revision Date:</b> $values{'lastrevisiondate'}<br />
 <b>Publisher/Owner:</b> $owner<br />  <b>Publisher/Owner:</b> $values{'owner'}<br />
 <b>Copyright/Distribution:</b> $cprtag<br />  <b>Copyright/Distribution:</b> $values{'copyrighttag'}<br />
 <b>Repository Location:</b> $hostname<br />  <b>Repository Location:</b> $values{'hostname'}<br />
 <b>Abstract:</b> $shortabstract<br />  <b>Abstract:</b> $values{'shortabstract'}<br />
 $extrashow  $values{'extrashow'}
 </p>  </p>
 END  END
     return $result;      return $result;
Line 1647  END Line 1789  END
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
 sub xml_sgml_view {  sub xml_sgml_view {
     my ($title,$author,$subject,$url,$keywords,$version,      my %values = @_;
  $notes,$shortabstract,$mime,$lang,  
  $creationdate,$lastrevisiondate,$owner,$copyright,  
  $hostname,$httphost,$extrashow)=@_;  
     my $cprtag=&Apache::loncommon::copyrightdescription($copyright);  
     my $mimetag=&Apache::loncommon::filedescription($mime);  
     my $language=&Apache::loncommon::languagedescription($lang);  
     my $result=<<END;      my $result=<<END;
 <pre>  <pre>
 &lt;LonCapaResource&gt;  &lt;LonCapaResource&gt;
 &lt;url&gt;$url&lt;/url&gt;  &lt;url&gt;$values{'url'}&lt;/url&gt;
 &lt;title&gt;$title&lt;/title&gt;  &lt;title&gt;$values{'title'}&lt;/title&gt;
 &lt;author&gt;$author&lt;/author&gt;  &lt;author&gt;$values{'author'}&lt;/author&gt;
 &lt;subject&gt;$subject&lt;/subject&gt;  &lt;subject&gt;$values{'subject'}&lt;/subject&gt;
 &lt;keywords&gt;$keywords&lt;/keywords&gt;  &lt;keywords&gt;$values{'keywords'}&lt;/keywords&gt;
 &lt;notes&gt;$notes&lt;/notes&gt;  &lt;notes&gt;$values{'notes'}&lt;/notes&gt;
 &lt;mimeInfo&gt;  &lt;mimeInfo&gt;
 &lt;mime&gt;$mime&lt;/mime&gt;  &lt;mime&gt;$values{'mime'}&lt;/mime&gt;
 &lt;mimetag&gt;$mimetag&lt;/mimetag&gt;  &lt;mimetag&gt;$values{'mimetag'}&lt;/mimetag&gt;
 &lt;/mimeInfo&gt;  &lt;/mimeInfo&gt;
 &lt;languageInfo&gt;  &lt;languageInfo&gt;
 &lt;language&gt;$lang&lt;/language&gt;  &lt;language&gt;$values{'lang'}&lt;/language&gt;
 &lt;languagetag&gt;$language&lt;/languagetag&gt;  &lt;languagetag&gt;$values{'language'}&lt;/languagetag&gt;
 &lt;/languageInfo&gt;  &lt;/languageInfo&gt;
 &lt;creationdate&gt;$creationdate&lt;/creationdate&gt;  &lt;creationdate&gt;$values{'creationdate'}&lt;/creationdate&gt;
 &lt;lastrevisiondate&gt;$lastrevisiondate&lt;/lastrevisiondate&gt;  &lt;lastrevisiondate&gt;$values{'lastrevisiondate'}&lt;/lastrevisiondate&gt;
 &lt;owner&gt;$owner&lt;/owner&gt;  &lt;owner&gt;$values{'owner'}&lt;/owner&gt;
 &lt;copyrightInfo&gt;  &lt;copyrightInfo&gt;
 &lt;copyright&gt;$copyright&lt;/copyright&gt;  &lt;copyright&gt;$values{'copyright'}&lt;/copyright&gt;
 &lt;copyrighttag&gt;$cprtag&lt;/copyrighttag&gt;  &lt;copyrighttag&gt;$values{'copyrighttag'}&lt;/copyrighttag&gt;
 &lt;/copyrightInfo&gt;  &lt;/copyrightInfo&gt;
 &lt;repositoryLocation&gt;$hostname&lt;/repositoryLocation&gt;  &lt;repositoryLocation&gt;$values{'hostname'}&lt;/repositoryLocation&gt;
 &lt;shortabstract&gt;$shortabstract&lt;/shortabstract&gt;  &lt;shortabstract&gt;$values{'shortabstract'}&lt;/shortabstract&gt;
 &lt;/LonCapaResource&gt;  &lt;/LonCapaResource&gt;
 </pre>  </pre>
 $extrashow  $values{'extrashow'}
 END  END
     return $result;      return $result;
 }  }
Line 1722  sub filled { Line 1858  sub filled {
 sub output_blank_field_error {  sub output_blank_field_error {
     my ($r)=@_;      my ($r)=@_;
     # make query information persistent to allow for subsequent revision      # make query information persistent to allow for subsequent revision
     my $persistent=&make_persistent();      my $persistent=&make_persistent(\%ENV);
   
     $r->print(<<BEGINNING);      $r->print(<<BEGINNING);
 <html>  <html>
Line 1768  Output a full html page with an error me Line 1904  Output a full html page with an error me
 sub output_date_error {  sub output_date_error {
     my ($r,$message)=@_;      my ($r,$message)=@_;
     # make query information persistent to allow for subsequent revision      # make query information persistent to allow for subsequent revision
     my $persistent=&make_persistent();      my $persistent=&make_persistent(\%ENV);
   
     $r->print(<<RESULTS);      $r->print(<<RESULTS);
 <html>  <html>
Line 1860  input $r, loncapa server id, and an icon Line 1996  input $r, loncapa server id, and an icon
 ######################################################################  ######################################################################
 sub popwin_imgupdate {  sub popwin_imgupdate {
     my ($r,$server,$icon) = @_;      my ($r,$server,$icon) = @_;
     &popwin_js($r,'popwin.document.img_'.$hostdomains{$server}.'_'.$server.'.'.      &popwin_js($r,'popwin.document.img_'.$Apache::lonnet::hostdom{$server}.
        'src="/adm/lonIcons/'.$icon.'";');                 '_'.$server.'.'.'src="/adm/lonIcons/'.$icon.'";');
 }      }    
   
 1;  1;
Line 1872  __END__ Line 2008  __END__
   
 =back   =back 
   
 =over 4  
   
 =head1 HANDLER SUBROUTINE  
   
 This routine is called by Apache and mod_perl.  
   
 =over 4  
   
 =item *  
   
 configure dynamic components of interface  
   
 =item *  
   
 determine current user  
   
 =item *  
   
 see if a search invocation should be done  
   
 =item *  
   
 else, begin building search interface to output  
   
 =item *  
   
 compute date selection boxes  
   
 =item *  
   
 compute customized metadata field  
   
 =item *  
   
 print screen  
   
 =back  
   
 =head1 OTHER SUBROUTINES  
   
 =over 4  
   
 =item *  
   
 get_unprocessed_cgi() : reads in critical name/value pairs that may have not  
 been processed and passed into %ENV by the web server  
   
 =item *  
   
 make_persistent() : makes a set of hidden HTML fields to make  
 SQL search interface information to be persistent  
   
 =back  
   
 WEB INTERFACE COMPONENT FUNCTIONS  
   
 =over 4  
   
 =item *  
   
 simpletextfield(name,value) : returns HTML formatted string for simple text  
 field  
   
 =item *  
   
 simplecheckbox(name,value) : returns HTML formatted string for simple  
 checkbox  
   
 =item *  
   
 searchphrasefield(title,name,value) : returns HTML formatted string for  
 a search expression phrase field  
   
 =item *  
   
 dateboxes(name, defaultmonth, defaultday, defaultyear) : returns HTML  
 formatted string for a calendar date  
   
 =item *  
   
 selectbox(title,name,value,%HASH=options) : returns HTML formatted string for  
 a selection box field  
   
 =back  
   
 SEARCH FUNCTIONS  
   
 =over 4  
   
 =item *  
   
 advancedsearch(server reference, environment reference) : perform a complex  
 multi-field logical query  
   
 =item *  
   
 basicsearch(server reference, environment reference) : perform a simple  
 single-field logical query  
   
 =item *  
   
 build_SQL_query(field name, logic) : builds a SQL query string from a  
 logical expression with AND/OR keywords  
   
 =item *  
   
 build_custommetadata_query(field_name, logic_statement) : builds a perl  
 regular expression from a logical expression with AND/OR keywords  
   
 =item *  
   
 recursive_SQL_query_build(field name, reverse notation expression) :   
 builds a SQL query string from a reverse notation expression  
 logical expression with AND/OR keywords  
   
 =item *  
   
 build_date_queries(cmonth1, cday1, cyear1, cmonth2, cday2, cyear2,  
 lmonth1, lday1, lyear1, lmonth2, lday2, lyear2) :  
 Builds a SQL logic query to check time/date entries.  
   
 =back  
   
 OUTPUTTING RESULTS FUNCTION  
   
 =over 4  
   
 =item *  
   
 output_results(output mode, server reference, environment reference,  
 reply list reference) : outputs results from search  
   
 =back  
   
 DIFFERENT WAYS TO VIEW METADATA RECORDS  
   
 =over 4  
   
 =item *  
   
 detailed_citation_view(ORDERED METADATA LIST FOR A RESULT OBJECT INSTANCE) :  
 see metadata viewing notes below   
   
 =item *  
   
 summary_view(ORDERED METADATA LIST FOR A RESULT OBJECT INSTANCE) :  
 see metadata viewing notes below   
   
 =item *  
   
 fielded_format_view(ORDERED METADATA LIST FOR A RESULT OBJECT INSTANCE) :  
 see metadata viewing notes below   
   
 =item *  
   
 xml_sgml_view(ORDERED METADATA LIST FOR A RESULT OBJECT INSTANCE) :  
 see metadata viewing notes below   
   
 =back  
   
   _____________________________________________________________________  
  | * Metadata viewing notes                                            |  
  | Output is a HTML-ified string.                                      |  
  | Input arguments are title, author, subject, url, keywords, version, |  
  | notes, short abstract, mime, language, creation date,               |  
  | last revision date, owner, copyright, hostname, httphost, and       |  
  | extra custom metadata to show.                                      |  
   ---------------------------------------------------------------------  
   
 TEST CONDITIONAL FUNCTIONS  
   
 =over 4  
   
 =item *  
   
 filled(field) : determines whether a given field has been filled  
   
 =back  
   
 ERROR FUNCTIONS  
   
 =over 4  
   
 =item *  
   
 output_blank_field_error(server reference) : outputs a message saying that  
 more fields need to be filled in  
   
 =item *  
   
 output_date_error(server reference, error message) :   
   
   
 =back  
   
 =cut  =cut

Removed from v.1.125  
changed lines
  Added in v.1.136


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>