--- loncom/interface/lonsearchcat.pm 2003/09/28 00:14:25 1.187 +++ loncom/interface/lonsearchcat.pm 2005/11/26 02:42:45 1.247 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Search Catalog # -# $Id: lonsearchcat.pm,v 1.187 2003/09/28 00:14:25 www Exp $ +# $Id: lonsearchcat.pm,v 1.247 2005/11/26 02:42:45 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -48,7 +48,7 @@ described at http://www.lon-capa.org. lonsearchcat presents the user with an interface to search the LON-CAPA digital library. lonsearchcat also initiates the execution of a search by sending the search parameters to LON-CAPA servers. The progress of -search (on a server basis) is displayed to the user in a seperate window. +search (on a server basis) is displayed to the user in a separate window. =head1 Internals @@ -59,130 +59,67 @@ search (on a server basis) is displayed ############################################################################### ############################################################################### -############################################################################### -## ## -## ORGANIZATION OF THIS PERL MODULE ## -## ## -## 1. Modules used by this module ## -## 2. Variables used throughout the module ## -## 3. handler subroutine called via Apache and mod_perl ## -## 4. Other subroutines ## -## ## -############################################################################### - package Apache::lonsearchcat; -# ------------------------------------------------- modules used by this module use strict; use Apache::Constants qw(:common :http); -use Apache::lonnet(); +use Apache::lonnet; use Apache::File(); use CGI qw(:standard); use Text::Query; use GDBM_File; use Apache::loncommon(); use Apache::lonmysql(); +use Apache::lonmeta; +use Apache::lonhtmlcommon; use Apache::lonlocal; - -# ---------------------------------------- variables used throughout the module +use LONCAPA::lonmetadata(); +use HTML::Entities(); +use Parse::RecDescent; +use Apache::lonnavmaps; ###################################################################### ###################################################################### - -=pod - -=item Global variables - -=over 4 - -=item $importbutton - -button to take the select results and go to group sorting - -=item %groupsearch_db - -Database hash used to save values for the groupsearch RAT interface. - -=item $diropendb - -The full path to the (temporary) search database file. This is set and -used in &handler() and is also used in &output_results(). - -=item %Views - -Hash which associates an output view description with the function -that produces it. Adding a new view type should be as easy as -adding a line to the definition of this hash and making sure the function -takes the proper parameters. - -=item $bodytag - -LON-CAPA standard body tag, gotten from &Apache::lonnet::bodytag. -No title, no table, just a tag. - -=back - -=cut - +## +## Global variables +## ###################################################################### ###################################################################### +my %groupsearch_db; # Database hash used to save values for the + # groupsearch RAT interface. +my %persistent_db; # gdbm hash which holds data which is supposed to + # persist across calls to lonsearchcat.pm -# -- dynamically rendered interface components -my $importbutton; # button to take the selected results and go to group sorting +# The different view modes and associated functions -# -- miscellaneous variables -my %groupsearch_db; # database hash -my $diropendb = ""; # db file -# View Description Function Pointer -my %Views = ("Detailed Citation View" => \&detailed_citation_view, - "Summary View" => \&summary_view, - "Fielded Format" => \&fielded_format_view, - "XML/SGML" => \&xml_sgml_view, - "Compact View" => \&compact_view); -my %persistent_db; -my $hidden_fields; -my $bodytag; - -# -# For course search -# -my %alreadyseen; -my $hashtied; -my %hash; -my $totalfound; - -###################################################################### -###################################################################### - -=pod - -=item &handler() - main handler invoked by httpd child - -=item Variables - -=over 4 - -=item $hidden - -holds 'hidden' html forms - -=item $scrout - -string that holds portions of the screen output - -=back - -=cut +my %Views = ("detailed" => \&detailed_citation_view, + "summary" => \&summary_view, + "fielded" => \&fielded_format_view, + "xml" => \&xml_sgml_view, + "compact" => \&compact_view); ###################################################################### ###################################################################### sub handler { my $r = shift; +# &set_defaults(); # + # set form defaults + # + my $hidden_fields;# Hold all the hidden fields used to keep track + # of the search system state + my $importbutton; # button to take the selected results and go to group + # sorting + my $diropendb; # The full path to the (temporary) search database file. + # This is set and used in &handler() and is also used in + # &output_results(). + my $bodytag; # LON-CAPA standard body tag, gotten from + # &Apache::lonnet::bodytag. + # No title, no table, just a tag. 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 # normal invocation. @@ -200,67 +137,97 @@ sub handler { &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['catalogmode','launch','acts','mode','form','element','pause', 'phase','persistent_db_id','table','start','show', - 'cleargroupsort']); + 'cleargroupsort','titleelement']); ## ## The following is a trick - we wait a few seconds if asked to so ## the daemon running the search can get ahead of the daemon ## printing the results. We only need (theoretically) to do ## this once, so the pause indicator is deleted ## - if (exists($ENV{'form.pause'})) { + if (exists($env{'form.pause'})) { sleep(1); - delete($ENV{'form.pause'}); + delete($env{'form.pause'}); } ## ## Initialize global variables ## my $domain = $r->dir_config('lonDefDomain'); - $diropendb= "/home/httpd/perl/tmp/$ENV{'user.domain'}_$ENV{'user.name'}_searchcat.db"; + $diropendb= "/home/httpd/perl/tmp/". + "$env{'user.domain'}_$env{'user.name'}_searchcat.db"; # # set the name of the persistent database - # $ENV{'form.persistent_db_id'} can only have digits in it. - if (! exists($ENV{'form.persistent_db_id'}) || - ($ENV{'form.persistent_db_id'} =~ /\D/) || - ($ENV{'form.launch'} eq '1')) { - $ENV{'form.persistent_db_id'} = time; + # $env{'form.persistent_db_id'} can only have digits in it. + if (! exists($env{'form.persistent_db_id'}) || + ($env{'form.persistent_db_id'} =~ /\D/) || + ($env{'form.launch'} eq '1')) { + $env{'form.persistent_db_id'} = time; } $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1); my $persistent_db_file = "/home/httpd/perl/tmp/". &Apache::lonnet::escape($domain). - '_'.&Apache::lonnet::escape($ENV{'user.name'}). - '_'.$ENV{'form.persistent_db_id'}.'_persistent_search.db'; + '_'.&Apache::lonnet::escape($env{'user.name'}). + '_'.$env{'form.persistent_db_id'}.'_persistent_search.db'; ## - if (! &get_persistent_form_data($persistent_db_file)) { - if ($ENV{'form.phase'} =~ /(run_search|results)/) { - &Apache::lonnet::logthis("lonsearchcat:Unable to recover data ". - "from $persistent_db_file"); - $r->print(< -LON-CAPA Search Error + &Apache::lonhtmlcommon::clear_breadcrumbs(); + if (exists($env{'request.course.id'}) && $env{'request.course.id'} ne '') { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>'/adm/searchcat?'. + 'catalogmode='.$env{'form.catalogmode'}. + '&launch='.$env{'form.launch'}. + '&mode='.$env{'form.mode'}, + text=>"Course and Catalog Search", + target=>'_top', + bug=>'Searching',}); + } else { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>'/adm/searchcat?'. + 'catalogmode='.$env{'form.catalogmode'}. + '&launch='.$env{'form.launch'}. + '&mode='.$env{'form.mode'}, + text=>"Catalog Search", + target=>'_top', + bug=>'Searching',}); + } + # + if ($env{'form.phase'} !~ m/(basic|adv|course)_search/) { + if (! &get_persistent_form_data($persistent_db_file)) { + if ($env{'form.phase'} =~ /(run_search|results)/) { + &Apache::lonnet::logthis('lonsearchcat:'. + 'Unable to recover data from '. + $persistent_db_file); + my $html=&Apache::lonxml::xmlbegin(); + $r->print(< +LON-CAPA Search Error $bodytag We were unable to retrieve data describing your search. This is a serious error and has been logged. Please alert your LON-CAPA administrator. END - return OK; + return OK; + } } + } else { + &clean_up_environment(); } ## ## Clear out old values from groupsearch database ## untie %groupsearch_db if (tied(%groupsearch_db)); - if (($ENV{'form.cleargroupsort'} eq '1') || - (($ENV{'form.launch'} eq '1') && - ($ENV{'form.catalogmode'} eq 'groupsearch'))) { + if (($env{'form.cleargroupsort'} eq '1') || + (($env{'form.launch'} eq '1') && + ($env{'form.catalogmode'} eq 'groupsearch'))) { if (tie(%groupsearch_db,'GDBM_File',$diropendb,&GDBM_WRCREAT(),0640)) { &start_fresh_session(); untie %groupsearch_db; - delete($ENV{'form.cleargroupsort'}); + delete($env{'form.cleargroupsort'}); } else { # This is a stupid error to give to the user. # It really tells them nothing. - $r->print(''.$bodytag. + my $html=&Apache::lonxml::xmlbegin(); + $r->print($html.''.$bodytag. 'Unable to tie hash to db file'); return OK; } @@ -269,37 +236,36 @@ END ## Configure hidden fields ## $hidden_fields = ''."\n"; - if (exists($ENV{'form.catalogmode'})) { - $hidden_fields .= ''."\n"; - } - if (exists($ENV{'form.form'})) { - $hidden_fields .= ''."\n"; - } - if (exists($ENV{'form.element'})) { - $hidden_fields .= ''."\n"; - } - if (exists($ENV{'form.mode'})) { - $hidden_fields .= ''."\n"; + $env{'form.persistent_db_id'}.'" />'."\n"; + if (exists($env{'form.catalogmode'})) { + $hidden_fields .= &hidden_field('catalogmode'); + } + if (exists($env{'form.form'})) { + $hidden_fields .= &hidden_field('form'); + } + if (exists($env{'form.element'})) { + $hidden_fields .= &hidden_field('element'); + } + if (exists($env{'form.titleelement'})) { + $hidden_fields .= &hidden_field('titleelement'); + } + if (exists($env{'form.mode'})) { + $hidden_fields .= &hidden_field('mode'); } ## ## Configure dynamic components of interface ## - if ($ENV{'form.catalogmode'} eq 'interactive') { + if ($env{'form.catalogmode'} eq 'interactive') { $closebutton="'/adm/searchcat?phase=disp_adv&'. + 'catalogmode='.$env{'form.catalogmode'}. + '&launch='.$env{'form.launch'}. + '&mode='.$env{'form.mode'}, + text=>"Advanced Search", + bug=>'Searching',}); + } elsif ($env{'form.searchmode'} eq 'course search') { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>'/adm/searchcat?phase=disp_adv&'. + 'catalogmode='.$env{'form.catalogmode'}. + '&launch='.$env{'form.launch'}. + '&mode='.$env{'form.mode'}, + text=>"Course Search", + bug=>'Searching',}); + } ## ## Switch on the phase ## - if ($ENV{'form.phase'} eq 'disp_basic') { - &print_basic_search_form($r,$closebutton); - } elsif ($ENV{'form.phase'} eq 'disp_adv') { - &print_advanced_search_form($r,$closebutton); - } elsif ($ENV{'form.phase'} eq 'results') { - &display_results($r,$importbutton,$closebutton); - } elsif ($ENV{'form.phase'} =~ /^(sort|run_search)$/) { + if ($env{'form.phase'} eq 'disp_basic') { + &print_basic_search_form($r,$closebutton,$hidden_fields); + } elsif ($env{'form.phase'} eq 'disp_adv') { + &print_advanced_search_form($r,$closebutton,$hidden_fields); + } elsif ($env{'form.phase'} eq 'results') { + &display_results($r,$importbutton,$closebutton,$diropendb); + } elsif ($env{'form.phase'} =~ /^(sort|run_search)$/) { my ($query,$customquery,$customshow,$libraries,$pretty_string) = &get_persistent_data($persistent_db_file, ['query','customquery','customshow', 'libraries','pretty_string']); - if ($ENV{'form.phase'} eq 'sort') { + if ($env{'form.phase'} eq 'sort') { &print_sort_form($r,$pretty_string); - } elsif ($ENV{'form.phase'} eq 'run_search') { + } elsif ($env{'form.phase'} eq 'run_search') { &run_search($r,$query,$customquery,$customshow, $libraries,$pretty_string); } - } elsif ($ENV{'form.phase'} eq 'course_search') { + } elsif ($env{'form.phase'} eq 'course_search') { &course_search($r); - } elsif(($ENV{'form.phase'} eq 'basic_search') || - ($ENV{'form.phase'} eq 'adv_search')) { - $ENV{'form.searchmode'} = 'basic'; - if ($ENV{'form.phase'} eq 'adv_search') { - $ENV{'form.searchmode'} = 'advanced'; + } 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 $pretty_string; + if ($env{'form.phase'} eq 'basic_search') { + ($query,$pretty_string,$libraries) = + &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) + = &parse_advanced_search($r,$closebutton,$hidden_fields); + return OK if (! defined($query)); } + &make_persistent({ query => $query, + customquery => $customquery, + customshow => $customshow, + libraries => $libraries, + pretty_string => $pretty_string }, + $persistent_db_file); + # # Set up table if (! defined(&create_results_table())) { + my $errorstring=&Apache::lonmysql::get_error(); + &Apache::lonnet::logthis('lonsearchcat.pm: Unable to create '. + 'needed table. lonmysql error:'. + $errorstring); + my $html=&Apache::lonxml::xmlbegin(); $r->print(<Search Error +$html + +Search Error $bodytag Unable to create table in which to store search results. The search has been aborted. @@ -366,10 +384,13 @@ The search has been aborted. END return OK; } - delete($ENV{'form.launch'}); + delete($env{'form.launch'}); if (! &make_form_data_persistent($r,$persistent_db_file)) { - $r->print(<Search Error + my $html=&Apache::lonxml::xmlbegin(); + $r->print(< +Search Error $bodytag Unable to properly store search information. The search has been aborted. @@ -377,123 +398,235 @@ Unable to properly store search informat END return OK; } - # - # We are running a search - my ($query,$customquery,$customshow,$libraries) = - (undef,undef,undef,undef); - my $pretty_string; - if ($ENV{'form.phase'} eq 'basic_search') { - ($query,$pretty_string,$libraries) = - &parse_basic_search($r,$closebutton); - } else { # Advanced search - ($query,$customquery,$customshow,$libraries,$pretty_string) - = &parse_advanced_search($r,$closebutton); - return OK if (! defined($query)); - } - &make_persistent({ query => $query, - customquery => $customquery, - customshow => $customshow, - libraries => $libraries, - pretty_string => $pretty_string }, - $persistent_db_file); ## ## Print out the frames interface ## - &print_frames_interface($r); + if (defined($query)) { + &print_frames_interface($r); + } } return OK; } +# +# The mechanism used to store values away and retrieve them does not +# handle the case of missing environment variables being significant. +# +# This routine sets non existant checkbox form elements to ''. +# +sub clean_up_environment { + if ($env{'form.phase'} eq 'basic_search') { + if (! exists($env{'form.related'})) { + $env{'form.related'} = ''; + } + if (! exists($env{'form.domains'})) { + $env{'form.domains'} = ''; + } + } elsif ($env{'form.phase'} eq 'adv_search') { + foreach my $field ('title','keywords','notes', + 'abstract','standards','mime') { + if (! exists($env{'form.'.$field.'_related'})) { + $env{'form.'.$field.'_related'} = ''; + } + } + } elsif ($env{'form.phase'} eq 'course_search') { + if (! exists($env{'form.crsrelated'})) { + $env{'form.crsrelated'} = ''; + } + } +} + +sub hidden_field { + my ($name,$value) = @_; + if (! defined($value)) { + $value = $env{'form.'.$name}; + } + return ''.$/; +} + +###################################################################### ###################################################################### +## +## 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 $bodytag=&Apache::loncommon::bodytag('Course Search'); - my $pretty_search_string = ''.$ENV{'form.courseexp'}.''; - my $search_string = $ENV{'form.courseexp'}; + my $pretty_search_string = ''.$env{'form.courseexp'}.''; + my $search_string = $env{'form.courseexp'}; my @New_Words; - if ($ENV{'form.crsrelated'}) { - ($search_string,@New_Words) = &related_version($ENV{'form.courseexp'}); + undef(%alreadyseen); + if ($env{'form.crsrelated'}) { + ($search_string,@New_Words) = &related_version($env{'form.courseexp'}); if (@New_Words) { - $pretty_search_string .= " with related words: @New_Words."; + $pretty_search_string .= ' '.&mt("with related words").": @New_Words."; } else { - $pretty_search_string .= " with no related words."; + $pretty_search_string .= ' '.&mt('with no related words')."."; } } - my $fulltext=$ENV{'form.crsfulltext'}; + my $fulltext=$env{'form.crsfulltext'}; + my $discuss=$env{'form.crsdiscuss'}; my @allwords=($search_string,@New_Words); $totalfound=0; - $r->print('LON-CAPA Course Search'. - $bodytag.$pretty_search_string); + my $html=&Apache::lonxml::xmlbegin(); + $r->print($html.'LON-CAPA Course Search'. + $bodytag.'
'.$pretty_search_string.'

