--- loncom/interface/lonsearchcat.pm 2011/12/09 01:04:27 1.331 +++ loncom/interface/lonsearchcat.pm 2020/08/26 01:29:48 1.331.4.16 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Search Catalog # -# $Id: lonsearchcat.pm,v 1.331 2011/12/09 01:04:27 www Exp $ +# $Id: lonsearchcat.pm,v 1.331.4.16 2020/08/26 01:29:48 raeburn 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,7 +430,9 @@ sub hidden_field { ###################################################################### -=pod +=pod + +=over 4 =item &print_basic_search_form() @@ -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 .= '

'; } @@ -474,14 +479,31 @@ sub setup_basic_search { .&mt('use related words') .''; + my $anydom = 1; + if ($area eq 'res') { + unless (&Apache::lonnet::allowed('bre','/res/') eq 'F') { + $anydom = 0; + } + } + my $singledom; + my ($disabled,$checked); + if ($anydom) { + $singledom = $r->dir_config('lonDefDomain'); + if ($env{'form.domains'} eq $singledom) { + $checked = 1; + } + } else { + $singledom = $env{'user.domain'}; + $disabled = ' disabled="disabled"'; + $checked = 1; + } $onlysearchdomain = ''; $adv_search_link = '\n"; } @@ -1401,11 +1460,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 +1489,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 +1543,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 +1590,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); } @@ -1781,14 +1862,14 @@ sub build_date_queries { if ((defined($cafter) && ! defined($cbefore)) || (defined($cbefore) && ! defined($cafter))) { # This is bad, so let them know - $error = &mt('Incorrect entry for the creation date. '. + $error = &mt('Incorrect entry for the creation date. '. 'You must specify both the beginning and ending dates.'); } if (! defined($error) && ((defined($mafter) && ! defined($mbefore)) || (defined($mbefore) && ! defined($mafter)))) { # This is also bad, so let them know - $error = &mt('Incorrect entry for the last revision date. '. + $error = &mt('Incorrect entry for the last revision date. '. 'You must specify both the beginning and ending dates.'); } if (! defined($error)) { @@ -1800,6 +1881,8 @@ sub build_date_queries { my (undef,undef,undef,$cbday,$cbmon,$cbyear) = localtime($cbefore); # Correct for year being relative to 1900 $cayear+=1900; $cbyear+=1900; + # Correct month; localtime gives month 0..11 but MySQL expects 1..12 + $camon++; $cbmon++; my $cquery= '(creationdate BETWEEN '. "'".$cayear.'-'.$camon.'-'.$caday."'". @@ -1818,6 +1901,8 @@ sub build_date_queries { my (undef,undef,undef,$mbday,$mbmon,$mbyear) = localtime($mbefore); # Correct for year being relative to 1900 $mayear+=1900; $mbyear+=1900; + # Correct month; localtime gives month 0..11 but MySQL expects 1..12 + $mamon++; $mbmon++; my $mquery= '(lastrevisiondate BETWEEN '. "'".$mayear.'-'.$mamon.'-'.$maday."'". @@ -1860,10 +1945,11 @@ sub copyright_check { my (undef,undef,$resdom,$resname) = split('/', $Metadata->{'url'}); # Check for priv - if (($Metadata->{'copyright'} eq 'priv') && - (($env{'user.name'} ne $resname) && - ($env{'user.domain'} ne $resdom))) { - return 0; + if ($Metadata->{'copyright'} eq 'priv') { + unless (($env{'user.name'} eq $resname) && + ($env{'user.domain'} eq $resdom)) { + return 0; + } } # Check for domain if (($Metadata->{'copyright'} eq 'domain') && @@ -1901,11 +1987,13 @@ sub ensure_db_and_table { ## Sanity check the table id. ## if (! defined($table) || $table eq '' || $table =~ /\D/ ) { - $r->print(&Apache::loncommon::start_page(&mt('Error')) -. '

table: |'.$table.'|

' # SB + $r->print(&Apache::loncommon::start_page('Error') .'

' .&mt('Unable to retrieve search results. ' .'Unable to determine the table results were saved in.') + .'

' + . '

'.&mt('Internal info:').'

' + .'
'.$table.'
' .&Apache::loncommon::end_page() ); return undef; @@ -1915,9 +2003,12 @@ sub ensure_db_and_table { ## my $connection_result = &Apache::lonmysql::connect_to_db(); if (!defined($connection_result)) { - $r->print("Unable to connect to the MySQL database where your results". - " are saved.". - &Apache::loncommon::end_page()); + $r->print( + '

' + .&mt('Unable to connect to the MySQL database where your results are saved.') + .'

' + .&Apache::loncommon::end_page() + ); &Apache::lonnet::logthis("lonsearchcat: unable to get lonmysql to". " connect to database."); &Apache::lonnet::logthis(&Apache::lonmysql::get_error()); @@ -1925,13 +2016,20 @@ sub ensure_db_and_table { } my $table_check = &Apache::lonmysql::check_table($table); if (! defined($table_check)) { - $r->print("A MySQL error has occurred.". - &Apache::loncommon::end_page()); + $r->print( + '

' + .&mt('A MySQL error has occurred.') + .'

' + .&Apache::loncommon::end_page()); &Apache::lonnet::logthis("lonmysql was unable to determine the status". " of table ".$table); return undef; } elsif (! $table_check) { - $r->print("The table of results could not be found."); + $r->print( + '

