--- loncom/interface/lonsearchcat.pm 2009/07/03 08:18:48 1.311 +++ loncom/interface/lonsearchcat.pm 2013/12/13 16:40:01 1.331.4.8 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Search Catalog # -# $Id: lonsearchcat.pm,v 1.311 2009/07/03 08:18:48 bisitz Exp $ +# $Id: lonsearchcat.pm,v 1.331.4.8 2013/12/13 16:40:01 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,6 +76,7 @@ use HTML::Entities(); use Parse::RecDescent; use Apache::lonnavmaps; use Apache::lonindexer(); +use Apache::lonwishlist(); use LONCAPA; ###################################################################### @@ -118,8 +117,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 @@ -153,7 +150,8 @@ sub handler { ## 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 +162,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'; @@ -175,18 +173,9 @@ sub handler { if (&Apache::lonnet::allowed('bre',$env{'request.role.domain'})) { 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?'. @@ -266,25 +255,25 @@ sub handler { ## Configure dynamic components of interface ## if ($env{'form.catalogmode'} eq 'interactive') { - $closebutton=" +onclick='javascript:select_group()' /> END } else { $closebutton = ''; @@ -303,8 +292,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 +307,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 +319,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 +351,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 +425,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.=(< | |
- -
-' + .&viewoptions() + .'
' + .'' + .'' + .' ' + .$closebutton + .'
'; # - $scrout .= '$closebutton +
END my $srchtype = 'Catalog'; my $jscript; if ($env{'form.area'} eq 'portfolio') { $srchtype = 'Portfolio'; $jscript = ''; } my $scrout= &Apache::loncommon::start_page("Advanced $srchtype Search", $jscript); - $scrout .= <<"ENDHEADER"; -$bread_crumb -'; + $scrout .= &Apache::loncommon::end_page(); $r->print($scrout); return; @@ -1172,21 +905,23 @@ Outputs: text for box with view options ###################################################################### ###################################################################### sub viewoptions { - my $scrout = ''; + my $scrout; if (! defined($env{'form.viewselect'})) { $env{'form.viewselect'}='detailed'; } - $scrout.=&Apache::lonmeta::selectbox('viewselect', - $env{'form.viewselect'}, - \&viewoptiontext, - sort(keys(%Views))); - $scrout.= ' '; + $scrout .= '' + .&mt('Type:').' ' + .&Apache::lonmeta::selectbox('viewselect', + $env{'form.viewselect'}, + \&viewoptiontext, + sort(keys(%Views))) + .''; my $countselect = &Apache::lonmeta::selectbox('show', $env{'form.show'}, undef, (10,20,50,100,1000,10000)); - $scrout .= (' 'x2) - .&mt('[_1] Records per Page',$countselect) + $scrout .= ' ' + .&mt('Records per Page:').' '.$countselect .''.$/; return $scrout; } @@ -1278,8 +1013,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 @@ -1297,13 +1033,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); @@ -1321,7 +1069,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 @@ -1333,8 +1083,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; } @@ -1651,7 +1410,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."' + .&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; } ## @@ -2177,9 +1965,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()); @@ -2187,13 +1978,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; @@ -2251,13 +2049,15 @@ sub print_sort_form { } my $js =<' - .&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 .'
'; @@ -2400,16 +2200,28 @@ Returns: Nothing. sub update_count_status { my ($r,$text) = @_; $text =~ s/\'/\\\'/g; - $r->print - ("\n"); + $r->print(< +SCRIPT + $r->rflush(); } sub update_status { my ($r,$text) = @_; $text =~ s/\'/\\\'/g; - $r->print - ("\n"); + $r->print(< +SCRIPT + $r->rflush(); } @@ -2436,9 +2248,14 @@ sub update_seconds { my ($r) = @_; my $time = &time_left(); if (($last_time-$time) > 0) { - $r->print("\n"); + $r->print(< +SCRIPT + $r->rflush(); } $last_time = $time; @@ -2470,7 +2287,7 @@ sub revise_button { '&cleargroupsort=1'. '&phase='.$revise_phase; my $result = qq{ }; + qq{ onclick="parent.location='$newloc';" /> }; return $result; } @@ -2490,7 +2307,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'; @@ -2522,16 +2339,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; # @@ -2601,7 +2441,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 { @@ -2627,7 +2467,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; @@ -2691,7 +2534,7 @@ END last if ($connection->aborted()); &update_seconds($r); } - &update_status($r,&mt('Search Complete [_1]',$server)); + &update_status($r,&mt('Search Complete on Server [_1]',$server)); &update_seconds($r); # &Apache::lonmysql::disconnect_from_db(); # This is unneccessary @@ -2701,11 +2544,11 @@ END # loaded from /adm/searchcat $r->print(&Apache::loncommon::end_page()); # if ($env{'form.catalogmode'} ne 'import') { - $r->print(""); + $r->print(< +SCRIPT # } return; } @@ -2728,15 +2571,15 @@ sub prev_next_buttons { return '' if ($show eq 'all'); # No links if you get them all at once. # # Create buttons - my $buttons = ''; - $buttons .= ' 'x3; - $buttons .= '' + .'' + .' ' + .'' + .' ' + .'' + .''; } ###################################################################### @@ -2761,7 +2604,9 @@ sub display_results { ## my $viewfunction = $Views{$env{'form.viewselect'}}; if (!defined($viewfunction)) { - $r->print("Internal Error - Bad view selected.\n"); + $r->print('' + .&mt('Internal Error - Bad view selected.') + .'
'."\n"); $r->rflush(); return; } @@ -2778,7 +2623,10 @@ sub display_results { if ($env{'form.catalogmode'} eq 'import') { if (! tie(%groupsearch_db,'GDBM_File',$diropendb, &GDBM_WRCREAT(),0640)) { - $r->print('Unable to save import results.'. + $r->print(''. + &mt('Unable to save import results.'). + '
'. + ''. &Apache::loncommon::end_page()); $r->rflush(); return; @@ -2792,7 +2640,10 @@ sub display_results { ## 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.". + $r->print(''. + &mt('A MySQL error has occurred.'). + '
'. + ''. &Apache::loncommon::end_page()); &Apache::lonnet::logthis("lonmysql was unable to determine the number". " of rows in table ".$table); @@ -2897,37 +2748,40 @@ sub display_results { $env{'form.sortorder'}='asc'; } } - my $sortform = &mt('Sort by [_1] [_2]', - &Apache::loncommon::select_form($env{'form.sortfield'}, + my $sortform = '' + .&mt('Sort by:').' ' + .&Apache::loncommon::select_form($env{'form.sortfield'}, 'sortfield', - %sort_fields), - &Apache::loncommon::select_form($env{'form.sortorder'}, + \%sort_fields) + .' ' + .&Apache::loncommon::select_form($env{'form.sortorder'}, 'sortorder', - (asc =>&mt('Ascending'), + {asc =>&mt('Ascending'), desc=>&mt('Descending') - )) - ); + }) + .''; ## - ## Output links (if necessary) for 'prev' and 'next' pages. - $r->print - (''. - ''.$sortform.''. - ' | '. - &prev_next_buttons($min,$env{'form.show'},$total_results). - ' | '. - &viewoptions().' |
'.&mt('There are currently no results.').'
'. "". &Apache::loncommon::end_page()); return; } else { - $r->print('\n"; + my $output; if (! defined($Fields{'title'}) || $Fields{'title'} eq '') { $Fields{'title'} = 'Untitled'; } @@ -2972,21 +2827,27 @@ sub display_results { # Render the result into html $output.= &$viewfunction($prefix,%Fields); # Print them out as they come in. - $r->print($output); + $r->print(&Apache::loncommon::start_data_table_row() + .'
' + .&mt('There were no results matching your query.') + .'
'); } else { - $r->print - ('\n"; - $result .= ''.$values{'author'}.','. + 'target="preview">'.$values{'title'}."\n". + &display_tools($values{'title'}, $jumpurl). + "
\n".
+ ''.$values{'author'}.','.
' '.$values{'owner'}.'
';
foreach my $field
(
@@ -3464,7 +3358,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'},
@@ -3472,7 +3366,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'}});
@@ -3487,17 +3381,14 @@ sub detailed_citation_view {
if (exists($values{'shortabstract'}) && $values{'shortabstract'} ne '') {
$result .= '
'.$values{'shortabstract'}.'
'; } - $result .= ''. - &detailed_citation_view($prefix,%values). + return &detailed_citation_view($prefix,%values). ' | '. - &Apache::lonindexer::showpreview($values{'url'}). - ' |
'. - &summary_view($prefix,%values). + return &summary_view($prefix,%values). ' | '. - &Apache::lonindexer::showpreview($values{'url'}). - ' |
+
$errorstring
@@ -3811,23 +3706,32 @@ $parms is extra information to include i ###################################################################### sub output_blank_field_error { my ($r,$closebutton,$parms,$hidden_fields)=@_; - my $errormsg = &mt('You did not fill in enough information for the search to be started. You need to fill in relevant fields on the search page in order for a query to be processed.'); + my $errormsg = &mt('You did not fill in enough information for the search to be started. You need to fill in relevant fields on the search page in order for a query to be processed.'); my $revise = &mt('Revise Search Request'); - my $heading = &mt('Unactionable Search Queary'); + my $heading = &mt('Unactionable Search Query'); my $start_page = &Apache::loncommon::start_page('Search'); my $end_page = &Apache::loncommon::end_page(); + if ($closebutton) { + $closebutton = '
'.$closebutton.'
+
$errormsg
$end_page ENDPAGE @@ -3858,16 +3762,18 @@ sub output_date_error { # make query information persistent to allow for subsequent revision my $start_page = &Apache::loncommon::start_page('Search'); my $end_page = &Apache::loncommon::end_page(); + my $heading = &mt('Error'); $r->print(<+
$message
$end_page @@ -3908,7 +3814,6 @@ sub cleanup { &Apache::lonnet::logthis('Failed cleanup searchcat: groupsearch_db'); } } - &untiehash(); &Apache::lonmysql::disconnect_from_db(); return OK; }