--- loncom/interface/lonsearchcat.pm 2001/03/13 15:36:15 1.8 +++ loncom/interface/lonsearchcat.pm 2001/03/20 12:21:56 1.43 @@ -2,7 +2,41 @@ # Search Catalog # # 03/08/2001 Scott Harrison +# Scott Harrison: 03/12/2001, 03/13/2001, 03/14/2001, 03/15/2001, 03/19/2001 +# Scott Harrison: 03/20/2001 # +# Functions +# +# handler(server reference) : interacts with the Apache server layer +# (for /adm/searchcat URLs) +# simpletextfield(name,value) : returns HTML formatted string for simple text +# field +# simplecheckbox(name,value) : returns HTML formatted string for simple +# checkbox +# searchphrasefield(title,name,value) : returns HTML formatted string for +# a search expression phrase field +# dateboxes(name, defaultmonth, defaultday, defaultyear) : returns HTML +# formatted string +# for a calendar date +# selectbox(title,name,value,%HASH=options) : returns HTML formatted string for +# a selection box field +# advancedsearch(server reference, environment reference) : perform a complex +# multi-field logical query +# filled(field) : determines whether a given field has been filled +# basicsearch(server reference, environment reference) : perform a simple +# single-field logical query +# output_blank_field_error(server reference) : outputs a message saying that +# more fields need to be filled in +# output_results(output mode, +# server reference, +# environment reference, +# reply list reference) : outputs results from search +# build_SQL_query(field name, logic) : builds a SQL query string from a +# logical expression with AND/OR keywords +# recursive_SQL_query_build(field name, reverse notation expression) : +# builds a SQL query string from a reverse notation expression +# logical expression with AND/OR keywords + package Apache::lonsearchcat; use strict; @@ -10,6 +44,7 @@ use Apache::Constants qw(:common); use Apache::lonnet(); use Apache::File(); use CGI qw(:standard); +use Text::Query; my %language; my $scrout; @@ -77,30 +112,45 @@ END } if ($ENV{'form.basicsubmit'} eq 'SEARCH') { - return &basicsearch($r,$ENV{'form.basicexp'}); + return &basicsearch($r,\%ENV); + } + elsif ($ENV{'form.advancedsubmit'} eq 'SEARCH') { + return &advancedsearch($r,\%ENV); } $scrout=''; # building a part of screen output $scrout.=&searchphrasefield('Limit by title','title', - ''); + $ENV{'form.title'}); $scrout.=&searchphrasefield('Limit by author','author', - ''); + $ENV{'form.author'}); $scrout.=&searchphrasefield('Limit by subject','subject', - ''); + $ENV{'form.subject'}); + + $scrout.=&searchphrasefield('Limit by keywords','keywords', + $ENV{'form.keywords'}); + + $scrout.=&searchphrasefield('Limit by URL','url', + $ENV{'form.url'}); + + $scrout.=&searchphrasefield('Limit by version','version', + $ENV{'form.version'}); $scrout.=&searchphrasefield('Limit by notes','notes', - ''); + $ENV{'form.notes'}); $scrout.=&searchphrasefield('Limit by abstract','abstract', - ''); + $ENV{'form.abstract'}); + $ENV{'form.mime'}='notxxx' unless length($ENV{'form.mime'}); $scrout.=&selectbox('Limit by MIME type','mime', - 'notxxx',%mimetag); + $ENV{'form.mime'},%mimetag); + + $ENV{'form.language'}='any' unless length($ENV{'form.language'}); $scrout.=&selectbox('Limit by language','language', - 'any',%language); + $ENV{'form.language'},%language); # ------------------------------------------------ Compute date selection boxes @@ -111,11 +161,19 @@ END
between: CREATIONDATESTART - $scrout.=&dateboxes('creationdatestart'); + $scrout.=&dateboxes('creationdatestart',1,1,1976, + $ENV{'form.creationdatestart_month'}, + $ENV{'form.creationdatestart_day'}, + $ENV{'form.creationdatestart_year'}, + ); $scrout.=<
between: LASTREVISIONDATESTART - $scrout.=&dateboxes('lastrevisiondatestart'); + $scrout.=&dateboxes('lastrevisiondatestart',1,1,1976, + $ENV{'form.lastrevisiondatestart_month'}, + $ENV{'form.lastrevisiondatestart_day'}, + $ENV{'form.lastrevisiondatestart_year'}, + ); $scrout.=< +LIMIT BY OTHER METADATA FIELDS: + +For author-specific metadata, enter in an expression in the form of +key=value separated by operators such as AND or OR.
+Example: grandmother=75 OR grandfather=85 +
+CUSTOMMETADATA +$scrout.=&simpletextfield('custommetadata',$ENV{'form.custommetadata'}); +$scrout.=' initial users of this system do not need to worry about this option'; # ---------------------------------------------------------------- Print screen $r->print(< - - -Title only +ENDDOCUMENT + $r->print(&simpletextfield('basicexp',$ENV{'form.basicexp'})); + $r->print(' '); + $r->print(&simplecheckbox('titleonly',$ENV{'form.titleonly'})); + $r->print('Title only '); + $r->print(&simplecheckbox('allversions',$ENV{'form.allversions'})); + $r->print(<Search historic archives
@@ -181,10 +267,16 @@ ENDDOCUMENT # --------------------------------------------------------- Various form fields -sub textfield { - my ($title,$name,$value)=@_; - return "\n

