--- loncom/interface/lonsearchcat.pm 2004/01/01 20:13:17 1.200 +++ loncom/interface/lonsearchcat.pm 2004/05/07 18:50:14 1.224 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Search Catalog # -# $Id: lonsearchcat.pm,v 1.200 2004/01/01 20:13:17 www Exp $ +# $Id: lonsearchcat.pm,v 1.224 2004/05/07 18:50:14 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -48,7 +48,7 @@ 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 @@ -73,6 +73,8 @@ use Apache::lonmysql(); use Apache::lonmeta; use Apache::lonhtmlcommon; use Apache::lonlocal; +use LONCAPA::lonmetadata(); +use HTML::Entities(); ###################################################################### ###################################################################### @@ -115,7 +117,7 @@ sub handler { my $loaderror=&Apache::lonnet::overloaderror($r); if ($loaderror) { return $loaderror; } - + # my $closebutton; # button that closes the search window # This button is different for the RAT compared to # normal invocation. @@ -148,7 +150,8 @@ sub handler { ## Initialize global variables ## my $domain = $r->dir_config('lonDefDomain'); - $diropendb= "/home/httpd/perl/tmp/$ENV{'user.domain'}_$ENV{'user.name'}_searchcat.db"; + $diropendb= "/home/httpd/perl/tmp/". + "$ENV{'user.domain'}_$ENV{'user.name'}_searchcat.db"; # # set the name of the persistent database # $ENV{'form.persistent_db_id'} can only have digits in it. @@ -163,11 +166,32 @@ sub handler { '_'.&Apache::lonnet::escape($ENV{'user.name'}). '_'.$ENV{'form.persistent_db_id'}.'_persistent_search.db'; ## - 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"); - $r->print(<'/adm/searchcat?'. + 'catalogmode='.$ENV{'form.catalogmode'}. + '&launch='.$ENV{'form.launch'}. + '&mode='.$ENV{'form.mode'}, + text=>"Course and Catalog Search", + bug=>'Searching',}); + } else { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>'/adm/searchcat?'. + 'catalogmode='.$ENV{'form.catalogmode'}. + '&launch='.$ENV{'form.launch'}. + '&mode='.$ENV{'form.mode'}, + text=>"Catalog Search", + 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); + $r->print(< LON-CAPA Search Error $bodytag @@ -176,8 +200,11 @@ error and has been logged. Please alert END - return OK; + return OK; + } } + } else { + &clean_up_environment(); } ## ## Clear out old values from groupsearch database @@ -204,24 +231,19 @@ END $hidden_fields = ''."\n"; if (exists($ENV{'form.catalogmode'})) { - $hidden_fields .= ''."\n"; + $hidden_fields .= &hidden_field('catalogmode'); } if (exists($ENV{'form.form'})) { - $hidden_fields .= ''."\n"; + $hidden_fields .= &hidden_field('form'); } if (exists($ENV{'form.element'})) { - $hidden_fields .= ''."\n"; + $hidden_fields .= &hidden_field('element'); } if (exists($ENV{'form.titleelement'})) { - $hidden_fields .= ''."\n"; + $hidden_fields .= &hidden_field('titleelement'); } if (exists($ENV{'form.mode'})) { - $hidden_fields .= ''."\n"; + $hidden_fields .= &hidden_field('mode'); } ## ## Configure dynamic components of interface @@ -263,6 +285,32 @@ END } $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'; + } elsif ($ENV{'form.phase'} eq 'course_search') { + $ENV{'form.searchmode'} = 'course_search'; + } + # + if ($ENV{'form.searchmode'} eq 'advanced') { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>'/adm/searchcat?phase=disp_adv&'. + 'catalogmode='.$ENV{'form.catalogmode'}. + '&launch='.$ENV{'form.launch'}. + '&mode='.$ENV{'form.mode'}, + text=>"Advanced Search", + bug=>'Searching',}); + } elsif ($ENV{'form.searchmode'} eq 'course search') { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>'/adm/searchcat?phase=disp_adv&'. + 'catalogmode='.$ENV{'form.catalogmode'}. + '&launch='.$ENV{'form.launch'}. + '&mode='.$ENV{'form.mode'}, + text=>"Course Search", + bug=>'Searching',}); + } ## ## Switch on the phase ## @@ -287,19 +335,17 @@ END &course_search($r); } elsif(($ENV{'form.phase'} eq 'basic_search') || ($ENV{'form.phase'} eq 'adv_search')) { - $ENV{'form.searchmode'} = 'basic'; - if ($ENV{'form.phase'} eq 'adv_search') { - $ENV{'form.searchmode'} = 'advanced'; - } # Set up table if (! defined(&create_results_table())) { my $errorstring=&Apache::lonmysql::get_error(); + &Apache::lonnet::logthis('lonsearchcat.pm: Unable to create '. + 'needed table. lonmysql error:'. + $errorstring); $r->print(<Search Error $bodytag Unable to create table in which to store search results. The search has been aborted. -
$errorstring END @@ -343,6 +389,42 @@ END return OK; } +# +# 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'} = ''; + } + } +} + +sub hidden_field { + my ($name,$value) = @_; + if (! defined($value)) { + $value = $ENV{'form.'.$name}; + } + return ''.$/; +} + ###################################################################### ###################################################################### ## @@ -375,7 +457,7 @@ sub course_search { my @allwords=($search_string,@New_Words); $totalfound=0; $r->print('LON-CAPA Course Search'. - $bodytag.$pretty_search_string); + $bodytag.'
'.$pretty_search_string.'

'); $r->rflush(); # ======================================================= Go through the course undef %alreadyseen; @@ -452,6 +534,16 @@ sub untiehash { } # End of course search scoping +sub search_html_header { + my $Str = < + +The LearningOnline Network with CAPA + +ENDHEADER + return $Str; +} + ###################################################################### ###################################################################### @@ -459,88 +551,92 @@ sub untiehash { =item &print_basic_search_form() -Returns a scalar which holds html for the basic search form. +Prints the form for the basic search. Sorry the name is so cryptic. =cut ###################################################################### ###################################################################### - -sub print_basic_search_form{ +sub print_basic_search_form { my ($r,$closebutton,$hidden_fields) = @_; - my $bodytag=&Apache::loncommon::bodytag('Search'); - my $scrout=<<"ENDDOCUMENT"; - - -The LearningOnline Network with CAPA - - -$bodytag -ENDDOCUMENT -if (&Apache::lonnet::allowed('bre',$ENV{'request.role.domain'})) { - my $CatalogSearch=&mt('Catalog Search'); - my $Statement=&mt('Enter terms or phrases separated by AND, OR, or NOT then press SEARCH below'); - $scrout.=(<$CatalogSearch -
- -$hidden_fields -

-$Statement. -

-

