--- loncom/interface/lonsearchcat.pm 2009/10/06 10:31:41 1.315
+++ loncom/interface/lonsearchcat.pm 2024/07/04 17:21:22 1.331.4.17
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Search Catalog
#
-# $Id: lonsearchcat.pm,v 1.315 2009/10/06 10:31:41 bisitz Exp $
+# $Id: lonsearchcat.pm,v 1.331.4.17 2024/07/04 17:21:22 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);
######################################################################
######################################################################
@@ -118,8 +118,6 @@ sub handler {
# This is set and used in &handler() and is also used in
# &output_results().
- my $loaderror=&Apache::lonnet::overloaderror($r);
- if ($loaderror) { return $loaderror; }
#
my $closebutton; # button that closes the search window
# This button is different for the RAT compared to
@@ -146,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
@@ -164,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';
@@ -172,21 +171,12 @@ 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');
}
- if (exists($env{'request.course.id'}) && $env{'request.course.id'} ne '') {
- push(@allowed_searches,'course');
- }
my $crumb_text = 'Portfolio Search';
- if (@allowed_searches == 3) {
- $crumb_text = 'Course, Portfolio and Catalog Search';
- } elsif (@allowed_searches ==2) {
- if (grep(/^res$/,@allowed_searches)) {
- $crumb_text = 'Portfolio and Catalog Search';
- } elsif (grep(/^course$/,@allowed_searches)) {
- $crumb_text = 'Portfolio and Course Search';
- }
+ if (@allowed_searches ==2) {
+ $crumb_text = 'Portfolio and Catalog Search';
}
&Apache::lonhtmlcommon::add_breadcrumb
({href=>'/adm/searchcat?'.
@@ -204,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;
}
}
@@ -303,8 +296,6 @@ END
if ($env{'form.phase'} eq 'adv_search' ||
$env{'form.phase'} eq 'disp_adv') {
$env{'form.searchmode'} = 'advanced';
- } elsif ($env{'form.phase'} eq 'course_search') {
- $env{'form.searchmode'} = 'course_search';
}
#
if ($env{'form.searchmode'} eq 'advanced') {
@@ -320,16 +311,7 @@ END
'&mode='.$env{'form.mode'},
text=>"Advanced $srchtype Search",
bug=>'Searching',});
- } elsif ($env{'form.searchmode'} eq 'course search') {
- &Apache::lonhtmlcommon::add_breadcrumb
- ({href=>'/adm/searchcat?'.&Apache::loncommon::inhibit_menu_check().
- '&phase=disp_adv'.
- 'catalogmode='.$env{'form.catalogmode'}.
- '&launch='.$env{'form.launch'}.
- '&mode='.$env{'form.mode'},
- text=>"Course Search",
- bug=>'Searching',});
- }
+ }
##
## Switch on the phase
##
@@ -341,33 +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 'course_search') {
- &course_search($r);
} 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));
}
@@ -375,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
@@ -448,246 +429,10 @@ sub hidden_field {
}
######################################################################
-######################################################################
-##
-## Course Search
-##
-######################################################################
-######################################################################
-{ # Scope the course search to avoid global variables
-#
-# Variables For course search
-my %alreadyseen;
-my %hash;
-my $totalfound;
-
-sub make_symb {
- my ($id)=@_;
- my ($mapid,$resid)=split(/\./,$id);
- my $map=$hash{'map_id_'.$mapid};
- my $res=$hash{'src_'.$id};
- my $symb=&Apache::lonnet::encode_symb($map,$resid,$res);
- return $symb;
-}
-
-sub course_search {
- my $r=shift;
- my $pretty_search_string = ''.$env{'form.courseexp'}.'';
- my $search_string = $env{'form.courseexp'};
- my @New_Words;
- undef(%alreadyseen);
- if ($env{'form.crsrelated'}) {
- ($search_string,@New_Words) = &related_version($env{'form.courseexp'});
- if (@New_Words) {
- $pretty_search_string .= ' '.&mt("with related words").": @New_Words.";
- } else {
- $pretty_search_string .= ' '.&mt('with no related words').".";
- }
- }
- my $fulltext=$env{'form.crsfulltext'};
- my $discuss=$env{'form.crsdiscuss'};
- my @allwords=($search_string,@New_Words);
- $totalfound=0;
-
- &Apache::lonhtmlcommon::add_breadcrumb
- ({href=>'/adm/searchcat?'.&Apache::loncommon::inhibit_menu_check().
- '&phase=disp_adv'.
- 'catalogmode='.$env{'form.catalogmode'}.
- '&launch='.$env{'form.launch'}.
- '&mode='.$env{'form.mode'},
- text=>"Course Search",
- bug=>'Searching',});
- $r->print(&Apache::loncommon::start_page('Course Search'));
- $r->print(&Apache::lonhtmlcommon::breadcrumbs('Searching','Searching',
- $env{'form.catalogmode'} ne 'import'));
-
- $r->print(
- '
'.&mt('No matches found in resources.').'
'); - } - -# Check discussions if requested - if ($discuss) { - my $totaldiscussions = 0; - $r->print(''.&mt('No matches found in postings.').'
'); - } - } else { - $r->print('-$lt{'note'}. -
-
-ENDCOURSESEARCH
- $scrout.=' '.
- &Apache::lonhtmlcommon::textbox('courseexp',
- $env{'form.courseexp'},40);
- my $crscheckbox =
- &Apache::lonhtmlcommon::checkbox('crsfulltext',
- $env{'form.crsfulltext'});
- my $relcheckbox =
- &Apache::lonhtmlcommon::checkbox('crsrelated',
- $env{'form.crsrelated'});
- my $discheckbox =
- &Apache::lonhtmlcommon::checkbox('crsdiscuss',
- $env{'form.crsrelated'});
- $scrout.=(< | |
- -
-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; @@ -2209,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()); @@ -2219,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; @@ -2286,8 +2090,8 @@ 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'); @@ -2321,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 .'
'; @@ -2459,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(< +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; @@ -2541,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'; @@ -2573,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::all_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; # @@ -2618,6 +2458,8 @@ END ## ## Prepare for the big loop. my $hitcountsum; + my $oldhitcountsum; + my $displaycount; my %matches; my $server; my $status; @@ -2652,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 { @@ -2664,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 @@ -2678,7 +2520,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; @@ -2714,8 +2559,8 @@ END if ($area eq 'portfolio') { next if (defined($matches{$Fields{'url'}})); - # Skip if inaccessible - next if (!&Apache::lonnet::portfolio_access($Fields{'url'})); + # Skip unless access control set to public or passphrase-protected + next unless (($Fields{'scope'} eq 'public') || ($Fields{'scope'} eq 'guest')); $matches{$Fields{'url'}} = 1; } # @@ -2737,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); @@ -2831,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.'). '
'. @@ -2838,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 @@ -2960,13 +2819,13 @@ sub display_results { .&mt('Sort by:').' ' .&Apache::loncommon::select_form($env{'form.sortfield'}, 'sortfield', - %sort_fields) + \%sort_fields) .' ' .&Apache::loncommon::select_form($env{'form.sortorder'}, 'sortorder', - (asc =>&mt('Ascending'), + {asc =>&mt('Ascending'), desc=>&mt('Descending') - )) + }) .''; ## ## Display links for 'prev' and 'next' pages (if necessary) and Display Options @@ -2980,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; @@ -3347,15 +3205,25 @@ SCRIPT } function select_group() { parent.window.location= - "/adm/groupsort?mode=$env{'form.mode'}&catalogmode=import$inhibit_menu&acts="+ + "/adm/groupsort?mode=$env{'form.mode'}&catalogmode=import$inhibit_menu&acts="+ parent.statusframe.document.forms.statusform.elements.acts.value; } // ]]> SCRIPT + $js.=< +SCRIPT + my $start_page = &Apache::loncommon::start_page(undef,$js, - {'only_body' =>1}); + {'only_body' =>1, + 'add_wishlist' =>1, + 'add_modal' =>1}); my $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
(
@@ -3541,7 +3453,7 @@ sub detailed_citation_view {
$result .= ''.&mt($field->{'translate'}).'';
foreach my $item (split(',',$values{$field->{'name'}})){
$item = &Apache::lonnet::clutter($item);
- $result .= &display_url($item,[2,0,1]);
+ $result .= '
'.&display_url($item,1).'
';
}
} elsif (exists($field->{'format'}) && $field->{'format'} ne ''){
$result.= &mt($field->{'translate'},
@@ -3549,7 +3461,7 @@ sub detailed_citation_view {
$values{$field->{'name'}}))."
\n";
} else {
if ($field->{'special'} eq 'url link') {
- $result .= &display_url($jumpurl,[3,0,1]);
+ $result .= '
'.&display_url($jumpurl,1).'
';
} else {
$result.= &mt($field->{'translate'},
$values{$field->{'name'}});
@@ -3603,11 +3515,12 @@ sub summary_view {
}
my $jumpurl=$values{'url'};
$jumpurl=~s|^/ext/|http://|;
- my $link = &display_url($jumpurl,[2,0,1]);
-
- $result.=<
+ my $link = '
'.&display_url($jumpurl,1).'
';
+ $result .=
+ ''.$values{'title'}.''.
+ &display_tools($values{'title'}, $jumpurl).<
$values{'author'}, $values{'owner'} -- $values{'lastrevisiondate'}
$values{'copyrighttag'}
@@ -3639,7 +3552,7 @@ sub compact_view {
my $jumpurl=$values{'url'};
$jumpurl=~s|^/ext/|http://|;
- my $link = &display_url($jumpurl,[1,1,1]);
+ my $link = &display_url($jumpurl,1);
my $result =
$prefix.'';
@@ -3651,14 +3564,15 @@ 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;
}
sub display_url {
- my ($url,$crumb_args) = @_;
+ my ($url,$skiplast) = @_;
my $link;
if ($url=~m|^/ext/|) {
$url=~s|^/ext/|http://|;
@@ -3666,10 +3580,17 @@ sub display_url {
} elsif ($url=~m{^(http://|/uploaded/)}) {
$link=''.$url.'';
} else {
- $link=&Apache::lonhtmlcommon::crumbs($url,
- 'preview',
- '',
- (($env{'form.catalogmode'} eq 'import')?'parent.statusframe.document.forms.statusform':''),@{$crumb_args}).' ';
+ # 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',
+ '',
+ '',
+ $skiplast,$onclick).' ';
}
return $link;
}
@@ -3697,8 +3618,11 @@ $prefix