--- loncom/interface/lonsearchcat.pm 2002/06/25 15:08:59 1.129 +++ loncom/interface/lonsearchcat.pm 2003/10/24 21:09:24 1.191 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Search Catalog # -# $Id: lonsearchcat.pm,v 1.129 2002/06/25 15:08:59 matthew Exp $ +# $Id: lonsearchcat.pm,v 1.191 2003/10/24 21:09:24 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,14 +25,6 @@ # # http://www.lon-capa.org/ # -# YEAR=2001 -# 3/8, 3/12, 3/13, 3/14, 3/15, 3/19 Scott Harrison -# 3/20, 3/21, 3/22, 3/26, 3/27, 4/2, 8/15, 8/24, 8/25 Scott Harrison -# 10/12,10/14,10/15,10/16,11/28,11/29,12/10,12/12,12/16 Scott Harrison -# YEAR=2002 -# 1/17 Scott Harrison -# 6/17 Matthew Hall -# ############################################################################### ############################################################################### @@ -40,7 +32,7 @@ =head1 NAME -lonsearchcat +lonsearchcat - LONCAPA Search Interface =head1 SYNOPSIS @@ -82,13 +74,15 @@ package Apache::lonsearchcat; # ------------------------------------------------- modules used by this module use strict; -use Apache::Constants qw(:common); +use Apache::Constants qw(:common :http); use Apache::lonnet(); use Apache::File(); use CGI qw(:standard); use Text::Query; use GDBM_File; use Apache::loncommon(); +use Apache::lonmysql(); +use Apache::lonlocal; # ---------------------------------------- variables used throughout the module @@ -101,23 +95,31 @@ use Apache::loncommon(); =over 4 -=item $closebutton - -button that closes the search window - =item $importbutton -button to take the selecte results and go to group sorting +button to take the select results and go to group sorting -=item %hash +=item %groupsearch_db -The ubiquitous database hash +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 @@ -126,13 +128,29 @@ used in &handler() and is also used in & ###################################################################### # -- dynamically rendered interface components -my $closebutton; # button that closes the search window my $importbutton; # button to take the selected results and go to group sorting # -- miscellaneous variables -my %hash; # database hash +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; + ###################################################################### ###################################################################### @@ -160,80 +178,232 @@ string that holds portions of the screen ###################################################################### sub handler { my $r = shift; - untie %hash; + # - $r->content_type('text/html'); + 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. + # + &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; return OK if $r->header_only; - - my $domain = $r->dir_config('lonDefDomain'); - $diropendb= "/home/httpd/perl/tmp/".&Apache::lonnet::escape($domain). - "\_".&Apache::lonnet::escape($ENV{'user.name'})."_searchcat.db"; - + ## + ## Prevent caching of the search interface window. Hopefully this means + ## we will get the launch=1 passed in a little more. + &Apache::loncommon::no_cache($r); + ## + ## Pick up form fields passed in the links. + ## &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, - ['catalogmode','launch','acts','mode','form','element', - 'reqinterface']); + ['catalogmode','launch','acts','mode','form','element','pause', + 'phase','persistent_db_id','table','start','show', + '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'})) { + sleep(1); + 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"; + # + # 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; + } + $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'; + ## + 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 +$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; + } + } ## - ## Clear out old values from database + ## Clear out old values from groupsearch database ## - if ($ENV{'form.launch'} eq '1') { - if (tie(%hash,'GDBM_File',$diropendb,&GDBM_WRCREAT,0640)) { + untie %groupsearch_db if (tied(%groupsearch_db)); + 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 %hash; + untie %groupsearch_db; + delete($ENV{'form.cleargroupsort'}); } else { - $r->print('Unable to tie hash to db '. - 'file'); + # This is a stupid error to give to the user. + # It really tells them nothing. + $r->print(''.$bodytag. + 'Unable to tie hash to db file'); return OK; } } ## - ## Produce some output, so people know it is working + ## Configure hidden fields ## - $r->print("\n"); - $r->rflush; + $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.titleelement'})) { + $hidden_fields .= ''."\n"; + } + if (exists($ENV{'form.mode'})) { + $hidden_fields .= ''."\n"; + } ## ## Configure dynamic components of interface ## - my $hidden; # Holds 'hidden' html forms if ($ENV{'form.catalogmode'} eq 'interactive') { - $hidden="". - "\n"; - $closebutton=""."\n"; + $closebutton=" -END - $closebutton=< -END + $closebutton=" END + } else { + $closebutton = ''; + $importbutton = ''; + } + ## + ## Sanity checks on form elements + ## + if (!defined($ENV{'form.viewselect'})) { + if (($ENV{'form.catalogmode'} eq 'groupsearch') || + ($ENV{'form.catalogmode'} eq 'interactive')) { + $ENV{'form.viewselect'} ="Compact View"; + } else { + $ENV{'form.viewselect'} ="Detailed Citation View"; + } } - $hidden .= < - - - -END - ## - ## What are we doing? - ## - if ($ENV{'form.basicsubmit'} eq 'SEARCH') { - # Perform basic search and give results - return &basicsearch($r,\%ENV,$hidden); - } elsif ($ENV{'form.advancedsubmit'} eq 'SEARCH') { - # Perform advanced search and give results - return &advancedsearch($r,\%ENV,$hidden); - } elsif ($ENV{'form.reqinterface'} eq 'advanced') { - # Output the advanced interface - $r->print(&advanced_search_form($closebutton,$hidden)); - return OK; - } else { - # Output normal search interface - $r->print(&basic_search_form($closebutton,$hidden)); + $ENV{'form.phase'} = 'disp_basic' if (! exists($ENV{'form.phase'})); + $ENV{'form.show'} = 20 if (! exists($ENV{'form.show'})); + ## + ## 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)$/) { + 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') { + &print_sort_form($r,$pretty_string); + } elsif ($ENV{'form.phase'} eq 'run_search') { + &run_search($r,$query,$customquery,$customshow, + $libraries,$pretty_string); + } + } 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'; + } + # Set up table + if (! defined(&create_results_table())) { + $r->print(<Search Error +$bodytag +Unable to create table in which to store search results. +The search has been aborted. + + +END + return OK; + } + delete($ENV{'form.launch'}); + if (! &make_form_data_persistent($r,$persistent_db_file)) { + $r->print(<Search Error +$bodytag +Unable to properly store search information. The search has been aborted. + + +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); } return OK; } @@ -241,9 +411,114 @@ END ###################################################################### ###################################################################### +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 @New_Words; + if ($ENV{'form.crsrelated'}) { + ($search_string,@New_Words) = &related_version($ENV{'form.courseexp'}); + if (@New_Words) { + $pretty_search_string .= " with related words: @New_Words."; + } else { + $pretty_search_string .= " with no related words."; + } + } + my $fulltext=$ENV{'form.crsfulltext'}; + my @allwords=($search_string,@New_Words); + $totalfound=0; + $r->print('LON-CAPA Course Search'. + $bodytag.$pretty_search_string); + $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(); + unless ($totalfound) { + $r->print('

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