- - - - -
-ENDDOCUMENT - $scrout.=' '.&Apache::lonhtmlcommon::textbox('basicexp', - $ENV{'form.basicexp'},40). - ' '; - my $relatedcheckbox = &Apache::lonhtmlcommon::checkbox('related', - $ENV{'form.related'}); - my $domain = $r->dir_config('lonDefDomain'); - my $domaincheckbox = &Apache::lonhtmlcommon::checkbox('domains', - $ENV{'form.domains'}); - my $srch=&mt('Search'); - my $header=&mt('Advanced Search'); - my $userelatedwords=&mt('use related words'); - my $onlysearchdomain=&mt('only search domain'); - my $view=&viewoptions(); - $scrout.=<$header
$relatedcheckbox $userelatedwords$domaincheckbox $onlysearchdomain $domain
-

-$view -

  -$closebutton -END - $scrout.=< - -ENDDOCUMENT + my $bodytag=&Apache::loncommon::bodytag('Search'). + &Apache::lonhtmlcommon::breadcrumbs(undef,'Searching','Searching', + undef,undef,! $ENV{'form.launch'}); + my $scrout = &search_html_header().$bodytag; + if (&Apache::lonnet::allowed('bre',$ENV{'request.role.domain'})) { + # Define interface components + my $userelatedwords= + &mt('[_1] use related words', + &Apache::lonhtmlcommon::checkbox + ('related',$ENV{'form.related'},'related')); + my $onlysearchdomain= + &mt('[_1] only search domain [_2]', + &Apache::lonhtmlcommon::checkbox('domains', + $ENV{'form.domains'}, + $r->dir_config('lonDefDomain') + ), + $r->dir_config('lonDefDomain') + ); + my $adv_search_link = + ''.&mt('Advanced Search').''; + # + $scrout.='

'. + ''. + $hidden_fields; + # + $scrout .= '
'.$/; + if ($ENV{'request.course.id'}) { + $scrout .= '

'.&mt('LON-CAPA Catalog Search').'

'; + } else { + # No need to tell them they are searching + $scrout.= ('
'x2); + } + $scrout.=''. + ''. + ''. + ''.$/; + # +# $scrout .= ''.$/; + $scrout .= ''.$/; + $scrout .= '
'. + &Apache::lonhtmlcommon::textbox('basicexp', + $ENV{'form.basicexp'},50).'
'. + ''.&searchhelp().''.'
'. + ''.(' 'x3).$adv_search_link.''.'
'. + ''.(' 'x1).$userelatedwords.''.'
'. + ''.(' 'x1).$onlysearchdomain.''.'
'. + '
'. +# ''. +# $userelatedwords.(' 'x3). +# $onlysearchdomain.(' 'x2).$adv_search_link. +# ''. +# '
'. + ''. + ''. + (' 'x2).$closebutton.(' 'x2). + &viewoptions(). + ''. + '
'.$/.'
'.'
'; } if ($ENV{'request.course.id'}) { - my %lt=&Apache::lonlocal::texthash( - 'srch' => 'Search', + my %lt=&Apache::lonlocal::texthash('srch' => 'Search', 'header' => 'Course Search', 'note' => 'Enter terms or phrases, then press "Search" below', 'use' => 'use related words', 'full' =>'fulltext search (time consuming)' ); $scrout.=(< +

$lt{'header'}

-
$hidden_fields

@@ -566,6 +662,8 @@ ENDCOURSESEARCH

 

+
+ ENDENDCOURSE } $scrout.=(< 'Search', 'reset' => 'Reset', 'help' => 'Help'); - my $advanced_buttons = <<"END"; -

+ my $advanced_buttons=<<"END"; $closebutton - -

END - if (!defined($ENV{'form.viewselect'})) { - $ENV{'form.viewselect'} ="Detailed Citation View"; - } - my $bodytag=&Apache::loncommon::bodytag('Advanced Catalog Search'); - my $scrout=<<"ENDHEADER"; - - -The LearningOnline Network with CAPA - - + my $scrout=&search_html_header(); + $scrout .= <<"ENDHEADER"; $bodytag -Enter terms or phrases separated by search operators -such as AND, OR, or NOT.
+

$advanced_buttons -$hidden_fields - ENDHEADER - $scrout.=&viewoptions(); + $scrout.=(' 'x2).&viewoptions().'

'.$hidden_fields. + ''; my %fields=&Apache::lonmeta::fieldnames(); -# set undefined to "any" - $ENV{'form.language'}='any' unless length($ENV{'form.language'}); - $ENV{'form.domains'} = 'any' if (! exists($ENV{'form.domains'})); - $ENV{'form.category'}='any' if (! defined($ENV{'form.category'})); - - - $scrout.=''; - $scrout.="\n"; - foreach ('title','author','keywords','notes','abstract','standards', - 'lowestgradelevel','highestgradelevel','mime') { - $scrout.=''; - } - # Hack - an empty table row. - $scrout.="\n"; -# $scrout.=&searchphrasefield('file
extension','extension', -# $ENV{'form.extension'}); -# $scrout.="\n"; -# $scrout.=&searchphrasefield('publisher
owner','owner', - # $ENV{'form.owner'}); - $scrout.="
".&mt('Field').''.&mt('Value').'' - .&mt('Related').'
'.&mt('Words')."
'.&titlefield($fields{$_}).''. - &Apache::lonmeta::prettyinput($_,$ENV{'form.'.$_},$_,'advsearch', - 1,'',$ENV{'form.'.$_.'_related'}). - '
   
   
\n"; -# $scrout.=&Apache::lonmeta::selectbox('File Category','category', -# $ENV{'form.category'}, -# 'any','Any category', -# undef, -# (&Apache::loncommon::filecategories())); - #---------------------------------------------------------------- - # Allow restriction to multiple domains. - # I make the crazy assumption that there will never be a domain 'any'. # - my @allowed_domains = (ref($ENV{'form.domains'}) ? @{$ENV{'form.domains'}} - : ($ENV{'form.domains'}) ); - my %domain_hash = (); - foreach (@allowed_domains) { - $domain_hash{$_}++; + $scrout .= '

'.&mt('Standard Metadata').'

'; + $scrout .= "\n"; + $scrout .= '\n"; + my %related_word_search = + ('title' => 1, + 'author' => 0, + 'owner' => 0, + 'authorspace' => 0, + 'modifyinguser'=> 0, + 'keywords' => 1, + 'notes' => 1, + 'abstract' => 1, + 'standards'=> 1, + 'mime' => 1, + ); + # + foreach my $field ('title','author','owner','authorspace','modifyinguser', + 'keywords','notes','abstract','standards','mime') { + $scrout.=''.$/; + } + foreach my $field ('lowestgradelevel','highestgradelevel') { + $scrout.=''. + ''. + ''.$/; + } + $scrout.=''.$/; + $scrout.=''.$/; + # + # Misc metadata + $scrout.=''.$/; + $scrout.=''; + $scrout .= "
 '. + (' 'x2).&searchhelp()."
