--- loncom/interface/lonsearchcat.pm 2011/01/17 20:18:02 1.327 +++ loncom/interface/lonsearchcat.pm 2016/11/09 01:11:04 1.350 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Search Catalog # -# $Id: lonsearchcat.pm,v 1.327 2011/01/17 20:18:02 www Exp $ +# $Id: lonsearchcat.pm,v 1.350 2016/11/09 01:11:04 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 ############################################################################### @@ -78,7 +76,9 @@ use HTML::Entities(); use Parse::RecDescent; use Apache::lonnavmaps; use Apache::lonindexer(); +use Apache::lonwishlist(); use LONCAPA; +use Time::HiRes qw(sleep); ###################################################################### ###################################################################### @@ -144,14 +144,15 @@ sub handler { ## this once, so the pause indicator is deleted ## if (exists($env{'form.pause'})) { - sleep(1); + sleep(0.1); delete($env{'form.pause'}); } ## ## Initialize global variables ## my $domain = $r->dir_config('lonDefDomain'); - $diropendb= "/home/httpd/perl/tmp/". + my $temp_file_dir = LONCAPA::tempdir(); + $diropendb= $temp_file_dir . "$env{'user.domain'}_$env{'user.name'}_sel_res.db"; # # set the name of the persistent database @@ -162,7 +163,7 @@ sub handler { $env{'form.persistent_db_id'} = time; } - my $persistent_db_file = "/home/httpd/perl/tmp/". + my $persistent_db_file = $temp_file_dir . &escape($domain). '_'.&escape($env{'user.name'}). '_'.$env{'form.persistent_db_id'}.'_persistent_search.db'; @@ -170,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'; @@ -193,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; } } @@ -319,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)); } @@ -351,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 @@ -425,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. @@ -443,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 .= '

'; } @@ -472,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"; } @@ -1399,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 '') { @@ -1427,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); } ###################################################################### @@ -1461,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'}; @@ -1507,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); } @@ -1779,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)) { @@ -1858,10 +1941,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') && @@ -1899,11 +1983,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; @@ -1913,9 +1999,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()); @@ -1923,13 +2012,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; @@ -1985,21 +2081,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'); @@ -2025,7 +2107,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 .'

'; @@ -2163,8 +2245,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; @@ -2245,7 +2340,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'; @@ -2277,16 +2372,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; # @@ -2322,6 +2440,7 @@ END ## ## Prepare for the big loop. my $hitcountsum; + my $oldhitcountsum; my %matches; my $server; my $status; @@ -2356,7 +2475,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 { @@ -2368,7 +2487,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 @@ -2382,7 +2501,10 @@ END next; } $status=~s|/||g; - my $datafile=$r->dir_config('lonDaemons').'/tmp/'.$status; + + + + my $datafile=LONCAPA::tempdir().$status; if (-e $datafile && ! -e "$datafile.end") { &update_status($r,&mt('Receiving results from [_1]',$server)); next; @@ -2441,7 +2563,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); @@ -2535,6 +2663,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.'). '

'. @@ -2543,6 +2673,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 @@ -2684,8 +2818,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; @@ -3058,91 +3191,18 @@ SCRIPT SCRIPT - # HTML-Markup for 'Set a link for this resource to wishlist' - # this is written via JavaScript document.write (function set_wishlistlink) - # it is split into 3 parts and the inputfields for title and path are left out - # these fields are inserted later to set the values for title and path - # automatically via JavaScript (document.title and location.pathname) - my %folders = &Apache::lonnet::get('wishlist',['folders']); - if ($folders{'folders'} eq '') { - $folders{'folders'} = ''; - } - my $start_page_wishlistlink = - &Apache::loncommon::start_page('Set link to wishlist',undef, - {'only_body' => 1, - 'js_ready' => 1, - 'bgcolor' => '#FFFFFF',}); - - my $warningLink = &mt('You must insert a title!'); - - my $in_page_wishlistlink1 = '

'.&mt('Set a link to wishlist').'

'. - '
'. - &Apache::lonhtmlcommon::start_pick_box(). - &Apache::lonhtmlcommon::row_title(&mt('Link Title')); - - my $in_page_wishlistlink2 = &Apache::lonhtmlcommon::row_closure(). - &Apache::lonhtmlcommon::row_title(&mt('Path')); - - my $in_page_wishlistlink3 = &Apache::lonhtmlcommon::row_closure(). - &Apache::lonhtmlcommon::row_title(&mt('Note')). - ''. - &Apache::lonhtmlcommon::row_closure(1). - &Apache::lonhtmlcommon::end_pick_box(). - '

'. - ''. - ''. - ''. - '
'; - - # remove all \n for inserting on javascript document.write - $in_page_wishlistlink1 =~ s/\n//g; - $in_page_wishlistlink2 =~ s/\n//g; - $in_page_wishlistlink3 =~ s/\n//g; - - my $end_page_wishlistlink = - &Apache::loncommon::end_page({'js_ready' => 1}); - - # Add JavaScript-function to set link for a ressource to wishlist $js.=< -// ' - +'function newlinksubmit(){' - +'var title = document.getElementsByName("title")[0].value;' - +'if (!title) {' - +'alert("$warningLink");' - +'return false;}' - +'return true;}' - +'<\/scr'+'ipt>' - +'$in_page_wishlistlink1' - +'' - +'$in_page_wishlistlink2' - +'' - +'$in_page_wishlistlink3' - +'$end_page_wishlistlink' ); - wishlistlink.document.close(); -} - -// ]]> + \$(document).ready(function() { + parent.done_loading_results(); + }); SCRIPT my $start_page = &Apache::loncommon::start_page(undef,$js, - {'only_body' =>1}); + {'only_body' =>1, + 'add_wishlist' =>1, + 'add_modal' =>1}); my $result=< @@ -3169,10 +3229,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 @@ -3234,6 +3312,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 @@ -3258,16 +3358,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 ( @@ -3395,16 +3493,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'}
@@ -3449,13 +3541,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; } @@ -3469,12 +3557,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; } @@ -3497,20 +3590,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') { @@ -3784,7 +3873,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{$_}; } @@ -3802,7 +3891,6 @@ sub cleanup { &Apache::lonnet::logthis('Failed cleanup searchcat: groupsearch_db'); } } - &untiehash(); &Apache::lonmysql::disconnect_from_db(); return OK; }