$title:
". - ''; +sub simpletextfield { + my ($name,$value)=@_; + return ''; +} + +sub simplecheckbox { + my ($name,$value)=@_; + my $checked=''; + $checked="CHECKED" if $value eq 'on'; + return ''; } sub searchphrasefield { @@ -200,137 +292,147 @@ END } sub dateboxes { - my ($name)=@_; - return< - - - - - - - - - - + + + + + + + + + + +END + $month=~s/(\"$currentmonth\")/$1 SELECTED/ if length($currentmonth); + my $day=< - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +END + $day=~s/(\"$currentday\")/$1 SELECTED/ if length($currentday); + my $year=< - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + END + $year=~s/(\"$currentyear\")/$1 SELECTED/ if length($currentyear); + return "$month$day$year"; } sub selectbox { @@ -346,56 +448,162 @@ sub selectbox { return $selout.''; } +# ------------------------------------------------ Performing a advanced search +sub advancedsearch { + my ($r,$envhash)=@_; + my %ENV=%{$envhash}; + + my $fillflag=0; + for my $field ('title','author','subject','keywords','url','version', + 'notes','abstract','mime','language','owner', + 'custommetadata') { + if (&filled($ENV{"form.$field"})) { + $fillflag++; + } + } + + unless ($fillflag) { + &output_blank_field_error($r); + return OK; + } + + my $query=''; +# my $concatarg=join('," ",', +# ('title', 'author', 'subject', 'notes', 'abstract')); + + $query="select * from metadata where concat(title) like '\%$ENV{'form.title'}\%'"; + my $reply=&Apache::lonnet::metadata_query($query); + + &output_results('Advanced',$r,$envhash,$reply); + return OK; +} + +# ---------------------------------------------------- see if a field is filled +sub filled { + my ($field)=@_; + if ($field=~/\S/) { + return 1; + } + else { + return 0; + } +} + # --------------------------------------------------- Performing a basic search sub basicsearch { - my ($r,$expression)=@_; + my ($r,$envhash)=@_; + my %ENV=%{$envhash}; - my $query=$expression; - $query="select * from metadata where concat(title,\" \",author) like '\%$expression\%'"; - my $reply=&Apache::lonnet::reply("querysend:$query",'msul3'); - - my @results; - - my $replyfile=''; - $reply=~/^([\.\w]+)$/; # must do since 'use strict' checks for tainting - $replyfile=$r->dir_config('lonDaemons').'/tmp/'.$1; - $reply=~/(.*?)\_/; - my $hostname=$1; + unless (&filled($ENV{'form.basicexp'})) { + &output_blank_field_error($r); + return OK; + } - { - while (1) { - last if -e $replyfile; - sleep 1; + my $query=''; + my $concatarg=join('," ",', + ('title', 'author', 'subject', 'notes', 'abstract')); + + $query="select * from metadata where concat($concatarg) like '\%$ENV{'form.basicexp'}\%'"; + my $reply=&Apache::lonnet::metadata_query($query); + &output_results('Basic',$r,$envhash,$reply); + return OK; +} + +sub output_blank_field_error { + my ($r)=@_; + # make query information persistent to allow for subsequent revision + my $persistent=''; + map { + if (/^form\./ && !/submit/) { + my $name=$_; + my $key=$name; + $name=~s/^form\.//; + $persistent.=< +END + } + } (keys %ENV); + + $r->print(< + +The LearningOnline Network with CAPA +BEGINNING + $r->print(< + + +

Search Catalog

+
+$persistent + + +
+

Helpful Message

+

+Incorrect search query due to blank entry fields. +You need to fill in the relevant +fields on the search page in order for a query to be +processed. +

+ + +RESULTS +} + +# ----------------------------- format and output results based on a reply list +sub output_results { + my ($mode,$r,$envhash,@replylist)=@_; + my %ENV=%{$envhash}; + foreach my $reply (@replylist) { + + my @results; + + my $replyfile=''; + $reply=~/^([\.\w]+)$/; # must do since 'use strict' checks for tainting + $replyfile=$r->dir_config('lonDaemons').'/tmp/'.$1; + $reply=~/(.*?)\_/; + my $hostname=$1; + + { + while (1) { + last if -e $replyfile; + sleep 1; + } + # QUESTION: how should I handle this error condition.. + # I'm sure there is syntax elsewhere I can use.. + my $fh=Apache::File->new($replyfile) or + ($r->print('file cannot be opened') and return OK); + @results=<$fh>; } - # QUESTION: how should I handle this error condition.. - # I'm sure there is syntax elsewhere I can use.. - my $fh=Apache::File->new($replyfile) or - ($r->print('file cannot be opened') and return OK); - @results=<$fh>; - } - - my $compiledresult=''; - - foreach my $result (@results) { - my ($title,$author,$subject,$notes,$abstract,$mime,$lang, - $creationdate,$lastrevisiondate,$owner,$copyright - )=map {&Apache::lonnet::unescape($_)} (split(/\,/,$result)); - my $shortabstract=$abstract; - $shortabstract=substr($abstract,0,200) if length($abstract)>200; - $compiledresult.=<200; + $compiledresult.=< END - $compiledresult.=< +onClick="javascript:select_data('$title','$url')">
END - $compiledresult.=<URL: $url +
Title: $title
Author(s): $author
Subject: $subject
-Keyword(s): not available yet
+Keyword(s): $keywords
Notes: $notes
Abstract: $shortabstract
MIME Type: $mimetag{$mime}
@@ -407,20 +615,31 @@ END Repository Location: $hostname