'); + } +# =================================================== 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; + } + } +} + +sub untiehash { + if ($hashtied) { untie %hash; } + $hashtied=0; +} + +# =============================== This pulls up a resource and its dependencies + +sub checkonthis { + my ($r,$url,$level,$title,$fulltext,@allwords)=@_; + $alreadyseen{$url}=1; + $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); + } + $result=~s/\s+/ /gs; + my $applies=0; + foreach (@allwords) { + if ($_=~/\w/) { + if ($result=~/$_/si) { + $applies++; + } + } + } +# Does this resource apply? + if ($applies) { + $r->print('
'); + for (my $i=0;$i<=$level*5;$i++) { + $r->print(' '); + } + $r->print(''. + ($title?$title:$url).'
'); + $totalfound++; + } elsif ($fulltext) { + $r->print(' .'); + } + $r->rflush(); +# Check also the dependencies of this one + my $dependencies= + &Apache::lonnet::metadata($url,'dependencies'); + foreach (split(/\,/,$dependencies)) { + if (($_=~/^\/res\//) && (!$alreadyseen{$_})) { + &checkonthis($r,$_,$level+1,'',$fulltext,@allwords); + } + } +} + +###################################################################### +###################################################################### + =pod -=item &basic_search_form() +=item &print_basic_search_form() Returns a scalar which holds html for the basic search form. @@ -252,8 +527,9 @@ Returns a scalar which holds html for th ###################################################################### ###################################################################### -sub basic_search_form{ - my ($closebutton,$hidden) = @_; +sub print_basic_search_form{ + my ($r,$closebutton) = @_; + my $bodytag=&Apache::loncommon::bodytag('Search'); my $scrout=<<"ENDDOCUMENT"; @@ -266,14 +542,16 @@ sub basic_search_form{ } - - -

