--- loncom/interface/lonsearchcat.pm 2002/06/20 19:43:50 1.125
+++ loncom/interface/lonsearchcat.pm 2016/08/04 20:59:22 1.349
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Search Catalog
#
-# $Id: lonsearchcat.pm,v 1.125 2002/06/20 19:43:50 matthew Exp $
+# $Id: lonsearchcat.pm,v 1.349 2016/08/04 20:59:22 raeburn 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
@@ -56,126 +48,801 @@ 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.
+search (on a server basis) is displayed to the user in a separate window.
=head1 Internals
-=over 4
-
=cut
###############################################################################
###############################################################################
-## ##
-## ORGANIZATION OF THIS PERL MODULE ##
-## ##
-## 1. Modules used by this module ##
-## 2. Choices for different output views (detailed, summary, xml, etc) ##
-## 3. BEGIN block (to be run once after compilation) ##
-## 4. Handling routine called via Apache and mod_perl ##
-## 5. Other subroutines ##
-## ##
-###############################################################################
-
package Apache::lonsearchcat;
-# ------------------------------------------------- modules used by this module
use strict;
-use Apache::Constants qw(:common);
-use Apache::lonnet();
+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();
-
-# ---------------------------------------- variables used throughout the module
+use Apache::lonmysql();
+use Apache::lonmeta;
+use Apache::lonhtmlcommon;
+use Apache::lonlocal;
+use LONCAPA::lonmetadata();
+use HTML::Entities();
+use Parse::RecDescent;
+use Apache::lonnavmaps;
+use Apache::lonindexer();
+use Apache::lonwishlist();
+use LONCAPA;
+use Time::HiRes qw(sleep);
######################################################################
######################################################################
+##
+## Global variables
+##
+######################################################################
+######################################################################
+my %groupsearch_db; # Database hash used to save values for the
+ # groupsearch RAT interface.
+my %persistent_db; # gdbm hash which holds data which is supposed to
+ # persist across calls to lonsearchcat.pm
+
+# The different view modes and associated functions
+
+my %Views = ("detailed" => \&detailed_citation_view,
+ "detailedpreview" => \&detailed_citation_preview,
+ "summary" => \&summary_view,
+ "summarypreview" => \&summary_preview,
+ "fielded" => \&fielded_format_view,
+ "xml" => \&xml_sgml_view,
+ "compact" => \&compact_view);
-=pod
-
-=item Global variables
-
-=over 4
+######################################################################
+######################################################################
+sub handler {
+ my $r = shift;
+# &set_defaults();
+ #
+ # set form defaults
+ #
+ my $hidden_fields;# Hold all the hidden fields used to keep track
+ # of the search system state
+ my $importbutton; # button to take the selected results and go to group
+ # sorting
+ my $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 %hostdomains
+ #
+ 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;
+ ##
+ ## 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','pause',
+ 'phase','persistent_db_id','table','start','show',
+ 'cleargroupsort','titleelement','area','inhibitmenu']);
+ ##
+ ## 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(0.1);
+ delete($env{'form.pause'});
+ }
+ ##
+ ## Initialize global variables
+ ##
+ my $domain = $r->dir_config('lonDefDomain');
+ my $temp_file_dir = LONCAPA::tempdir();
+ $diropendb= $temp_file_dir .
+ "$env{'user.domain'}_$env{'user.name'}_sel_res.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;
+ }
-matches host name to host domain
+ my $persistent_db_file = $temp_file_dir .
+ &escape($domain).
+ '_'.&escape($env{'user.name'}).
+ '_'.$env{'form.persistent_db_id'}.'_persistent_search.db';
+ ##
+ &Apache::lonhtmlcommon::clear_breadcrumbs();
-=item %hostips
+ my @allowed_searches = ('portfolio');
+ if (&Apache::lonnet::allowed('bre',$env{'request.role.domain'}) eq 'F') {
+ push(@allowed_searches,'res');
+ }
+ my $crumb_text = 'Portfolio Search';
+ if (@allowed_searches ==2) {
+ $crumb_text = 'Portfolio and Catalog Search';
+ }
+ &Apache::lonhtmlcommon::add_breadcrumb
+ ({href=>'/adm/searchcat?'.
+ &Apache::loncommon::inhibit_menu_check().
+ '&catalogmode='.$env{'form.catalogmode'}.
+ '&launch='.$env{'form.launch'}.
+ '&mode='.$env{'form.mode'},
+ text=>"$crumb_text",
+ target=>'_top',
+ bug=>'Searching',});
+ #
+ if ($env{'form.phase'} !~ m/(basic|adv|course)_search/) {
+ 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);
+ my $msg =
+ &mt('We were unable to retrieve data describing your search.').
+ ' '.&mt('This is a serious error and has been logged.').
+ '
'.
+ &mt('Please alert your LON-CAPA administrator.');
+ &Apache::loncommon::simple_error_page(
+ $r,'Search Error',
+ $msg,
+ {'no_auto_mt_msg' => 1});
+ return OK;
+ }
+ }
+ } else {
+ &clean_up_environment();
+ }
+ ##
+ ## Clear out old values from groupsearch database
+ ##
+ untie %groupsearch_db if (tied(%groupsearch_db));
+ if (($env{'form.cleargroupsort'} eq '1') ||
+ (($env{'form.launch'} eq '1') &&
+ ($env{'form.catalogmode'} eq 'import'))) {
+ if (tie(%groupsearch_db,'GDBM_File',$diropendb,&GDBM_WRCREAT(),0640)) {
+ &start_fresh_session();
+ untie %groupsearch_db;
+ delete($env{'form.cleargroupsort'});
+ } else {
+ # This is a stupid error to give to the user.
+ # It really tells them nothing.
+ my $msg = 'Unable to tie hash to db file.';
+ &Apache::loncommon::simple_error_page($r,'Search Error',
+ $msg);
+ return OK;
+ }
+ }
+ ##
+ ## Configure hidden fields
+ ##
+ $hidden_fields = ''."\n";
+ if (exists($env{'form.catalogmode'})) {
+ $hidden_fields .= &hidden_field('catalogmode');
+ }
+ if (exists($env{'form.form'})) {
+ $hidden_fields .= &hidden_field('form');
+ }
+ if (exists($env{'form.element'})) {
+ $hidden_fields .= &hidden_field('element');
+ }
+ if (exists($env{'form.titleelement'})) {
+ $hidden_fields .= &hidden_field('titleelement');
+ }
+ if (exists($env{'form.mode'})) {
+ $hidden_fields .= &hidden_field('mode');
+ }
+ if (exists($env{'form.area'})) {
+ $hidden_fields .= &hidden_field('area');
+ }
+ if (exists($env{'form.inhibitmenu'})) {
+ $hidden_fields .= &hidden_field('inhibitmenu');
+ }
+ ##
+ ## Configure dynamic components of interface
+ ##
+ if ($env{'form.catalogmode'} eq 'interactive') {
+ $closebutton="
+END
+ } else {
+ $closebutton = '';
+ $importbutton = '';
+ }
+ ##
+ ## Sanity checks on form elements
+ ##
+ if (!defined($env{'form.viewselect'})) {
+ $env{'form.viewselect'} ="summary";
+ }
+ $env{'form.phase'} = 'disp_basic' if (! exists($env{'form.phase'}));
+ $env{'form.show'} = 20 if (! exists($env{'form.show'}));
+ #
+ $env{'form.searchmode'} = 'basic' if (! exists($env{'form.searchmode'}));
+ if ($env{'form.phase'} eq 'adv_search' ||
+ $env{'form.phase'} eq 'disp_adv') {
+ $env{'form.searchmode'} = 'advanced';
+ }
+ #
+ if ($env{'form.searchmode'} eq 'advanced') {
+ my $srchtype = 'Catalog';
+ if ($env{'form.area'} eq 'portfolio') {
+ $srchtype = 'Portfolio';
+ }
+ &Apache::lonhtmlcommon::add_breadcrumb
+ ({href=>'/adm/searchcat?'.&Apache::loncommon::inhibit_menu_check().
+ '&phase=disp_adv'.
+ '&catalogmode='.$env{'form.catalogmode'}.
+ '&launch='.$env{'form.launch'}.
+ '&mode='.$env{'form.mode'},
+ text=>"Advanced $srchtype Search",
+ bug=>'Searching',});
+ }
+ ##
+ ## Switch on the phase
+ ##
+ if ($env{'form.phase'} eq 'disp_basic') {
+ &print_basic_search_form($r,$closebutton,$hidden_fields);
+ } elsif ($env{'form.phase'} eq 'disp_adv') {
+ &print_advanced_search_form($r,$closebutton,$hidden_fields);
+ } elsif ($env{'form.phase'} eq 'results') {
+ &display_results($r,$importbutton,$closebutton,$diropendb,
+ $env{'form.area'});
+ } elsif ($env{'form.phase'} =~ /^(sort|run_search)$/) {
+ my ($query,$customquery,$customshow,$libraries,$pretty_string,$domainsref) =
+ &get_persistent_data($persistent_db_file,
+ ['query','customquery','customshow',
+ 'libraries','pretty_string','domains']);
+ 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,$env{'form.area'},$domainsref);
+ }
+ } elsif(($env{'form.phase'} eq 'basic_search') ||
+ ($env{'form.phase'} eq 'adv_search')) {
+ #
+ # We are running a search, try to parse it
+ my ($query,$customquery,$customshow,$libraries,$domains) =
+ (undef,undef,undef,undef,undef);
+ my $pretty_string;
+ if ($env{'form.phase'} eq 'basic_search') {
+ ($query,$pretty_string,$libraries,$domains) =
+ &parse_basic_search($r,$closebutton,$hidden_fields);
+ return OK if (! defined($query));
+ &make_persistent({ basicexp => $env{'form.basicexp'}},
+ $persistent_db_file);
+ } else { # Advanced search
+ ($query,$customquery,$customshow,$libraries,$pretty_string,$domains)
+ = &parse_advanced_search($r,$closebutton,$hidden_fields);
+ return OK if (! defined($query));
+ }
+ &make_persistent({ query => $query,
+ customquery => $customquery,
+ customshow => $customshow,
+ libraries => $libraries,
+ pretty_string => $pretty_string,
+ domains => $domains },
+ $persistent_db_file);
+ #
+ # Set up table
+ if (! defined(&create_results_table($env{'form.area'}))) {
+ my $errorstring=&Apache::lonmysql::get_error();
+ &Apache::lonnet::logthis('lonsearchcat.pm: Unable to create '.
+ 'needed table. lonmysql error:'.
+ $errorstring);
+
+ my $msg =
+ 'Unable to create table in which to save search results. '.
+ 'The search has been aborted.';
+ &Apache::loncommon::simple_error_page($r,'Search Error',
+ $msg);
+ return OK;
+ }
+ delete($env{'form.launch'});
+ if (! &make_form_data_persistent($r,$persistent_db_file)) {
+ my $msg=
+ 'Unable to properly save search information. '.
+ 'The search has been aborted.';
+ &Apache::loncommon::simple_error_page($r,'Search Error',
+ $msg);
+ return OK;
+ }
+ ##
+ ## Print out the frames interface
+ ##
+ if (defined($query)) {
+ &print_frames_interface($r);
+ }
+ }
+ return OK;
+}
-matches host name to host ip
+#
+# The mechanism used to store values away and retrieve them does not
+# handle the case of missing environment variables being significant.
+#
+# This routine sets non existant checkbox form elements to ''.
+#
+sub clean_up_environment {
+ if ($env{'form.phase'} eq 'basic_search') {
+ if (! exists($env{'form.related'})) {
+ $env{'form.related'} = '';
+ }
+ if (! exists($env{'form.domains'})) {
+ $env{'form.domains'} = '';
+ }
+ } elsif ($env{'form.phase'} eq 'adv_search') {
+ foreach my $field ('title','keywords','notes',
+ 'abstract','standards','mime') {
+ if (! exists($env{'form.'.$field.'_related'})) {
+ $env{'form.'.$field.'_related'} = '';
+ }
+ }
+ } elsif ($env{'form.phase'} eq 'course_search') {
+ if (! exists($env{'form.crsrelated'})) {
+ $env{'form.crsrelated'} = '';
+ }
+ }
+}
-=item %hitcount
+sub hidden_field {
+ my ($name,$value) = @_;
+ if (! defined($value)) {
+ $value = $env{'form.'.$name};
+ }
+ return ''.$/;
+}
-stores number of hits per host
+######################################################################
-=item $closebutton
+=pod
-button that closes the search window
+=over 4
+
+=item &print_basic_search_form()
-=item $importbutton
+Prints the form for the basic search. Sorry the name is so cryptic.
-button to take the selecte results and go to group sorting
+=cut
-=item %hash
+######################################################################
+######################################################################
+sub print_basic_search_form {
+ my ($r,$closebutton,$hidden_fields) = @_;
+ my $result = ($env{'form.catalogmode'} ne 'import');
+ my $bread_crumb =
+ &Apache::lonhtmlcommon::breadcrumbs('Searching','Search_Basic',
+ $env{'form.catalogmode'} ne 'import');
+ my $scrout = &Apache::loncommon::start_page('Content Library').$bread_crumb;
+# Search form for resource space
+ if (&Apache::lonnet::allowed('bre',$env{'request.role.domain'}) eq 'F') {
+ $scrout .= &setup_basic_search($r,'res',$hidden_fields,$closebutton);
+ $scrout .= '
+ + +$closebutton +
+END + my $srchtype = 'Catalog'; + my $jscript; + if ($env{'form.area'} eq 'portfolio') { + $srchtype = 'Portfolio'; + $jscript = ''; + } + my $scrout= &Apache::loncommon::start_page("Advanced $srchtype Search", + $jscript); + $scrout .= $bread_crumb; + + $scrout .= ''; -BEGIN { - foreach (keys (%Apache::lonnet::libserv)) { - $hostdomains{$_}=$Apache::lonnet::hostdom{$_}; - $hostips{$_}=$Apache::lonnet::hostip{$_}; - } + $scrout .= &Apache::loncommon::end_page(); + $r->print($scrout); + return; } ###################################################################### @@ -183,328 +850,103 @@ BEGIN { =pod -=item &handler() - main handler invoked by httpd child +=item &titlefield() -=item Variables +Inputs: title text -=over 4 +Outputs: titletext with font wrapper -=item $hidden +=cut -holds 'hidden' html forms +###################################################################### +###################################################################### +sub titlefield { + my $title=shift; + return $title; +} -=item $scrout +###################################################################### +###################################################################### -string that holds portions of the screen output +=pod -=back +=item viewoptiontext() + +Inputs: codename for view option + +Outputs: displayed text =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=<'.
- ''.$uctitle.':
';
+ return &make_persistent(\%save,$filename);
}
######################################################################
@@ -674,126 +1138,354 @@ sub selectbox {
=pod
-=item &advancedsearch()
+=item &parse_advanced_search()
+
+Parse advanced search form and return the following:
+
+=over 4
+
+=item $query Scalar containing an SQL query.
+
+=item $customquery Scalar containing a custom query.
+
+=item $customshow Scalar containing commands to show custom metadata.
+
+=item $libraries_to_query Reference to array of domains to search.
+
+=back
=cut
######################################################################
######################################################################
-sub advancedsearch {
- my ($r,$envhash,$hidden)=@_;
- my %ENV=%{$envhash};
+sub parse_advanced_search {
+ my ($r,$closebutton,$hidden_fields)=@_;
+ my @BasicFields = ('title','author','subject','keywords','url','version',
+ 'notes','abstract','extension','owner','authorspace',
+# 'custommetadata','customshow',
+ 'modifyinguser','standards','mime');
+ my @StatsFields = &statfields();
+ my @EvalFields = &evalfields();
my $fillflag=0;
+ my $pretty_search_string = "";
# Clean up fields for safety
- for my $field ('title','author','subject','keywords','url','version',
- 'creationdatestart_month','creationdatestart_day',
+ for my $field (@BasicFields,
+ 'creationdatestart_month','creationdatestart_day',
'creationdatestart_year','creationdateend_month',
'creationdateend_day','creationdateend_year',
'lastrevisiondatestart_month','lastrevisiondatestart_day',
'lastrevisiondatestart_year','lastrevisiondateend_month',
- 'lastrevisiondateend_day','lastrevisiondateend_year',
- 'notes','abstract','mime','language','owner',
- 'custommetadata','customshow') {
- $ENV{"form.$field"}=~s/[^\w\/\s\(\)\=\-\"\']//g;
+ 'lastrevisiondateend_day','lastrevisiondateend_year') {
+ $env{'form.'.$field}=~s/[^\w\/\s\(\)\=\-\"\'.\*]//g;
}
foreach ('mode','form','element') {
# is this required? Hmmm.
- next unless (exists($ENV{"form.$_"}));
- $ENV{"form.$_"}=&Apache::lonnet::unescape($ENV{"form.$_"});
- $ENV{"form.$_"}=~s/[^\w\/\s\(\)\=\-\"\']//g;
- }
+ next if (! exists($env{'form.'.$_}));
+ $env{'form.'.$_}=&unescape($env{'form.'.$_});
+ $env{'form.'.$_}=~s/[^\w\/\s\(\)\=\-\"\']//g;
+ }
+ # Preprocess the category form element.
+ $env{'form.category'} = 'any' if (! defined($env{'form.category'}) ||
+ ref($env{'form.category'}));
+ #
# Check to see if enough information was filled in
- for my $field ('title','author','subject','keywords','url','version',
- 'notes','abstract','mime','language','owner',
- 'custommetadata') {
- if (&filled($ENV{"form.$field"})) {
+ foreach my $field (@BasicFields) {
+ if (&filled($env{'form.'.$field})) {
$fillflag++;
}
}
- unless ($fillflag) {
- &output_blank_field_error($r);
- return OK;
+ foreach my $field (@StatsFields,@EvalFields) {
+ if (&filled($env{'form.'.$field.'_max'})) {
+ $fillflag++;
+ }
+ if (&filled($env{'form.'.$field.'_min'})) {
+ $fillflag++;
+ }
+ }
+
+ for my $field ('lowestgradelevel','highestgradelevel') {
+ if ( $env{'form.'.$field} =~ /^\d+$/ &&
+ $env{'form.'.$field} > 0) {
+ $fillflag++;
+ }
+ }
+ if ($env{'form.area'} eq 'portfolio') {
+ # Added metadata fields
+ for (my $i=0; $i<$env{'form.numaddedfields'} ; $i++) {
+ my $field = $env{'form.addedfield_'.$i};
+ $field =~ s/^\s*(\S*)\s*$/$1/;
+ $field =~ s/\W/_/g;
+ if ($field ne '') {
+ $fillflag++;
+ }
+ }
+ }
+ if (! $fillflag) {
+ &output_blank_field_error($r,$closebutton,
+ 'phase=disp_adv',$hidden_fields);
+ return ;
}
# Turn the form input into a SQL-based query
my $query='';
my @queries;
+ my $font = '';
# Evaluate logical expression AND/OR/NOT phrase fields.
- foreach my $field ('title','author','subject','notes','abstract','url',
- 'keywords','version','owner') {
- if ($ENV{'form.'.$field}) {
- push @queries,&build_SQL_query($field,$ENV{'form.'.$field});
- }
+ foreach my $field (@BasicFields) {
+ next if (!defined($env{'form.'.$field}) || $env{'form.'.$field} eq '');
+ my ($error,$SQLQuery) =
+ &process_phrase_input($env{'form.'.$field},
+ $env{'form.'.$field.'_related'},$field);
+ if (defined($error)) {
+ &output_unparsed_phrase_error($r,$closebutton,'phase=disp_adv',
+ $hidden_fields,$field);
+ return;
+ } else {
+ $pretty_search_string .=
+ $font.$field.': '.$env{'form.'.$field};
+ if ($env{'form.'.$field.'_related'}) {
+ my @Words =
+ &Apache::loncommon::get_related_words
+ ($env{'form.'.$field});
+ if (@Words) {
+ $pretty_search_string.= ' with related words: '.
+ join(', ',@Words[0..4]);
+ } else {
+ $pretty_search_string.= ' with related words.';
+ }
+ }
+ $pretty_search_string .= '
';
+ push (@queries,$SQLQuery);
+ }
}
- # Evaluate option lists
- if ($ENV{'form.language'} and $ENV{'form.language'} ne 'any') {
- push @queries,"(language like \"$ENV{'form.language'}\")";
+ #
+ # Make the 'mime' from 'form.category' and 'form.extension'
+ #
+ my $searchphrase;
+ if (exists($env{'form.category'}) &&
+ $env{'form.category'} !~ /^\s*$/ &&
+ $env{'form.category'} ne 'any') {
+ my @extensions = &Apache::loncommon::filecategorytypes
+ ($env{'form.category'});
+ if (scalar(@extensions) > 0) {
+ $searchphrase = join(' OR ',@extensions);
+ }
}
- if ($ENV{'form.mime'} and $ENV{'form.mime'} ne 'any') {
- push @queries,"(mime like \"$ENV{'form.mime'}\")";
+ if (defined($searchphrase)) {
+ my ($error,$SQLsearch) = &process_phrase_input($searchphrase,0,'mime');
+ push @queries,$SQLsearch;
+ $pretty_search_string .=$font.'mime contains '.
+ $searchphrase.'
';
}
- if ($ENV{'form.copyright'} and $ENV{'form.copyright'} ne 'any') {
- push @queries,"(copyright like \"$ENV{'form.copyright'}\")";
+ #
+ # Evaluate option lists
+ if ($env{'form.lowestgradelevel'} &&
+ $env{'form.lowestgradelevel'} ne '0' &&
+ $env{'form.lowestgradelevel'} =~ /^\d+$/) {
+ push(@queries,
+ '(lowestgradelevel>='.$env{'form.lowestgradelevel'}.')');
+ $pretty_search_string.="lowestgradelevel>=".
+ $env{'form.lowestgradelevel'}."
\n";
+ }
+ if ($env{'form.highestgradelevel'} &&
+ $env{'form.highestgradelevel'} ne '0' &&
+ $env{'form.highestgradelevel'} =~ /^\d+$/) {
+ push(@queries,
+ '(highestgradelevel<='.$env{'form.highestgradelevel'}.')');
+ $pretty_search_string.="highestgradelevel<=".
+ $env{'form.highestgradelevel'}."
\n";
+ }
+ if ($env{'form.language'} and $env{'form.language'} ne 'any') {
+ push @queries,"(language like \"$env{'form.language'}\")";
+ $pretty_search_string.=$font."language= ".
+ &Apache::loncommon::languagedescription($env{'form.language'}).
+ "
\n";
+ }
+ if ($env{'form.copyright'} and $env{'form.copyright'} ne 'any') {
+ push @queries,"(copyright like \"$env{'form.copyright'}\")";
+ $pretty_search_string.=$font."copyright = ".
+ &Apache::loncommon::copyrightdescription($env{'form.copyright'}).
+ "
\n";
+ }
+ if ($env{'form.area'} eq 'portfolio') {
+ #
+ # Added metadata fields
+ for (my $i=0; $i<$env{'form.numaddedfields'} ; $i++) {
+ my $field = $env{'form.addedfield_'.$i};
+ $field =~ s/^\s*(\S*)\s*$/$1/;
+ $field =~ s/\W/_/g;
+ $field =~ tr/A-Z/a-z/;
+ if ($field ne '') {
+ my $value = $env{'form.addedvalues_'.$i};
+ if ($value ne '') {
+ $value =~ s/'/''/g; #' stupid emacs
+ my ($error,$query) =
+ &process_phrase_input($value,0,'pf.value');
+ if (!defined($error)) {
+ push(@queries,"pf.field = '$field' AND $query");
+ $pretty_search_string .=
+ $font.$field.': '.
+ $env{'form.addedvalues_'.$i}.'
';
+ }
+ } else {
+ push(@queries,"pf.field = '$field' AND pf.value IS NULL");
+ }
+ }
+ }
+ } else {
+ #
+ # Statistics
+ foreach my $field (@StatsFields,@EvalFields) {
+ my ($min,$max);
+ if (exists($env{'form.'.$field.'_min'}) &&
+ $env{'form.'.$field.'_min'} ne '') {
+ $min = $env{'form.'.$field.'_min'};
+ }
+ if (exists($env{'form.'.$field.'_max'}) &&
+ $env{'form.'.$field.'_max'} ne '') {
+ $max = $env{'form.'.$field.'_max'};
+ }
+ next if (! defined($max) && ! defined($min));
+ if (defined($min) && defined($max)) {
+ ($min,$max) = sort {$a <=>$b} ($min,$max);
+ }
+ if (defined($min) && $min =~ /^(\d+\.\d+|\d+|\.\d+)$/) {
+ push(@queries,'('.$field.'>'.$min.')');
+ $pretty_search_string.=$font.$field.'>'.$min.'
';
+ }
+ if (defined($max) && $max =~ /^(\d+\.\d+|\d+|\.\d+)$/) {
+ push(@queries,'('.$field.'<'.$max.')');
+ $pretty_search_string.=$font.$field.'<'.$max.'
';
+ }
+ }
}
+ #
# Evaluate date windows
- my $datequery=&build_date_queries(
- $ENV{'form.creationdatestart_month'},
- $ENV{'form.creationdatestart_day'},
- $ENV{'form.creationdatestart_year'},
- $ENV{'form.creationdateend_month'},
- $ENV{'form.creationdateend_day'},
- $ENV{'form.creationdateend_year'},
- $ENV{'form.lastrevisiondatestart_month'},
- $ENV{'form.lastrevisiondatestart_day'},
- $ENV{'form.lastrevisiondatestart_year'},
- $ENV{'form.lastrevisiondateend_month'},
- $ENV{'form.lastrevisiondateend_day'},
- $ENV{'form.lastrevisiondateend_year'},
- );
- # Test to see if date windows are legitimate
- if ($datequery=~/^Incorrect/) {
- &output_date_error($r,$datequery);
- return OK;
- }
- elsif ($datequery) {
+ my $cafter =
+ &Apache::lonhtmlcommon::get_date_from_form('creationdate1');
+ my $cbefore =
+ &Apache::lonhtmlcommon::get_date_from_form('creationdate2');
+ if ($cafter > $cbefore) {
+ my $tmp = $cafter;
+ $cafter = $cbefore;
+ $cbefore = $tmp;
+ }
+ my $mafter =
+ &Apache::lonhtmlcommon::get_date_from_form('revisiondate1');
+ my $mbefore =
+ &Apache::lonhtmlcommon::get_date_from_form('revisiondate2');
+ if ($mafter > $mbefore) {
+ my $tmp = $mafter;
+ $mafter = $mbefore;
+ $mbefore = $tmp;
+ }
+ my ($datequery,$error,$prettydate)=&build_date_queries($cafter,$cbefore,
+ $mafter,$mbefore);
+ if (defined($error)) {
+ &output_date_error($r,$error,$closebutton,$hidden_fields);
+ } elsif (defined($datequery)) {
+ # Here is where you would set up pretty_search_string to output
+ # date query information.
+ $pretty_search_string .= '
'.$prettydate.'
';
push @queries,$datequery;
}
+ #
# Process form information for custom metadata querying
- my $customquery='';
- if ($ENV{'form.custommetadata'}) {
- $customquery=&build_custommetadata_query('custommetadata',
- $ENV{'form.custommetadata'});
+ my $customquery=undef;
+ ##
+ ## The custom metadata search was removed q long time ago mostly
+ ## because I was unable to figureout exactly how it worked and could
+ ## not imagine people actually using it. MH
+ ##
+ # if ($env{'form.custommetadata'}) {
+ # $pretty_search_string .=$font."Custom Metadata Search: ".
+ # $env{'form.custommetadata'}."
\n";
+ # $customquery=&build_custommetadata_query('custommetadata',
+ # $env{'form.custommetadata'});
+ # }
+ my $customshow=undef;
+ # if ($env{'form.customshow'}) {
+ # $pretty_search_string .=$font."Custom Metadata Display: ".
+ # $env{'form.customshow'}."
\n";
+ # $customshow=$env{'form.customshow'};
+ # $customshow=~s/[^\w\s]//g;
+ # my @fields=split(/\s+/,$customshow);
+ # $customshow=join(" ",@fields);
+ # }
+ ##
+ ## Deal with restrictions to given domains
+ ##
+ my ($libraries_to_query,$pretty_domains_string,$domains_to_query) =
+ &parse_domain_restrictions();
+ if ($pretty_domains_string) {
+ $pretty_search_string .= $pretty_domains_string."
\n";
}
- my $customshow='';
- if ($ENV{'form.customshow'}) {
- $customshow=$ENV{'form.customshow'};
- $customshow=~s/[^\w\s]//g;
- my @fields=split(/\s+/,$customshow);
- $customshow=join(" ",@fields);
- }
- # 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).
+ #
if (@queries) {
- $query=join(" AND ",@queries);
- $query="select * from metadata where $query";
- my $reply; # reply hash reference
- unless ($customquery or $customshow) {
- $reply=&Apache::lonnet::metadata_query($query);
- }
- else {
- $reply=&Apache::lonnet::metadata_query($query,
- $customquery,$customshow);
- }
- &output_results('Advanced',$r,$envhash,$customquery,$reply,$hidden);
+ if ($env{'form.area'} eq 'portfolio') {
+ $query ="SELECT pm.*,pa.keynum,pa.scope FROM portfolio_metadata pm, portfolio_access pa, portfolio_addedfields pf WHERE (pm.url = pa.url AND pf.url = pm.url AND (pa.start < UTC_TIMESTAMP() AND (pa.end IS NULL OR pa.end > UTC_TIMESTAMP())) AND (".join(') AND (',@queries).'))';
+ } else {
+ $query="SELECT * FROM metadata WHERE (".join(") AND (",@queries).')';
+ }
+ } elsif ($customquery) {
+ $query = '';
+ }
+ #&Apache::lonnet::logthis('advanced query = '.$/.$query);
+ return ($query,$customquery,$customshow,$libraries_to_query,
+ $pretty_search_string,$domains_to_query);
+}
+
+sub parse_domain_restrictions {
+ my $libraries_to_query = undef;
+ my $domains_to_query = undef;
+ # $env{'form.domains'} can be either a scalar or an array reference.
+ # We need an array.
+ if (! exists($env{'form.domains'}) || $env{'form.domains'} eq '') {
+ return (undef,'',undef);
}
- elsif ($customquery) {
- my $reply; # reply hash reference
- $reply=&Apache::lonnet::metadata_query('',
- $customquery,$customshow);
- &output_results('Advanced',$r,$envhash,$customquery,$reply,$hidden);
+ my @allowed_domains = &Apache::loncommon::get_env_multiple('form.domains');
+ #
+ my %domain_hash = ();
+ my $pretty_domains_string;
+ foreach (@allowed_domains) {
+ $domain_hash{$_}++;
+ }
+ if ($domain_hash{'any'}) {
+ $pretty_domains_string = &mt("in all LON-CAPA domains.");
+ } else {
+ if (@allowed_domains > 1) {
+ $pretty_domains_string = &mt("in LON-CAPA domains:");
+ } else {
+ $pretty_domains_string = &mt("in LON-CAPA domain ");
+ }
+ foreach (sort @allowed_domains) {
+ $pretty_domains_string .= "".$_." ";
+ }
+ my %library_servers = &Apache::lonnet::get_unique_servers(\@allowed_domains,
+ 'library');
+ my (%older_library_servers,%okdoms,%domains_for_id);
+ map { $okdoms{$_} = 1; } @allowed_domains;
+ foreach my $key (keys(%library_servers)) {
+ if (&Apache::lonnet::get_server_loncaparev('',$key) =~ /^\'?(\d+)\.(\d+)/) {
+ my $major = $1;
+ my $minor = $2;
+ if (($major < 2) || (($major == 2) && ($minor < 11))) {
+ map { $older_library_servers{$_} = 1; }
+ &Apache::lonnet::machine_ids($library_servers{$key});
+ } else {
+ my %possdoms;
+ map { $possdoms{$_}=1 if ($okdoms{$_}); }
+ &Apache::lonnet::machine_domains($library_servers{$key});
+ $domains_for_id{$key} = join(',',sort(keys(%possdoms)));
+ }
+ }
+ }
+ my %servers = (%library_servers,%older_library_servers);
+ $libraries_to_query = [keys(%servers)];
+ $domains_to_query = \%domains_for_id;
}
- # should not get to this point
- return 'Error. Should not have gone to this point.';
+ return ($libraries_to_query,
+ $pretty_domains_string,
+ $domains_to_query);
}
######################################################################
@@ -801,74 +1493,285 @@ sub advancedsearch {
=pod
-=item &basicsearch()
+=item &parse_basic_search()
+
+Parse the basic search form and return a scalar containing an sql query.
=cut
######################################################################
######################################################################
-sub basicsearch {
- my ($r,$envhash,$hidden)=@_;
- my %ENV=%{$envhash};
+sub parse_basic_search {
+ my ($r,$closebutton)=@_;
+ #
# Clean up fields for safety
for my $field ('basicexp') {
- $ENV{"form.$field"}=~s/[^\w\s\(\)\-]//g;
+ $env{"form.$field"}=~s/[^\w\s\'\"\!\(\)\-\*]//g;
}
foreach ('mode','form','element') {
# is this required? Hmmm.
- next unless (exists($ENV{"form.$_"}));
- $ENV{"form.$_"}=&Apache::lonnet::unescape($ENV{"form.$_"});
- $ENV{"form.$_"}=~s/[^\w\/\s\(\)\=\-\"\']//g;
+ next unless (exists($env{"form.$_"}));
+ $env{"form.$_"}=&unescape($env{"form.$_"});
+ $env{"form.$_"}=~s/[^\w\/\s\(\)\=\-\"\']//g;
}
-
- # Check to see if enough is filled in
- unless (&filled($ENV{'form.basicexp'})) {
- &output_blank_field_error($r);
+ my ($libraries_to_query,$pretty_domains_string,$domains_to_query) =
+ &parse_domain_restrictions();
+ #
+ # Check to see if enough of a query is filled in
+ my $search_string = $env{'form.basicexp'};
+ if (! &filled($search_string)) {
+ &output_blank_field_error($r,$closebutton,'phase=disp_basic');
return OK;
}
+ my $pretty_search_string=$search_string;
+ my @Queries;
+ my @fields = ('title','author','subject','notes','abstract','keywords');
+ my $searchfield;
+ if ($env{'form.area'} eq 'portfolio') {
+ $searchfield = 'concat_ws(" ",pm.'.join(',pm.',@fields).')';
+ } else {
+ $searchfield = 'concat_ws(" ",'.join(',',@fields).')';
+ }
+ my ($error,$SQLQuery) = &process_phrase_input($search_string,
+ $env{'form.related'},
+ $searchfield);
+ if ($error) {
+ &output_unparsed_phrase_error($r,$closebutton,'phase=disp_basic',
+ '','basicexp');
+ return;
+ }
+ push(@Queries,$SQLQuery);
+ #foreach my $q (@Queries) {
+ # &Apache::lonnet::logthis(' '.$q);
+ #}
+ my $final_query;
+ if ($env{'form.area'} eq 'portfolio') {
+ $final_query = 'SELECT pm.*,pa.keynum,pa.scope FROM portfolio_metadata pm, portfolio_access pa WHERE (pm.url = pa.url AND (pa.start < UTC_TIMESTAMP() AND (pa.end IS NULL OR pa.end > UTC_TIMESTAMP())) AND '.join(" AND ",@Queries).')';
+ } else {
+ $final_query = 'SELECT * FROM metadata WHERE '.join(" AND ",@Queries);
+ }
+ #
+ if ($env{'form.related'}) {
+ $pretty_search_string.=' '.&mt('(including related words)');
+ }
+ if (defined($pretty_domains_string) && $pretty_domains_string ne '') {
+ $pretty_search_string .= ' '.$pretty_domains_string;
+ }
+ $pretty_search_string .= "
\n";
+ $pretty_search_string =~ s:^
and ::;
+ &Apache::lonnet::logthis('simple search final query = '.$/.$final_query);
+ return ($final_query,$pretty_search_string,
+ $libraries_to_query,$domains_to_query);
+}
- # Build SQL query string based on form page
- my $query='';
- my $concatarg=join('," ",',
- ('title', 'author', 'subject', 'notes', 'abstract',
- 'keywords'));
- $concatarg='title' if $ENV{'form.titleonly'};
-
- $query=&build_SQL_query('concat('.$concatarg.')',$ENV{'form.'.'basicexp'});
-
- # Get reply (either a hash reference to filehandles or bad connection)
- my $reply=&Apache::lonnet::metadata_query('select * from metadata where '.$query);
- # Output search results
+###############################################################
+###############################################################
- &output_results('Basic',$r,$envhash,$query,$reply,$hidden);
+my @Phrases;
- return OK;
+sub concat {
+ my ($item) = @_;
+ my $results = '';
+ foreach (@$item) {
+ if (ref($_) eq 'ARRAY') {
+ $results .= join(' ',@$_);
+ }
+ }
+ return $results;
}
+sub process_phrase_input {
+ my ($phrase,$related,$field)=@_;
+ #&Apache::lonnet::logthis('phrase = :'.$phrase.':');
+ my $grammar = <<'ENDGRAMMAR';
+ searchphrase:
+ expression /^\Z/ {
+ # &Apache::lonsearchcat::print_item(\@item,0);
+ [@item];
+ }
+ expression:
+ phrase(s) {
+ [@item];
+ }
+ phrase:
+ orword {
+ [@item];
+ }
+ | andword {
+ [@item];
+ }
+ | minusword {
+ unshift(@::Phrases,$item[1]->[0]);
+ unshift(@::Phrases,$item[1]->[1]);
+ [@item];
+ }
+ | word {
+ unshift(@::Phrases,$item[1]);
+ [@item];
+ }
+ #
+ orword:
+ word 'OR' phrase {
+ unshift(@::Phrases,'OR');
+ unshift(@::Phrases,$item[1]);
+ [@item];
+ }
+ | word 'or' phrase {
+ unshift(@::Phrases,'OR');
+ unshift(@::Phrases,$item[1]);
+ [@item];
+ }
+ | minusword 'OR' phrase {
+ unshift(@::Phrases,'OR');
+ unshift(@::Phrases,$item[1]->[0]);
+ unshift(@::Phrases,$item[1]->[1]);
+ [@item];
+ }
+ | minusword 'or' phrase {
+ unshift(@::Phrases,'OR');
+ unshift(@::Phrases,$item[1]->[0]);
+ unshift(@::Phrases,$item[1]->[1]);
+ [@item];
+ }
+ andword:
+ word phrase {
+ unshift(@::Phrases,'AND');
+ unshift(@::Phrases,$item[1]);
+ [@item];
+ }
+ | minusword phrase {
+ unshift(@::Phrases,'AND');
+ unshift(@::Phrases,$item[1]->[0]);
+ unshift(@::Phrases,$item[1]->[1]);
+ [@item];
+ }
+ #
+ minusword:
+ '-' word {
+ [$item[2],'NOT'];
+ }
+ word:
+ "'" term(s) "'" {
+ &Apache::lonsearchcat::concat(\@item);
+ }
+ | '"' term(s) '"' {
+ &Apache::lonsearchcat::concat(\@item);
+ }
+ | term {
+ $item[1];
+ }
+ term:
+ /[\w\Q:!@#$%^&*()+_=|{}<>,.;\\\/?\E\-]+/ {
+ $item[1];
+ }
+ENDGRAMMAR
+ #
+ # The end result of parsing the phrase with the grammar is an array
+ # @::Phrases.
+ # $phrase = "gene splicing" or cat -> "gene splicing","OR","cat"
+ # $phrase = "genetic engineering" -dna ->
+ # "genetic engineering","AND","NOT","dna"
+ # $phrase = cat or dog -poodle -> "cat","OR","dog","AND","NOT","poodle"
+ undef(@::Phrases);
+ my $p = new Parse::RecDescent($grammar);
+ if (! defined($p->searchphrase($phrase))) {
+ &Apache::lonnet::logthis('lonsearchcat:unable to process:'.$phrase);
+ return 'Unable to process phrase '.$phrase;
+ }
+ #
+ # Go through the phrases and make sense of them.
+ # Apply modifiers NOT OR and AND to the phrases.
+ my @NewPhrases;
+ while(@::Phrases) {
+ my $phrase = shift(@::Phrases);
+ # &Apache::lonnet::logthis('phrase = '.$phrase);
+ my $phrasedata;
+ if ($phrase =~ /^(NOT|OR|AND)$/) {
+ if ($phrase eq 'OR') {
+ $phrasedata->{'or'}++;
+ if (! @::Phrases) { $phrasedata = undef; last; }
+ $phrase = shift(@::Phrases);
+ } elsif ($phrase eq 'AND') {
+ $phrasedata->{'and'}++;
+ if (! @::Phrases) { $phrasedata = undef; last; }
+ $phrase = shift(@::Phrases);
+ }
+ if ($phrase eq 'NOT') {
+ $phrasedata->{'negate'}++;
+ if (! @::Phrases) { $phrasedata = undef; last; }
+ $phrase = shift(@::Phrases);
+ }
+ }
+ $phrasedata->{'phrase'} = $phrase;
+ if ($related) {
+ my @NewWords;
+ (undef,@NewWords) = &related_version($phrasedata->{'phrase'});
+ $phrasedata->{'related_words'} = \@NewWords;
+ }
+ push(@NewPhrases,$phrasedata);
+ }
+ #
+ # Actually build the sql query from the phrases
+ my $SQLQuery;
+ foreach my $phrase (@NewPhrases) {
+ my $query;
+ if ($phrase->{'negate'}) {
+ $query .= $field.' NOT LIKE "%'.$phrase->{'phrase'}.'%"';
+ } else {
+ $query .= $field.' LIKE "%'.$phrase->{'phrase'}.'%"';
+ }
+ foreach my $related (@{$phrase->{'related_words'}}) {
+ if ($phrase->{'negate'}) {
+ $query .= ' AND '.$field.' NOT LIKE "%'.$related.'%"';
+ } else {
+ $query .= ' OR '.$field.' LIKE "%'.$related.'%"';
+ }
+ }
+ if ($SQLQuery) {
+ if ($phrase->{'or'}) {
+ $SQLQuery .= ' OR ('.$query.')';
+ } else {
+ $SQLQuery .= ' AND ('.$query.')';
+ }
+ } else {
+ $SQLQuery = '('.$query.')';
+ }
+ }
+ #
+ # &Apache::lonnet::logthis("SQLQuery = $SQLQuery");
+ #
+ return undef,$SQLQuery;
+}
######################################################################
######################################################################
=pod
-=item &build_SQL_query()
+=item &related_version()
+
+Modifies an input string to include related words. Words in the string
+are replaced with parenthesized lists of 'OR'd words. For example
+"torque" is replaced with "(torque OR word1 OR word2 OR ...)".
+
+Note: Using this twice on a string is probably silly.
=cut
######################################################################
######################################################################
-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;
+sub related_version {
+ my ($word) = @_;
+ return (undef) if (lc($word) =~ /\b(or|and|not)\b/);
+ my @Words = &Apache::loncommon::get_related_words($word);
+ # Only use 4 related words
+ @Words = ($#Words>4? @Words[0..4] : @Words);
+ my $result = join " OR ", ($word,@Words);
+ return $result,sort(@Words);
}
+
######################################################################
######################################################################
@@ -876,14 +1779,15 @@ sub build_SQL_query {
=item &build_custommetadata_query()
+Constructs a custom metadata query using a rather heinous regular
+expression.
+
=cut
######################################################################
######################################################################
sub build_custommetadata_query {
my ($field_name,$logic_statement)=@_;
- &Apache::lonnet::logthis("Entered build_custommetadata_query:".
- $field_name.':'.$logic_statement);
my $q=new Text::Query('abc',
-parse => 'Text::Query::ParseAdvanced',
-build => 'Text::Query::BuildAdvancedString');
@@ -898,413 +1802,1215 @@ sub build_custommetadata_query {
\*$2\[\^\\<\]?# *wordtwo[^\<]
\*\\<\\\/$1\\>?# *\<\/wordone\>
/g;
- &Apache::lonnet::logthis("match expression: ".$matchexp);
return $matchexp;
}
+
######################################################################
######################################################################
=pod
-=item &recursive_SQL_query_build()
+=item &build_date_queries()
+
+Builds a SQL logic query to check time/date entries.
+Also reports errors (check for /^Incorrect/).
+
+=cut
+
+######################################################################
+######################################################################
+sub build_date_queries {
+ my ($cafter,$cbefore,$mafter,$mbefore) = @_;
+ my ($result,$error,$pretty_string);
+ #
+ # Verify the input
+ if (! defined($cafter) && ! defined($cbefore) &&
+ ! defined($mafter) && ! defined($mbefore)) {
+ # This is an okay situation, so return undef for the error
+ return (undef,undef,undef);
+ }
+ if ((defined($cafter) && ! defined($cbefore)) ||
+ (defined($cbefore) && ! defined($cafter))) {
+ # This is bad, so let them know
+ $error = &mt('Incorrect entry for the creation date. '.
+ 'You must specify both the beginning and ending dates.');
+ }
+ if (! defined($error) &&
+ ((defined($mafter) && ! defined($mbefore)) ||
+ (defined($mbefore) && ! defined($mafter)))) {
+ # This is also bad, so let them know
+ $error = &mt('Incorrect entry for the last revision date. '.
+ 'You must specify both the beginning and ending dates.');
+ }
+ if (! defined($error)) {
+ #
+ # Build the queries
+ my @queries;
+ if (defined($cbefore) && defined($cafter)) {
+ my (undef,undef,undef,$caday,$camon,$cayear) = localtime($cafter);
+ my (undef,undef,undef,$cbday,$cbmon,$cbyear) = localtime($cbefore);
+ # Correct for year being relative to 1900
+ $cayear+=1900; $cbyear+=1900;
+ my $cquery=
+ '(creationdate BETWEEN '.
+ "'".$cayear.'-'.$camon.'-'.$caday."'".
+ ' AND '.
+ "'".$cbyear.'-'.$cbmon.'-'.$cbday." 23:59:59')";
+ $pretty_string .= '
' if (defined($pretty_string));
+ $pretty_string .=
+ &mt('created between [_1] and [_2]',
+ &Apache::lonlocal::locallocaltime($cafter),
+ &Apache::lonlocal::locallocaltime($cbefore+24*60*60-1));
+ push(@queries,$cquery);
+ $pretty_string =~ s/ 00:00:00//g;
+ }
+ if (defined($mbefore) && defined($mafter)) {
+ my (undef,undef,undef,$maday,$mamon,$mayear) = localtime($mafter);
+ my (undef,undef,undef,$mbday,$mbmon,$mbyear) = localtime($mbefore);
+ # Correct for year being relative to 1900
+ $mayear+=1900; $mbyear+=1900;
+ my $mquery=
+ '(lastrevisiondate BETWEEN '.
+ "'".$mayear.'-'.$mamon.'-'.$maday."'".
+ ' AND '.
+ "'".$mbyear.'-'.$mbmon.'-'.$mbday." 23:59:59')";
+ push(@queries,$mquery);
+ $pretty_string .= '
' if (defined($pretty_string));
+ $pretty_string .=
+ &mt('last revised between [_1] and [_2]',
+ &Apache::lonlocal::locallocaltime($mafter),
+ &Apache::lonlocal::locallocaltime($mbefore+24*60*60-1));
+ $pretty_string =~ s/ 00:00:00//g;
+ }
+ if (@queries) {
+ $result .= join(" AND ",@queries);
+ }
+ }
+ return ($result,$error,$pretty_string);
+}
+
+######################################################################
+######################################################################
+
+=pod
+
+=item ©right_check()
+
+Inputs: $Metadata, a hash pointer of metadata for a resource.
+
+Returns: 1 if the resource is available to the user making the query,
+ 0 otherwise.
=cut
######################################################################
######################################################################
-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 'not') {
- $value=~s/like/not like/;
-# $replacement="($dkey not like $value)";
- $replacement="$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
- );
+sub copyright_check {
+ my $Metadata = shift;
+ # Check copyright tags and skip results the user cannot use
+ my (undef,undef,$resdom,$resname) = split('/',
+ $Metadata->{'url'});
+ # Check for priv
+ if ($Metadata->{'copyright'} eq 'priv') {
+ unless (($env{'user.name'} eq $resname) &&
+ ($env{'user.domain'} eq $resdom)) {
+ return 0;
+ }
}
- &recursive_SQL_query_build($dkey,$pattern);
+ # Check for domain
+ if (($Metadata->{'copyright'} eq 'domain') &&
+ ($env{'user.domain'} ne $resdom)) {
+ return 0;
+ }
+ # Check for custom rights
+ if ($Metadata->{'copyright'} eq 'custom') {
+ return &Apache::lonnet::customaccess('bre',$Metadata->{'url'});
+ }
+ return 1;
}
######################################################################
######################################################################
-=pod
+=pod
-=item &build_date_queries()
+=item &ensure_db_and_table()
+
+Ensure we can get lonmysql to connect to the database and the table we
+need exists.
+
+Inputs: $r, table id
+
+Returns: undef on error, 1 if the table exists.
=cut
######################################################################
######################################################################
-sub build_date_queries {
- my ($cmonth1,$cday1,$cyear1,$cmonth2,$cday2,$cyear2,
- $lmonth1,$lday1,$lyear1,$lmonth2,$lday2,$lyear2)=@_;
- my @queries;
- if ($cmonth1 or $cday1 or $cyear1 or $cmonth2 or $cday2 or $cyear2) {
- unless ($cmonth1 and $cday1 and $cyear1 and
- $cmonth2 and $cday2 and $cyear2) {
- return "Incorrect entry for the creation date. You must specify ".
- "a starting month, day, and year and an ending month, ".
- "day, and year.";
- }
- my $cnumeric1=sprintf("%d%2d%2d",$cyear1,$cmonth1,$cday1);
- $cnumeric1+=0;
- my $cnumeric2=sprintf("%d%2d%2d",$cyear2,$cmonth2,$cday2);
- $cnumeric2+=0;
- if ($cnumeric1>$cnumeric2) {
- return "Incorrect entry for the creation date. The starting ".
- "date must occur before the ending date.";
- }
- my $cquery="(creationdate BETWEEN '$cyear1-$cmonth1-$cday1' AND '".
- "$cyear2-$cmonth2-$cday2 23:59:59')";
- push @queries,$cquery;
- }
- if ($lmonth1 or $lday1 or $lyear1 or $lmonth2 or $lday2 or $lyear2) {
- unless ($lmonth1 and $lday1 and $lyear1 and
- $lmonth2 and $lday2 and $lyear2) {
- return "Incorrect entry for the last revision date. You must ".
- "specify a starting month, day, and year and an ending ".
- "month, day, and year.";
- }
- my $lnumeric1=sprintf("%d%2d%2d",$lyear1,$lmonth1,$lday1);
- $lnumeric1+=0;
- my $lnumeric2=sprintf("%d%2d%2d",$lyear2,$lmonth2,$lday2);
- $lnumeric2+=0;
- if ($lnumeric1>$lnumeric2) {
- return "Incorrect entry for the last revision date. The ".
- "starting date must occur before the ending date.";
- }
- my $lquery="(lastrevisiondate BETWEEN '$lyear1-$lmonth1-$lday1' AND '".
- "$lyear2-$lmonth2-$lday2 23:59:59')";
- push @queries,$lquery;
+sub ensure_db_and_table {
+ my ($r,$table) = @_;
+ ##
+ ## Sanity check the table id.
+ ##
+ if (! defined($table) || $table eq '' || $table =~ /\D/ ) {
+ $r->print(&Apache::loncommon::start_page('Error')
+ .'
' + .&mt('Unable to retrieve search results. ' + .'Unable to determine the table results were saved in.') + .'
' + . ''.&mt('Internal info:').'
' + .''.$table.'' + .&Apache::loncommon::end_page() + ); + return undef; } - if (@queries) { - return join(" AND ",@queries); + ## + ## Make sure we can connect and the table exists. + ## + my $connection_result = &Apache::lonmysql::connect_to_db(); + if (!defined($connection_result)) { + $r->print( + '
' + .&mt('Unable to connect to the MySQL database where your results are saved.') + .'
' + .&Apache::loncommon::end_page() + ); + &Apache::lonnet::logthis("lonsearchcat: unable to get lonmysql to". + " connect to database."); + &Apache::lonnet::logthis(&Apache::lonmysql::get_error()); + return undef; + } + my $table_check = &Apache::lonmysql::check_table($table); + if (! defined($table_check)) { + $r->print( + '' + .&mt('A MySQL error has occurred.') + .'
' + .&Apache::loncommon::end_page()); + &Apache::lonnet::logthis("lonmysql was unable to determine the status". + " of table ".$table); + return undef; + } elsif (! $table_check) { + $r->print( + '' + .&mt('The table of results could not be found.') + .'
' + ); + &Apache::lonnet::logthis("The user requested a table, ".$table. + ", that could not be found."); + return undef; } - return ''; + return 1; } ###################################################################### ###################################################################### -=pod +=pod -=item &output_results() +=item &print_sort_form() -Format and output results based on a reply list. -There are two windows that this function writes to. The main search -window ("srch") has a listing of the results. A secondary window ("popwin") -gives the status of the network search (time elapsed, number of machines -contacted, etc.) +The sort feature is not implemented at this time. This form just prints +a link to change the search query. =cut ###################################################################### ###################################################################### -sub output_results { - my $fnum; # search result counter - my ($mode,$r,$envhash,$query,$replyref,$hidden)=@_; - my %ENV=%{$envhash}; - my %rhash=%{$replyref}; - my $compiledresult=''; - my $timeremain=300; # (seconds) - my $elapsetime=0; - my $resultflag=0; - my $tflag=1; - # - # make query information persistent to allow for subsequent revision - my $persistent=&make_persistent(); - # spit out the generic header - $r->print(&search_results_header()); +sub print_sort_form { + my ($r,$pretty_query_string) = @_; + + ## + my %SortableFields=&Apache::lonlocal::texthash( + id => 'Default', + title => 'Title', + author => 'Author', + subject => 'Subject', + url => 'URL', + version => 'Version Number', + mime => 'Mime type', + lang => 'Language', + owner => 'Owner/Publisher', + copyright => 'Copyright', + hostname => 'Host', + creationdate => 'Creation Date', + lastrevisiondate => 'Revision Date' + ); + ## + my $table = $env{'form.table'}; + return if (! &ensure_db_and_table($r,$table)); + ## + ## Get the number of results + ## + my $total_results = &Apache::lonmysql::number_of_rows($table); + if (! defined($total_results)) { + $r->print("A MySQL error has occurred.". + &Apache::loncommon::end_page()); + &Apache::lonnet::logthis("lonmysql was unable to determine the number". + " of rows in table ".$table); + &Apache::lonnet::logthis(&Apache::lonmysql::get_error()); + return; + } + my $start_page = &Apache::loncommon::start_page('Results',undef); + my $breadcrumbs= + &Apache::lonhtmlcommon::breadcrumbs('Searching','Searching', + $env{'form.catalogmode'} ne 'import'); + + my $result = <' + .&mt('Total of [quant,_1,match,matches] to your query.',$total_results) + .' '.$revise.'
' + .''.&mt('Search: ').$pretty_query_string + .'
'; + $r->print($result.&Apache::loncommon::end_page()); + return; +} + +##################################################################### +##################################################################### + +=pod + +=item MySQL Table Description + +MySQL table creation requires a precise description of the data to be +stored. The use of the correct types to hold data is vital to efficient +storage and quick retrieval of records. The columns must be described in +the following format: + +=cut + +##################################################################### +##################################################################### +# +# These should probably be scoped but I don't have time right now... +# +my @Datatypes; +my @Fullindicies; + +###################################################################### +###################################################################### + +=pod + +=item &create_results_table() + +Creates the table of search results by calling lonmysql. Stores the +table id in $env{'form.table'} + +Inputs: search area - either res or portfolio + +Returns: the identifier of the table on success, undef on error. + +=cut + +###################################################################### +###################################################################### +sub set_up_table_structure { + my ($tabletype) = @_; + my ($datatypes,$fullindicies) = + &LONCAPA::lonmetadata::describe_metadata_storage($tabletype); + # Copy the table description before modifying it... + @Datatypes = @{$datatypes}; + unshift(@Datatypes,{name => 'id', + type => 'MEDIUMINT', + restrictions => 'UNSIGNED NOT NULL', + primary_key => 'yes', + auto_inc => 'yes' }); + @Fullindicies = @{$fullindicies}; + return; +} + +sub create_results_table { + my ($area) = @_; + if ($area eq 'portfolio') { + &set_up_table_structure('portfolio_search'); + } else { + &set_up_table_structure('metadata'); + } + my $table = &Apache::lonmysql::create_table + ( { columns => \@Datatypes, + FULLTEXT => [{'columns' => \@Fullindicies},], + } ); + if (defined($table)) { + $env{'form.table'} = $table; + return $table; + } + return undef; # Error... +} + +###################################################################### +###################################################################### + +=pod + +=item Search Status update functions + +Each of the following functions changes the values of one of the +input fields used to display the search status to the user. The names +should be explanatory. + +Inputs: Apache request handler ($r), text to display. + +Returns: Nothing. + +=over 4 + +=item &update_count_status() + +=item &update_status() + +=item &update_seconds() + +=back + +=cut + +###################################################################### +###################################################################### +sub update_count_status { + my ($r,$text) = @_; + $text =~ s/\'/\\\'/g; + $r->print(< +SCRIPT + $r->rflush(); - # begin showing the cataloged results - $r->print(<Basic search: '.$ENV{'form.basicexp'}.'
'); - } elsif ($mode eq 'Advanced') { - $r->print('Advanced search '.$query.'
'); + my $start_page = &Apache::loncommon::start_page('Search Status',undef); + my $breadcrumbs = + &Apache::lonhtmlcommon::breadcrumbs('Searching','Searching', + $env{'form.catalogmode'} ne 'import'); + $r->print(<' + .'' + .' ' + .'' + .' ' + .'' + .'
'; +} + +###################################################################### +###################################################################### + +=pod + +=item &display_results() + +Prints the results out for selection and perusal. + +=cut + +###################################################################### +###################################################################### +sub display_results { + my ($r,$importbutton,$closebutton,$diropendb,$area) = @_; + my $connection = $r->connection; + $r->print(&search_results_header($importbutton,$closebutton)); + ## + ## Set viewing function + ## + my $viewfunction = $Views{$env{'form.viewselect'}}; + if (!defined($viewfunction)) { + $r->print('' + .&mt('Internal Error - Bad view selected.') + .'
'."\n"); + $r->rflush(); + return; + } ## - ## Prepare for the main loop below + ## $checkbox_num is a count of the number of checkboxes output on the + ## page this is used only during catalogmode=import. + my $checkbox_num = 0; ## - my $servercount=0; - my $hitcountsum=0; - my $servernum=(keys %rhash); - my $serversleft=$servernum; - ## - ## Run until we run out of time or we run out of servers - ## - while($serversleft && $timeremain) { - ## - ## %rhash has servers deleted from it as results come in - ## (within the foreach loop below). - ## - foreach my $rkey (sort keys %rhash) { - $servercount++; - $tflag=1; - $compiledresult=''; - my $hostname=$rkey; - my $reply=$rhash{$rkey}; - my @results; - if ($reply eq 'con_lost') { - &popwin_imgupdate($r,$rkey,"srvbad.gif"); - $serversleft--; - delete $rhash{$rkey}; + ## Get the catalog controls setup + ## + my $action = "/adm/searchcat?phase=results"; + ## + ## Deal with import by opening the import db file. + if ($env{'form.catalogmode'} eq 'import') { + if (! tie(%groupsearch_db,'GDBM_File',$diropendb, + &GDBM_WRCREAT(),0640)) { + # NOTE: this can happen when a previous request to searchcat?phase=results gets interrupted + # (%groupsearch_db is not untied) + $r->print(''. + &mt('Unable to save import results.'). + '
'. + ''. + &Apache::loncommon::end_page()); + $r->rflush(); + return; + } + # untie %groupsearch_db if the connection gets aborted before the end + $r->register_cleanup(sub { + untie %groupsearch_db if (tied(%groupsearch_db)); + }); + } + ## + ## Prepare the table for querying + my $table = $env{'form.table'}; + return if (! &ensure_db_and_table($r,$table)); + ## + ## Get the number of results + my $total_results = &Apache::lonmysql::number_of_rows($table); + if (! defined($total_results)) { + $r->print(''. + &mt('A MySQL error has occurred.'). + '
'. + ''. + &Apache::loncommon::end_page()); + &Apache::lonnet::logthis("lonmysql was unable to determine the number". + " of rows in table ".$table); + &Apache::lonnet::logthis(&Apache::lonmysql::get_error()); + return; + } + ## + ## Determine how many results we need to get + $env{'form.start'} = 1 if (! exists($env{'form.start'})); + $env{'form.show'} = 20 if (! exists($env{'form.show'})); + if (exists($env{'form.prev'})) { + $env{'form.start'} -= $env{'form.show'}; + } elsif (exists($env{'form.next'})) { + $env{'form.start'} += $env{'form.show'}; + } + $env{'form.start'} = 1 if ($env{'form.start'}<1); + $env{'form.start'} = $total_results if ($env{'form.start'}>$total_results); + my $min = $env{'form.start'}; + my $max; + if ($env{'form.show'} eq 'all') { + $max = $total_results ; + } else { + $max = $min + $env{'form.show'} - 1; + $max = $total_results if ($max > $total_results); + } + ## + ## Output form elements + $r->print(&hidden_field('table'). + &hidden_field('phase'). + &hidden_field('persistent_db_id'). + &hidden_field('start'). + &hidden_field('area') + ); + # + # Build sorting selector + my @fields = + ( + {key=>'default' }, + {key=>'title' }, + {key =>'author' }, + {key =>'subject'}, + {key =>'url',desc=>'URL'}, + {key =>'keywords'}, + {key =>'language'}, + {key =>'creationdate'}, + {key =>'lastrevisiondate'}, + {key =>'owner'}, + {key =>'copyright'}, + {key =>'authorspace'}, + {key =>'lowestgradelevel'}, + {key =>'highestgradelevel'}, + {key =>'standards',desc=>'Standards'}, + ); + if ($area eq 'portfolio') { + push(@fields, + ( + {key => 'scope'}, + {key => 'keynum'}, + )); + } else { + push(@fields, + ( + {key =>'count',desc=>'Number of accesses'}, + {key =>'stdno',desc=>'Students Attempting'}, + {key =>'avetries',desc=>'Average Number of Tries'}, + {key =>'difficulty',desc=>'Mean Degree of Difficulty'}, + {key =>'disc',desc=>'Mean Degree of Discrimination'}, + {key =>'clear',desc=>'Evaluation: Clear'}, + {key =>'technical',desc=>'Evaluation: Technically Correct'}, + {key =>'correct',desc=>'Evaluation: Material is Correct'}, + {key =>'helpful',desc=>'Evaluation: Material is Helpful'}, + {key =>'depth',desc=>'Evaluation: Material has Depth'}, + )); + } + my %fieldnames = &Apache::lonmeta::fieldnames(); + my @field_order; + foreach my $field_data (@fields) { + push(@field_order,$field_data->{'key'}); + if (! exists($field_data->{'desc'})) { + $field_data->{'desc'}=$fieldnames{$field_data->{'key'}}; + } else { + if (! defined($field_data->{'desc'})) { + $field_data->{'desc'} = ucfirst($field_data->{'key'}); + } + $field_data->{'desc'} = &mt($field_data->{'desc'}); + } + } + my %sort_fields = map {$_->{'key'},$_->{'desc'}} @fields; + $sort_fields{'select_form_order'} = \@field_order; + $env{'form.sortorder'} = 'desc' if (! exists($env{'form.sortorder'})); + if (! exists($env{'form.sortfield'})) { + if ($area eq 'portfolio') { + $env{'form.sortfield'} = 'owner'; + } else { + $env{'form.sortfield'} = 'count'; + } + } + if (! exists($env{'form.sortorder'})) { + if ($env{'form.sortfield'}=~/^(count|stdno|disc|clear|technical|correct|helpful)$/) { + $env{'form.sortorder'}='desc'; } else { - # must do since 'use strict' checks for tainting - $reply=~/^([\.\w]+)$/; - my $replyfile=$r->dir_config('lonDaemons').'/tmp/'.$1; - $reply=~/(.*?)\_/; - { - my $temp=0; - WLOOP: while (1) { - if (-e $replyfile && $tflag) { - &popwin_imgupdate($r,$rkey,"srvhalf.gif"); - &popwin_js($r,'popwin.hc["'.$rkey.'"]='. - '"still transferring..."'.';'); - $tflag=0; - } - if (-e "$replyfile.end") { - $serversleft--; - delete $rhash{$rkey}; - if (-s $replyfile) { - &popwin_imgupdate($r,$rkey,"srvgood.gif"); - my $fh=Apache::File->new($replyfile) or - ($r->print('ERROR: file '. - $replyfile.' cannot be opened') and - return OK); - @results=<$fh> if $fh; - $hitcount{$rkey}=@results+0; - &popwin_js($r,'popwin.hc["'.$rkey.'"]='. - $hitcount{$rkey}.';'); - $hitcountsum+=$hitcount{$rkey}; - &popwin_js($r,'popwin.document.forms.popremain.'. - 'numhits.value='.$hitcountsum.';'); - } - else { - &popwin_imgupdate($r,$rkey,"srvempty.gif"); - &popwin_js($r,'popwin.hc["'.$rkey.'"]=0;'); - } - last WLOOP; - } # end of if ( -e "$replyfile.end") - last WLOOP unless $timeremain; - sleep 1; # wait for daemons to write files? - $timeremain--; - $elapsetime++; - last WLOOP if ($temp>1); - &popwin_js($r,"popwin.document.popremain.". - "elapsetime.value=$elapsetime;"); - $temp++; - } - } - &popwin_js($r,'popwin.document.whirly.'. - 'src="/adm/lonIcons/lonanimend.gif";'); - } # end of if ($reply eq 'con_lost') else statement - my $customshow=''; - my $extrashow=''; - my @customfields; - if ($ENV{'form.customshow'}) { - $customshow=$ENV{'form.customshow'}; - $customshow=~s/[^\w\s]//g; - my @fields=map {"$_:"} - split(/\s+/,$customshow); - @customfields=split(/\s+/,$customshow); - if ($customshow) { - $extrashow="'.&mt('There are currently no results.').'
'. + "". + &Apache::loncommon::end_page()); + return; + } else { + $r->print('' + .&mt('There were no results matching your query.') + .'
'); + } else { + $r->print( + &prev_next_buttons($min,$env{'form.show'},$total_results, + "table=".$env{'form.table'}. + "&phase=results". + "&persistent_db_id=". + $env{'form.persistent_db_id'}) + ); + } + $r->print("".&Apache::loncommon::end_page()); + $r->rflush(); + untie %groupsearch_db if (tied(%groupsearch_db)); + return; +} + +###################################################################### +###################################################################### + +=pod + +=item &catalogmode_output($title,$url,$fnum,$checkbox_num) + +Returns html needed for the various catalog modes. Gets inputs from +$env{'form.catalogmode'}. Stores data in %groupsearch_db. + +=cut + +###################################################################### +###################################################################### +sub catalogmode_output { + my $output = ''; + my ($title,$url,$fnum,$checkbox_num) = @_; + if ($env{'form.catalogmode'} eq 'interactive') { + $title=~ s/\'/\\\'/g; + if ($env{'form.catalogmode'} eq 'interactive') { + $output.=<
-Subject: $subject
-Keyword(s): $keywords
-Notes: $notes
-MIME Type:
-END
- $result.=&Apache::loncommon::filedescription($mime);
- $result.=<
-$shortabstract -
-END + my ($prefix,%values) = @_; + my $result; + my $jumpurl=$values{'url'}; + $jumpurl=~s|^/ext/|http://|; + $result .= + ''.$prefix. + ''.' '. + ''.$values{'title'}."\n". + &display_tools($values{'title'}, $jumpurl). + "\n".
+ ''.$values{'author'}.','.
+ ' '.$values{'owner'}.'
';
+ foreach my $field
+ (
+ { name=>'url',
+ translate => 'URL: [_1]',
+ special => 'url link',},
+ { name=>'subject',
+ translate => 'Subject: [_1]',},
+ { name=>'keywords',
+ translate => 'Keywords: [_1]',},
+ { name=>'notes',
+ translate => 'Notes: [_1]',},
+ { name=>'mimetag',
+ translate => 'MIME Type: [_1]',},
+ { name=>'standards',
+ translate => 'Standards:[_1]',},
+ { name=>'copyrighttag',
+ translate => 'Copyright/Distribution: [_1]',},
+ { name=>'count',
+ format => "%d",
+ translate => 'Access Count: [_1]',},
+ { name=>'stdno',
+ format => "%d",
+ translate => 'Number of Students: [_1]',},
+ { name=>'avetries',
+ format => "%.2f",
+ translate => 'Average Tries: [_1]',},
+ { name=>'disc',
+ format => "%.2f",
+ translate => 'Degree of Discrimination: [_1]',},
+ { name=>'difficulty',
+ format => "%.2f",
+ translate => 'Degree of Difficulty: [_1]',},
+ { name=>'clear',
+ format => "%.2f",
+ translate => 'Clear: [_1]',},
+ { name=>'depth',
+ format => "%.2f",
+ translate => 'Depth: [_1]',},
+ { name=>'helpful',
+ format => "%.2f",
+ translate => 'Helpful: [_1]',},
+ { name=>'correct',
+ format => "%.2f",
+ translate => 'Correct: [_1]',},
+ { name=>'technical',
+ format => "%.2f",
+ translate => 'Technical: [_1]',},
+ { name=>'comefrom_list',
+ type => 'list',
+ translate => 'Resources that lead up to this resource in maps',},
+ { name=>'goto_list',
+ type => 'list',
+ translate => 'Resources that follow this resource in maps',},
+ { name=>'sequsage_list',
+ type => 'list',
+ translate => 'Resources using or importing resource',},
+ ) {
+ next if (! exists($values{$field->{'name'}}) ||
+ $values{$field->{'name'}} eq '');
+ if (exists($field->{'type'}) && $field->{'type'} eq 'list') {
+ $result .= ''.&mt($field->{'translate'}).'';
+ foreach my $item (split(',',$values{$field->{'name'}})){
+ $item = &Apache::lonnet::clutter($item);
+ $result .= '
'.&display_url($item,1).'
';
+ }
+ } elsif (exists($field->{'format'}) && $field->{'format'} ne ''){
+ $result.= &mt($field->{'translate'},
+ sprintf($field->{'format'},
+ $values{$field->{'name'}}))."
\n";
+ } else {
+ if ($field->{'special'} eq 'url link') {
+ $result .= '
'.&display_url($jumpurl,1).'
';
+ } else {
+ $result.= &mt($field->{'translate'},
+ $values{$field->{'name'}});
+ }
+ $result .= "
\n";
+ }
+ }
+ $result .= "
'.$values{'extrashow'}.'
'; + } + if (exists($values{'shortabstract'}) && $values{'shortabstract'} ne '') { + $result .= ''.$values{'shortabstract'}.'
'; + } return $result; } +sub detailed_citation_preview { + my ($prefix,%values)=@_; + return &detailed_citation_view($prefix,%values). + '+$errorstring +
++$revise +
+$end_page +ENDPAGE } ###################################################################### @@ -1715,41 +3739,51 @@ sub filled { =item &output_blank_field_error() +Output a complete page that indicates the user has not filled in enough +information to do a search. + +Inputs: $r (Apache request handle), $closebutton, $parms. + +Returns: nothing + +$parms is extra information to include in the 'Revise search request' link. + =cut ###################################################################### ###################################################################### sub output_blank_field_error { - my ($r)=@_; - # make query information persistent to allow for subsequent revision - my $persistent=&make_persistent(); + my ($r,$closebutton,$parms,$hidden_fields)=@_; + my $errormsg = &mt('You did not fill in enough information for the search to be started. You need to fill in relevant fields on the search page in order for a query to be processed.'); + my $revise = &mt('Revise Search Request'); + my $heading = &mt('Unactionable Search Query'); + my $start_page = &Apache::loncommon::start_page('Search'); + my $end_page = &Apache::loncommon::end_page(); + if ($closebutton) { + $closebutton = ''.$closebutton.'
+$errormsg +
-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. +$revise
- - -RESULTS +$end_page +ENDPAGE + return; } ###################################################################### @@ -1761,35 +3795,36 @@ RESULTS Output a full html page with an error message. +Inputs: + + $r, the request pointer. + $message, the error message for the user. + $closebutton, the specialized close button needed for groupsearch. + =cut ###################################################################### ###################################################################### sub output_date_error { - my ($r,$message)=@_; + my ($r,$message,$closebutton,$hidden_fields)=@_; # make query information persistent to allow for subsequent revision - my $persistent=&make_persistent(); - + my $start_page = &Apache::loncommon::start_page('Search'); + my $end_page = &Apache::loncommon::end_page(); + my $heading = &mt('Error'); $r->print(<+
$message
- - +$end_page RESULTS } @@ -1800,7 +3835,7 @@ RESULTS =item &start_fresh_session() -Cleans the global %hash by removing all fields which begin with +Cleans the global %groupsearch_db by removing all fields which begin with 'pre_' or 'store'. =cut @@ -1808,263 +3843,33 @@ Cleans the global %hash by removing all ###################################################################### ###################################################################### sub start_fresh_session { - delete $hash{'mode_catalog'}; - foreach (keys %hash) { + delete $groupsearch_db{'mode_catalog'}; + foreach (keys(%groupsearch_db)) { if ($_ =~ /^pre_/) { - delete $hash{$_}; + delete $groupsearch_db{$_}; } if ($_ =~ /^store/) { - delete $hash{$_}; + delete $groupsearch_db{$_}; } } } -###################################################################### -###################################################################### - -=pod - -=item &popwin_js() send javascript to popwin - -=cut +1; -###################################################################### -###################################################################### -sub popwin_js { - # Print javascript out to popwin, but make sure we dont generate - # any javascript errors in doing so. - my ($r,$text) = @_; - $r->print(<<"END"); - -END - $r->rflush(); + &Apache::lonmysql::disconnect_from_db(); + return OK; } -###################################################################### -###################################################################### - -=pod - -=item &popwin_imgupdate() - -Send a given image (and its location) out to the browser. Takes as -input $r, loncapa server id, and an icon URL. - -=cut - -###################################################################### -###################################################################### -sub popwin_imgupdate { - my ($r,$server,$icon) = @_; - &popwin_js($r,'popwin.document.img_'.$hostdomains{$server}.'_'.$server.'.'. - 'src="/adm/lonIcons/'.$icon.'";'); -} - -1; - __END__ =pod =back -=over 4 - -=head1 HANDLER SUBROUTINE - -This routine is called by Apache and mod_perl. - -=over 4 - -=item * - -configure dynamic components of interface - -=item * - -determine current user - -=item * - -see if a search invocation should be done - -=item * - -else, begin building search interface to output - -=item * - -compute date selection boxes - -=item * - -compute customized metadata field - -=item * - -print screen - -=back - -=head1 OTHER SUBROUTINES - -=over 4 - -=item * - -get_unprocessed_cgi() : reads in critical name/value pairs that may have not -been processed and passed into %ENV by the web server - -=item * - -make_persistent() : makes a set of hidden HTML fields to make -SQL search interface information to be persistent - -=back - -WEB INTERFACE COMPONENT FUNCTIONS - -=over 4 - -=item * - -simpletextfield(name,value) : returns HTML formatted string for simple text -field - -=item * - -simplecheckbox(name,value) : returns HTML formatted string for simple -checkbox - -=item * - -searchphrasefield(title,name,value) : returns HTML formatted string for -a search expression phrase field - -=item * - -dateboxes(name, defaultmonth, defaultday, defaultyear) : returns HTML -formatted string for a calendar date - -=item * - -selectbox(title,name,value,%HASH=options) : returns HTML formatted string for -a selection box field - -=back - -SEARCH FUNCTIONS - -=over 4 - -=item * - -advancedsearch(server reference, environment reference) : perform a complex -multi-field logical query - -=item * - -basicsearch(server reference, environment reference) : perform a simple -single-field logical query - -=item * - -build_SQL_query(field name, logic) : builds a SQL query string from a -logical expression with AND/OR keywords - -=item * - -build_custommetadata_query(field_name, logic_statement) : builds a perl -regular expression from a logical expression with AND/OR keywords - -=item * - -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 - -=item * - -build_date_queries(cmonth1, cday1, cyear1, cmonth2, cday2, cyear2, -lmonth1, lday1, lyear1, lmonth2, lday2, lyear2) : -Builds a SQL logic query to check time/date entries. - -=back - -OUTPUTTING RESULTS FUNCTION - -=over 4 - -=item * - -output_results(output mode, server reference, environment reference, -reply list reference) : outputs results from search - -=back - -DIFFERENT WAYS TO VIEW METADATA RECORDS - -=over 4 - -=item * - -detailed_citation_view(ORDERED METADATA LIST FOR A RESULT OBJECT INSTANCE) : -see metadata viewing notes below - -=item * - -summary_view(ORDERED METADATA LIST FOR A RESULT OBJECT INSTANCE) : -see metadata viewing notes below - -=item * - -fielded_format_view(ORDERED METADATA LIST FOR A RESULT OBJECT INSTANCE) : -see metadata viewing notes below - -=item * - -xml_sgml_view(ORDERED METADATA LIST FOR A RESULT OBJECT INSTANCE) : -see metadata viewing notes below - -=back - - _____________________________________________________________________ - | * Metadata viewing notes | - | Output is a HTML-ified string. | - | Input arguments are title, author, subject, url, keywords, version, | - | notes, short abstract, mime, language, creation date, | - | last revision date, owner, copyright, hostname, httphost, and | - | extra custom metadata to show. | - --------------------------------------------------------------------- - -TEST CONDITIONAL FUNCTIONS - -=over 4 - -=item * - -filled(field) : determines whether a given field has been filled - -=back - -ERROR FUNCTIONS - -=over 4 - -=item * - -output_blank_field_error(server reference) : outputs a message saying that -more fields need to be filled in - -=item * - -output_date_error(server reference, error message) : - - -=back - =cut