--- loncom/interface/lonsearchcat.pm 2001/03/15 18:23:54 1.17 +++ loncom/interface/lonsearchcat.pm 2001/03/21 01:19:37 1.49 @@ -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,12 +44,20 @@ use Apache::Constants qw(:common); use Apache::lonnet(); use Apache::File(); use CGI qw(:standard); +use Text::Query; my %language; my $scrout; my %metadatafields; my %cprtag; my %mimetag; +my $closebutton; +my $viewselect=< + + + +END sub handler { my $r = shift; @@ -41,6 +83,10 @@ sub handler { END + $closebutton=< +END + # ------------------------------------------------ First, check out environment $metadatafields{'owner'}=$ENV{'user.name'}.'@'.$ENV{'user.domain'}; @@ -77,7 +123,10 @@ 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 @@ -210,7 +259,8 @@ ENDDOCUMENT
- +$closebutton +$viewselect


Advanced Search

@@ -218,7 +268,8 @@ $scrout

- +$closebutton +$viewselect

@@ -229,12 +280,6 @@ ENDDOCUMENT # --------------------------------------------------------- Various form fields -sub textfield { - my ($title,$name,$value)=@_; - return "\n

$title:
". - ''; -} - sub simpletextfield { my ($name,$value)=@_; return ''; @@ -416,55 +461,167 @@ sub selectbox { return $selout.''; } +# ----------------------------------------------- Performing an 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 @queries; + # Go through logical expression AND/OR/NOT phrase fields. + foreach my $field ('title','author','subject','notes','abstract') { + if ($ENV{'form.'.$field}) { + push @queries,&build_SQL_query($field,$ENV{'form.'.$field}); + } + } + if (@queries) { + $query=join(" and ",@queries); + $query="select * from metadata where $query"; + my $reply=&Apache::lonnet::metadata_query($query); + &output_results('Advanced',$r,$envhash,$query,$reply); + } + else { + &output_results('Advanced',$r,$envhash,$query); + } + 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 $reply=&Apache::lonnet::metadata_query($query); + unless (&filled($ENV{'form.basicexp'})) { + &output_blank_field_error($r); + return OK; + } - my @results; + my $query=''; + my $concatarg=join('," ",', + ('title', 'author', 'subject', 'notes', 'abstract')); - my $replyfile=''; - $reply=~/^([\.\w]+)$/; # must do since 'use strict' checks for tainting - $replyfile=$r->dir_config('lonDaemons').'/tmp/'.$1; - $reply=~/(.*?)\_/; - my $hostname=$1; + $query="select * from metadata where concat($concatarg) like '\%$ENV{'form.basicexp'}\%'"; + my $reply=&Apache::lonnet::metadata_query($query); + &output_results('Basic',$r,$envhash,$query,$reply); + return OK; +} - { - 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>; - } +# ---------------- Message to output when there are not enough fields filled in +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 + +$closebutton +
+

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,$query,@replylist)=@_; + my %ENV=%{$envhash}; my $compiledresult=''; - foreach my $result (@results) { - my ($title,$author,$subject,$url,$keywords,$version, - $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.=<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>; + } + + foreach my $result (@results) { + my ($title,$author,$subject,$url,$keywords,$version, + $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.=< END - $compiledresult.=<
END - $compiledresult.=<URL: $url + $compiledresult.=<URL: $url
Title: $title
Author(s): $author
@@ -481,31 +638,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"; + } - # 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); + } + } (keys %ENV); - $r->print(<print(< The LearningOnline Network with CAPA BEGINNING - $r->print(<print(< function select_data(title,url) { changeTitle(title); @@ -523,7 +680,7 @@ BEGINNING } SCRIPT - $r->print(<print(< @@ -531,21 +688,74 @@ SCRIPT - +$closebutton $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 +$query +

+RESULTS + } + $r->print(<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($logic_statement); + my $matchexp=${$q}{'matchexp'}; chomp $matchexp; + my $sql_query=&recursive_SQL_query_build($field_name,$matchexp); + return $sql_query; +} + +# - 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(.*) \]/; + my ($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;