--- loncom/interface/lonsearchcat.pm 2013/08/13 13:27:38 1.331.4.6 +++ loncom/interface/lonsearchcat.pm 2015/06/16 20:24:39 1.346 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Search Catalog # -# $Id: lonsearchcat.pm,v 1.331.4.6 2013/08/13 13:27:38 raeburn Exp $ +# $Id: lonsearchcat.pm,v 1.346 2015/06/16 20:24:39 damieng Exp $ # # Copyright Michigan State University Board of Trustees # @@ -52,8 +52,6 @@ search (on a server basis) is displayed =head1 Internals -=over 4 - =cut ############################################################################### @@ -80,6 +78,7 @@ use Apache::lonnavmaps; use Apache::lonindexer(); use Apache::lonwishlist(); use LONCAPA; +use Time::HiRes qw(sleep); ###################################################################### ###################################################################### @@ -145,7 +144,7 @@ sub handler { ## this once, so the pause indicator is deleted ## if (exists($env{'form.pause'})) { - sleep(1); + sleep(0.1); delete($env{'form.pause'}); } ## @@ -172,7 +171,7 @@ sub handler { &Apache::lonhtmlcommon::clear_breadcrumbs(); my @allowed_searches = ('portfolio'); - if (&Apache::lonnet::allowed('bre',$env{'request.role.domain'})) { + if (&Apache::lonnet::allowed('bre',$env{'request.role.domain'}) eq 'F') { push(@allowed_searches,'res'); } my $crumb_text = 'Portfolio Search'; @@ -195,12 +194,15 @@ sub handler { &Apache::lonnet::logthis('lonsearchcat:'. 'Unable to recover data from '. $persistent_db_file); - my $msg = - 'We were unable to retrieve data describing your search. '. - 'This is a serious error and has been logged. '. - 'Please alert your LON-CAPA administrator.'; - &Apache::loncommon::simple_error_page($r,'Search Error', - $msg); + my $msg = + &mt('We were unable to retrieve data describing your search.'). + ' '.&mt('This is a serious error and has been logged.'). + '
'. + &mt('Please alert your LON-CAPA administrator.'); + &Apache::loncommon::simple_error_page( + $r,'Search Error', + $msg, + {'no_auto_mt_msg' => 1}); return OK; } } @@ -321,31 +323,31 @@ END &display_results($r,$importbutton,$closebutton,$diropendb, $env{'form.area'}); } elsif ($env{'form.phase'} =~ /^(sort|run_search)$/) { - my ($query,$customquery,$customshow,$libraries,$pretty_string) = + my ($query,$customquery,$customshow,$libraries,$pretty_string,$domainsref) = &get_persistent_data($persistent_db_file, ['query','customquery','customshow', - 'libraries','pretty_string']); + 'libraries','pretty_string','domains']); if ($env{'form.phase'} eq 'sort') { &print_sort_form($r,$pretty_string); } elsif ($env{'form.phase'} eq 'run_search') { &run_search($r,$query,$customquery,$customshow, - $libraries,$pretty_string,$env{'form.area'}); + $libraries,$pretty_string,$env{'form.area'},$domainsref); } } elsif(($env{'form.phase'} eq 'basic_search') || ($env{'form.phase'} eq 'adv_search')) { # # We are running a search, try to parse it - my ($query,$customquery,$customshow,$libraries) = - (undef,undef,undef,undef); + my ($query,$customquery,$customshow,$libraries,$domains) = + (undef,undef,undef,undef,undef); my $pretty_string; if ($env{'form.phase'} eq 'basic_search') { - ($query,$pretty_string,$libraries) = + ($query,$pretty_string,$libraries,$domains) = &parse_basic_search($r,$closebutton,$hidden_fields); return OK if (! defined($query)); &make_persistent({ basicexp => $env{'form.basicexp'}}, $persistent_db_file); } else { # Advanced search - ($query,$customquery,$customshow,$libraries,$pretty_string) + ($query,$customquery,$customshow,$libraries,$pretty_string,$domains) = &parse_advanced_search($r,$closebutton,$hidden_fields); return OK if (! defined($query)); } @@ -353,7 +355,8 @@ END customquery => $customquery, customshow => $customshow, libraries => $libraries, - pretty_string => $pretty_string }, + pretty_string => $pretty_string, + domains => $domains }, $persistent_db_file); # # Set up table @@ -427,8 +430,10 @@ sub hidden_field { ###################################################################### -=pod +=pod +=over 4 + =item &print_basic_search_form() Prints the form for the basic search. Sorry the name is so cryptic. @@ -445,7 +450,7 @@ sub print_basic_search_form { $env{'form.catalogmode'} ne 'import'); my $scrout = &Apache::loncommon::start_page('Content Library').$bread_crumb; # Search form for resource space - if (&Apache::lonnet::allowed('bre',$env{'request.role.domain'})) { + if (&Apache::lonnet::allowed('bre',$env{'request.role.domain'}) eq 'F') { $scrout .= &setup_basic_search($r,'res',$hidden_fields,$closebutton); $scrout .= '

