# The LearningOnline Network with CAPA # Search Catalog # # $Id: lonsearchcat.pm,v 1.142 2002/07/12 21:02:27 matthew Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # 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 # ############################################################################### ############################################################################### =pod =head1 NAME lonsearchcat - LONCAPA Search Interface =head1 SYNOPSIS Search interface to LON-CAPAs digital library =head1 DESCRIPTION This module enables searching for a distributed browseable catalog. This is part of the LearningOnline Network with CAPA project 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. =head1 Internals =over 4 =cut ############################################################################### ############################################################################### ############################################################################### ## ## ## 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); use Apache::lonnet(); use Apache::File(); use CGI qw(:standard); use Text::Query; use GDBM_File; use Apache::loncommon(); # ---------------------------------------- variables used throughout the module ###################################################################### ###################################################################### =pod =item Global variables =over 4 =item $closebutton button that closes the search window =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. =back =cut ###################################################################### ###################################################################### # -- 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 %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 ); ###################################################################### ###################################################################### =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 ###################################################################### ###################################################################### sub handler { my $r = shift; untie %groupsearch_db; $r->content_type('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"; &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['catalogmode','launch','acts','mode','form','element', 'reqinterface']); ## ## Clear out old values from database ## if ($ENV{'form.launch'} eq '1') { if (tie(%groupsearch_db,'GDBM_File',$diropendb,&GDBM_WRCREAT,0640)) { &start_fresh_session(); untie %groupsearch_db; } else { $r->print('Unable to tie hash to db '. 'file'); return OK; } } ## ## Produce some output, so people know it is working ## $r->print("\n"); $r->rflush; ## ## Configure dynamic components of interface ## my $hidden; # Holds 'hidden' html forms if ($ENV{'form.catalogmode'} eq 'interactive') { $hidden="". "\n"; $closebutton=""."\n"; } elsif ($ENV{'form.catalogmode'} eq 'groupsearch') { $hidden=< END $closebutton=< END $importbutton=< END } $hidden .= &make_persistent({ "form.mode" => $ENV{'form.mode'}, "form.form" => $ENV{'form.form'}, "form.element" => $ENV{'form.element'}, "form.date" => 2 }); ## ## What are we doing? ## my $searchtype; $searchtype = 'Basic' if ($ENV{'form.basicsubmit'} eq 'SEARCH'); $searchtype = 'Advanced' if ($ENV{'form.advancedsubmit'} eq 'SEARCH'); if ($searchtype) { # We are running a search my ($query,$customquery,$customshow,$libraries) = (undef,undef,undef,undef); if ($searchtype eq 'Basic') { $query = &parse_basic_search($r); } elsif ($ENV{'form.advancedsubmit'} eq 'SEARCH') { ($query,$customquery,$customshow,$libraries) = &parse_advanced_search($r); return OK if (! defined($query)); } # Output some information to the user. $r->print(&search_results_header($searchtype)); $r->print("Sending search request to LON-CAPA servers.
\n"); $r->rflush(); # Send query statements over the network to be processed by # either the SQL database or a recursive scheme of 'grep'-like # actions (for custom metadata). my $reply=&Apache::lonnet::metadata_query($query,$customquery, $customshow,$libraries); $r->rflush(); &output_results($searchtype,$r,$reply,$hidden); } else { # # We need to get information to search on # # Set the default view if it is not already set. if (!defined($ENV{'form.viewselect'})) { $ENV{'form.viewselect'} ="Detailed Citation View"; } # Output the advanced interface if ($ENV{'form.reqinterface'} eq 'advanced') { $r->print(&advanced_search_form($closebutton,$hidden)); } else { # Output normal search interface $r->print(&basic_search_form($closebutton,$hidden)); } } return OK; } ###################################################################### ###################################################################### =pod =item &basic_search_form() Returns a scalar which holds html for the basic search form. =cut ###################################################################### ###################################################################### sub basic_search_form{ my ($closebutton,$hidden) = @_; my $scrout=<<"ENDDOCUMENT"; The LearningOnline Network with CAPA

Search Catalog

$hidden

Basic Search

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

ENDDOCUMENT $scrout.=' '.&simpletextfield('basicexp',$ENV{'form.basicexp'},40). ' '; # $scrout.=&simplecheckbox('allversions',$ENV{'form.allversions'}); # $scrout.='Search historic archives'; my $checkbox = &simplecheckbox('related',$ENV{'form.related'}); $scrout.=<Advanced Search
$checkbox use related words

   $closebutton END $scrout.=&selectbox(undef,'viewselect', $ENV{'form.viewselect'}, undef,undef,undef, sort(keys(%Views))); $scrout.=<

ENDDOCUMENT return $scrout; } ###################################################################### ###################################################################### =pod =item &advanced_search_form() Returns a scalar which holds html for the advanced search form. =cut ###################################################################### ###################################################################### sub advanced_search_form{ my ($closebutton,$hidden) = @_; my $advanced_buttons = <<"END";

$closebutton

END if (!defined($ENV{'form.viewselect'})) { $ENV{'form.viewselect'} ="Detailed Citation View"; } my $scrout=<<"ENDHEADER"; The LearningOnline Network with CAPA

Advanced Catalog Search


Enter terms or phrases separated by search operators such as AND, OR, or NOT.
$advanced_buttons $hidden \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.=&searchphrasefield('publisher
owner','owner', $ENV{'form.owner'}); $scrout.="
VIEW: ENDHEADER $scrout.=&selectbox(undef,'viewselect', $ENV{'form.viewselect'}, undef,undef,undef, sort(keys(%Views))); $scrout.="Related
Words
   
   
\n"; $ENV{'form.category'}='any' unless length($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; my $size = (scalar @domains < ($size - 1) ? scalar @domains + 1 : $size); # standalone machines do not get to choose a domain to search. if ((scalar @domains) == 1) { $scrout .=''."\n"; } else { $scrout.="\n".''. 'DOMAINS
'. '\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'}); $scrout.=< ENDDOCUMENT return $scrout; } ###################################################################### ###################################################################### =pod =item &make_persistent() 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. =cut ###################################################################### ###################################################################### sub make_persistent { my %save = %{shift()}; my $persistent=''; foreach (keys %save) { if (/^form\./ && !/submit/) { my $name=$_; my @values = (ref($save{$name}) ? @{$save{$name}} : ($save{$name})); $name=~s/^form\.//; foreach (@values) { s/\"/\'/g; # do not mess with html field syntax $persistent.=< END } } } return $persistent; } ###################################################################### # 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)=@_; my $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(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