--- loncom/interface/lonsearchcat.pm 2002/07/26 16:37:58 1.144 +++ loncom/interface/lonsearchcat.pm 2002/07/30 20:26:05 1.149 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Search Catalog # -# $Id: lonsearchcat.pm,v 1.144 2002/07/26 16:37:58 matthew Exp $ +# $Id: lonsearchcat.pm,v 1.149 2002/07/30 20:26:05 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -103,10 +103,6 @@ use Apache::lonmysql(); =over 4 -=item $closebutton - -button that closes the search window - =item $importbutton button to take the select results and go to group sorting @@ -127,10 +123,6 @@ that produces it. Adding a new view typ adding a line to the definition of this hash and making sure the function takes the proper parameters. -=item $results_db - -The name of the database results from searches are put in. - =back =cut @@ -139,20 +131,18 @@ The name of the database results from se ###################################################################### # -- 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 %groupsearch_db; # database hash my $diropendb = ""; # db file - -my $results_db = ""; # 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; ###################################################################### ###################################################################### @@ -180,27 +170,58 @@ string that holds portions of the screen ###################################################################### sub handler { my $r = shift; - untie %groupsearch_db; - + # + 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"; - $results_db = "/home/httpd/perl/tmp/".&Apache::lonnet::escape($domain). - '_'.&Apache::lonnet::escape($ENV{'user.name'})."_searchresults.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 groupsearch database ## + untie %groupsearch_db if (tied(%groupsearch_db)); if ($ENV{'form.launch'} eq '1') { - if (tie(%groupsearch_db,'GDBM_File',$diropendb,&GDBM_WRCREAT,0640)) { + if (tie(%groupsearch_db,'GDBM_File',$diropendb,&GDBM_WRCREAT(),0640)) { &start_fresh_session(); untie %groupsearch_db; } else { @@ -210,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 @@ -234,53 +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'} = 'disp_basic' 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); my $pretty_string; - if ($searchtype eq 'Basic') { - ($query,$pretty_string) = &parse_basic_search($r); - } elsif ($ENV{'form.advancedsubmit'} eq 'SEARCH') { + 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); + = &parse_advanced_search($r,$closebutton); return OK if (! defined($query)); } - # Output some information to the user. - $r->print(&search_results_header($searchtype,$pretty_string)); - $r->print("Sending search request to LON-CAPA servers.
\n"); - $r->rflush(); - &run_search($r,$query,$customquery,$customshow,$libraries); - &display_results($r,$searchtype,$hidden,$importbutton, - $closebutton); - - $r->rflush(); - } 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 - 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)); - } + &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; } @@ -290,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. @@ -299,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"; @@ -317,7 +363,8 @@ sub basic_search_form{

Search Catalog

-$hidden + +$hidden_fields

Basic Search

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

@@ -352,7 +399,8 @@ END ENDDOCUMENT - return $scrout; + $r->print($scrout); + return; } ###################################################################### ###################################################################### @@ -368,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";