'; } @@ -1012,8 +1017,9 @@ Outputs: array of values. Returns undef 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. +values unescaped. If the item is 'domains; then the returned +value will be a hash pointer. Otherwise, if the item contains +commas (before unescaping), the returned value will be an array pointer. =cut @@ -1031,13 +1037,25 @@ sub get_persistent_data { push @Values, undef; next; } - my @values = map { - &unescape($_); - } split(',',$persistent_db{$name}); - if (@values <= 1) { - push @Values,$values[0]; + if ($name eq 'domains') { + my %valueshash; + my @items= map { &unescape($_); } split(',',$persistent_db{$name}); + foreach my $item (@items) { + if ($item =~ /=/) { + my ($key,$val) = map { &unescape($_); } split(/=/,$item); + $valueshash{$key} = $val; + } + } + push(@Values,\%valueshash); } else { - push @Values,\@values; + my @values = map { + &unescape($_); + } split(',',$persistent_db{$name}); + if (@values <= 1) { + push @Values,$values[0]; + } else { + push @Values,\@values; + } } } untie (%persistent_db); @@ -1055,7 +1073,9 @@ Inputs: Hash of values to save, filename 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 separated string. +elements escaped and concatenated in a comma separated string. Values +that are hash pointers will have their keys and values escaped and +concatenated in a comma separated string. =cut @@ -1067,8 +1087,17 @@ sub make_persistent { return undef if (! tie(%persistent_db,'GDBM_File', $filename,&GDBM_WRCREAT(),0640)); foreach my $name (keys(%save)) { - my @values = (ref($save{$name}) ? @{$save{$name}} : ($save{$name})); - # We handle array references, but not recursively. + my @values=(); + if (ref($save{$name}) eq 'ARRAY') { + @values = @{$save{$name}}; + } elsif (ref($save{$name}) eq 'HASH') { + foreach my $key (%{$save{$name}}) { + push(@values,&escape($key).'='.&escape($save{$name}{$key})); + } + } else { + @values = $save{$name}; + } + # We handle array and hash references, but not recursively. my $store = join(',', map { &escape($_); } @values ); $persistent_db{$name} = $store; } @@ -1385,7 +1414,8 @@ sub parse_advanced_search { ## ## Deal with restrictions to given domains ## - my ($libraries_to_query,$pretty_domains_string) = &parse_domain_restrictions(); + my ($libraries_to_query,$pretty_domains_string,$domains_to_query) = + &parse_domain_restrictions(); if ($pretty_domains_string) { $pretty_search_string .= $pretty_domains_string."
\n"; } @@ -1401,11 +1431,12 @@ sub parse_advanced_search { } #&Apache::lonnet::logthis('advanced query = '.$/.$query); return ($query,$customquery,$customshow,$libraries_to_query, - $pretty_search_string); + $pretty_search_string,$domains_to_query); } sub parse_domain_restrictions { my $libraries_to_query = undef; + my $domains_to_query = undef; # $env{'form.domains'} can be either a scalar or an array reference. # We need an array. if (! exists($env{'form.domains'}) || $env{'form.domains'} eq '') { @@ -1429,12 +1460,32 @@ sub parse_domain_restrictions { foreach (sort @allowed_domains) { $pretty_domains_string .= "".$_." "; } - my %servers = &Apache::lonnet::get_unique_servers(\@allowed_domains, - 'library'); + my %library_servers = &Apache::lonnet::get_unique_servers(\@allowed_domains, + 'library'); + my (%older_library_servers,%okdoms,%domains_for_id); + map { $okdoms{$_} = 1; } @allowed_domains; + foreach my $key (keys(%library_servers)) { + if (&Apache::lonnet::get_server_loncaparev('',$key) =~ /^\'?(\d+)\.(\d+)/) { + my $major = $1; + my $minor = $2; + if (($major < 2) || (($major == 2) && ($minor < 11))) { + map { $older_library_servers{$_} = 1; } + &Apache::lonnet::machine_ids($library_servers{$key}); + } else { + my %possdoms; + map { $possdoms{$_}=1 if ($okdoms{$_}); } + &Apache::lonnet::machine_domains($library_servers{$key}); + $domains_for_id{$key} = join(',',sort(keys(%possdoms))); + } + } + } + my %servers = (%library_servers,%older_library_servers); $libraries_to_query = [keys(%servers)]; + $domains_to_query = \%domains_for_id; } return ($libraries_to_query, - $pretty_domains_string); + $pretty_domains_string, + $domains_to_query); } ###################################################################### @@ -1463,7 +1514,8 @@ sub parse_basic_search { $env{"form.$_"}=&unescape($env{"form.$_"}); $env{"form.$_"}=~s/[^\w\/\s\(\)\=\-\"\']//g; } - my ($libraries_to_query,$pretty_domains_string) = &parse_domain_restrictions(); + my ($libraries_to_query,$pretty_domains_string,$domains_to_query) = + &parse_domain_restrictions(); # # Check to see if enough of a query is filled in my $search_string = $env{'form.basicexp'}; @@ -1509,7 +1561,7 @@ sub parse_basic_search { $pretty_search_string =~ s:^
and ::; &Apache::lonnet::logthis('simple search final query = '.$/.$final_query); return ($final_query,$pretty_search_string, - $libraries_to_query); + $libraries_to_query,$domains_to_query); } @@ -1999,21 +2051,7 @@ sub print_sort_form { &Apache::lonnet::logthis(&Apache::lonmysql::get_error()); return; } - my $js =< -// - -END - - my $start_page = &Apache::loncommon::start_page('Results',$js); + my $start_page = &Apache::loncommon::start_page('Results',undef); my $breadcrumbs= &Apache::lonhtmlcommon::breadcrumbs('Searching','Searching', $env{'form.catalogmode'} ne 'import'); @@ -2039,7 +2077,7 @@ END # $result.="\n"; my $revise = &revise_button(); $result.='