'.&titlefield($fields{$field}).''. + &Apache::lonmeta::prettyinput($field, + $ENV{'form.'.$field}, + $field, + 'advsearch', + $related_word_search{$field}, + '', + $ENV{'form.'.$field.'_related'}, + 50); + if ($related_word_search{$field}) { + $scrout .= 'related words'; + } else { + $scrout .= ' '; + } + $scrout .= '
'.&titlefield($fields{$field}).''. + &Apache::lonmeta::prettyinput($field, + $ENV{'form.'.$field}, + $field, + 'advsearch', + 0). + '
'. + &titlefield(&mt('MIME Type Category')).''. + &Apache::loncommon::filecategoryselect('category', + $ENV{'form.category'}). + '
'. + &titlefield(&mt('Domains')).''. + &Apache::loncommon::domain_select('domains', + $ENV{'form.domains'},1). + '
'. + &titlefield(&mt('Copyright/Distribution')).''. + &Apache::lonmeta::selectbox('copyright', + '',, + \&Apache::loncommon::copyrightdescription, + ( undef, + &Apache::loncommon::copyrightids) + ).'
'. + &titlefield(&mt('Language')).''. + &Apache::lonmeta::selectbox('language', + 'notset',, + \&Apache::loncommon::languagedescription, + ('any',&Apache::loncommon::languageids) + ).'
\n"; + # + # Dynamic metadata + $scrout .= '

'.&mt('Problem Statistics').'

'; + $scrout .= "\n"; + $scrout .= ''. + ''."\n"; + foreach my $statistic + ({ name=>'count', + description=>'Network-wide number of accesses (hits)',}, + { name=>'stdno', + description=> + 'Total number of students who have worked on this problem',}, + { name => 'avetries', + description=>'Average number of tries till solved',}, + { name => 'difficulty', + description=>'Degree of difficulty',}, + { name => 'disc', + description=>'Degree of discrimination'}) { + $scrout .= ''.$/; + } + $scrout .= "
 '.&mt('Minimum').''.&mt('Maximum').'
'. + &titlefield(&mt($statistic->{'description'})). + ''. + ''. + ''. + ''. + '
\n"; + $scrout .= '

'.&mt('Evaluation Data').'

'; + $scrout .= "\n"; + $scrout .= ''. + ''."\n"; + foreach my $evaluation + ( { name => 'clear', + description => 'Material presented in clear way'}, + { name =>'depth', + description => 'Material covered with sufficient depth'}, + { name => 'helpful', + description => 'Material is helpful'}, + { name => 'correct', + description => 'Material appears to be correct'}, + { name => 'technical', + description => 'Resource is technically correct'}){ + $scrout .= ''.$/; } - my @domains =&Apache::loncommon::get_domains(); - # adjust the size of the select box - my $size = 4; - $size = (scalar @domains < ($size - 1) ? scalar @domains + 1 : $size); - $scrout.="\n".&titlefield(&mt('Domain to be searched')). - '\n"; - -# ------------------------------------------- Compute customized metadata field -# $scrout.=< -#LIMIT BY SPECIAL METADATA FIELDS: -# -#For resource-specific metadata, enter in an expression in the form of -#key=value separated by operators such as AND, OR or NOT.
-#Example: grandmother=75 OR grandfather=85 -#
-#CUSTOMMETADATA -# $scrout.=&simpletextfield('custommetadata',$ENV{'form.custommetadata'}); -# $scrout.=< -#SHOW SPECIAL METADATA FIELDS: -# -#Enter in a space-separated list of special metadata fields to show -#in a fielded listing for each record result. -#
-#CUSTOMSHOW -# $scrout.=&simpletextfield('customshow',$ENV{'form.customshow'}); + $scrout .= "
 '.&mt('Minimum').''.&mt('Maximum').'
'. + &titlefield(&mt($evaluation->{'description'})). + ''. + ''. + ''. + ''. + '
\n"; + # + # Creation/Modification date limits + $scrout .= '

'.&mt('Creation and Modification dates').'