END -} + } - unless ($compiledresult) { - $compiledresult="There were no results that matched your query"; - } + unless ($compiledresult) { + $compiledresult="There were no results that matched your query"; + } -# Question... allow to ask question from this page, or click to -# search again? - $r->print(< +END + } + } (keys %ENV); + + $r->print(< The LearningOnline Network with CAPA BEGINNING - $r->print(<print(< function select_data(title,url) { changeTitle(title); @@ -438,25 +657,79 @@ BEGINNING } SCRIPT - $r->print(<print(<

Search Catalog

-Revise search request + + +$persistent

Search Query

+RESULTS + if ($mode eq 'Basic') { + $r->print(< -Basic search: $expression +Basic search: $ENV{'form.basicexp'}

+RESULTS + elsif ($mode eq 'Advanced') { + $r->print(< +Advanced search +

+RESULTS + }

Search Results

$compiledresult RESULTS + } +} - return OK; +# ------------------------------------------------------------- build_SQL_query +sub build_SQL_query { + my ($field_name,$logic_statement)=@_; + my $q=new Text::Query('abc', + -parse => 'Text::Query::ParseAdvanced', + -build => 'Text::Query::Build'); + $q->prepare($statement); + my $matchexp=${$q}{'matchexp'}; chomp $matchexp; + my $sql_query=&recursive_SQL_query_build($field_name,$matchexp); +} + +# - Recursively parse a reverse notation expression into a SQL query expression +sub recursive_SQL_query_build { + my ($dkey,$pattern)=@_; + my @matches=($pattern=~/(\[[^\]|\[]*\])/g); + return $pattern unless @matches; + foreach my $match (@matches) { + $match=~/\[ (\w+)\s(.*) \]/; + ($key,$value)=($1,$2); + my $replacement=''; + if ($key eq 'literal') { + $replacement="($dkey like \"\%$value\%\")"; + } + elsif ($key eq 'and') { + $value=~/(.*[\"|\)]) ([|\(|\^].*)/; + $replacement="($1 AND $2)"; + } + elsif ($key eq 'or') { + $value=~/(.*[\"|\)]) ([|\(|\^].*)/; + $replacement="($1 OR $2)"; + } + substr($pattern, + index($pattern,$match), + length($match), + $replacement + ); + } + &recursive_SQL_query_build($dkey,$pattern); } 1;