' - .&mt('There are [_1] matches to your query.',$total_results) + .&mt('Total of [quant,_1,match,matches] to your query.',$total_results) .' '.$revise.'

' .'

'.&mt('Search: ').$pretty_query_string .'

'; @@ -2177,8 +2215,21 @@ SCRIPT $r->rflush(); } +sub reload_result_frame { + my ($r) = @_; + my $newloc = '/adm/searchcat?phase=results&persistent_db_id='. + $env{'form.persistent_db_id'}; + $r->print(< + parent.update_results("$newloc"); + +SCRIPT + + $r->rflush(); +} + { - my $max_time = 300; # seconds for the search to complete + my $max_time = 60; # seconds for the search to complete my $start_time = 0; my $last_time = 0; @@ -2259,7 +2310,7 @@ results into MySQL. ###################################################################### sub run_search { my ($r,$query,$customquery,$customshow,$serverlist, - $pretty_string,$area) = @_; + $pretty_string,$area,$domainsref) = @_; my $tabletype = 'metadata'; if ($area eq 'portfolio') { $tabletype = 'portfolio_search'; @@ -2291,16 +2342,39 @@ END $r->rflush(); # # Determine the servers we need to contact. - my @Servers_to_contact; + my (@Servers_to_contact,%domains_by_server); if (defined($serverlist)) { if (ref($serverlist) eq 'ARRAY') { @Servers_to_contact = @$serverlist; } else { @Servers_to_contact = ($serverlist); } + if (ref($domainsref) eq 'HASH') { + foreach my $server (@Servers_to_contact) { + $domains_by_server{$server} = $domainsref->{$server}; + } + } } else { - my %all_library_servers = &Apache::lonnet::unique_library(); + my %library_servers = &Apache::lonnet::unique_library(); + my (%all_library_servers, %older_library_servers); + foreach my $key (keys(%library_servers)) { + if (&Apache::lonnet::get_server_loncaparev('',$key) =~ /^\'?(\d+)\.(\d+)/) { + my $major = $1; + my $minor = $2; + if (($major < 2) || (($major == 2) && ($minor < 11))) { + map { $older_library_servers{$_} = 1; } + &Apache::lonnet::machine_ids($library_servers{$key}); + } + } + } + %all_library_servers = (%library_servers,%older_library_servers); @Servers_to_contact = sort(keys(%all_library_servers)); + foreach my $server (@Servers_to_contact) { + my %possdoms; + map { $possdoms{$_}=1; } &Apache::lonnet::machine_domains($all_library_servers{$server}); + $domains_by_server{$server} = + join(',',sort(&Apache::lonnet::machine_domains($all_library_servers{$server}))); + } } my %Server_status; # @@ -2336,6 +2410,7 @@ END ## ## Prepare for the big loop. my $hitcountsum; + my $oldhitcountsum; my %matches; my $server; my $status; @@ -2370,7 +2445,7 @@ END my $server = shift(@Servers_to_contact); &update_status($r,&mt('contacting [_1]',$server)); my $reply=&Apache::lonnet::metadata_query($query,$customquery, - $customshow,[$server]); + $customshow,[$server],\%domains_by_server); ($server) = keys(%$reply); $Server_status{$server} = $reply->{$server}; } else { @@ -2382,7 +2457,7 @@ END &update_status($r, &mt('waiting on [_1]',join(' ',keys(%Server_status)))); } - sleep(1); + sleep(0.1); } # # Loop through the servers we have contacted but do not @@ -2458,7 +2533,13 @@ END delete($Server_status{$server}); } last if ($connection->aborted()); - &update_count_status($r,$hitcountsum); + if ($oldhitcountsum < $hitcountsum) { + &update_count_status($r,$hitcountsum); + if ($hitcountsum <= $env{'form.show'}) { + reload_result_frame($r); + } + $oldhitcountsum = $hitcountsum; + } } last if ($connection->aborted()); &update_seconds($r); @@ -2552,6 +2633,8 @@ sub display_results { if ($env{'form.catalogmode'} eq 'import') { if (! tie(%groupsearch_db,'GDBM_File',$diropendb, &GDBM_WRCREAT(),0640)) { + # NOTE: this can happen when a previous request to searchcat?phase=results gets interrupted + # (%groupsearch_db is not untied) $r->print('