'; + $scrout .= "\n\n"; + my $cafter = + &Apache::lonhtmlcommon::date_setter('advsearch', # formname + 'creationdate1', # fieldname + 0, # current value + '', # special + 1, # includeempty + '', # state + 1, # no_hh_mm_ss + ); + my $cbefore = + &Apache::lonhtmlcommon::date_setter('advsearch', # formname + 'creationdate2', # fieldname + 0, # current value + '', # special + 1, # includeempty + '', # state + 1, # no_hh_mm_ss + ); + $scrout .= &mt(''. + ''. + ''. + '',$cafter,$cbefore); + my $lafter = + &Apache::lonhtmlcommon::date_setter('advsearch', + 'revisiondate1', + 0, # current value + '', # special + 1, # includeempty + '', # state + 1, # no_hh_mm_ss + ); + my $lbefore = + &Apache::lonhtmlcommon::date_setter('advsearch', + 'revisiondate2', + 0, # current value + '', # special + 1, # includeempty + '', # state + 1, # no_hh_mm_ss + ); + $scrout .= &mt(''. + ''. + ''. + '',$lafter,$lbefore); + $scrout.="
Created between[_1]
and [_2]
Last modified between [_1]
and[_2]
\n"; $scrout.=< @@ -711,12 +900,13 @@ ENDDOCUMENT $r->print($scrout); return; } + ###################################################################### ###################################################################### =pod -=item &titlefield +=item &titlefield() Inputs: title text @@ -726,17 +916,17 @@ Outputs: titletext with font wrapper ###################################################################### ###################################################################### - sub titlefield { my $title=shift; - return ''.$title.''; + return $title; } + ###################################################################### ###################################################################### =pod -=item viewoptiontext +=item viewoptiontext() Inputs: codename for view option @@ -746,21 +936,23 @@ Outputs: displayed text ###################################################################### ###################################################################### - sub viewoptiontext { my $code=shift; - my %desc=&Apache::lonlocal::texthash('detailed' => "Detailed Citation View", - 'xml' => 'XML/SGML', - 'compact' => 'Compact View', - 'fielded' => 'Fielded Format', - 'summary' => 'Summary View'); + my %desc=&Apache::lonlocal::texthash + ('detailed' => "Detailed Citation View", + 'xml' => 'XML/SGML', + 'compact' => 'Compact View', + 'fielded' => 'Fielded Format', + 'summary' => 'Summary View'); return $desc{$code}; } + +###################################################################### ###################################################################### =pod -=item viewoptions +=item viewoptions() Inputs: none @@ -770,21 +962,22 @@ Outputs: text for box with view options ###################################################################### ###################################################################### - sub viewoptions { - my $scrout="\n\n".'
'.&mt('View Options').''. - &mt('Records per Page').'
'; - unless ($ENV{'form.viewselect'}) { $ENV{'form.viewselect'}='detailed'; } + my $scrout; + if (! defined($ENV{'form.viewselect'})) { + $ENV{'form.viewselect'}='detailed'; + } $scrout.=&Apache::lonmeta::selectbox('viewselect', $ENV{'form.viewselect'}, \&viewoptiontext, sort(keys(%Views))); - $scrout.=''; - $scrout.=&Apache::lonmeta::selectbox('show', - $ENV{'form.show'}, - undef, - (10,20,50,100,1000,10000)); - $scrout.="
\n\n"; + $scrout.= '  '; + my $countselect = &Apache::lonmeta::selectbox('show', + $ENV{'form.show'}, + undef, + (10,20,50,100,1000,10000)); + $scrout .= (' 'x2).&mt('[_1] Records per Page',$countselect). + ''.$/; return $scrout; } @@ -793,7 +986,26 @@ sub viewoptions { =pod -=item &get_persistent_form_data +=item searchhelp() + +Inputs: none + +Outputs: return little blurb on how to enter searches + +=cut + +###################################################################### +###################################################################### +sub searchhelp { + return &mt('Enter terms or phrases separated by AND, OR, or NOT'); +} + +###################################################################### +###################################################################### + +=pod + +=item &get_persistent_form_data() Inputs: filename of database @@ -848,7 +1060,7 @@ sub get_persistent_form_data { =pod -=item &get_persistent_data +=item &get_persistent_data() Inputs: filename of database, ref to array of values to recover. @@ -899,7 +1111,7 @@ Inputs: Hash of values to save, filename Store variables away to the %persistent_db. Values will be escaped. Values that are array pointers will have their -elements escaped and concatenated in a comma seperated string. +elements escaped and concatenated in a comma separated string. =cut @@ -931,7 +1143,7 @@ Inputs: filename of persistent database. Store most form variables away to the %persistent_db. Values will be escaped. Values that are array pointers will have their -elements escaped and concatenated in a comma seperated string. +elements escaped and concatenated in a comma separated string. =cut @@ -975,40 +1187,59 @@ Parse advanced search form and return th ###################################################################### sub parse_advanced_search { my ($r,$closebutton,$hidden_fields)=@_; + my @BasicFields = ('title','author','subject','keywords','url','version', + 'notes','abstract','extension','owner', +# 'custommetadata','customshow', + 'modifyinguser','standards','mime'); + my @StatsFields = &statfields(); + my @EvalFields = &evalfields(); my $fillflag=0; my $pretty_search_string = "
\n"; # 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','extension','language','owner', - 'custommetadata','customshow','category') { - $ENV{"form.$field"}=~s/[^\w\/\s\(\)\=\-\"\']//g; + 'lastrevisiondateend_day','lastrevisiondateend_year') { + $ENV{'form.'.$field}=~s/[^\w\/\s\(\)\=\-\"\']//g; + $ENV{'form.'.$field}=~s/(not\s*$|^\s*(and|or)|)//gi; } 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.'.$_}=&Apache::lonnet::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','category','extension','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,$closebutton,'phase=disp_adv',$hidden_fields); + 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 (! $fillflag) { + &output_blank_field_error($r,$closebutton, + 'phase=disp_adv',$hidden_fields); return ; } # Turn the form input into a SQL-based query @@ -1016,10 +1247,9 @@ sub parse_advanced_search { my @queries; my $font = ''; # Evaluate logical expression AND/OR/NOT phrase fields. - foreach my $field ('title','author','subject','notes','abstract','url', - 'keywords','version','owner','standards') { - if ($ENV{'form.'.$field}) { - my $searchphrase = $ENV{'form.'.$field}; + foreach my $field (@BasicFields) { + next if (!defined($ENV{'form.'.$field}) || $ENV{'form.'.$field} eq ''); + foreach my $searchphrase(&process_phrase_input($ENV{'form.'.$field})){ $pretty_search_string .= $font."$field contains ". $searchphrase.""; if ($ENV{'form.'.$field.'_related'}) { @@ -1033,7 +1263,7 @@ sub parse_advanced_search { } } $pretty_search_string .= "
\n"; - push @queries,&build_SQL_query($field,$searchphrase); + push @queries,&build_SQL_query($field,$searchphrase); } } # @@ -1049,18 +1279,29 @@ sub parse_advanced_search { $searchphrase = join(' OR ',@extensions); } } - if (exists($ENV{'form.extension'}) && $ENV{'form.extension'} !~ /^\s*$/) { - $searchphrase .= ' OR ' if (defined($searchphrase)); - my @extensions = split(/,/,$ENV{'form.extension'}); - $searchphrase .= join(' OR ',@extensions); - } if (defined($searchphrase)) { push @queries,&build_SQL_query('mime',$searchphrase); $pretty_search_string .=$font.'mime contains '. $searchphrase.'
'; } - ##### + # # 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= ". @@ -1074,48 +1315,84 @@ sub parse_advanced_search { "
\n"; } # + # 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,$closebutton,$hidden_fields); - return ; - } 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=undef; -# if ($ENV{'form.custommetadata'}) { -# $pretty_search_string .=$font."Custom Metadata Search: ". -# $ENV{'form.custommetadata'}."
\n"; -# $customquery=&build_custommetadata_query('custommetadata', -# $ENV{'form.custommetadata'}); -# } + ## + ## 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); -# } - ## --------------------------------------------------------------- + # 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) = @@ -1123,11 +1400,11 @@ sub parse_advanced_search { $pretty_search_string .= $pretty_domains_string."
\n"; # if (@queries) { - $query=join(" AND ",@queries); - $query="select * from metadata where $query"; + $query="SELET * FROM metadata WHERE ".join(" AND ",@queries); } elsif ($customquery) { $query = ''; } +# &Apache::lonnet::logthis('query = '.$/.$query); return ($query,$customquery,$customshow,$libraries_to_query, $pretty_search_string); } @@ -1136,7 +1413,7 @@ sub parse_domain_restrictions { my $libraries_to_query = undef; # $ENV{'form.domains'} can be either a scalar or an array reference. # We need an array. - if (! exists($ENV{'form.domains'})) { + if (! exists($ENV{'form.domains'}) || $ENV{'form.domains'} eq '') { return (undef,''); } my @allowed_domains; @@ -1145,6 +1422,7 @@ sub parse_domain_restrictions { } else { @allowed_domains = ($ENV{'form.domains'}); } + # my %domain_hash = (); my $pretty_domains_string; foreach (@allowed_domains) { @@ -1185,6 +1463,7 @@ Parse the basic search form and return a ###################################################################### sub parse_basic_search { my ($r,$closebutton)=@_; + # # Clean up fields for safety for my $field ('basicexp') { $ENV{"form.$field"}=~s/[^\w\s\(\)\-]//g; @@ -1197,46 +1476,84 @@ sub parse_basic_search { } my ($libraries_to_query,$pretty_domains_string) = &parse_domain_restrictions(); - # Check to see if enough is filled in - unless (&filled($ENV{'form.basicexp'})) { + # + # Check to see if enough of a query is filled in + my $search_string = $ENV{'form.basicexp'}; + $search_string =~ s/(not\s*$|^\s*(and|or)|)//gi; + if (! &filled($search_string)) { &output_blank_field_error($r,$closebutton,'phase=disp_basic'); return OK; } - my $pretty_search_string = ''.$ENV{'form.basicexp'}.''; - my $search_string = $ENV{'form.basicexp'}; - if ($ENV{'form.related'}) { - my @New_Words; - ($search_string,@New_Words) = &related_version($ENV{'form.basicexp'}); - if (@New_Words) { - $pretty_search_string .= " with related words: @New_Words."; - } else { - $pretty_search_string .= " with no related words."; - } - } - # Build SQL query string based on form page - my $query=''; + my $pretty_search_string=''; + my @Queries; my $concatarg=join(',', - ('title', 'author', 'subject', 'notes', 'abstract', + ('title', 'author', 'subject', 'notes', 'abstract', 'keywords')); - $concatarg='title' if $ENV{'form.titleonly'}; - $query=&build_SQL_query('concat_ws(" ",'.$concatarg.')',$search_string); + foreach my $search (&process_phrase_input($search_string)){ + $pretty_search_string .= '
'.''.$search.''; + if ($ENV{'form.related'}) { + my @New_Words; + ($search,@New_Words) = &related_version($search); + next if (! $search); + if (@New_Words) { + $pretty_search_string .= + " with related words: @New_Words"; + } + } + # + # Build SQL query string based on form page + push(@Queries, + &build_SQL_query('concat_ws(" ",'.$concatarg.')',$search)); + } + my $final_query = 'SELECT * FROM metadata WHERE '.join(" AND ",@Queries); + # if (defined($pretty_domains_string) && $pretty_domains_string ne '') { $pretty_search_string .= ' '.$pretty_domains_string; } $pretty_search_string .= "
\n"; - my $final_query = 'SELECT * FROM metadata WHERE '.$query; + $pretty_search_string =~ s:^
::; # &Apache::lonnet::logthis($final_query); return ($final_query,$pretty_search_string, $libraries_to_query); } +sub process_phrase_input { + my ($phrase)=@_; + my @Phrases; + # &Apache::lonnet::logthis('phrase = :'.$phrase.':'); + my $in_quotes = 0; + my @Words = split(/\s+/,$phrase); + foreach my $word (@Words) { + $word =~ s/(\w+)\"(\w+)/$1$2/g; + if ($in_quotes) { + if ($word =~ s/(\")$//) { + $in_quotes = 0; + } + if ($Phrases[-1] ne '') { + $Phrases[-1] .= ' '; + } + $Phrases[-1] .= $word; + } else { + if ($word =~ s/^(\")//) { + $in_quotes=1; + } + push(@Phrases,$word); + } + } + # + #foreach my $p (@Phrases) { + # &Apache::lonnet::logthis(' subphrase = '.$p); + #} + # + return @Phrases; +} ###################################################################### ###################################################################### =pod -=item &related_version +=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 @@ -1249,19 +1566,13 @@ Note: Using this twice on a string is pr ###################################################################### ###################################################################### sub related_version { - my $search_string = shift; - my $result = $search_string; - my %New_Words = (); - while ($search_string =~ /(\w+)/cg) { - my $word = $1; - next if (lc($word) =~ /\b(or|and|not)\b/); - my @Words = &Apache::loncommon::get_related_words($word); - @Words = ($#Words>4? @Words[0..4] : @Words); - foreach (@Words) { $New_Words{$_}++;} - my $replacement = join " OR ", ($word,@Words); - $result =~ s/(\b)$word(\b)/$1($replacement)$2/g; - } - return $result,sort(keys(%New_Words)); + 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); } ###################################################################### @@ -1285,7 +1596,9 @@ sub build_SQL_query { -build => 'Text::Query::Build'); $q->prepare($logic_statement); my $matchexp=${$q}{'matchexp'}; chomp $matchexp; + &Apache::lonnet::logthis('matchexp = '.$matchexp); my $sql_query=&recursive_SQL_query_build($field_name,$matchexp); + &Apache::lonnet::logthis('sql_query = '.$sql_query); return $sql_query; } @@ -1379,51 +1692,73 @@ Also reports errors (check for /^Incorre ###################################################################### ###################################################################### 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; + 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 (@queries) { - return join(" AND ",@queries); + 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 ''; + return ($result,$error,$pretty_string); } ###################################################################### @@ -1433,6 +1768,11 @@ sub build_date_queries { =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 ###################################################################### @@ -1456,13 +1796,12 @@ sub copyright_check { return 1; } - ###################################################################### ###################################################################### =pod -=item &ensure_db_and_table +=item &ensure_db_and_table() Ensure we can get lonmysql to connect to the database and the table we need exists. @@ -1518,7 +1857,10 @@ sub ensure_db_and_table { =pod -=item &print_sort_form +=item &print_sort_form() + +The sort feature is not implemented at this time. This form just prints +a link to change the search query. =cut @@ -1526,7 +1868,11 @@ sub ensure_db_and_table { ###################################################################### sub print_sort_form { my ($r,$pretty_query_string) = @_; - my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1); + my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1). + &Apache::lonhtmlcommon::breadcrumbs + (undef,'Searching','Searching',undef,undef, + ! ($ENV{'form.catalogmode'} eq 'groupsearch')); + ## my %SortableFields=&Apache::lonlocal::texthash( id => 'Default', @@ -1619,61 +1965,11 @@ the following format: ##################################################################### ##################################################################### - -my @Datatypes = - ( { name => 'id', - type => 'MEDIUMINT', - restrictions => 'UNSIGNED NOT NULL', - primary_key => 'yes', - auto_inc => 'yes' }, - { name => 'title', type=>'TEXT'}, - { name => 'author', type=>'TEXT'}, - { name => 'subject', type=>'TEXT'}, - { name => 'url', type=>'TEXT', restrictions => 'NOT NULL' }, - { name => 'keywords', type=>'TEXT'}, - { name => 'version', type=>'TEXT'}, - { name => 'notes', type=>'TEXT'}, - { name => 'abstract', type=>'TEXT'}, - { name => 'mime', type=>'TEXT'}, - { name => 'language', type=>'TEXT'}, - { name => 'owner', type=>'TEXT'}, - { name => 'copyright', type=>'TEXT'}, - { name => 'dependencies', type=>'TEXT'}, - { name => 'modifyinguser', type=>'TEXT'}, - { name => 'authorspace', type=>'TEXT'}, - { name => 'lowestgradelevel', type=>'INT'}, - { name => 'highestgradelevel', type=>'INT'}, - { name => 'standards', type=>'TEXT'}, - { name => 'count', type=>'INT'}, - { name => 'course', type=>'INT'}, - { name => 'course_list', type=>'TEXT'}, - { name => 'goto', type=>'INT'}, - { name => 'goto_list', type=>'TEXT'}, - { name => 'comefrom', type=>'INT'}, - { name => 'comefrom_list', type=>'TEXT'}, - { name => 'sequsage', type=>'INT'}, - { name => 'sequsage_list', type=>'TEXT'}, - { name => 'stdno', type=>'INT'}, - { name => 'stdno_list', type=>'TEXT'}, - { name => 'avetries', type=>'FLOAT'}, - { name => 'avetries_list', type=>'TEXT'}, - { name => 'difficulty', type=>'FLOAT'}, - { name => 'difficulty_list', type=>'TEXT'}, - { name => 'clear', type=>'FLOAT'}, - { name => 'technical', type=>'FLOAT'}, - { name => 'correct', type=>'FLOAT'}, - { name => 'helpful', type=>'FLOAT'}, - { name => 'depth', type=>'FLOAT'}, - { name => 'hostname', type=> 'TEXT'}, - #-------------------------------------------------- - { name => 'creationdate', type=>'DATETIME'}, - { name => 'lastrevisiondate', type=>'DATETIME'}, - #-------------------------------------------------- - ); - -my @Fullindicies = - qw/title/; -# qw/title author subject abstract mime language owner copyright/; +# +# These should probably be scoped but I don't have time right now... +# +my @Datatypes; +my @Fullindicies; ###################################################################### ###################################################################### @@ -1693,7 +1989,22 @@ Returns: the identifier of the table on ###################################################################### ###################################################################### +sub set_up_table_structure { + my ($datatypes,$fullindicies) = + &LONCAPA::lonmetadata::describe_metadata_storage(); + # 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 { + &set_up_table_structure(); my $table = &Apache::lonmysql::create_table ( { columns => \@Datatypes, FULLTEXT => [{'columns' => \@Fullindicies},], @@ -1750,12 +2061,37 @@ sub update_status { $r->rflush(); } +{ + my $max_time = 40; # seconds for the search to complete + my $start_time = 0; + my $last_time = 0; + +sub reset_timing { + $start_time = 0; + $last_time = 0; +} + +sub time_left { + if ($start_time == 0) { + $start_time = time; + } + my $time_left = $max_time - (time - $start_time); + $time_left = 0 if ($time_left < 0); + return $time_left; +} + sub update_seconds { - my ($r,$text) = @_; - $text =~ s/\'/\\\'/g; - $r->print - ("\n"); - $r->rflush(); + my ($r) = @_; + my $time = &time_left(); + if (($last_time-$time) > 0) { + $r->print("\n"); + $r->rflush(); + } + $last_time = $time; +} + } ###################################################################### @@ -1763,7 +2099,7 @@ sub update_seconds { =pod -=item &revise_button +=item &revise_button() Inputs: None @@ -1790,7 +2126,10 @@ sub revise_button { =pod -=item &run_search +=item &run_search() + +Executes a search query by sending it the the other servers and putting the +results into MySQL. =cut @@ -1799,13 +2138,12 @@ sub revise_button { sub run_search { my ($r,$query,$customquery,$customshow,$serverlist,$pretty_string) = @_; my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1); + $bodytag.= + &Apache::lonhtmlcommon::breadcrumbs(undef,'Searching','Searching', + undef,undef,! $ENV{'form.launch'}); + my $connection = $r->connection; # - # Timing variables - # - my $starttime = time; - my $max_time = 30; # seconds for the search to complete - # # Print run_search header # $r->print(< 2) { $pretty_string = join '
',(@Lines[0..2],'....
'); } - $r->print(&mt("Search").": ".$pretty_string); + $r->print(&mt("Search: [_1]",$pretty_string)); $r->rflush(); # # Determine the servers we need to contact. - # my @Servers_to_contact; if (defined($serverlist)) { if (ref($serverlist) eq 'ARRAY') { @@ -1838,6 +2175,8 @@ END @Servers_to_contact = sort(keys(%Apache::lonnet::libserv)); } my %Server_status; + # + # Check on the mysql table we will use to store results. my $table =$ENV{'form.table'}; if (! defined($table) || $table eq '' || $table =~ /\D/ ) { $r->print("Unable to determine table id to store search results in.". @@ -1854,13 +2193,17 @@ END return; } if (! $table_status) { + &Apache::lonnet::logthis("lonmysql error = ". + &Apache::lonmysql::get_error()); + &Apache::lonnet::logthis("lonmysql debug = ". + &Apache::lonmysql::get_debug()); + &Apache::lonnet::logthis('table status = "'.$table_status.'"'); $r->print("The table id,$table, we tried to use is invalid.". "The search has been aborted."); return; } ## ## Prepare for the big loop. - ## my $hitcountsum; my $server; my $status; @@ -1878,17 +2221,18 @@ END END $r->rflush(); - my $time_remaining = $max_time - (time - $starttime) ; - my $last_time = $time_remaining; - &update_seconds($r,$time_remaining); - &update_status($r,'contacting '.$Servers_to_contact[0]); - while (($time_remaining > 0) && + &reset_timing(); + &update_seconds($r); + &update_status($r,&mt('contacting [_1]',$Servers_to_contact[0])); + while (&time_left() && ((@Servers_to_contact) || keys(%Server_status))) { - # Send out a search request if it needs to be done. + &update_seconds($r); + # + # Send out a search request if (@Servers_to_contact) { # Contact one server my $server = shift(@Servers_to_contact); - &update_status($r,&mt('contacting').' '.$server); + &update_status($r,&mt('contacting [_1]',$server)); my $reply=&Apache::lonnet::metadata_query($query,$customquery, $customshow,[$server]); ($server) = keys(%$reply); @@ -1900,16 +2244,16 @@ END # left to contact. if (scalar (keys(%Server_status))) { &update_status($r, - &mt('waiting on').' '.(join(' ',keys(%Server_status)))); + &mt('waiting on [_1]',join(' ',keys(%Server_status)))); } sleep(1); } # - # # Loop through the servers we have contacted but do not # have results from yet, looking for results. while (my ($server,$status) = each(%Server_status)) { last if ($connection->aborted()); + &update_seconds($r); if ($status eq 'con_lost') { delete ($Server_status{$server}); next; @@ -1917,12 +2261,12 @@ END $status=~/^([\.\w]+)$/; my $datafile=$r->dir_config('lonDaemons').'/tmp/'.$1; if (-e $datafile && ! -e "$datafile.end") { - &update_status($r,&mt('Receiving results from').' '.$server); + &update_status($r,&mt('Receiving results from [_1]',$server)); next; } last if ($connection->aborted()); if (-e "$datafile.end") { - &update_status($r,&mt('Reading results from').' '.$server); + &update_status($r,&mt('Reading results from [_1]',$server)); if (-z "$datafile") { delete($Server_status{$server}); next; @@ -1937,29 +2281,30 @@ END # Read in the whole file. while (my $result = <$fh>) { last if ($connection->aborted()); - # handle custom fields? Someday we will! + # + # Records are stored one per line chomp($result); - next unless $result; + next if (! $result); + # # Parse the result. my %Fields = &parse_raw_result($result,$server); $Fields{'hostname'} = $server; + # + # Skip based on copyright next if (! ©right_check(\%Fields)); + # # Store the result in the mysql database my $result = &Apache::lonmysql::store_row($table,\%Fields); if (! defined($result)) { $r->print(&Apache::lonmysql::get_error()); } - # $r->print(&Apache::lonmysql::get_debug()); + # $hitcountsum ++; - $time_remaining = $max_time - (time - $starttime) ; - if ($last_time - $time_remaining > 0) { - &update_seconds($r,$time_remaining); - $last_time = $time_remaining; - } + &update_seconds($r); if ($hitcountsum % 50 == 0) { &update_count_status($r,$hitcountsum); } - } # End of foreach (@results) + } $fh->close(); # $server is only deleted if the results file has been # found and (successfully) opened. This may be a bad idea. @@ -1969,35 +2314,35 @@ END &update_count_status($r,$hitcountsum); } last if ($connection->aborted()); - # Finished looping through the servers - $starttime = time if (@Servers_to_contact); - $time_remaining = $max_time - (time - $starttime) ; - if ($last_time - $time_remaining > 0) { - $last_time = $time_remaining; - &update_seconds($r,$time_remaining); - } + &update_seconds($r); } - &update_status($r,&mt('Search Complete').$server); - &update_seconds($r,0); - &Apache::lonmysql::disconnect_from_db(); + &update_status($r,&mt('Search Complete [_1]',$server)); + &update_seconds($r); + # + &Apache::lonmysql::disconnect_from_db(); # This is unneccessary + # # We have run out of time or run out of servers to talk to and - # results to get. + # results to get, so let the client know the top frame needs to be + # loaded from /adm/searchcat $r->print(""); - if ($ENV{'form.catalogmode'} ne 'groupsearch') { +# if ($ENV{'form.catalogmode'} ne 'groupsearch') { $r->print(""); - } +# } return; } ###################################################################### ###################################################################### + =pod -=item &prev_next_buttons +=item &prev_next_buttons() + +Returns html for the previous and next buttons on the search results page. =cut @@ -2006,43 +2351,27 @@ END sub prev_next_buttons { my ($current_min,$show,$total,$parms) = @_; return '' if ($show eq 'all'); # No links if you get them all at once. - my $links; - ## - ## Prev - my $prev_min = $current_min - $show; - $prev_min = 1 if $prev_min < 1; - if ($prev_min < $current_min) { - $links .= qq{ -prev -}; - } else { - $links .= 'prev'; - } - ## - ## Pages.... Someday. - ## - $links .= qq{   -reload -}; - ## - ## Next - my $next_min = $current_min + $show; - $next_min = $current_min if ($next_min > $total); - if ($next_min != $current_min) { - $links .= qq{   -next -}; - } else { - $links .= ' next'; - } - return $links; + # + # Create buttons + my $buttons = ''; + $buttons .= ' 'x3; + $buttons .= 'print("A MySQL error has occurred."); @@ -2098,9 +2424,15 @@ sub display_results { } ## ## Determine how many results we need to get - ## - $ENV{'form.start'} = 1 if (! exists($ENV{'form.start'})); - $ENV{'form.show'} = 'all' if (! exists($ENV{'form.show'})); + $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') { @@ -2110,18 +2442,22 @@ sub display_results { $max = $total_results if ($max > $total_results); } ## - ## Output links (if necessary) for 'prev' and 'next' pages. + ## Output form elements + $r->print(&hidden_field('table'). + &hidden_field('phase'). + &hidden_field('persistent_db_id'). + &hidden_field('start') + ); ## + ## Output links (if necessary) for 'prev' and 'next' pages. $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'}) - ."
\n" + ('
'. + &prev_next_buttons($min,$ENV{'form.show'},$total_results). + ''. + &viewoptions().'
' ); if ($total_results == 0) { - $r->print(''. + $r->print(''. '