@@ -401,7 +449,8 @@ Enter terms or phrases separated by sear such as AND, OR, or NOT.
$advanced_buttons -$hidden +$hidden_fields +
VIEW: @@ -453,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', @@ -548,7 +592,8 @@ $advanced_buttons ENDDOCUMENT - return $scrout; + $r->print($scrout); + return; } ###################################################################### @@ -556,34 +601,164 @@ ENDDOCUMENT =pod -=item &make_persistent() +=item &get_persistent_form_data -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. +Inputs: filename of database + +Outputs: returns undef on database errors. + +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 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]); + } + } + } + untie (%persistent_db); + return 1; +} +###################################################################### +###################################################################### + +=pod + +=item &get_persistent_data + +Inputs: filename of database, ref to array of values to recover. + +Outputs: array of values. Returns undef on error. + +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. =cut ###################################################################### ###################################################################### +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; +} + +###################################################################### +###################################################################### + +=pod + +=item &make_persistent() + +Inputs: Hash of values to save, filename of persistent database. + +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. + +=cut +###################################################################### +###################################################################### sub make_persistent { + my $r = shift; my %save = %{shift()}; - my $persistent=''; - foreach my $name (keys %save) { - if ($name =~ /^form\./ && $name !~ /submit/) { - my @values = (ref($save{$name}) ? @{$save{$name}} : ($save{$name})); - $name=~s/^form\.//; - foreach (@values) { - s/\"/\'/g; # do not mess with html field syntax - next if (! $_ ); - $persistent.=< -END - } - } + my $filename = shift; + return undef if (! tie(%persistent_db,'GDBM_File', + $filename,&GDBM_WRCREAT(),0640)); + foreach my $name (keys(%save)) { + next if (! exists($save{$name})); + next if (! defined($save{$name}) || $save{$name} eq ''); + my @values = (ref($save{$name}) ? @{$save{$name}} : ($save{$name})); + # We handle array references, but not recursively. + my $store = join(',', map { &Apache::lonnet::escape($_); } @values ); + $persistent_db{$name} = $store; + } + untie(%persistent_db); + return 1; +} + +###################################################################### +###################################################################### + +=pod + +=item &make_form_data_persistent() + +Inputs: filename of persistent database. + +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. + +=cut + +###################################################################### +###################################################################### +sub make_form_data_persistent { + my $r = shift; + my $filename = shift; + my %save; + foreach (keys(%ENV)) { + next if (! /^form/ || /submit/); + $save{$_} = $ENV{$_}; } - return $persistent; + return &make_persistent($r,\%save,$filename); } ###################################################################### @@ -893,7 +1068,7 @@ Parse advanced search form and return th ###################################################################### ###################################################################### sub parse_advanced_search { - my ($r)=@_; + my ($r,$closebutton)=@_; my $fillflag=0; my $pretty_search_string = "
\n"; # Clean up fields for safety @@ -929,7 +1104,7 @@ sub parse_advanced_search { } } unless ($fillflag) { - &output_blank_field_error($r); + &output_blank_field_error($r,$closebutton); return ; } # Turn the form input into a SQL-based query @@ -992,7 +1167,7 @@ sub parse_advanced_search { ); # Test to see if date windows are legitimate if ($datequery=~/^Incorrect/) { - &output_date_error($r,$datequery); + &output_date_error($r,$datequery,$closebutton); return ; } elsif ($datequery) { # Here is where you would set up pretty_search_string to output @@ -1072,7 +1247,7 @@ Parse the basic search form and return a ###################################################################### ###################################################################### sub parse_basic_search { - my ($r)=@_; + my ($r,$closebutton)=@_; # Clean up fields for safety for my $field ('basicexp') { $ENV{"form.$field"}=~s/[^\w\s\(\)\-]//g; @@ -1086,7 +1261,7 @@ sub parse_basic_search { # Check to see if enough is filled in unless (&filled($ENV{'form.basicexp'})) { - &output_blank_field_error($r); + &output_blank_field_error($r,$closebutton); return OK; } my $pretty_search_string = ''.$ENV{'form.basicexp'}.''; @@ -1362,9 +1537,7 @@ the following format: ## columns of type 'enum' cannot be used for FULLTEXT. ## my @DataOrder = qw/id title author subject url keywords version notes - abstract mime lang owner copyright creationdate lastrevisiondate hostname - idx_title idx_author idx_subject idx_abstract idx_mime idx_language - idx_owner idx_copyright/; + abstract mime lang owner copyright creationdate lastrevisiondate hostname/; my %Datatypes = ( id =>{ type => 'INT', @@ -1390,16 +1563,42 @@ my %Datatypes = creationdate =>{ type=>'DATETIME'}, lastrevisiondate =>{ type=>'DATETIME'}, #-------------------------------------------------- - idx_title =>{ type=>'FULLTEXT', target=>'title'}, - idx_author =>{ type=>'FULLTEXT', target=>'author'}, - idx_subject =>{ type=>'FULLTEXT', target=>'subject'}, - idx_abstract =>{ type=>'FULLTEXT', target=>'abstract'}, - idx_mime =>{ type=>'FULLTEXT', target=>'mime'}, - idx_language =>{ type=>'FULLTEXT', target=>'lang'}, - idx_owner =>{ type=>'FULLTEXT', target=>'owner'}, - idx_copyright =>{ type=>'FULLTEXT', target=>'copyright'}, ); +my @Fullindicies = + qw/title author subject abstract mime language owner copyright/; + +###################################################################### +###################################################################### + +=pod + +=item &create_results_table() + +Creates the table of search results by calling lonmysql. Stores the +table id in $ENV{'form.table'} + +Inputs: none. + +Returns: the identifier of the table on success, undef on error. + +=cut + +###################################################################### +###################################################################### +sub create_results_table { + my $table = &Apache::lonmysql::create_table + ( { columns => \%Datatypes, + column_order => \@DataOrder, + fullindex => \@Fullindicies, + } ); + if (defined($table)) { + $ENV{'form.table'} = $table; + return $table; + } + return undef; # Error... +} + ###################################################################### ###################################################################### @@ -1413,8 +1612,10 @@ my %Datatypes = ###################################################################### sub write_status { my ($r,$string) = @_; - $r->print("
".$string."
\n"); - $r->rflush(); + $string =~ s/(\')/\$1/g; + $string =~ s/\n//sg; +# $r->print("\n"); +# $r->rflush(); return; } @@ -1423,6 +1624,54 @@ sub write_status { =pod +=item Search Status update functions + +Each of the following functions changes the values of one of the +input fields used to display the search status to the user. + +=over 4 + +=item &update_count_status() + +=item &update_contact_status() + +=item &update_read_status() + +=back + +=cut + +###################################################################### +###################################################################### +sub update_count_status { + my ($r,$text) = @_; + $text =~ s/\'/\\\'/g; + $r->print + ("\n"); + $r->rflush(); +} + +sub update_contact_status { + my ($r,$text) = @_; + $text =~ s/\'/\\\'/g; + $r->print + ("\n"); + $r->rflush(); +} + +sub update_read_status { + my ($r,$text) = @_; + $text =~ s/\'/\\\'/g; + $r->print + ("\n"); + $r->rflush(); +} + +###################################################################### +###################################################################### + +=pod + =item &run_search =cut @@ -1430,7 +1679,21 @@ sub write_status { ###################################################################### ###################################################################### sub run_search { - my ($r,$query,$customquery,$customshow,$serverlist) = @_; + my ($r,$query,$customquery,$customshow,$serverlist,$pretty_string) = @_; + my $c = $r->connection; + # + # Timing variables + # + my $starttime = time; + my $max_time = 120; # seconds for the search to complete + # + # Print run_search header + # + $r->print("Search Status"); + $r->print("Search: ".$pretty_string."\n"); + $r->rflush(); + # + # Determine the servers we need to contact. # my @Servers_to_contact; if (defined($serverlist)) { @@ -1439,33 +1702,45 @@ sub run_search { @Servers_to_contact = sort(keys(%Apache::lonnet::libserv)); } my %Server_status; - # - # Timing variables - my $starttime = time; - my $max_time = 120; # seconds for the search to complete - # - # Create Table - ##################################### - my $table = &Apache::lonmysql::create_table - ( { columns => \%Datatypes, - column_order => \@DataOrder, - } ); - if (! defined($table)) { - # What do I do now? Print out an error page. - &Apache::lonnet::logthis("lonmysql attempted to create a table ". - "and this was the result:". + my $table =$ENV{'form.table'}; + if (! defined($table) || $table eq '') { + $r->print("Unable to determine table id to store search results in.". + "The search has been aborted."); + return; + } + my $table_status = &Apache::lonmysql::check_table($table); + if (! defined($table_status)) { + $r->print("Unable to determine status of table."); + &Apache::lonnet::logthis("Bogus table id of $table for ". + "$ENV{'user.name'} @ $ENV{'user.domain'}"); + &Apache::lonnet::logthis("lonmysql error = ". &Apache::lonmysql::get_error()); - $r->print("An internal error occured with the database.
". - "The error has been logged, but you should probably alert". - " your system administrator."); return; } - $ENV{'form.table'}=$table; - # - ##################################### + if (! $table_status) { + $r->print("The table id,$table, we tried to use is invalid.". + "The search has been aborted."); + return; + } + ## + ## Prepare for the big loop. + ## my $hitcountsum; my $server; my $status; + $r->print(< + + + + + + + +
ContactingReceivingTotal Matches
+ +END + $r->rflush(); while ((time - $starttime < $max_time) && ((@Servers_to_contact) || keys(%Server_status))) { # Send out a search request if it needs to be done. @@ -1474,29 +1749,27 @@ sub run_search { my $server = shift(@Servers_to_contact); my $reply=&Apache::lonnet::metadata_query($query,$customquery, $customshow,[$server]); - # We should let the user know we have contacted another server ($server) = keys(%$reply); $Server_status{$server} = $reply->{$server}; - # &write_status($r,"Contacted:$server:reply:". - # $Server_status{$server}); - # Hmmm, should we add to $max_time if we contact a server? + &update_contact_status($r,$server); } else { + &update_contact_status($r,'none'); sleep(1); # wait a sec. to give time for files to be written } while (my ($server,$status) = each(%Server_status)) { + last if ($c->aborted()); if ($status eq 'con_lost') { delete ($Server_status{$server}); - # &write_status($r,"Removing $server"); next; } $status=~/^([\.\w]+)$/; my $datafile=$r->dir_config('lonDaemons').'/tmp/'.$1; if (-e $datafile && ! -e "$datafile.end") { - # Let the user know we are receiving data from the server - &write_status($r,"$server:Receiving file"); next; } + last if ($c->aborted()); if (-e "$datafile.end") { + &update_read_status($r,$server); if (-z "$datafile") { delete($Server_status{$server}); next; @@ -1506,11 +1779,13 @@ sub run_search { # Error opening file... # Tell the user and exit...? # Should I give up on opening it? - &write_status("Unable to open $datafile"); + $r->print("Unable to open search results file for ". + "server $server. Omitting from search"); next; } # Read in the whole file. while (my $result = <$fh>) { + last if ($c->aborted()); # handle custom fields? Someday we will! chomp($result); next unless $result; @@ -1521,25 +1796,38 @@ sub run_search { # Store the result in the mysql database my $result = &Apache::lonmysql::store_row($table,\%Fields); if (! defined($result)) { - &write_status($r,&Apache::lonmysql::get_error()); + $r->print(&Apache::lonmysql::get_error()); } - # &write_status($r,&Apache::lonmysql::get_debug()); + # $r->print(&Apache::lonmysql::get_debug()); $hitcountsum ++; + &update_count_status($r,$hitcountsum) if ($hitcountsum % 50 == 0); } # End of foreach (@results) $fh->close(); # $server is only deleted if the results file has been # found and (successfully) opened. This may be a bad idea. delete($Server_status{$server}); + # $r->print("Received $new_count more results from ". + # $server."."); } + last if ($c->aborted()); + &update_count_status($r,$hitcountsum); } + last if ($c->aborted()); # Finished looping through the servers } + &update_read_status($r,'none'); &Apache::lonmysql::disconnect_from_db(); + # Let the user know + # # We have run out of time or run out of servers to talk to and # results to get. - if ($hitcountsum > 0) { - $r->print("

Total results = $hitcountsum

"); + $r->print("Search Completed.  "); + if ($hitcountsum) { + $r->print($hitcountsum." matches were found."); + } else { + $r->print("There were no successful matches to your query."); } + $r->print(""); return; } @@ -1547,27 +1835,45 @@ sub run_search { ###################################################################### =pod -=item &display_buttons +=item &prev_next_buttons =cut ###################################################################### ###################################################################### -sub display_buttons { - my ($low,$high,$otherparms) = @_; - my $maxshow = 20; - my $lowest = ($low - $maxshow < 0 ? 0 : $low-$maxshow); - my $highest = $high + $maxshow; - my ($previous,$current,$next); - if ($lowest < $low) { - $previous = qq{prev}; +sub prev_next_buttons { + my ($current_min,$show,$total,$parms) = @_; + return '' if ($show eq 'all'); # No links if you get them all at once. + my $links; + ## + ## Prev + my $prev_min = $current_min - $show; + $prev_min = 0 if $prev_min < 0; + if ($prev_min < $current_min) { + $links .= qq{ +prev +}; } else { - $previous = "prev"; + $links .= 'prev'; } - $current = qq{reload}; - $next = qq{next}; - my $result = $previous." ".$current." ".$next; - return $result; + ## + ## Pages.... Someday. + ## + $links .= qq{   +reload +}; + ## + ## Next + my $next_min = $current_min + $show; + $next_min = $current_min if ($next_min > $total); + if ($next_min != $current_min) { + $links .= qq{   +next +}; + } else { + $links .= ' next'; + } + return $links; } ###################################################################### ###################################################################### @@ -1581,7 +1887,8 @@ sub display_buttons { ###################################################################### ###################################################################### sub display_results { - my ($r,$mode,$hidden,$importbutton,$closebutton) = @_; + my ($r,$mode,$importbutton,$closebutton) = @_; + $r->print(&search_results_header()); ## ## Set viewing function ## @@ -1592,85 +1899,114 @@ sub display_results { return; } ## - ## make query information persistent to allow for subsequent revision + ## Get the catalog controls setup ## - my $persistent=&make_persistent(\%ENV); + my $action = "/adm/searchcat?phase=results"; ## - ## Get the catalog controls setup + ## Deal with groupsearch + ## + if ($ENV{'form.catalogmode'} eq 'groupsearch') { + if (! tie(%groupsearch_db,'GDBM_File',$diropendb, + &GDBM_WRCREAT(),0640)) { + $r->print('Unable to tie hash to db file'); + $r->rflush(); + return; + } + } + ## + ## Prepare the table for querying ## - my $action = "/adm/searchcat"; - if ($mode eq 'Basic') { - $action .= "?reqinterface=basic"; - } elsif ($mode eq 'Advanced') { - $action .= "?reqinterface=advanced"; - } - $r->print(< -$hidden - - -$importbutton -$closebutton -$persistent -
-CATALOGCONTROLS - if (! tie(%groupsearch_db,'GDBM_File',$diropendb,&GDBM_WRCREAT,0640)) { - $r->print('Unable to tie hash to db file'); - $r->rflush(); - return; - } - # - my $fnum; - # For now, just query the whole lot and spit them out. my $table = $ENV{'form.table'}; my $connection_result = &Apache::lonmysql::connect_to_db(); if (!defined($connection_result)) { - &write_status($r,&Apache::lonmysql::get_error()); + $r->print(&Apache::lonmysql::get_error()); } - my @Results = &Apache::lonmysql::get_rows($table,'id>=0'); - #&write_status($r,&Apache::lonmysql::get_debug()); - #&write_status($r,&Apache::lonmysql::get_error()); + my $table_check = &Apache::lonmysql::check_table($table); + if (! defined($table_check)) { + $r->print("A MySQL error has occurred."); + &Apache::lonnet::logthis("lonmysql was unable to determine the status". + " of table ".$table); + return; + } elsif (! $table_check) { + $r->print("The table of results could not be found."); + &Apache::lonnet::logthis("The user requested a table, ".$table. + ", that could not be found."); + return; + } + ## + ## Get the number of results + ## + my $total_results = &Apache::lonmysql::number_of_rows($table); + if (! defined($total_results)) { + $r->print("A MySQL error has occurred."); + &Apache::lonnet::logthis("lonmysql was unable to determine the number". + " of rows in table ".$table); + &Apache::lonnet::logthis(&Apache::lonmysql::get_error()); + &Apache::lonnet::logthis(&Apache::lonmysql::get_debug()); + return; + } + if ($total_results == 0) { + $r->print("There were no results matching your query.\n". + ""); + return; + } + ## + ## Determine how many results we need to get + ## + $ENV{'form.show'} = 20; + $ENV{'form.start'} = 0 if (! exists($ENV{'form.start'})); + $ENV{'form.show'} = 'all' if (! exists($ENV{'form.show'})); + my $min = $ENV{'form.start'}; + my $max; + if ($ENV{'form.show'} eq 'all') { + $max = $total_results ; + } else { + $max = $min + $ENV{'form.show'}; + $max = $total_results if ($max > $total_results); + } + ## + ## Output links (if necessary) for 'prev' and 'next' pages. + ## + $r->print("
Results $min to $max out of $total_results
\n"); + $r->print + ('
'. + &prev_next_buttons($min,$ENV{'form.show'},$total_results, + "table=".$ENV{'form.table'}. + "&phase=results". + "&persistent_db_id=".$ENV{'form.persistent_db_id'}) + ."
\n" + ); + ## + ## Get results from MySQL table + ## + my @Results = &Apache::lonmysql::get_rows($table, + 'id>'.$min.' AND id<='.$max); + ## + ## Loop through the results and output them. + ## foreach my $row (@Results) { my %Fields = %{&parse_row(@$row)}; - my $output="\n

\n"; - if ($ENV{'form.catalogmode'} eq 'interactive') { - my $titleesc=$Fields{'title'}; - $titleesc=~s/\'/\\'/; # ' - if ($ENV{'form.catalogmode'} eq 'interactive') { - $output.=< - -
-END - } - } - if ($ENV{'form.catalogmode'} eq 'groupsearch') { - $fnum+=0; - $groupsearch_db{"pre_${fnum}_link"}=$Fields{'url'}; - $groupsearch_db{"pre_${fnum}_title"}=$Fields{'title'}; - $output.=< - - -
-END -# -# - $fnum++; - } + my $output="

\n"; + $output.=&catalogmode_output($Fields{'title'},$Fields{'url'}); # Render the result into html $output.= &$viewfunction(%Fields); - if ($output) { - $output.="


"; - } + $output.="

\n
"; + # Print them out as they come in. $r->print($output); $r->rflush(); } if (@Results < 1) { $r->print("There were no results matching your query"); + } else { + $r->print + ('
'. + &prev_next_buttons($min,$ENV{'form.show'},$total_results, + "table=".$ENV{'form.table'}. + "&phase=results". + "&persistent_db_id=". + $ENV{'form.persistent_db_id'}) + ."
\n" + ); } $r->print(""); $r->rflush(); @@ -1683,6 +2019,53 @@ END =pod +=item &catalogmode_output($title,$url) + +Returns html needed for the various catalog modes. Gets inputs from +$ENV{'form.catalogmode'}. Stores data in %groupsearch_db and $fnum +(local variable). + +=cut + +###################################################################### +###################################################################### +{ +my $fnum; + +sub catalogmode_output { + my $output = ''; + my ($title,$url) = @_; + if ($ENV{'form.catalogmode'} eq 'interactive') { + $title=~ s/\'/\\'/g; # ' Escape single quotes. + if ($ENV{'form.catalogmode'} eq 'interactive') { + $output.=< + +END + } + } + if ($ENV{'form.catalogmode'} eq 'groupsearch') { + $fnum+=0; + $groupsearch_db{"pre_${fnum}_link"}=$url; + $groupsearch_db{"pre_${fnum}_title"}=$title; + $output.=< + + +END + $fnum++; + } + return $output; +} + +} +###################################################################### +###################################################################### + +=pod + =item &parse_row Parse a row returned from the database. @@ -1705,235 +2088,6 @@ sub parse_row { &Apache::loncommon::filedescription($Fields{'mime'}); return \%Fields; } -###################################################################### -###################################################################### - -=pod - -=item &output_results() - -Format and output results based on a reply list. -There are two windows that this function writes to. The main search -window ("srch") has a listing of the results. A secondary window ("popwin") -gives the status of the network search (time elapsed, number of machines -contacted, etc.) - -=cut - -###################################################################### -###################################################################### -sub output_results { -# &Apache::lonnet::logthis("output_results:".time); - my $fnum; # search result counter - 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(\%ENV); - # - # Begin producing output - $r->rflush(); - # - # begin showing the cataloged results - my $action = "/adm/searchcat"; - if ($mode eq 'Basic') { - $action .= "?reqinterface=basic"; - } elsif ($mode eq 'Advanced') { - $action .= "?reqinterface=advanced"; - } - $r->print(< -$hidden - - -$importbutton -$closebutton -$persistent -
-CATALOGCONTROLS - # - # make the pop-up window for status - $r->print(&make_popwin(%rhash)); - $r->rflush(); - ## - ## Prepare for the main loop below - ## - my $servercount=0; - my $hitcountsum=0; - my $servernum=(keys %rhash); - my $serversleft=$servernum; - ## - ## Run until we run out of time or we run out of servers - ## - while($serversleft && $timeremain) { - ## - ## %rhash has servers deleted from it as results come in - ## (within the foreach loop below). - ## - foreach my $rkey (sort keys %rhash) { -# &Apache::lonnet::logthis("Server $rkey:".time); - $servercount++; - $compiledresult=''; - my $reply=$rhash{$rkey}; - my @results; - if ($reply eq 'con_lost') { - &popwin_imgupdate($r,$rkey,"srvbad.gif"); - $serversleft--; - delete $rhash{$rkey}; - } else { - # must do since 'use strict' checks for tainting - $reply=~/^([\.\w]+)$/; - my $replyfile=$r->dir_config('lonDaemons').'/tmp/'.$1; - $reply=~/(.*?)\_/; - for (my $counter=0;$counter<2;$counter++) { - if (-e $replyfile && ! -e "$replyfile.end") { - &popwin_imgupdate($r,$rkey,"srvhalf.gif"); - &popwin_js($r,'popwin.hc["'.$rkey.'"]='. - '"still transferring..."'.';'); - } - # Are we finished transferring data? - if (-e "$replyfile.end") { - $serversleft--; - delete $rhash{$rkey}; - if (-s $replyfile) { - &popwin_imgupdate($r,$rkey,"srvgood.gif"); - my $fh; - unless ($fh=Apache::File->new($replyfile)){ - # Is it really appropriate to die on this error? - $r->print('ERROR: file '. - $replyfile.' cannot be opened'); - return OK; - } - @results=<$fh> if $fh; - my $hits =@results; - &popwin_js($r,'popwin.hc["'.$rkey.'"]='. - $hits.';'); - $hitcountsum+=$hits; - &popwin_js($r,'popwin.document.forms.popremain.'. - 'numhits.value='.$hitcountsum.';'); - } else { - &popwin_imgupdate($r,$rkey,"srvempty.gif"); - &popwin_js($r,'popwin.hc["'.$rkey.'"]=0;'); - } - last; - } # end of if ( -e "$replyfile.end") - last unless $timeremain; - sleep 1; # wait for daemons to write files? - $timeremain--; - $elapsetime++; - &popwin_js($r,"popwin.document.popremain.". - "elapsetime.value=$elapsetime;"); - } - &popwin_js($r,'popwin.document.whirly.'. - 'src="/adm/lonIcons/lonanimend.gif";'); - } # end of if ($reply eq 'con_lost') else statement - my %Fields = undef; # Holds the data to be sent to the various - # *_view routines. - my ($extrashow,$customfields,$customhash) = - &handle_custom_fields(\@results); - my @customfields = @$customfields; - my %customhash = %$customhash; - untie %groupsearch_db if (tied %groupsearch_db); - # - if (! tie(%groupsearch_db,'GDBM_File',$diropendb,&GDBM_WRCREAT,0640)) { - $r->print('Unable to tie hash to db '. - 'file'); - } else { - if ($ENV{'form.launch'} eq '1') { - &start_fresh_session(); - } - foreach my $result (@results) { - next if $result=~/^custom\=/; - chomp $result; - next unless $result; - %Fields = &parse_raw_result($result,$rkey); - # - # Check copyright tags and skip results the user cannot use - my (undef,undef,$resdom,$resname) = split('/',$Fields{'url'}); - # Check for priv - if (($Fields{'copyright'} eq 'priv') && - (($ENV{'user.name'} ne $resname) && - ($ENV{'user.domain'} ne $resdom))) { - next; - } - # Check for domain - if (($Fields{'copyright'} eq 'domain') && - ($ENV{'user.domain'} ne $resdom)) { - next; - } - # - $Fields{'extrashow'}=$extrashow; - if ($extrashow) { - foreach my $field (@customfields) { - my $value=''; - $value = $1 if ($customhash{$Fields{'url'}}=~/\<{$field}[^\>]*\>(.*?)\<\/{$field}[^\>]*\>/s); - $Fields{'extrashow'}=~s/\<\!\-\- $field \-\-\>/ $value/g; - } - } - $compiledresult.="\n

\n"; - if ($ENV{'form.catalogmode'} eq 'interactive') { - my $titleesc=$Fields{'title'}; - $titleesc=~s/\'/\\'/; # ' - $compiledresult.=< - -
-END - } - if ($ENV{'form.catalogmode'} eq 'groupsearch') { - $fnum+=0; - $groupsearch_db{"pre_${fnum}_link"}=$Fields{'url'}; - $groupsearch_db{"pre_${fnum}_title"}=$Fields{'title'}; - $compiledresult.=< - - -
-END -# -# - $fnum++; - } - # Render the result into html - $compiledresult.= &$viewfunction(%Fields, hostname => $rkey ); - if ($compiledresult or $servercount!=$servernum) { - $compiledresult.="


"; - } - } - untie %groupsearch_db; - } - if ($compiledresult) { - $resultflag=1; - $r->print($compiledresult); - } - } # End of foreach loop over servers remaining - } # End of big loop - while($serversleft && $timeremain) - unless ($resultflag) { - $r->print("\nThere were no results that matched your query\n"); - } - $r->print(''. - "\n"); - $r->print("\n\n"); - $r->rflush(); - return; -} ########################################################### ########################################################### @@ -2103,21 +2257,8 @@ Checked for existance & 'edit' mode. ###################################################################### ###################################################################### sub search_results_header { - my ($mode,$pretty_query) = @_; - $mode = lc($mode); - my $title; - if ($mode eq 'advanced') { - $title = "Advanced Search Results"; - } elsif ($mode eq 'basic') { - $title = "Basic Search Results"; - } my $result = ''; # output beginning of search page - $result.=< - -$title -BEGINNING # conditional output of script functions dependent on the mode in # which the search was invoked if ($ENV{'form.catalogmode'} eq 'interactive'){ @@ -2184,124 +2325,46 @@ SCRIPT } SCRIPT - $result.=< - function displayinfo(val) { - popwin.document.forms.popremain.sdetails.value=val; - } - function openhelp(val) { - openhelpwin=open('/adm/help/searchcat.html','helpscreen', - 'scrollbars=1,width=400,height=300'); - openhelpwin.focus(); - } - function abortsearch(val) { - popwin.close(); - } - -SCRIPT $result.=< - - -

$title

END - if ($pretty_query) { - $result .= "

Search query: $pretty_query

"; - } return $result; } ###################################################################### ###################################################################### - -=pod - -=item &make_popwin() - -Returns html with javascript in it to open up the status window. - -=cut +sub search_status_header { + return <Search Status + +

Search Status

+Sending search request to LON-CAPA servers.
+ENDSTATUS +} ###################################################################### ###################################################################### -sub make_popwin { - my %rhash = @_; - my $servernum=(keys %rhash); - my $hcinit; - my $grid="'
'+\n"; - # $sn is the server number, used ONLY to make sure we have - # rows of 10 each. No longer used to index images. - my $sn=1; - foreach my $sk (sort keys %rhash) { - $grid.="''+ - $grid.="'+\"'\"+')\">'+"; - $grid.="\n"; - $grid.="'\"".$sk.'+\n"; - $grid.="'
'+\n" unless $sn%10; - $sn++; - } - my $result.=< - popwin=open('','popwin','scrollbars=1,width=400,height=220'); - popwin.focus(); - popwin.document.writeln('<'+'html>'); - popwin.document.writeln('<'+'head>'); - popwin.document.writeln('<'+'script>'); - popwin.document.writeln('hc=new Array();$hcinit'); - popwin.document.writeln('<'+'/script>'); - popwin.document.writeln('<'+'/head>'+ - '<'+'body bgcolor="#FFFFFF">'+ - '<'+'image name="whirly" align="right" src="/adm/lonIcons/'+ - 'lonanim.gif" '+ - 'alt="animated logo" />'+ - '<'+'h3>Search Results Progress<'+'/h3>'+ - '<'+'form name="popremain">'+ - '<'+'tt>'+ - '<'+'br clear="all"/>PLEASE BE PATIENT'+ - '<'+'br />SCANNING $servernum SERVERS'+ - '<'+'br clear="all" />Number of record hits found '+ - '<'+'input type="text" size="10" name="numhits"'+ - ' value="0" />'+ - '<'+'br clear="all" />Time elapsed '+ - '<'+'input type="text" size="10" name="elapsetime"'+ - ' value="0" />'+ - '<'+'br />'+ - 'SERVER GRID (click on any cell for details)'+ - $grid - '<'+'br />'+ - 'Server details '+ - '<'+'input type="text" size="35" name="sdetails"'+ - ' value="" />'+ - '<'+'br />'+ - ' <'+'input type="button" name="button"'+ - ' value="close this window" '+ - ' onClick="javascript:opener.abortsearch()" />'+ - ' <'+'input type="button" name="button"'+ - ' value="help" onClick="javascript:opener.openhelp()" />'+ - '<'+'/tt>'+ - '<'+'/form>'+ - '<'+'/body><'+'/html>'); - popwin.document.close(); - -ENDPOP - return $result; +sub print_frames_interface { + my $r = shift; + my $basic_link = "/adm/searchcat?"."&table=".$ENV{'form.table'}. + "&persistent_db_id=".$ENV{'form.persistent_db_id'}; + my $run_search_link = $basic_link."&phase=run_search"; + my $results_link = $basic_link."&phase=results". + "&pause=10"."&start=0"."&show=20"; + my $result = <<"ENDFRAMES"; + + +LON-CAPA Digital Library Search Results + + + + + + +ENDFRAMES + + $r->print($result); + return; } ###################################################################### @@ -2356,7 +2419,6 @@ END =item &summary_view() =cut - ###################################################################### ###################################################################### sub summary_view { @@ -2490,10 +2552,8 @@ sub filled { ###################################################################### ###################################################################### sub output_blank_field_error { - my ($r)=@_; + my ($r,$closebutton)=@_; # make query information persistent to allow for subsequent revision - my $persistent=&make_persistent(\%ENV); - $r->print(< @@ -2505,9 +2565,9 @@ BEGINNING

Search Catalog

-$persistent - +$hidden_fields +Revise search request  $closebutton

Helpful Message

@@ -2531,15 +2591,19 @@ RESULTS Output a full html page with an error message. +Inputs: + + $r, the request pointer. + $message, the error message for the user. + $closebutton, the specialized close button needed for groupsearch. + =cut ###################################################################### ###################################################################### sub output_date_error { - my ($r,$message)=@_; + my ($r,$message,$closebutton)=@_; # make query information persistent to allow for subsequent revision - my $persistent=&make_persistent(\%ENV); - $r->print(< @@ -2549,7 +2613,7 @@ sub output_date_error {

Search Catalog

-$persistent +$hidden_fields $closebutton @@ -2589,51 +2653,6 @@ sub start_fresh_session { } } -###################################################################### -###################################################################### - -=pod - -=item &popwin_js() send javascript to popwin - -=cut - -###################################################################### -###################################################################### -sub popwin_js { - # Print javascript out to popwin, but make sure we dont generate - # any javascript errors in doing so. - my ($r,$text) = @_; - $r->print(<<"END"); - -END - $r->rflush(); -} - -###################################################################### -###################################################################### - -=pod - -=item &popwin_imgupdate() - -Send a given image (and its location) out to the browser. Takes as -input $r, loncapa server id, and an icon URL. - -=cut - -###################################################################### -###################################################################### -sub popwin_imgupdate { - my ($r,$server,$icon) = @_; - &popwin_js($r,'popwin.document.img_'.$Apache::lonnet::hostdom{$server}. - '_'.$server.'.'.'src="/adm/lonIcons/'.$icon.'";'); -} - 1; __END__