'.&mt('Course content').':
'); $r->rflush(); # ======================================================= Go through the course - $hashtied=0; - undef %alreadyseen; - %alreadyseen=(); my $c=$r->connection; - &tiehash(); - foreach (keys %hash) { - if ($c->aborted()) { last; } - if (($_=~/^src\_(.+)$/) && (!$alreadyseen{$hash{$_}})) { - &checkonthis($r,$hash{$_},0,$hash{'title_'.$1},$fulltext,@allwords); - } - } - &untiehash(); + if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.".db", + &GDBM_READER(),0640)) { + foreach (sort(keys(%hash))) { + if ($c->aborted()) { last; } + if (($_=~/^src\_(.+)$/)) { + if ($hash{'randomout_'.$1} & !$env{'request.role.adv'}) { + next; + } + my $symb=&make_symb($1); + &checkonthis($r,$1,$hash{$_},0,&Apache::lonnet::gettitle($symb), + $fulltext,$symb,@allwords); + } + } + untie(%hash); + } unless ($totalfound) { - $r->print('

'.&mt('No resources found').'.

'); + $r->print('

'.&mt('No matches found in resources').'.

'); } -# =================================================== Done going through course - $r->print(''); -} - -# ---------------------------------------------------------------- tie the hash -sub tiehash { - $hashtied=0; - if ($ENV{'request.course.fn'}) { - if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.".db", - &GDBM_READER(),0640)) { - $hashtied=1; +# Check discussions if requested + if ($discuss) { + my $totaldiscussions = 0; + $r->print('