'.&mt('There are currently no results').'.

'. ""); return; @@ -2131,12 +2467,10 @@ sub display_results { } ## ## Get results from MySQL table - ## my @Results = &Apache::lonmysql::get_rows($table, 'id>='.$min.' AND id<='.$max); ## ## Loop through the results and output them. - ## foreach my $row (@Results) { if ($connection->aborted()) { &cleanup(); @@ -2144,6 +2478,9 @@ sub display_results { } my %Fields = %{&parse_row(@$row)}; my $output="

\n"; + if (! defined($Fields{'title'}) || $Fields{'title'} eq '') { + $Fields{'title'} = 'Untitled'; + } my $prefix=&catalogmode_output($Fields{'title'},$Fields{'url'}, $Fields{'id'},$checkbox_num++); # Render the result into html @@ -2214,7 +2551,7 @@ END =pod -=item &parse_row +=item &parse_row() Parse a row returned from the database. @@ -2225,6 +2562,9 @@ Parse a row returned from the database. sub parse_row { my @Row = @_; my %Fields; + if (! scalar(@Datatypes)) { + &set_up_table_structure(); + } for (my $i=0;$i<=$#Row;$i++) { $Fields{$Datatypes[$i]->{'name'}}=&Apache::lonnet::unescape($Row[$i]); } @@ -2260,12 +2600,11 @@ The 'title' field is set to "Untitled" i ########################################################### sub parse_raw_result { my ($result,$hostname) = @_; -# conclude from self to others regarding fields - my %Fields=&Apache::lonmeta::metadata_col_to_hash( - map { - &Apache::lonnet::unescape($_); - } (split(/\,/,$result)) - ); + # conclude from self to others regarding fields + my %Fields=&LONCAPA::lonmetadata::metadata_col_to_hash + (map { + &Apache::lonnet::unescape($_); + } (split(/\,/,$result)) ); return %Fields; } @@ -2315,7 +2654,7 @@ sub handle_custom_fields { =pod -=item &search_results_header +=item &search_results_header() Output the proper html headers and javascript code to deal with different calling modes. @@ -2454,7 +2793,7 @@ SCRIPT $result.=< $bodytag -

