--- loncom/interface/lonsearchcat.pm 2002/07/05 18:56:52 1.136 +++ loncom/interface/lonsearchcat.pm 2002/07/30 20:08:04 1.148 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Search Catalog # -# $Id: lonsearchcat.pm,v 1.136 2002/07/05 18:56:52 matthew Exp $ +# $Id: lonsearchcat.pm,v 1.148 2002/07/30 20:08:04 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -40,7 +40,7 @@ =head1 NAME -lonsearchcat +lonsearchcat - LONCAPA Search Interface =head1 SYNOPSIS @@ -87,8 +87,10 @@ use Apache::lonnet(); use Apache::File(); use CGI qw(:standard); use Text::Query; +use DBI; use GDBM_File; use Apache::loncommon(); +use Apache::lonmysql(); # ---------------------------------------- variables used throughout the module @@ -101,23 +103,26 @@ use Apache::loncommon(); =over 4 -=item $closebutton - -button that closes the search window - =item $importbutton button to take the select results and go to group sorting -=item %hash +=item %groupsearch_db -The ubiquitous database hash +Database hash used to save values for the groupsearch RAT interface. =item $diropendb 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 @@ -126,13 +131,18 @@ used in &handler() and is also used in & ###################################################################### # -- 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 %hash; # database hash +my %groupsearch_db; # database hash my $diropendb = ""; # db file - +# 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 ); +my %persistent_db; +my $hidden_fields; ###################################################################### ###################################################################### @@ -160,26 +170,60 @@ string that holds portions of the screen ###################################################################### sub handler { my $r = shift; - untie %hash; - + # + my $closebutton; # button that closes the search window + # This button is different for the RAT compared to + # normal invocation. + # $r->content_type('text/html'); $r->send_http_header; return OK if $r->header_only; - + ## + ## Pick up form fields passed in the links. + ## + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, + ['catalogmode','launch','acts','mode','form','element','pause', + 'phase','persistent_db_id','table','start','show']); + ## + ## The following is a trick - we wait a few seconds if asked to so + ## the daemon running the search can get ahead of the daemon + ## printing the results. We only need (theoretically) to do + ## this once, so the pause indicator is deleted + ## + if (exists($ENV{'form.pause'})) { + sleep(3); + delete($ENV{'form.pause'}); + } + ## + ## Initialize global variables + ## my $domain = $r->dir_config('lonDefDomain'); $diropendb= "/home/httpd/perl/tmp/".&Apache::lonnet::escape($domain). "\_".&Apache::lonnet::escape($ENV{'user.name'})."_searchcat.db"; - - &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, - ['catalogmode','launch','acts','mode','form','element', - 'reqinterface']); + # + # set the name of the persistent database + # $ENV{'form.persistent_db_id'} can only have digits in it. + if (! exists($ENV{'form.persistent_db_id'}) || + ($ENV{'form.persistent_db_id'} =~ /\D/) || + ($ENV{'form.launch'} eq '1')) { + $ENV{'form.persistent_db_id'} = time; + } + my $persistent_db_file = "/home/httpd/perl/tmp/". + &Apache::lonnet::escape($domain). + '_'.&Apache::lonnet::escape($ENV{'user.name'}). + '_'.$ENV{'form.persistent_db_id'}.'_persistent_search.db'; + ## + if (! &get_persistent_form_data($r,$persistent_db_file)) { + &write_status($r,"Unable to get persistent data"); + } ## - ## Clear out old values from database + ## Clear out old values from groupsearch database ## + untie %groupsearch_db if (tied(%groupsearch_db)); if ($ENV{'form.launch'} eq '1') { - if (tie(%hash,'GDBM_File',$diropendb,&GDBM_WRCREAT,0640)) { + if (tie(%groupsearch_db,'GDBM_File',$diropendb,&GDBM_WRCREAT(),0640)) { &start_fresh_session(); - untie %hash; + untie %groupsearch_db; } else { $r->print('Unable to tie hash to db '. 'file'); @@ -187,23 +231,15 @@ sub handler { } } ## - ## Produce some output, so people know it is working - ## - $r->print("\n"); - $r->rflush; - ## ## Configure dynamic components of interface ## - my $hidden; # Holds 'hidden' html forms + $hidden_fields = ''; + ## if ($ENV{'form.catalogmode'} eq 'interactive') { - $hidden="". - "\n"; $closebutton=""."\n"; } elsif ($ENV{'form.catalogmode'} eq 'groupsearch') { - $hidden=< -END $closebutton=< END @@ -211,40 +247,86 @@ END END + } else { + $closebutton = ''; + $importbutton = ''; } - $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? - ## - my $searchtype; - $searchtype = 'Basic' if ($ENV{'form.basicsubmit'} eq 'SEARCH'); - $searchtype = 'Advanced' if ($ENV{'form.advancedsubmit'} eq 'SEARCH'); - if ($searchtype) { + ## + ## Sanity checks on form elements + ## + if (!defined($ENV{'form.viewselect'})) { + $ENV{'form.viewselect'} ="Detailed Citation View"; + } + $ENV{'form.phase'} = 'displaybasic' if (! exists($ENV{'form.phase'})); + ## + ## Switch on the phase + ## + if ($ENV{'form.phase'} eq 'disp_basic') { + &print_basic_search_form($r,$closebutton); + } elsif ($ENV{'form.phase'} eq 'disp_adv') { + &print_advanced_search_form($r,$closebutton); + } elsif ($ENV{'form.phase'} eq 'results') { + &display_results($r,$importbutton,$closebutton); + } elsif($ENV{'form.phase'} eq 'run_search') { + my ($query,$customquery,$customshow,$libraries,$pretty_string) = + &get_persistent_data($persistent_db_file, + ['query','customquery','customshow', + 'libraries','pretty_string']); + &write_status($r,"query = $query"); + &write_status($r,"customquery = $customquery"); + &write_status($r,"customshow = $customshow"); + &write_status($r,"libraries = $libraries"); + &write_status($r,"pretty_string = $pretty_string"); + &run_search($r,$query,$customquery,$customshow, + $libraries,$pretty_string); + } elsif(($ENV{'form.phase'} eq 'basic_search') || + ($ENV{'form.phase'} eq 'adv_search')) { + # Set up table + if (! defined(&create_results_table())) { + $r->print(<Search Error + +Unable to create table in which to store search results. +The search has been aborted. + + +END + return OK; + } + delete($ENV{'form.launch'}); + if (! &make_form_data_persistent($r,$persistent_db_file)) { + $r->print(<Search Error + +Unable to properly store search information. The search has been aborted. + + +END + return OK; + } + # + # 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); + my $pretty_string; + if ($ENV{'form.phase'} eq 'basic_search') { + ($query,$pretty_string) = &parse_basic_search($r,$closebutton); + } else { # Advanced search + ($query,$customquery,$customshow,$libraries,$pretty_string) + = &parse_advanced_search($r,$closebutton); 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') { - # Output the advanced interface - $r->print(&advanced_search_form($closebutton,$hidden)); - } else { - # Output normal search interface - $r->print(&basic_search_form($closebutton,$hidden)); + &make_persistent($r, + { query => $query, + customquery => $customquery, + customshow => $customshow, + libraries => $libraries, + pretty_string => $pretty_string }, + $persistent_db_file); + ## + ## Print out the frames interface + ## + &print_frames_interface($r); } return OK; } @@ -254,7 +336,7 @@ END =pod -=item &basic_search_form() +=item &print_basic_search_form() Returns a scalar which holds html for the basic search form. @@ -263,8 +345,8 @@ Returns a scalar which holds html for th ###################################################################### ###################################################################### -sub basic_search_form{ - my ($closebutton,$hidden) = @_; +sub print_basic_search_form{ + my ($r,$closebutton) = @_; my $scrout=<<"ENDDOCUMENT"; @@ -281,7 +363,8 @@ sub basic_search_form{