'.&mt('Discussion postings').':
'); + my $navmap = Apache::lonnavmaps::navmap->new(); + my @allres=$navmap->retrieveResources(); + my %discussiontime = &Apache::lonnet::dump('discussiontimes', + $env{'course.'.$env{'request.course.id'}.'.domain'}, + $env{'course.'.$env{'request.course.id'}.'.num'}); + foreach my $resource (@allres) { + my $result = ''; + my $applies = 0; + my $symb = $resource->symb(); + my $ressymb = $symb; + if ($symb =~ m#(___adm/\w+/\w+)/(\d+)/bulletinboard$#) { + $ressymb = 'bulletin___'.$2.$1.'/'.$2.'/bulletinboard'; + unless ($ressymb =~ m#bulletin___\d+___adm/wrapper#) { + $ressymb=~s#(bulletin___\d+___)#$1adm/wrapper/#; + } + } + if (defined($discussiontime{$ressymb})) { + my %contrib = &Apache::lonnet::restore($ressymb,$env{'request.course.id'}, + $env{'course.'.$env{'request.course.id'}.'.domain'}, + $env{'course.'.$env{'request.course.id'}.'.num'}); + if ($contrib{'version'}) { + for (my $id=1;$id<=$contrib{'version'};$id++) { + unless (($contrib{'hidden'}=~/\.$id\./) || ($contrib{'deleted'}=~/\.$id\./)) { + if ($contrib{$id.':subject'}) { + $result .= $contrib{$id.':subject'}; + } + if ($contrib{$id.':message'}) { + $result .= $contrib{$id.':message'}; + } + if ($contrib{$id,':attachmenturl'}) { + if ($contrib{$id,':attachmenturl'} =~ m-/([^/]+)$-) { + $result .= $1; + } + } + $applies = &checkwords($result,$applies,@allwords); + } + } + } + } +# Does this discussion apply? + if ($applies) { + my ($map,$ind,$url)=&Apache::lonnet::decode_symb($ressymb); + my $disctype = &mt('resource'); + if ($url =~ m#/bulletinboard$#) { + if ($url =~m#^adm/wrapper/adm/.*/bulletinboard$#) { + $url =~s#^adm/wrapper##; + } + $disctype = &mt('bulletin board'); + } else { + $url = '/res/'.$url; + } + if ($url =~ /\?/) { + $url .= '&symb='; + } else { + $url .= '?symb='; + } + $url .= &Apache::lonnet::escape($resource->symb()); + my $title = $resource->compTitle(); + $r->print('
'. + ($title?$title:$url).'  - '.$disctype.'
'); + $totaldiscussions++; + } else { + $r->print(' .'); + } } - } -} - -sub untiehash { - if ($hashtied) { untie %hash; } - $hashtied=0; + unless ($totaldiscussions) { + $r->print('