+ $importbutton END @@ -2512,11 +2851,47 @@ ENDFRAMES ###################################################################### ###################################################################### +sub has_stat_data { + my ($values) = @_; + if ( (defined($values->{'count'}) && $values->{'count'} ne '') || + (defined($values->{'stdno'}) && $values->{'stdno'} ne '') || + (defined($values->{'disc'}) && $values->{'disc'} ne '') || + (defined($values->{'avetries'}) && $values->{'avetries'} ne '') || + (defined($values->{'difficulty'}) && $values->{'difficulty'} ne '')) { + return 1; + } + return 0; +} + +sub statfields { + return ('count','stdno','disc','avetries','difficulty'); +} + +sub has_eval_data { + my ($values) = @_; + if ( (defined($values->{'clear'}) && $values->{'clear'} ne '') || + (defined($values->{'technical'}) && $values->{'technical'} ne '') || + (defined($values->{'correct'}) && $values->{'correct'} ne '') || + (defined($values->{'helpful'}) && $values->{'helpful'} ne '') || + (defined($values->{'depth'}) && $values->{'depth'} ne '')) { + return 1; + } + return 0; +} + +sub evalfields { + return ('clear','technical','correct','helpful','depth'); +} + +###################################################################### +###################################################################### + =pod =item Metadata Viewing Functions 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, and @@ -2532,26 +2907,76 @@ extra custom metadata to show. ###################################################################### sub detailed_citation_view { my ($prefix,%values) = @_; - my $icon=&Apache::loncommon::icon($values{'url'}); - my $result=<$prefix$values{'title'} -