' + .&mt('The table of results could not be found.') + .'

' + ); &Apache::lonnet::logthis("The user requested a table, ".$table. ", that could not be found."); return undef; @@ -2001,7 +2099,7 @@ sub print_sort_form { END - my $start_page = &Apache::loncommon::start_page('Results',$js); + my $start_page = &Apache::loncommon::start_page('Results'); my $breadcrumbs= &Apache::lonhtmlcommon::breadcrumbs('Searching','Searching', $env{'form.catalogmode'} ne 'import'); @@ -2027,7 +2125,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 .'

'; @@ -2165,8 +2263,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; @@ -2247,7 +2358,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'; @@ -2279,16 +2390,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; # @@ -2324,6 +2458,8 @@ END ## ## Prepare for the big loop. my $hitcountsum; + my $oldhitcountsum; + my $displaycount; my %matches; my $server; my $status; @@ -2358,7 +2494,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 { @@ -2370,7 +2506,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 @@ -2446,7 +2582,15 @@ 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'}) || + (!$displaycount && $hitcountsum)) { + reload_result_frame($r); + $displaycount = $hitcountsum; + } + $oldhitcountsum = $hitcountsum; + } } last if ($connection->aborted()); &update_seconds($r); @@ -2540,6 +2684,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.'). '

'. @@ -2547,7 +2693,11 @@ sub display_results { &Apache::loncommon::end_page()); $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 @@ -2689,8 +2839,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; @@ -3063,9 +3212,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=< @@ -3095,6 +3253,26 @@ sub print_frames_interface { // JS @@ -3157,6 +3335,28 @@ sub evalfields { ###################################################################### ###################################################################### +sub display_tools { + my ($title, $jumpurl) = @_; + my $result; + # Metadata + $result.= + &Apache::loncommon::modal_link( + $jumpurl.'.meta?inhibitmenu=yes', + 'Info', + 500,500,'_blank',undef,&mt('Metadata')); + # Stored Links + $result.= + '
'. + ''; + return $result; +} + +###################################################################### +###################################################################### + =pod =item Metadata Viewing Functions @@ -3181,16 +3381,14 @@ sub detailed_citation_view { my $result; my $jumpurl=$values{'url'}; $jumpurl=~s|^/ext/|http://|; - $result .= ''.$prefix. + $result .= + ''.$prefix. ''.' '. ''.$values{'title'}."\n". - ''. - ''; - $result .= "

\n"; - $result .= ''.$values{'author'}.','. + 'target="preview" onclick="openMyModal(this.href, 500, 500, \'yes\');return false;">'.$values{'title'}."\n". + &display_tools($values{'title'}, $jumpurl). + "

\n". + ''.$values{'author'}.','. ' '.$values{'owner'}.'
'; foreach my $field ( @@ -3318,16 +3516,10 @@ sub summary_view { my $jumpurl=$values{'url'}; $jumpurl=~s|^/ext/|http://|; my $link = '
'.&display_url($jumpurl,1).'
'; - - my $titleWL = &mt('Set link to wishlist'); - $result.=<$values{'title'} - - set wishlistlink - + $result .= + ''.$values{'title'}.''. + &display_tools($values{'title'}, $jumpurl).< $link
$values{'author'}, $values{'owner'} -- $values{'lastrevisiondate'}
@@ -3372,13 +3564,9 @@ sub compact_view { } $jumpurl = &HTML::Entities::encode($jumpurl,'<>&"'); $result.=' '. - ''. + ''. &HTML::Entities::encode($values{'title'},'<>&"').' '. - ''. - ''. - ''. + &display_tools($values{'title'}, $jumpurl). $link.' '.$values{'author'}.' ('.$values{'domain'}.')'; return $result; } @@ -3392,12 +3580,17 @@ sub display_url { } elsif ($url=~m{^(http://|/uploaded/)}) { $link=''.$url.''; } else { + # replace the links to open in a new window + # (because the search opens in a new window, it gets + # confusing when the links open a tab in the + # parent window; ideally we should not force windows) + my $onclick = " onclick=\"window.open(this.href, '_blank', 'toolbar=1,location=1,menubar=0');return false;\""; $link=&Apache::lonhtmlcommon::crumbs( $url, 'preview', '', - (($env{'form.catalogmode'} eq 'import')?'parent.statusframe.document.forms.statusform':''), - $skiplast).' '; + '', + $skiplast,$onclick).' '; } return $link; } @@ -3420,20 +3613,16 @@ sub fielded_format_view { my $jumpurl=$values{'url'}; $jumpurl=~s|^/ext/|http://|; - my $titleWL = ('Set link to wishlist'); my $result=<

URL:
$values{'url'} - - set wishlistlink - -
END + $result .= + &display_tools($values{'title'}, $jumpurl).' + '; foreach my $field ('title','author','domain','subject','keywords','notes', 'mimetag','language','creationdate','lastrevisiondate', 'owner','copyrighttag','hostname','abstract') { @@ -3707,7 +3896,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{$_}; } @@ -3725,7 +3914,6 @@ sub cleanup { &Apache::lonnet::logthis('Failed cleanup searchcat: groupsearch_db'); } } - &untiehash(); &Apache::lonmysql::disconnect_from_db(); return OK; }