Search Catalog

-$hidden + +$hidden_fields

Basic Search

Enter terms or phrases separated by AND, OR, or NOT @@ -295,27 +378,29 @@ ENDDOCUMENT ' '; # $scrout.=&simplecheckbox('allversions',$ENV{'form.allversions'}); # $scrout.='Search historic archives'; - $scrout.=<Advanced Search + my $checkbox = &simplecheckbox('related',$ENV{'form.related'}); + $scrout.=<Advanced Search +$checkbox use related words +

   $closebutton - - - +END + $scrout.=&selectbox(undef,'viewselect', + $ENV{'form.viewselect'}, + undef,undef,undef, + sort(keys(%Views))); + $scrout.=<

ENDDOCUMENT - return $scrout; + $r->print($scrout); + return; } ###################################################################### ###################################################################### @@ -331,8 +416,8 @@ Returns a scalar which holds html for th ###################################################################### ###################################################################### -sub advanced_search_form{ - my ($closebutton,$hidden) = @_; +sub print_advanced_search_form{ + my ($r,$closebutton) = @_; my $advanced_buttons = <<"END";

@@ -341,6 +426,9 @@ $closebutton

END + if (!defined($ENV{'form.viewselect'})) { + $ENV{'form.viewselect'} ="Detailed Citation View"; + } my $scrout=<<"ENDHEADER"; @@ -361,32 +449,34 @@ Enter terms or phrases separated by sear such as AND, OR, or NOT.
$advanced_buttons -$hidden +$hidden_fields + ENDHEADER - $scrout.=&searchphrasefield('title', 'title' ,$ENV{'form.title'}); + $scrout.=&selectbox(undef,'viewselect', + $ENV{'form.viewselect'}, + undef,undef,undef, + sort(keys(%Views))); + $scrout.="\n"; + $scrout.=&searchphrasefield_with_related('title', 'title' , + $ENV{'form.title'}); $scrout.=&searchphrasefield('author', 'author' ,$ENV{'form.author'}); - $scrout.=&searchphrasefield('subject', 'subject' ,$ENV{'form.subject'}); - $scrout.=&searchphrasefield('keywords','keywords',$ENV{'form.keywords'}); + $scrout.=&searchphrasefield_with_related('subject', 'subject' , + $ENV{'form.subject'}); + $scrout.=&searchphrasefield_with_related('keywords','keywords', + $ENV{'form.keywords'}); $scrout.=&searchphrasefield('URL', 'url' ,$ENV{'form.url'}); - $scrout.=&searchphrasefield('notes', 'notes' ,$ENV{'form.notes'}); - $scrout.=&searchphrasefield('abstract','abstract',$ENV{'form.abstract'}); + $scrout.=&searchphrasefield_with_related('notes', 'notes' , + $ENV{'form.notes'}); + $scrout.=&searchphrasefield_with_related('abstract','abstract', + $ENV{'form.abstract'}); # Hack - an empty table row. - $scrout.="\n"; + $scrout.="\n"; $scrout.=&searchphrasefield('file
extension','mime', $ENV{'form.mime'}); - $scrout.="\n"; + $scrout.="\n"; $scrout.=&searchphrasefield('publisher
owner','owner', $ENV{'form.owner'}); $scrout.="
VIEW: - - - -
Related
Words
  
   
  
   
\n"; @@ -412,22 +502,17 @@ ENDHEADER # 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 .=''."\n"; - } else { - $scrout.="\n".''. - 'DOMAINS
'. - ''."\n". + ''."\n"; - foreach my $dom (sort @domains) { - $scrout.="\n"; - } - $scrout.="\n"; + foreach my $dom (sort @domains) { + $scrout.="\n"; } + $scrout.="\n"; #---------------------------------------------------------------- $scrout.=&selectbox('Limit by language','language', $ENV{'form.language'},'any','Any Language', @@ -507,7 +592,8 @@ $advanced_buttons ENDDOCUMENT - return $scrout; + $r->print($scrout); + return; } ###################################################################### @@ -515,123 +601,194 @@ ENDDOCUMENT =pod -=item &make_persistent() +=item &get_persistent_form_data + +Inputs: filename of database + +Outputs: returns undef on database errors. -Returns a scalar which holds the current ENV{'form.*'} values in -a 'hidden' html input tag. This allows search interface information -to be somewhat persistent. +This function is the reverse of &make_persistent() for form data. +Retrieve persistent data from %persistent_db. Retrieved items will have their +values unescaped. If a form value already exists in $ENV, it will not be +overwritten. Form values that are array references may have values appended +to them. =cut ###################################################################### ###################################################################### - -sub make_persistent { - my %save = %{shift()}; - my $persistent=''; - foreach (keys %save) { - if (/^form\./ && !/submit/) { - my $name=$_; - my @values = (ref($save{$name}) ? @{$save{$name}} : ($save{$name})); - $name=~s/^form\.//; - foreach (@values) { - s/\"/\'/g; # do not mess with html field syntax - $persistent.=< -END +sub get_persistent_form_data { + my $r = shift; + my $filename = shift; + return 0 if (! -e $filename); + return undef if (! tie(%persistent_db,'GDBM_File',$filename, + &GDBM_READER(),0640)); + # + # These make sure we do not get array references printed out as 'values'. + my %arrays_allowed = ('form.category'=>1,'form.domains'=>1); + # + # Loop through the keys, looking for 'form.' + foreach my $name (keys(%persistent_db)) { + next if ($name !~ /^form./); + my @values = map { + &Apache::lonnet::unescape($_); + } split(',',$persistent_db{$name}); + next if (@values <1); + if (exists($ENV{$name})) { + if (ref($ENV{$name}) eq 'ARRAY') { + # If it is an array, tack @values on the end of it. + $ENV{$name} = [@$ENV{$name},@values]; + } elsif (! ref($ENV{$name}) && $arrays_allowed{$name}) { + # if arrays are allowed, turn it into one and add @values + $ENV{$name} = [$ENV{$name},@values]; + } # otherwise, assume the value in $ENV{$name} is better than ours. + } else { + if ($arrays_allowed{$name}) { + $ENV{$name} = [@values]; + } else { + $ENV{$name} = $values[0] if ($values[0]); } } } - return $persistent; + untie (%persistent_db); + return 1; } - - ###################################################################### ###################################################################### =pod -=item HTML form building functions +=item &get_persistent_data -=over 4 +Inputs: filename of database, ref to array of values to recover. -=item &simpletextfield() +Outputs: array of values. Returns undef on error. -Inputs: $name,$value,$size +This function is the reverse of &make_persistent(); +Retrieve persistent data from %persistent_db. Retrieved items will have their +values unescaped. If the item contains commas (before unescaping), the +returned value will be an array pointer. -Returns a text input field with the given name, value, and size. -If size is not specified, a value of 20 is used. - -=item &simplecheckbox() - -Inputs: $name,$value - -Returns a simple check box with the given $name. -If $value eq 'on' the box is checked. +=cut -=item &searchphrasefield() +###################################################################### +###################################################################### +sub get_persistent_data { + my $filename = shift; + my @Vars = @{shift()}; + my @Values; # Return array + return undef if (! -e $filename); + return undef if (! tie(%persistent_db,'GDBM_File',$filename, + &GDBM_READER(),0640)); + foreach my $name (@Vars) { + if (! exists($persistent_db{$name})) { + push @Values, undef; + next; + } + my @values = map { + &Apache::lonnet::unescape($_); + } split(',',$persistent_db{$name}); + if (@values == 1) { + push @Values,$values[0]; + } else { + push @Values,\@values; + } + } + untie (%persistent_db); + return @Values; +} -Inputs: $title,$name,$value +###################################################################### +###################################################################### -Returns html for a title line and an input field for entering search terms. -the instructions "Enter terms or phrases separated by search operators such -as AND, OR, or NOT." are given following the title. The entry field (which -is where the $name and $value are used) is an 80 column simpletextfield. +=pod -=item &dateboxes() +=item &make_persistent() -Returns html selection form elements for the specification of -the day, month, and year. +Inputs: Hash of values to save, filename of persistent database. -=item &selectbox() +Store variables away to the %persistent_db. +Values will be escaped. Values that are array pointers will have their +elements escaped and concatenated in a comma seperated string. -Returns a scalar containing an html tag. +Store most form variables away to the %persistent_db. +Values will be escaped. Values that are array pointers will have their +elements escaped and concatenated in a comma seperated string. -=item $default +=cut -The default value of the form. Can be $anyvalue or in @idlist. +###################################################################### +###################################################################### +sub make_form_data_persistent { + my $r = shift; + my $filename = shift; + my %save; + foreach (keys(%ENV)) { + next if (! /^form/ || /submit/); + $save{$_} = $ENV{$_}; + } + return &make_persistent($r,\%save,$filename); +} -=item $anyvalue +###################################################################### +# HTML form building functions # +###################################################################### -The