-$values{'author'}, $values{'owner'}
- -Subject: $values{'subject'}
-Keyword(s): $values{'keywords'}
-Notes: $values{'notes'}
-MIME Type: $values{'mimetag'}
-Language: $values{'language'}
-Copyright/Distribution: $values{'copyrighttag'}
-

-$values{'extrashow'} -

-$values{'shortabstract'} -

-
-END + my $result; + $result .= ''.$prefix. + ''.' '. + ''.$values{'title'}."\n"; + $result .= "

\n"; + $result .= ''.$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', + translate => 'Access Count: [_1]',}, + { name=>'stdno', + translate => 'Number of Students: [_1]',}, + { name=>'avetries', + translate => 'Average Tries: [_1]',}, + { name=>'disc', + translate => 'Degree of Discrimination: [_1]',}, + { name=>'difficulty', + translate => 'Degree of Difficulty: [_1]',}, + { name=>'clear', + translate => 'Clear: [_1]',}, + { name=>'depth', + translate => 'Depth: [_1]',}, + { name=>'helpful', + translate => 'Helpful: [_1]',}, + { name=>'correct', + translate => 'Correct: [_1]',}, + { name=>'technical', + translate => 'Technical: [_1]',}, + ) { + if (exists($values{$field->{'name'}}) && + $values{$field->{'name'}} ne '') { + if ($field->{'special'} eq 'url link') { + $result.= + &mt($field->{'translate'}, + ''. + $values{$field->{'name'}}. + ''); + } else { + $result.= &mt($field->{'translate'}, + $values{$field->{'name'}}); + } + $result .= "
\n"; + } + } + $result .= "