'. &mt('Unable to save import results.'). '

'. @@ -2560,6 +2643,10 @@ sub display_results { $r->rflush(); return; } + # untie %groupsearch_db if the connection gets aborted before the end + $r->register_cleanup(sub { + untie %groupsearch_db if (tied(%groupsearch_db)); + }); } ## ## Prepare the table for querying @@ -2701,8 +2788,7 @@ sub display_results { ); if ($total_results == 0) { - $r->print(''. - '

'.&mt('There are currently no results.').'

'. + $r->print('

'.&mt('There are currently no results.').'

'. "". &Apache::loncommon::end_page()); return; @@ -3075,9 +3161,18 @@ SCRIPT SCRIPT + $js.=< + \$(document).ready(function() { + parent.done_loading_results(); + }); + +SCRIPT + my $start_page = &Apache::loncommon::start_page(undef,$js, {'only_body' =>1, - 'add_wishlist' =>1}); + 'add_wishlist' =>1, + 'add_modal' =>1}); my $result=< @@ -3104,10 +3199,28 @@ sub print_frames_interface { my $results_link = &results_link(); my $js = < -// + +var loading_results = true; +var need_reloading = false; +var new_location; +function update_results(newloc) { + if (loading_results) { + need_reloading = true; + new_location = newloc; + } else { + loading_results = true; + resultsframe.location = newloc; + } +} +function done_loading_results() { + loading_results = false; + if (need_reloading) { + need_reloading = false; + update_results(new_location); + } +} JS @@ -3725,7 +3838,7 @@ Cleans the global %groupsearch_db by rem ###################################################################### sub start_fresh_session { delete $groupsearch_db{'mode_catalog'}; - foreach (keys %groupsearch_db) { + foreach (keys(%groupsearch_db)) { if ($_ =~ /^pre_/) { delete $groupsearch_db{$_}; }