--- loncom/interface/lonsearchcat.pm 2002/06/20 19:43:50 1.125
+++ loncom/interface/lonsearchcat.pm 2002/07/12 14:36:16 1.141
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Search Catalog
#
-# $Id: lonsearchcat.pm,v 1.125 2002/06/20 19:43:50 matthew Exp $
+# $Id: lonsearchcat.pm,v 1.141 2002/07/12 14:36:16 matthew Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -40,7 +40,7 @@
=head1 NAME
-lonsearchcat
+lonsearchcat - LONCAPA Search Interface
=head1 SYNOPSIS
@@ -67,14 +67,14 @@ search (on a server basis) is displayed
###############################################################################
###############################################################################
+###############################################################################
## ##
## ORGANIZATION OF THIS PERL MODULE ##
## ##
## 1. Modules used by this module ##
-## 2. Choices for different output views (detailed, summary, xml, etc) ##
-## 3. BEGIN block (to be run once after compilation) ##
-## 4. Handling routine called via Apache and mod_perl ##
-## 5. Other subroutines ##
+## 2. Variables used throughout the module ##
+## 3. handler subroutine called via Apache and mod_perl ##
+## 4. Other subroutines ##
## ##
###############################################################################
@@ -101,25 +101,13 @@ use Apache::loncommon();
=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
button that closes the search window
=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
@@ -130,6 +118,13 @@ The ubiquitous database hash
The full path to the (temporary) search database file. This is set and
used in &handler() and is also used in &output_results().
+=item %Views
+
+Hash which associates an output view description with the function
+that produces it. Adding a new view type should be as easy as
+adding a line to the definition of this hash and making sure the function
+takes the proper parameters.
+
=back
=cut
@@ -137,46 +132,19 @@ 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
my $closebutton; # button that closes the search window
my $importbutton; # button to take the selected results and go to group sorting
# -- miscellaneous variables
-my $yourself; # allows for quickly limiting to oneself
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{$_};
- }
-}
+# View Description Function Pointer
+my %Views = ("Detailed Citation View" => \&detailed_citation_view,
+ "Summary View" => \&summary_view,
+ "Fielded Format" => \&fielded_format_view,
+ "XML/SGML" => \&xml_sgml_view );
######################################################################
######################################################################
@@ -257,28 +225,49 @@ END
onClick='javascript:select_group()'>
END
}
- $hidden .= <
-
-
-
-END
+ $hidden .= &make_persistent({ "form.mode" => $ENV{'form.mode'},
+ "form.form" => $ENV{'form.form'},
+ "form.element" => $ENV{'form.element'},
+ "form.date" => 2 });
##
## What are we doing?
##
- if ($ENV{'form.basicsubmit'} eq 'SEARCH') {
- # Perform basic search and give results
- return &basicsearch($r,\%ENV,$hidden);
- } elsif ($ENV{'form.advancedsubmit'} eq 'SEARCH') {
- # Perform advanced search and give results
- return &advancedsearch($r,\%ENV,$hidden);
- } elsif ($ENV{'form.reqinterface'} eq 'advanced') {
+ my $searchtype;
+ $searchtype = 'Basic' if ($ENV{'form.basicsubmit'} eq 'SEARCH');
+ $searchtype = 'Advanced' if ($ENV{'form.advancedsubmit'} eq 'SEARCH');
+ if ($searchtype) {
+ # We are running a search
+ my ($query,$customquery,$customshow,$libraries) =
+ (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);
+ } else {
+ #
+ # We need to get information to search on
+ #
+ # Set the default view if it is not already set.
+ if (!defined($ENV{'form.viewselect'})) {
+ $ENV{'form.viewselect'} ="Detailed Citation View";
+ }
# Output the advanced interface
- $r->print(&advanced_search_form($closebutton,$hidden));
- return OK;
- } else {
- # Output normal search interface
- $r->print(&basic_search_form($closebutton,$hidden));
+ if ($ENV{'form.reqinterface'} eq 'advanced') {
+ $r->print(&advanced_search_form($closebutton,$hidden));
+ } else {
+ # Output normal search interface
+ $r->print(&basic_search_form($closebutton,$hidden));
+ }
}
return OK;
}
@@ -318,7 +307,8 @@ sub basic_search_form{
$hidden
Basic Search
-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.
@@ -367,6 +357,17 @@ Returns a scalar which holds html for th
sub advanced_search_form{
my ($closebutton,$hidden) = @_;
+ my $advanced_buttons = <<"END";
+
+
+
+$closebutton
+
+
+END
+ if (!defined($ENV{'form.viewselect'})) {
+ $ENV{'form.viewselect'} ="Detailed Citation View";
+ }
my $scrout=<<"ENDHEADER";
@@ -381,35 +382,76 @@ sub advanced_search_form{
-
Search Catalog
+
Advanced Catalog Search
+
+Enter terms or phrases separated by search operators
+such as AND, OR, or NOT.
@@ -515,7 +541,8 @@ ENDDOCUMENT
=item &make_persistent()
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
@@ -523,16 +550,19 @@ a 'hidden' html input tag.
######################################################################
sub make_persistent {
+ my %save = %{shift()};
my $persistent='';
- foreach (keys %ENV) {
+ foreach (keys %save) {
if (/^form\./ && !/submit/) {
my $name=$_;
- my $key=$name;
- $ENV{$key}=~s/\'//g; # do not mess with html field syntax
+ my @values = (ref($save{$name}) ? @{$save{$name}} : ($save{$name}));
$name=~s/^form\.//;
- $persistent.=<
+ foreach (@values) {
+ s/\"/\'/g; # do not mess with html field syntax
+ $persistent.=<
END
+ }
}
}
return $persistent;
@@ -573,8 +603,53 @@ is where the $name and $value are used)
=item &dateboxes()
+Returns html selection form elements for the specification of
+the day, month, and year.
+
=item &selectbox()
+Returns a scalar containing an html '.(defined($title)?'':' ');
}
######################################################################
@@ -674,15 +752,28 @@ sub selectbox {
=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
######################################################################
######################################################################
-sub advancedsearch {
- my ($r,$envhash,$hidden)=@_;
- my %ENV=%{$envhash};
+sub parse_advanced_search {
+ my ($r)=@_;
my $fillflag=0;
# Clean up fields for safety
for my $field ('title','author','subject','keywords','url','version',
@@ -693,7 +784,7 @@ sub advancedsearch {
'lastrevisiondatestart_year','lastrevisiondateend_month',
'lastrevisiondateend_day','lastrevisiondateend_year',
'notes','abstract','mime','language','owner',
- 'custommetadata','customshow') {
+ 'custommetadata','customshow','category') {
$ENV{"form.$field"}=~s/[^\w\/\s\(\)\=\-\"\']//g;
}
foreach ('mode','form','element') {
@@ -702,6 +793,12 @@ sub advancedsearch {
$ENV{"form.$_"}=&Apache::lonnet::unescape($ENV{"form.$_"});
$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
for my $field ('title','author','subject','keywords','url','version',
'notes','abstract','mime','language','owner',
@@ -712,25 +809,26 @@ sub advancedsearch {
}
unless ($fillflag) {
&output_blank_field_error($r);
- return OK;
+ return ;
}
# Turn the form input into a SQL-based query
my $query='';
my @queries;
# Evaluate logical expression AND/OR/NOT phrase fields.
foreach my $field ('title','author','subject','notes','abstract','url',
- 'keywords','version','owner') {
+ 'keywords','version','owner','mime') {
if ($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
if ($ENV{'form.language'} and $ENV{'form.language'} ne 'any') {
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') {
push @queries,"(copyright like \"$ENV{'form.copyright'}\")";
}
@@ -752,48 +850,53 @@ sub advancedsearch {
# Test to see if date windows are legitimate
if ($datequery=~/^Incorrect/) {
&output_date_error($r,$datequery);
- return OK;
+ return ;
}
elsif ($datequery) {
push @queries,$datequery;
}
# Process form information for custom metadata querying
- my $customquery='';
+ my $customquery=undef;
if ($ENV{'form.custommetadata'}) {
$customquery=&build_custommetadata_query('custommetadata',
$ENV{'form.custommetadata'});
}
- my $customshow='';
+ my $customshow=undef;
if ($ENV{'form.customshow'}) {
$customshow=$ENV{'form.customshow'};
$customshow=~s/[^\w\s]//g;
my @fields=split(/\s+/,$customshow);
$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
- # metadata).
+ ## ---------------------------------------------------------------
+ ## Deal with restrictions to given domains
+ ##
+ 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) {
$query=join(" AND ",@queries);
$query="select * from metadata where $query";
- my $reply; # reply hash reference
- unless ($customquery or $customshow) {
- $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);
+ } elsif ($customquery) {
+ $query = '';
}
- # should not get to this point
- return 'Error. Should not have gone to this point.';
+ return ($query,$customquery,$customshow,$libraries_to_query);
}
######################################################################
@@ -801,15 +904,16 @@ sub advancedsearch {
=pod
-=item &basicsearch()
+=item &parse_basic_search()
+
+Parse the basic search form and return a scalar containing an sql query.
=cut
######################################################################
######################################################################
-sub basicsearch {
- my ($r,$envhash,$hidden)=@_;
- my %ENV=%{$envhash};
+sub parse_basic_search {
+ my ($r)=@_;
# Clean up fields for safety
for my $field ('basicexp') {
$ENV{"form.$field"}=~s/[^\w\s\(\)\-]//g;
@@ -826,7 +930,18 @@ sub basicsearch {
&output_blank_field_error($r);
return OK;
}
-
+ if ($ENV{'form.related'}) {
+ my $tmp = $ENV{'form.basicexp'};
+ while ($ENV{'form.basicexp'} =~ /(\w+)/cg) {
+ my $word = $1;
+ my @Words = &Apache::loncommon::get_related_words($word);
+ my $replacement = join " OR ", ($word,
+ ($#Words>4? @Words[0..4] : @Words)
+ );
+ $tmp =~ s/\b$word\b/ $replacement /g;
+ }
+ $ENV{'form.basicexp'} = $tmp;
+ }
# Build SQL query string based on form page
my $query='';
my $concatarg=join('," ",',
@@ -835,15 +950,7 @@ sub basicsearch {
$concatarg='title' if $ENV{'form.titleonly'};
$query=&build_SQL_query('concat('.$concatarg.')',$ENV{'form.'.'basicexp'});
-
- # 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;
+ return 'select * from metadata where '.$query;
}
@@ -854,6 +961,9 @@ sub basicsearch {
=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
######################################################################
@@ -876,14 +986,15 @@ sub build_SQL_query {
=item &build_custommetadata_query()
+Constructs a custom metadata query using a rather heinous regular
+expression.
+
=cut
######################################################################
######################################################################
sub build_custommetadata_query {
my ($field_name,$logic_statement)=@_;
- &Apache::lonnet::logthis("Entered build_custommetadata_query:".
- $field_name.':'.$logic_statement);
my $q=new Text::Query('abc',
-parse => 'Text::Query::ParseAdvanced',
-build => 'Text::Query::BuildAdvancedString');
@@ -898,7 +1009,6 @@ sub build_custommetadata_query {
\*$2\[\^\\<\]?# *wordtwo[^\<]
\*\\<\\\/$1\\>?# *\<\/wordone\>
/g;
- &Apache::lonnet::logthis("match expression: ".$matchexp);
return $matchexp;
}
@@ -909,6 +1019,8 @@ sub build_custommetadata_query {
=item &recursive_SQL_query_build()
+Recursively constructs an SQL query. Takes as input $dkey and $pattern.
+
=cut
######################################################################
@@ -953,6 +1065,9 @@ sub recursive_SQL_query_build {
=item &build_date_queries()
+Builds a SQL logic query to check time/date entries.
+Also reports errors (check for /^Incorrect/).
+
=cut
######################################################################
@@ -1023,30 +1138,41 @@ contacted, etc.)
######################################################################
######################################################################
sub output_results {
+# &Apache::lonnet::logthis("output_results:".time);
my $fnum; # search result counter
- my ($mode,$r,$envhash,$query,$replyref,$hidden)=@_;
- my %ENV=%{$envhash};
+ my ($mode,$r,$replyref,$hidden)=@_;
my %rhash=%{$replyref};
my $compiledresult='';
my $timeremain=300; # (seconds)
my $elapsetime=0;
my $resultflag=0;
my $tflag=1;
+ ##
+ ## Set viewing function
+ ##
+ my $viewfunction = $Views{$ENV{'form.viewselect'}};
+ if (!defined($viewfunction)) {
+ $r->print("Internal Error - Bad view selected.\n");
+ $r->rflush();
+ return;
+ }
#
# make query information persistent to allow for subsequent revision
- my $persistent=&make_persistent();
- # spit out the generic header
- $r->print(&search_results_header());
+ my $persistent=&make_persistent(\%ENV);
+ #
+ # Begin producing output
+ $r->print(&search_results_header($mode));
$r->rflush();
+ #
# begin showing the cataloged results
- $r->print(<
-
-
-