Search Catalog

+$bodytag +ENDDOCUMENT +if (&Apache::lonnet::allowed('bre',$ENV{'request.role.domain'})) { + $scrout.=(<Catalog Search
-$hidden -

Basic Search

+ +$hidden_fields

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

@@ -282,30 +560,76 @@ then press SEARCH below. ENDDOCUMENT $scrout.=' '.&simpletextfield('basicexp',$ENV{'form.basicexp'},40). ' '; -# $scrout.=&simplecheckbox('allversions',$ENV{'form.allversions'}); -# $scrout.='Search historic archives'; - $scrout.=<Advanced Search + 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 + } + if ($ENV{'request.course.id'}) { + my %lt=&Apache::lonlocal::texthash( + 'srch' => 'Search', + 'header' => 'Course Search', + 'note' => 'Enter terms or phrases, then press "Search" below', + ); + $scrout.=(< +

$lt{'header'}

+
+ +$hidden_fields +

+$lt{'note'}. +

+

+ + + + +
+ENDCOURSESEARCH + $scrout.=' '. + &simpletextfield('courseexp',$ENV{'form.courseexp'},40); + my $crscheckbox = + &simplecheckbox('crsfulltext',$ENV{'form.crsfulltext'}); + my $relcheckbox = + &simplecheckbox('crsrelated',$ENV{'form.crsrelated'}); + $scrout.=(<
$relcheckbox use related words
$crscheckbox fulltext search (time consuming)

+

+ENDENDCOURSE + } + $scrout.=(< ENDDOCUMENT - return $scrout; + $r->print($scrout); + return; } ###################################################################### ###################################################################### @@ -321,25 +645,20 @@ Returns a scalar which holds html for th ###################################################################### ###################################################################### -sub advanced_search_form{ - my ($closebutton,$hidden) = @_; +sub print_advanced_search_form{ + my ($r,$closebutton) = @_; 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"; @@ -352,50 +671,86 @@ END } - - -

Advanced Catalog Search

-
-Enter terms or quoted phrases separated by search operators +$bodytag +Enter terms or phrases separated by search operators such as AND, OR, or NOT.
+ $advanced_buttons -$hidden +$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','mime', - $ENV{'form.mime'}); - $scrout.="\n"; + $scrout.="\n"; + $scrout.=&searchphrasefield('file
extension','extension', + $ENV{'form.extension'}); + $scrout.="\n"; $scrout.=&searchphrasefield('publisher
owner','owner', $ENV{'form.owner'}); $scrout.="
VIEW: ENDHEADER - $scrout.=&searchphrasefield('title','title', - $ENV{'form.title'}); - $scrout.=&searchphrasefield('author','author', - $ENV{'form.author'}); - $scrout.=&searchphrasefield('subject','subject', - $ENV{'form.subject'}); - $scrout.=&searchphrasefield('keywords','keywords', - $ENV{'form.keywords'}); - $scrout.=&searchphrasefield('URL','url', - $ENV{'form.url'}); -# $scrout.=&searchphrasefield('Limit by version','version', -# $ENV{'form.version'}); - $scrout.=&searchphrasefield('notes','notes', - $ENV{'form.notes'}); - $scrout.=&searchphrasefield('abstract','abstract', - $ENV{'form.abstract'}); + $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.mime'}='any' unless length($ENV{'form.mime'}); -# $scrout.=&selectbox('Limit by MIME type','mime', -# $ENV{'form.mime'}, -# 'any','Any type', -# \&{Apache::loncommon::filedescriptionex}, -# (&Apache::loncommon::fileextensions)); + $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{$_}++; + } + 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::languagedescription, (&Apache::loncommon::languageids), ); # ------------------------------------------------ Compute date selection boxes @@ -442,36 +797,37 @@ LASTREVISIONDATEEND $scrout.=&selectbox('Limit by copyright/distribution','copyright', $ENV{'form.copyright'}, 'any','Any copyright/distribution', - \&{Apache::loncommon::copyrightdescription}, + \&Apache::loncommon::copyrightdescription, (&Apache::loncommon::copyrightids), ); # ------------------------------------------- Compute customized metadata field - $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'}); +# $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'}); $scrout.=< ENDDOCUMENT - return $scrout; + $r->print($scrout); + return; } ###################################################################### @@ -479,43 +835,178 @@ ENDDOCUMENT =pod -=item &make_persistent() +=item &get_persistent_form_data -Returns a scalar which holds the current ENV{'form.*'} values in -a 'hidden' html input tag. This allows search interface information -to be somewhat persistent. +Inputs: filename of database + +Outputs: returns undef on database errors. + +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 +overwritten. Form values that are array references may have values appended +to them. =cut ###################################################################### ###################################################################### +sub get_persistent_form_data { + my $filename = shift; + return 0 if (! -e $filename); + return undef if (! tie(%persistent_db,'GDBM_File',$filename, + &GDBM_READER(),0640)); + # + # These make sure we do not get array references printed out as 'values'. + my %arrays_allowed = ('form.domains'=>1); + # + # Loop through the keys, looking for 'form.' + foreach my $name (keys(%persistent_db)) { + next if ($name !~ /^form./); + # Kludgification begins! + if ($name eq 'form.domains' && + $ENV{'form.searchmode'} eq 'basic' && + $ENV{'form.phase'} ne 'disp_basic') { + next; + } + # End kludge (hopefully) + 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]; + } else { + $ENV{$name} = $values[0] if ($values[0]); + } + } + untie (%persistent_db); + return 1; +} -sub make_persistent { - my $persistent=''; - foreach (keys %ENV) { - if (/^form\./ && !/submit/) { - my $name=$_; - my $key=$name; - $ENV{$key}=~s/\'//g; # do not mess with html field syntax - $name=~s/^form\.//; - $persistent.=< -END +###################################################################### +###################################################################### + +=pod + +=item &get_persistent_data + +Inputs: filename of database, ref to array of values to recover. + +Outputs: array of values. Returns undef on error. + +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. + +=cut + +###################################################################### +###################################################################### +sub get_persistent_data { + my $filename = shift; + my @Vars = @{shift()}; + my @Values; # Return array + return undef if (! -e $filename); + return undef if (! tie(%persistent_db,'GDBM_File',$filename, + &GDBM_READER(),0640)); + foreach my $name (@Vars) { + if (! exists($persistent_db{$name})) { + push @Values, undef; + next; + } + my @values = map { + &Apache::lonnet::unescape($_); + } split(',',$persistent_db{$name}); + if (@values <= 1) { + push @Values,$values[0]; + } else { + push @Values,\@values; } } - return $persistent; + untie (%persistent_db); + return @Values; } +###################################################################### +###################################################################### + +=pod + +=item &make_persistent() + +Inputs: Hash of values to save, filename of persistent database. + +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. + +=cut + +###################################################################### +###################################################################### +sub make_persistent { + my %save = %{shift()}; + my $filename = shift; + 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 $store = join(',', map { &Apache::lonnet::escape($_); } @values ); + $persistent_db{$name} = $store; + } + untie(%persistent_db); + return 1; +} ###################################################################### ###################################################################### =pod +=item &make_form_data_persistent() + +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. + +=cut + +###################################################################### +###################################################################### +sub make_form_data_persistent { + my $r = shift; + my $filename = shift; + my %save; + foreach (keys(%ENV)) { + next if (!/^form/ || /submit/); + $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 @@ -523,6 +1014,23 @@ 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 @@ -530,91 +1038,106 @@ Inputs: $name,$value Returns a simple check box with the given $name. If $value eq 'on' the box is checked. -=item &searchphrasefield() +=cut -Inputs: $title,$name,$value +############################################### +############################################### -Returns html for a title line and an input field for entering search terms. -the instructions "Enter terms or phrases separated by search operators such -as AND, OR, or NOT." are given following the title. The entry field (which -is where the $name and $value are used) is an 80 column simpletextfield. +sub simplecheckbox{ + my ($name,$value,$checked)=@_; + $checked="checked" if ($value eq 'on'); + return ''; +} -=item &dateboxes() +############################################### +############################################### -Returns html selection form elements for the specification of -the day, month, and year. +=pod -=item &selectbox() +=item &fieldtitle() -Returns a scalar containing an html tag. +=pod -=item $default +=item &searchphrasefield() -The default value of the form. Can be $anyvalue or in @idlist. +Inputs: $title,$name,$value -=item $anyvalue +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. -The \n"; + $month .="\n"; $i++; } $month.="\n"; @@ -660,42 +1183,124 @@ END 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