'.&mt('No matches found in postings').'.

'); + } + } + +# =================================================== Done going through course + $r->print(''); } # =============================== This pulls up a resource and its dependencies sub checkonthis { - my ($r,$url,$level,$title,$fulltext,@allwords)=@_; - $alreadyseen{$url}=1; + my ($r,$id,$url,$level,$title,$fulltext,$symb,@allwords)=@_; + $alreadyseen{$id}=1; + if (&Apache::loncommon::connection_aborted($r)) { return; } $r->rflush(); - my $result=&Apache::lonnet::metadata($url,'title').' '. - &Apache::lonnet::metadata($url,'subject').' '. - &Apache::lonnet::metadata($url,'abstract').' '. - &Apache::lonnet::metadata($url,'keywords'); - if (($url) && ($fulltext)) { - $result.=&Apache::lonnet::ssi_body($url); + + my $result=$title.' '; + if ($env{'request.role.adv'} || !$hash{'encrypted_'.$id}) { + $result.=&Apache::lonnet::metadata($url,'title').' '. + &Apache::lonnet::metadata($url,'subject').' '. + &Apache::lonnet::metadata($url,'abstract').' '. + &Apache::lonnet::metadata($url,'keywords'); + } + my ($extension)=($url=~/\.(\w+)$/); + if (&Apache::loncommon::fileembstyle($extension) eq 'ssi' && + ($url) && ($fulltext)) { + $result.=&Apache::lonnet::ssi_body($url.'?symb='.&Apache::lonnet::escape($symb)); } $result=~s/\s+/ /gs; - my $applies=0; - foreach (@allwords) { - if ($_=~/\w/) { - if ($result=~/$_/si) { - $applies++; - } - } - } + my $applies = 0; + $applies = &checkwords($result,$applies,@allwords); # Does this resource apply? if ($applies) { $r->print('
'); for (my $i=0;$i<=$level*5;$i++) { $r->print(' '); } - $r->print(''. - ($title?$title:$url).'
'); + my $href=$url; + if ($hash{'encrypted_'.$id} && !$env{'request.role.adv'}) { + $href=&Apache::lonenc::encrypted($href) + .'?symb='.&Apache::lonenc::encrypted($symb); + } else { + $href.='?symb='.&Apache::lonnet::escape($symb); + } + $r->print(''.($title?$title:$url). + '
'); $totalfound++; } elsif ($fulltext) { $r->print(' .'); @@ -503,12 +636,43 @@ sub checkonthis { my $dependencies= &Apache::lonnet::metadata($url,'dependencies'); foreach (split(/\,/,$dependencies)) { - if (($_=~/^\/res\//) && (!$alreadyseen{$_})) { - &checkonthis($r,$_,$level+1,'',$fulltext,@allwords); + if (($_=~/^\/res\//) && (!$alreadyseen{$id})) { + &checkonthis($r,$id,$_,$level+1,'',$fulltext,undef,@allwords); } } } +sub checkwords { + my ($result,$applies,@allwords) = @_; + foreach (@allwords) { + if ($_=~/\w/) { + if ($result=~/$_/si) { + $applies++; + } + } + } + return $applies; +} + +sub untiehash { + if (tied(%hash)) { + untie(%hash); + } +} + +} # End of course search scoping + +sub search_html_header { + my $html=&Apache::lonxml::xmlbegin(); + my $Str = < +The LearningOnline Network with CAPA + +ENDHEADER + return $Str; +} + ###################################################################### ###################################################################### @@ -516,86 +680,92 @@ sub checkonthis { =item &print_basic_search_form() -Returns a scalar which holds html for the basic search form. +Prints the form for the basic search. Sorry the name is so cryptic. =cut ###################################################################### ###################################################################### - -sub print_basic_search_form{ - my ($r,$closebutton) = @_; - my $bodytag=&Apache::loncommon::bodytag('Search'); - my $scrout=<<"ENDDOCUMENT"; - - -The LearningOnline Network with CAPA - - -$bodytag -ENDDOCUMENT -if (&Apache::lonnet::allowed('bre',$ENV{'request.role.domain'})) { - $scrout.=(<Catalog Search -
- -$hidden_fields -

