--- loncom/interface/lonsearchcat.pm 2001/03/12 17:28:32 1.7 +++ loncom/interface/lonsearchcat.pm 2002/07/09 17:27:11 1.140 @@ -1,810 +1,2043 @@ -# The LearningOnline Network +# The LearningOnline Network with CAPA # Search Catalog # -# 03/08/2001 Scott Harrison +# $Id: lonsearchcat.pm,v 1.140 2002/07/09 17:27:11 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(); -my %language; -my $scrout; -my %metadatafields; -my %cprtag; -my %mimetag; +# ---------------------------------------- variables used throughout the module -sub handler { - my $r = shift; +###################################################################### +###################################################################### -# -------------------------------------- see if called from an interactive mode - map { - my ($name, $value) = split(/=/,$_); - $value =~ tr/+/ /; - $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; - if ($name eq 'catalogmode') { - $ENV{'form.'.$name}=$value; - } - } (split(/&/,$ENV{'QUERY_STRING'})); +=pod - $r->content_type('text/html'); - $r->send_http_header; - return OK if $r->header_only; +=item Global variables - %metadatafields=(); +=over 4 -# ------------------------------------------------ First, check out environment - $metadatafields{'owner'}=$ENV{'user.name'}.'@'.$ENV{'user.domain'}; +=item $closebutton - - %language=(); - $language{'any'}='Any language'; - { - my $fh=Apache::File->new($r->dir_config('lonTabDir').'/language.tab'); - map { - $_=~/(\w+)\s+([\w\s\-]+)/; - $language{$1}=$2; - } <$fh>; - } +button that closes the search window - %cprtag=(); - $cprtag{'any'}='Any copyright/distribution'; - { - my $fh=Apache::File->new($r->dir_config('lonIncludes').'/copyright.tab'); - map { - $_=~/(\w+)\s+([\w\s\-]+)/; - $cprtag{$1}=$2; - } <$fh>; - } +=item $importbutton - %mimetag=(); - $mimetag{'any'}='Any type'; - { - my $fh=Apache::File->new($r->dir_config('lonTabDir').'/filetypes.tab'); - map { - $_=~/(\w+)\s+(\w+)\s+([\w\s\-]+)/; - $mimetag{$1}=".$1 $3"; - } <$fh>; - } +button to take the select results and go to group sorting - if ($ENV{'form.basicsubmit'} eq 'SEARCH') { - return &basicsearch($r,$ENV{'form.basicexp'}); - } +=item %hash - $scrout=""; - $scrout.=&searchphrasefield('Limit by title','title', - ''); +The ubiquitous database hash - $scrout.=&searchphrasefield('Limit by author','author', - ''); +=item $diropendb - $scrout.=&searchphrasefield('Limit by subject','subject', - ''); +The full path to the (temporary) search database file. This is set and +used in &handler() and is also used in &output_results(). - $scrout.=&searchphrasefield('Limit by notes','notes', - ''); +=item %Views - $scrout.=&searchphrasefield('Limit by abstract','abstract', - ''); +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. - $scrout.=&selectbox('Limit by MIME type','mime', - 'notxxx',%mimetag); +=back - $scrout.=&selectbox('Limit by language','language', - 'any',%language); - - $scrout.=< -LIMIT BY CREATION DATE RANGE: - -
-between: - - -and: - - -

-CREATIONDATEEND +=cut - $scrout.=< -LIMIT BY LAST REVISION DATE RANGE: - -
between: - - -and: - - -

-LASTREVISIONDATEEND +###################################################################### +###################################################################### - $scrout.=&searchphrasefield('Limit by publisher/owner','owner', - $metadatafields{'owner'}); +# -- 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 - $scrout.=&selectbox('Limit by copyright/distribution','copyright', - 'any',%cprtag); +# -- miscellaneous variables +my %hash; # database hash +my $diropendb = ""; # db file -# ---------------------------------------------------------------- Print screen - $r->print(< \&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 %hash; + + $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(%hash,'GDBM_File',$diropendb,&GDBM_WRCREAT,0640)) { + &start_fresh_session(); + untie %hash; + } 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)); + } + # 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). + $r->rflush(); + my $reply=&Apache::lonnet::metadata_query($query,$customquery, + $customshow,$libraries); + &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.

-Enter terms or phrases separated by search operators -such as AND or OR then press SEARCH below. Terms should be specific -to the title, author, subject, notes, or abstract information associated -with a resource. -
- - -Title only -
- + +
+ENDDOCUMENT + $scrout.=' '.&simpletextfield('basicexp',$ENV{'form.basicexp'},40). + ' '; +# $scrout.=&simplecheckbox('allversions',$ENV{'form.allversions'}); +# $scrout.='Search historic archives'; + $scrout.=<Advanced Search

-
-

Advanced Search

-$scrout

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

ENDDOCUMENT - return OK; -} + return $scrout; +} +###################################################################### +###################################################################### -# --------------------------------------------------------- Various form fields +=pod -sub textfield { - my ($title,$name,$value)=@_; - return "\n

$title:
". - ''; +=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('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('notes', 'notes' ,$ENV{'form.notes'}); + $scrout.=&searchphrasefield('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.="
  
  
\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; +} + + +###################################################################### +###################################################################### + +=pod + +=item HTML form building functions + +=over 4 + +=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. + +=item &simplecheckbox() + +Inputs: $name,$value + +Returns a simple check box with the given $name. +If $value eq 'on' the box is checked. + +=item &searchphrasefield() + +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. + +=item &dateboxes() + +Returns html selection form elements for the specification of +the day, month, and year. + +=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