"; + if (exists($values{'extrashow'}) && $values{'extrashow'} ne '') { + $result .= '

'.$values{'extrashow'}.'

'; + } + if (exists($values{'shortabstract'}) && $values{'shortabstract'} ne '') { + $result .= '

'.$values{'shortabstract'}.'

'; + } + $result .= '
'."\n"; return $result; } @@ -2569,7 +2994,8 @@ sub summary_view { my ($prefix,%values) = @_; my $icon=&Apache::loncommon::icon($values{'url'}); my $result=<  +$values{'author'}
$values{'title'}
$values{'owner'} -- $values{'lastrevisiondate'}
@@ -2594,12 +3020,11 @@ END ###################################################################### sub compact_view { my ($prefix,%values) = @_; - my $icon=&Apache::loncommon::icon($values{'url'}); - my $result=< -$values{'title'} -$values{'author'}
-END + my $result = + $prefix.' '. + $values{'title'}.''.(' 'x2). + ''.$values{'author'}.'
'; return $result; } @@ -2618,28 +3043,35 @@ END sub fielded_format_view { my ($prefix,%values) = @_; my $icon=&Apache::loncommon::icon($values{'url'}); + my %Translated = &Apache::lonmeta::fieldnames(); my $result=< -URL: $values{'url'} -
-Title: $values{'title'}
-Author(s): $values{'author'}
-Subject: $values{'subject'}
-Keyword(s): $values{'keywords'}
-Notes: $values{'notes'}
-MIME Type: $values{'mimetag'}
-Language: $values{'language'}
-Creation Date: $values{'creationdate'}
-Last Revision Date: $values{'lastrevisiondate'}
-Publisher/Owner: $values{'owner'}
-Copyright/Distribution: $values{'copyrighttag'}
-Repository Location: $values{'hostname'}
-Abstract: $values{'shortabstract'}
-$values{'extrashow'} -

-
+
+
URL:
+
$values{'url'}
END + foreach my $field ('title','author','subject','keywords','notes', + 'mimetag','language','creationdate','lastrevisiondate', + 'owner','copyrighttag','hostname','abstract') { + $result .= (' 'x4).'
'.$Translated{$field}.'
'."\n". + (' 'x8).'
'.$values{$field}.'
'."\n"; + } + if (&has_stat_data(\%values)) { + foreach my $field (&statfields()) { + $result .= (' 'x4).'
'.$Translated{$field}.'
'."\n". + (' 'x8).'
'.$values{$field}.'
'."\n"; + } + } + if (&has_eval_data(\%values)) { + foreach my $field (&evalfields()) { + $result .= (' 'x4).'
'.$Translated{$field}.'
'."\n". + (' 'x8).'
'.$values{$field}.'
'."\n"; + } + } + $result .= "
\n"; + $result .= $values{'extrashow'}; + $result .= '
'."\n"; return $result; } @@ -2658,34 +3090,59 @@ END ###################################################################### sub xml_sgml_view { my ($prefix,%values) = @_; + my $xml = ''."\n"; + # The usual suspects + foreach my $field ('url','title','author','subject','keywords','notes') { + $xml .= qq{<$field>$values{$field}}."\n"; + } + # + $xml .= "\n"; + foreach my $field ('mime','mimetag') { + $xml .= qq{<$field>$values{$field}}."\n"; + } + $xml .= "\n"; + # + $xml .= "\n"; + foreach my $field ('language','languagetag') { + $xml .= qq{<$field>$values{$field}}."\n"; + } + $xml .= "\n"; + # + foreach my $field ('creationdate','lastrevisiondate','owner') { + $xml .= qq{<$field>$values{$field}}."\n"; + } + # + $xml .= "\n"; + foreach my $field ('copyright','copyrighttag') { + $xml .= qq{<$field>$values{$field}}."\n"; + } + $xml .= "\n"; + $xml .= qq{$values{'hostname'}}. + "\n"; + $xml .= qq{$values{'shortabstract'}}."\n"; + # + if (&has_stat_data(\%values)){ + $xml .= "\n"; + foreach my $field (&statfields()) { + $xml .= qq{<$field>$values{$field}}."\n"; + } + $xml .= "\n"; + } + # + if (&has_eval_data(\%values)) { + $xml .= "\n"; + foreach my $field (&evalfields) { + $xml .= qq{<$field>$values{$field}}."\n"; + } + $xml .= "\n"; + } + # + $xml .= "\n"; + $xml = &HTML::Entities::encode($xml,'<>&'); my $result=< -<LonCapaResource> -<url>$values{'url'}</url> -<title>$values{'title'}</title> -<author>$values{'author'}</author> -<subject>$values{'subject'}</subject> -<keywords>$values{'keywords'}</keywords> -<notes>$values{'notes'}</notes> -<mimeInfo> -<mime>$values{'mime'}</mime> -<mimetag>$values{'mimetag'}</mimetag> -</mimeInfo> -<languageInfo> -<language>$values{'language'}</language> -<languagetag>$values{'languagetag'}</languagetag> -</languageInfo> -<creationdate>$values{'creationdate'}</creationdate> -<lastrevisiondate>$values{'lastrevisiondate'}</lastrevisiondate> -<owner>$values{'owner'}</owner> -<copyrightInfo> -<copyright>$values{'copyright'}</copyright> -<copyrighttag>$values{'copyrighttag'}</copyrighttag> -</copyrightInfo> -<repositoryLocation>$values{'hostname'}</repositoryLocation> -<shortabstract>$values{'shortabstract'}</shortabstract> -</LonCapaResource> +$xml $values{'extrashow'}
@@ -2707,10 +3164,9 @@ END sub filled { my ($field)=@_; if ($field=~/\S/ && $field ne 'any') { - return 1; - } - else { - return 0; + return 1; + } else { + return 0; } }