-Enter terms or phrases separated by AND, OR, or NOT -then press SEARCH below. -

-

- - - - -
-ENDDOCUMENT - $scrout.=' '.&simpletextfield('basicexp',$ENV{'form.basicexp'},40). - ' '; - my $relatedcheckbox = &simplecheckbox('related',$ENV{'form.related'}); - my $domain = $r->dir_config('lonDefDomain'); - my $domaincheckbox = &simplecheckbox('domains',$domain); - my $srch=&mt('Search'); - my $header=&mt('Advanced Search'); - $scrout.=<$header
$relatedcheckbox use related words$domaincheckbox only search domain $domain
-

-

  -$closebutton -END - $scrout.=&selectbox('View','viewselect', - $ENV{'form.viewselect'}, - undef,undef,undef, - sort(keys(%Views))); - $scrout.=&selectbox('Per page','show', - $ENV{'form.show'}, - undef,undef,undef, - (10,20,50,100)); - $scrout.=< - -ENDDOCUMENT +sub print_basic_search_form { + my ($r,$closebutton,$hidden_fields) = @_; + my $result = ($env{'form.catalogmode'} ne 'groupsearch'); + my $bodytag=&Apache::loncommon::bodytag('Search'). + &Apache::lonhtmlcommon::breadcrumbs(undef,'Searching','Search_Basic', + undef,undef, + $env{'form.catalogmode'} ne 'groupsearch'); + my $scrout = &search_html_header().$bodytag; + if (&Apache::lonnet::allowed('bre',$env{'request.role.domain'})) { + # Define interface components + my $userelatedwords= ''; + my $onlysearchdomain=''; + my $adv_search_link = + ''.&mt('Advanced Search').''; + # + $scrout.='

'. + ''. + $hidden_fields; + # + $scrout .= '
'.$/; + if ($env{'request.course.id'}) { + $scrout .= '

'.&mt('LON-CAPA Catalog Search').'

'; + } else { + # No need to tell them they are searching + $scrout.= ('
'x2); + } + $scrout.=''. + ''. + ''. + ''.$/; + # + $scrout .= ''.$/; + $scrout .= '
'. + &Apache::lonhtmlcommon::textbox + ('basicexp', + &HTML::Entities::encode($env{'form.basicexp'},'<>&"'),50 + ). + '
'. + ''.&searchhelp().''.'
'. + ''.(' 'x3).$adv_search_link.''.'
'. + ''.(' 'x1).$userelatedwords.''.'
'. + ''.(' 'x1).$onlysearchdomain.''.'
'. + '
'. + ''. + ''. + (' 'x2).$closebutton.(' 'x2). + &viewoptions(). + ''. + '
'.$/.'
'.'
'; } - if ($ENV{'request.course.id'}) { - my %lt=&Apache::lonlocal::texthash( - 'srch' => 'Search', + if ($env{'request.course.id'}) { + my %lt=&Apache::lonlocal::texthash('srch' => 'Search', 'header' => 'Course Search', 'note' => 'Enter terms or phrases, then press "Search" below', + 'use' => 'use related words', + 'full' =>'fulltext search (time consuming)', + 'disc' => 'search discussion postings (resources and bulletin boards)', ); $scrout.=(< +

$lt{'header'}

-
$hidden_fields

@@ -606,18 +776,27 @@ $lt{'note'}. ENDCOURSESEARCH $scrout.=' '. - &simpletextfield('courseexp',$ENV{'form.courseexp'},40); + &Apache::lonhtmlcommon::textbox('courseexp', + $env{'form.courseexp'},40); my $crscheckbox = - &simplecheckbox('crsfulltext',$ENV{'form.crsfulltext'}); + &Apache::lonhtmlcommon::checkbox('crsfulltext', + $env{'form.crsfulltext'}); my $relcheckbox = - &simplecheckbox('crsrelated',$ENV{'form.crsrelated'}); + &Apache::lonhtmlcommon::checkbox('crsrelated', + $env{'form.crsrelated'}); + my $discheckbox = + &Apache::lonhtmlcommon::checkbox('crsdiscuss', + $env{'form.crsrelated'}); $scrout.=(< -$relcheckbox use related words -$crscheckbox fulltext search (time consuming) + + +

 

+
+ ENDENDCOURSE } $scrout.=(< - - + my ($r,$closebutton,$hidden_fields) = @_; + my $bodytag=&Apache::loncommon::bodytag('Advanced Catalog Search'). + &Apache::lonhtmlcommon::breadcrumbs(undef,'Searching', + 'Search_Advanced', + undef,undef, + $env{'form.catalogmode'} ne 'groupsearch'); + my %lt=&Apache::lonlocal::texthash('srch' => 'Search', + 'reset' => 'Reset', + 'help' => 'Help'); + my $advanced_buttons=<<"END"; + + $closebutton - -

END - if (!defined($ENV{'form.viewselect'})) { - $ENV{'form.viewselect'} ="Detailed Citation View"; - } - my $bodytag=&Apache::loncommon::bodytag('Advanced Catalog Search'); - my $scrout=<<"ENDHEADER"; - - -The LearningOnline Network with CAPA - - + my $scrout=&search_html_header(); + $scrout .= <<"ENDHEADER"; $bodytag -Enter terms or phrases separated by search operators -such as AND, OR, or NOT.
-
+ +

$advanced_buttons -$hidden_fields - - - -\n"; - $scrout.=&searchphrasefield_with_related('title', 'title' , - $ENV{'form.title'}); - $scrout.=&searchphrasefield('author', 'author' ,$ENV{'form.author'}); - $scrout.=&searchphrasefield_with_related('subject', 'subject' , - $ENV{'form.subject'}); - $scrout.=&searchphrasefield_with_related('keywords','keywords', - $ENV{'form.keywords'}); - $scrout.=&searchphrasefield('URL', 'url' ,$ENV{'form.url'}); - $scrout.=&searchphrasefield_with_related('notes', 'notes' , - $ENV{'form.notes'}); - $scrout.=&searchphrasefield_with_related('abstract','abstract', - $ENV{'form.abstract'}); - # Hack - an empty table row. - $scrout.="\n"; - $scrout.=&searchphrasefield('file
extension','extension', - $ENV{'form.extension'}); - $scrout.="\n"; - $scrout.=&searchphrasefield('publisher
owner','owner', - $ENV{'form.owner'}); - $scrout.="
VIEW: ENDHEADER - $scrout.=&selectbox(undef,'viewselect', - $ENV{'form.viewselect'}, - undef,undef,undef, - sort(keys(%Views))); - $scrout.=' '; - $scrout.=&selectbox(undef,'show', - $ENV{'form.show'}, - undef,undef,undef, - (10,20,50,100)); - $scrout.=' '. - ''.&mt('Per Page').''; - $scrout.="".&mt('Related').'
'.&mt('Words')."
   
   
\n"; - $ENV{'form.category'}='any' if (! defined($ENV{'form.category'})); - $scrout.=&selectbox('File Category','category', - $ENV{'form.category'}, - 'any','Any category', - undef, - (&Apache::loncommon::filecategories())); - $ENV{'form.language'}='any' unless length($ENV{'form.language'}); - #---------------------------------------------------------------- - # Allow restriction to multiple domains. - # I make the crazy assumption that there will never be a domain 'any'. - # - $ENV{'form.domains'} = 'any' if (! exists($ENV{'form.domains'})); - my @allowed_domains = (ref($ENV{'form.domains'}) ? @{$ENV{'form.domains'}} - : ($ENV{'form.domains'}) ); - my %domain_hash = (); - foreach (@allowed_domains) { - $domain_hash{$_}++; + $scrout.=(' 'x2).&viewoptions().'

'.$hidden_fields. + ''; + my %fields=&Apache::lonmeta::fieldnames(); + # + $scrout .= '

'.&mt('Standard Metadata').'

'; + $scrout .= "\n"; + $scrout .= '\n"; + my %related_word_search = + ('title' => 1, + 'author' => 0, + 'owner' => 0, + 'authorspace' => 0, + 'modifyinguser'=> 0, + 'keywords' => 1, + 'notes' => 1, + 'abstract' => 1, + 'standards'=> 1, + 'mime' => 1, + 'subject' => 1, + ); + # + foreach my $field ('title','author','subject','owner','authorspace', + 'modifyinguser','keywords','notes','abstract', + 'standards','mime') { + $scrout.=''.$/; } - my @domains =&Apache::loncommon::get_domains(); - # adjust the size of the select box - my $size = 4; - $size = (scalar @domains < ($size - 1) ? scalar @domains + 1 : $size); - $scrout.="\n".''. - 'DOMAINS TO BE SEARCHED
'. - '\n"; - #---------------------------------------------------------------- - $scrout.=&selectbox('Limit by language','language', - $ENV{'form.language'},'any','Any Language', - \&Apache::loncommon::languagedescription, - (&Apache::loncommon::languageids), - ); -# ------------------------------------------------ Compute date selection boxes - $scrout.=< -LIMIT BY CREATION DATE RANGE: - -
-between: -CREATIONDATESTART - $scrout.=&dateboxes('creationdatestart',1,1,1976, - $ENV{'form.creationdatestart_month'}, - $ENV{'form.creationdatestart_day'}, - $ENV{'form.creationdatestart_year'}, - ); - $scrout.="and:\n"; - $scrout.=&dateboxes('creationdateend',12,31,2051, - $ENV{'form.creationdateend_month'}, - $ENV{'form.creationdateend_day'}, - $ENV{'form.creationdateend_year'}, - ); - $scrout.="

"; - $scrout.=< -LIMIT BY LAST REVISION DATE RANGE: - -
between: -LASTREVISIONDATESTART - $scrout.=&dateboxes('lastrevisiondatestart',1,1,1976, - $ENV{'form.lastrevisiondatestart_month'}, - $ENV{'form.lastrevisiondatestart_day'}, - $ENV{'form.lastrevisiondatestart_year'}, - ); - $scrout.=< -#LIMIT BY SPECIAL METADATA FIELDS: -# -#For resource-specific metadata, enter in an expression in the form of -#key=value separated by operators such as AND, OR or NOT.
-#Example: grandmother=75 OR grandfather=85 -#
-#CUSTOMMETADATA -# $scrout.=&simpletextfield('custommetadata',$ENV{'form.custommetadata'}); -# $scrout.=< -#SHOW SPECIAL METADATA FIELDS: -# -#Enter in a space-separated list of special metadata fields to show -#in a fielded listing for each record result. -#
-#CUSTOMSHOW -# $scrout.=&simpletextfield('customshow',$ENV{'form.customshow'}); + foreach my $field ('lowestgradelevel','highestgradelevel') { + $scrout.='
'. + ''. + ''.$/; + } + $scrout.=''.$/; + $scrout.=''.$/; + # + # Misc metadata + $scrout.=''.$/; + $scrout.=''; + $scrout .= "
 '. + (' 'x2).&searchhelp()."
'.&titlefield($fields{$field}).''. + &Apache::lonmeta::prettyinput($field, + $env{'form.'.$field}, + $field, + 'advsearch', + $related_word_search{$field}, + '', + $env{'form.'.$field.'_related'}, + 50); + if ($related_word_search{$field}) { + $scrout .= 'related words'; + } else { + $scrout .= ' '; + } + $scrout .= '
'.&titlefield($fields{$field}).''. + &Apache::lonmeta::prettyinput($field, + $env{'form.'.$field}, + $field, + 'advsearch', + 0). + '
'. + &titlefield(&mt('MIME Type Category')).''. + &Apache::loncommon::filecategoryselect('category', + $env{'form.category'}). + '
'. + &titlefield(&mt('Domains')).''. + &Apache::loncommon::domain_select('domains', + $env{'form.domains'},1). + '
'. + &titlefield(&mt('Copyright/Distribution')).''. + &Apache::lonmeta::selectbox('copyright', + $env{'form.copyright'}, + \&Apache::loncommon::copyrightdescription, + ( undef, + &Apache::loncommon::copyrightids) + ).'
'. + &titlefield(&mt('Language')).''. + &Apache::lonmeta::selectbox('language', + $env{'form.language'}, + \&Apache::loncommon::languagedescription, + ('any',&Apache::loncommon::languageids) + ).'
\n"; + # + # Dynamic metadata + $scrout .= '

'.&mt('Problem Statistics').'

'; + $scrout .= "\n"; + $scrout .= ''. + ''."\n"; + foreach my $statistic + ({ name=>'count', + description=>'Network-wide number of accesses (hits)',}, + { name=>'stdno', + description=> + 'Total number of students who have worked on this problem',}, + { name => 'avetries', + description=>'Average number of tries till solved',}, + { name => 'difficulty', + description=>'Degree of difficulty',}, + { name => 'disc', + description=>'Degree of discrimination'}) { + $scrout .= ''.$/; + } + $scrout .= "
 '.&mt('Minimum').''.&mt('Maximum').'
'. + &titlefield(&mt($statistic->{'description'})). + ''. + ''. + ''. + ''. + '
\n"; + $scrout .= '

'.&mt('Evaluation Data').'

'; + $scrout .= "\n"; + $scrout .= ''. + ''."\n"; + foreach my $evaluation + ( { name => 'clear', + description => 'Material presented in clear way'}, + { name =>'depth', + description => 'Material covered with sufficient depth'}, + { name => 'helpful', + description => 'Material is helpful'}, + { name => 'correct', + description => 'Material appears to be correct'}, + { name => 'technical', + description => 'Resource is technically correct'}){ + $scrout .= ''.$/; + } + $scrout .= "
 '.&mt('Minimum').''.&mt('Maximum').'
'. + &titlefield(&mt($evaluation->{'description'})). + ''. + ''. + ''. + ''. + '
\n"; + # + # Creation/Modification date limits + $scrout .= '

'.&mt('Creation and Modification dates').'

'; + $scrout .= "\n\n"; + my $cafter = + &Apache::lonhtmlcommon::date_setter('advsearch', # formname + 'creationdate1', # fieldname + 0, # current value + '', # special + 1, # includeempty + '', # state + 1, # no_hh_mm_ss + ); + my $cbefore = + &Apache::lonhtmlcommon::date_setter('advsearch', # formname + 'creationdate2', # fieldname + 0, # current value + '', # special + 1, # includeempty + '', # state + 1, # no_hh_mm_ss + ); + $scrout .= &mt(''. + ''. + ''. + '',$cafter,$cbefore); + my $lafter = + &Apache::lonhtmlcommon::date_setter('advsearch', + 'revisiondate1', + 0, # current value + '', # special + 1, # includeempty + '', # state + 1, # no_hh_mm_ss + ); + my $lbefore = + &Apache::lonhtmlcommon::date_setter('advsearch', + 'revisiondate2', + 0, # current value + '', # special + 1, # includeempty + '', # state + 1, # no_hh_mm_ss + ); + $scrout .= &mt(''. + ''. + ''. + '',$lafter,$lbefore); + $scrout.="
Created between[_1]
and [_2]
Last modified between [_1]
and[_2]
\n"; $scrout.=< @@ -831,7 +1040,106 @@ ENDDOCUMENT =pod -=item &get_persistent_form_data +=item &titlefield() + +Inputs: title text + +Outputs: titletext with font wrapper + +=cut + +###################################################################### +###################################################################### +sub titlefield { + my $title=shift; + return $title; +} + +###################################################################### +###################################################################### + +=pod + +=item viewoptiontext() + +Inputs: codename for view option + +Outputs: displayed text + +=cut + +###################################################################### +###################################################################### +sub viewoptiontext { + my $code=shift; + my %desc=&Apache::lonlocal::texthash + ('detailed' => "Detailed Citation View", + 'xml' => 'XML/SGML', + 'compact' => 'Compact View', + 'fielded' => 'Fielded Format', + 'summary' => 'Summary View'); + return $desc{$code}; +} + +###################################################################### +###################################################################### + +=pod + +=item viewoptions() + +Inputs: none + +Outputs: text for box with view options + +=cut + +###################################################################### +###################################################################### +sub viewoptions { + my $scrout; + if (! defined($env{'form.viewselect'})) { + $env{'form.viewselect'}='detailed'; + } + $scrout.=&Apache::lonmeta::selectbox('viewselect', + $env{'form.viewselect'}, + \&viewoptiontext, + sort(keys(%Views))); + $scrout.= '  '; + my $countselect = &Apache::lonmeta::selectbox('show', + $env{'form.show'}, + undef, + (10,20,50,100,1000,10000)); + $scrout .= (' 'x2).&mt('[_1] Records per Page',$countselect). + ''.$/; + return $scrout; +} + +###################################################################### +###################################################################### + +=pod + +=item searchhelp() + +Inputs: none + +Outputs: return little blurb on how to enter searches + +=cut + +###################################################################### +###################################################################### +sub searchhelp { + return &mt('Enter words and quoted phrases'); +} + +###################################################################### +###################################################################### + +=pod + +=item &get_persistent_form_data() Inputs: filename of database @@ -839,7 +1147,7 @@ Outputs: returns undef on database error This function is the reverse of &make_persistent() for form data. Retrieve persistent data from %persistent_db. Retrieved items will have their -values unescaped. If a form value already exists in $ENV, it will not be +values unescaped. If a form value already exists in $env, it will not be overwritten. Form values that are array references may have values appended to them. @@ -861,20 +1169,20 @@ sub get_persistent_form_data { next if ($name !~ /^form./); # Kludgification begins! if ($name eq 'form.domains' && - $ENV{'form.searchmode'} eq 'basic' && - $ENV{'form.phase'} ne 'disp_basic') { + $env{'form.searchmode'} eq 'basic' && + $env{'form.phase'} ne 'disp_basic') { next; } # End kludge (hopefully) - next if (exists($ENV{$name})); + next if (exists($env{$name})); my @values = map { &Apache::lonnet::unescape($_); } split(',',$persistent_db{$name}); next if (@values <1); if ($arrays_allowed{$name}) { - $ENV{$name} = [@values]; + $env{$name} = [@values]; } else { - $ENV{$name} = $values[0] if ($values[0]); + $env{$name} = $values[0] if ($values[0]); } } untie (%persistent_db); @@ -886,7 +1194,7 @@ sub get_persistent_form_data { =pod -=item &get_persistent_data +=item &get_persistent_data() Inputs: filename of database, ref to array of values to recover. @@ -937,7 +1245,7 @@ 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 seperated string. +elements escaped and concatenated in a comma separated string. =cut @@ -969,7 +1277,7 @@ Inputs: filename of persistent database. Store most form 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 seperated string. +elements escaped and concatenated in a comma separated string. =cut @@ -979,296 +1287,14 @@ sub make_form_data_persistent { my $r = shift; my $filename = shift; my %save; - foreach (keys(%ENV)) { + foreach (keys(%env)) { next if (!/^form/ || /submit/); - $save{$_} = $ENV{$_}; + $save{$_} = $env{$_}; } return &make_persistent(\%save,$filename); } ###################################################################### -# HTML form building functions # -###################################################################### - -=pod - -=item HTML form building functions - -=over 4 - -=cut - -############################################### -############################################### - -=pod - -=item &simpletextfield() - -Inputs: $name,$value,$size - -Returns a text input field with the given name, value, and size. -If size is not specified, a value of 20 is used. - -=cut - -############################################### -############################################### - -sub simpletextfield { - my ($name,$value,$size)=@_; - $size = 20 if (! defined($size)); - return ''; -} - -############################################### -############################################### - -=pod - -=item &simplecheckbox() - -Inputs: $name,$value - -Returns a simple check box with the given $name. -If $value eq 'on' the box is checked. - -=cut - -############################################### -############################################### - -sub simplecheckbox{ - my ($name,$value,$checked)=@_; - $checked="checked" if ($value eq 'on'); - return ''; -} - -############################################### -############################################### - -=pod - -=item &fieldtitle() - -Input: $title - -Returns a scalar with html which will display $title as a search -field heading. - -=cut - -############################################### -############################################### - -sub fieldtitle { - my $title = uc(&mt(shift())); - return ''.$title. - ': '; -} - -############################################### -############################################### - -=pod - -=item &searchphrasefield() - -Inputs: $title,$name,$value - -Returns html for a title line and an input field for entering search terms. -The entry field (which is where the $name and $value are used) is a 50 column -simpletextfield. The html returned is for a row in a three column table. - -=cut - -############################################### -############################################### - -sub searchphrasefield { - my ($title,$name,$value)=@_; - return ''.&fieldtitle($title).''. - &simpletextfield($name,$value,50)." \n"; -} - -############################################### -############################################### - -=pod - -=item &searchphrasefield_with_related() - -Inputs: $title,$name,$value - -Returns html for a title line and an input field for entering search terms -and a check box for 'related words'. The entry field (which is where the -$name and $value are used) is a 50 column simpletextfield. The name of -the related words checkbox is "$name_related". - -=cut - -############################################### -############################################### - -sub searchphrasefield_with_related { - my ($title,$name,$value)=@_; - return ''.&fieldtitle($title).''. - &simpletextfield($name,$value,50).' '. - &simplecheckbox($name.'_related',$ENV{'form.'.$name.'_related'}). - " \n"; -} - -############################################### -############################################### - -=pod - -=item &dateboxes() - -Returns html selection form elements for the specification of -the day, month, and year. - -=cut - -############################################### -############################################### - -sub dateboxes { - my ($name,$defaultmonth,$defaultday,$defaultyear, - $currentmonth,$currentday,$currentyear)=@_; - ($defaultmonth,$defaultday,$defaultyear)=('','',''); - # - # Day - my $day=< - -END - for (my $i = 1; $i<=31; $i++) { - $day.="\n"; - } - $day.="\n"; - $day=~s/(\"$currentday\")/$1 SELECTED/ if length($currentday); - # - # Month - my $month=< - -END - my $i = 1; - foreach (qw/January February March April May June - July August September October November December /){ - $month .="\n"; - $i++; - } - $month.="\n"; - $month=~s/(\"$currentmonth\")/$1 SELECTED/ if length($currentmonth); - # - # Year (obviously) - my $year=< - -END - my $maxyear = 2051; - for (my $i = 1976; $i<=$maxyear; $i++) { - $year.="\n"; - } - $year.="\n"; - $year=~s/(\"$currentyear\")/$1 SELECTED/ if length($currentyear); - return "$month$day$year"; -} - -############################################### -############################################### - -=pod - -=item &selectbox() - -Returns a scalar containing an html tag. - -=item $default - -The default value of the form. Can be $anyvalue, or in @idlist. - -=item $anyvalue - -The