Diff for /loncom/interface/lonsearchcat.pm between versions 1.223 and 1.239

version 1.223, 2004/05/05 17:29:06 version 1.239, 2005/03/16 22:46:24
Line 75  use Apache::lonhtmlcommon; Line 75  use Apache::lonhtmlcommon;
 use Apache::lonlocal;  use Apache::lonlocal;
 use LONCAPA::lonmetadata();  use LONCAPA::lonmetadata();
 use HTML::Entities();  use HTML::Entities();
   use Parse::RecDescent;
   use Apache::lonnavmaps;
   
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
Line 174  sub handler { Line 176  sub handler {
                    '&launch='.$ENV{'form.launch'}.                     '&launch='.$ENV{'form.launch'}.
                    '&mode='.$ENV{'form.mode'},                     '&mode='.$ENV{'form.mode'},
               text=>"Course and Catalog Search",                text=>"Course and Catalog Search",
                 target=>'_top',
               bug=>'Searching',});                bug=>'Searching',});
     } else {      } else {
         &Apache::lonhtmlcommon::add_breadcrumb          &Apache::lonhtmlcommon::add_breadcrumb
Line 182  sub handler { Line 185  sub handler {
                    '&launch='.$ENV{'form.launch'}.                     '&launch='.$ENV{'form.launch'}.
                    '&mode='.$ENV{'form.mode'},                     '&mode='.$ENV{'form.mode'},
               text=>"Catalog Search",                text=>"Catalog Search",
                 target=>'_top',
               bug=>'Searching',});                bug=>'Searching',});
     }      }
     #      #
Line 191  sub handler { Line 195  sub handler {
                 &Apache::lonnet::logthis('lonsearchcat:'.                  &Apache::lonnet::logthis('lonsearchcat:'.
                                          'Unable to recover data from '.                                           'Unable to recover data from '.
                                          $persistent_db_file);                                           $persistent_db_file);
                 $r->print(<<END);   my $html=&Apache::lonxml::xmlbegin();
 <html>   $r->print(<<END);
 <head><title>LON-CAPA Search Error</title></head>  $html
   <head>
   <title>LON-CAPA Search Error</title></head>
 $bodytag  $bodytag
 We were unable to retrieve data describing your search.  This is a serious  We were unable to retrieve data describing your search.  This is a serious
 error and has been logged.  Please alert your LON-CAPA administrator.  error and has been logged.  Please alert your LON-CAPA administrator.
Line 220  END Line 226  END
  } else {   } else {
             # This is a stupid error to give to the user.                # This is a stupid error to give to the user.  
             # It really tells them nothing.              # It really tells them nothing.
     $r->print('<html><head></head>'.$bodytag.      my $html=&Apache::lonxml::xmlbegin();
       $r->print($html.'<head></head>'.$bodytag.
                       'Unable to tie hash to db file</body></html>');                        'Unable to tie hash to db file</body></html>');
     return OK;      return OK;
  }   }
Line 335  END Line 342  END
         &course_search($r);          &course_search($r);
     } elsif(($ENV{'form.phase'} eq 'basic_search') ||      } elsif(($ENV{'form.phase'} eq 'basic_search') ||
             ($ENV{'form.phase'} eq 'adv_search')) {              ($ENV{'form.phase'} eq 'adv_search')) {
           #
           # We are running a search, try to parse it
           my ($query,$customquery,$customshow,$libraries) = 
               (undef,undef,undef,undef);
           my $pretty_string;
           if ($ENV{'form.phase'} eq 'basic_search') {
               ($query,$pretty_string,$libraries) = 
                   &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) 
                   = &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 },
                            $persistent_db_file);
           #
         # Set up table          # Set up table
         if (! defined(&create_results_table())) {          if (! defined(&create_results_table())) {
     my $errorstring=&Apache::lonmysql::get_error();      my $errorstring=&Apache::lonmysql::get_error();
             &Apache::lonnet::logthis('lonsearchcat.pm: Unable to create '.              &Apache::lonnet::logthis('lonsearchcat.pm: Unable to create '.
                                      'needed table.  lonmysql error:'.                                       'needed table.  lonmysql error:'.
                                      $errorstring);                                       $errorstring);
       my $html=&Apache::lonxml::xmlbegin();
             $r->print(<<END);              $r->print(<<END);
 <html><head><title>Search Error</title></head>  $html
   <head>
   <title>Search Error</title></head>
 $bodytag  $bodytag
 Unable to create table in which to store search results.    Unable to create table in which to store search results.  
 The search has been aborted.  The search has been aborted.
Line 353  END Line 386  END
         }          }
         delete($ENV{'form.launch'});          delete($ENV{'form.launch'});
         if (! &make_form_data_persistent($r,$persistent_db_file)) {          if (! &make_form_data_persistent($r,$persistent_db_file)) {
             $r->print(<<END);      my $html=&Apache::lonxml::xmlbegin();
 <html><head><title>Search Error</title></head>      $r->print(<<END);
   $html
   <head>
   <title>Search Error</title></head>
 $bodytag  $bodytag
 Unable to properly store search information.  The search has been aborted.  Unable to properly store search information.  The search has been aborted.
 </body>  </body>
Line 362  Unable to properly store search informat Line 398  Unable to properly store search informat
 END  END
             return OK;              return OK;
         }          }
         #  
         # We are running a search  
         my ($query,$customquery,$customshow,$libraries) =   
             (undef,undef,undef,undef);  
         my $pretty_string;  
         if ($ENV{'form.phase'} eq 'basic_search') {  
             ($query,$pretty_string,$libraries) =   
                 &parse_basic_search($r,$closebutton,$hidden_fields);  
         } else {                      # Advanced search  
             ($query,$customquery,$customshow,$libraries,$pretty_string)   
                 = &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 },  
                          $persistent_db_file);  
         ##          ##
         ## Print out the frames interface          ## Print out the frames interface
         ##          ##
         &print_frames_interface($r);          if (defined($query)) {
               &print_frames_interface($r);
           }
     }      }
     return OK;      return OK;
 }   } 
Line 439  my %alreadyseen; Line 458  my %alreadyseen;
 my %hash;  my %hash;
 my $totalfound;  my $totalfound;
   
   sub make_symb {
       my ($id)=@_;
       my ($mapid,$resid)=split(/\./,$id);
       my $map=$hash{'map_id_'.$mapid};
       my $res=$hash{'src_'.$id};
       my $symb=&Apache::lonnet::encode_symb($map,$resid,$res);
       return $symb;
   }
   
 sub course_search {  sub course_search {
     my $r=shift;      my $r=shift;
     my $bodytag=&Apache::loncommon::bodytag('Course Search');      my $bodytag=&Apache::loncommon::bodytag('Course Search');
     my $pretty_search_string = '<b>'.$ENV{'form.courseexp'}.'</b>';      my $pretty_search_string = '<b>'.$ENV{'form.courseexp'}.'</b>';
     my $search_string = $ENV{'form.courseexp'};      my $search_string = $ENV{'form.courseexp'};
     my @New_Words;      my @New_Words;
       undef(%alreadyseen);
     if ($ENV{'form.crsrelated'}) {      if ($ENV{'form.crsrelated'}) {
         ($search_string,@New_Words) = &related_version($ENV{'form.courseexp'});          ($search_string,@New_Words) = &related_version($ENV{'form.courseexp'});
         if (@New_Words) {          if (@New_Words) {
Line 454  sub course_search { Line 483  sub course_search {
         }          }
     }      }
     my $fulltext=$ENV{'form.crsfulltext'};      my $fulltext=$ENV{'form.crsfulltext'};
       my $discuss=$ENV{'form.crsdiscuss'};
     my @allwords=($search_string,@New_Words);      my @allwords=($search_string,@New_Words);
     $totalfound=0;      $totalfound=0;
     $r->print('<html><head><title>LON-CAPA Course Search</title></head>'.      my $html=&Apache::lonxml::xmlbegin();
       $bodytag.'<hr /><center><font size="+2" face="arial">'.$pretty_search_string.'</font></center><hr />');      $r->print($html.'<head><title>LON-CAPA Course Search</title></head>'.
         $bodytag.'<hr /><center><font size="+2" face="arial">'.$pretty_search_string.'</font></center><hr /><b>'.&mt('Course content').':</b><br />');
     $r->rflush();      $r->rflush();
 # ======================================================= Go through the course  # ======================================================= Go through the course
     undef %alreadyseen;  
     %alreadyseen=();  
     my $c=$r->connection;      my $c=$r->connection;
     if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.".db",      if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.".db",
             &GDBM_READER(),0640)) {              &GDBM_READER(),0640)) {
         foreach (keys %hash) {          foreach (sort(keys(%hash))) {
             if ($c->aborted()) { last; }              if ($c->aborted()) { last; }
             if (($_=~/^src\_(.+)$/) && (!$alreadyseen{$hash{$_}})) {              if (($_=~/^src\_(.+)$/)) {
                 &checkonthis($r,$hash{$_},0,$hash{'title_'.$1},$fulltext,   if ($hash{'randomout_'.$1} & !$ENV{'request.role.adv'}) {
                              @allwords);      next; 
    }
    my $symb=&make_symb($1);
                   &checkonthis($r,$1,$hash{$_},0,&Apache::lonnet::gettitle($symb),
        $fulltext,$symb,@allwords);
             }              }
         }          }
         untie(%hash);          untie(%hash);
     }      }
     unless ($totalfound) {      unless ($totalfound) {
  $r->print('<p>'.&mt('No resources found').'.</p>');   $r->print('<p>'.&mt('No matches found in resources').'.</p>');
       }
   
   # Check discussions if requested
       if ($discuss) {
           my $totaldiscussions = 0;
           $r->print('<br /><br /><b>'.&mt('Discussion postings').':</b><br />'); 
           my $navmap = Apache::lonnavmaps::navmap->new();
           my @allres=$navmap->retrieveResources();
           my %discussiontime = &Apache::lonnet::dump('discussiontimes',
                                  $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
                                  $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
           foreach my $resource (@allres) {
               my $result = '';
               my $applies = 0;
               my $symb = $resource->symb();
               my $ressymb = $symb;
               if ($symb =~ m#(___adm/\w+/\w+)/(\d+)/bulletinboard$#) {
                   $ressymb = 'bulletin___'.$2.$1.'/'.$2.'/bulletinboard';
                   unless ($ressymb =~ m#bulletin___\d+___adm/wrapper#) {
                       $ressymb=~s#(bulletin___\d+___)#$1adm/wrapper/#;
                   }
               }
               if (defined($discussiontime{$ressymb})) { 
                   my %contrib = &Apache::lonnet::restore($ressymb,$ENV{'request.course.id'},
                        $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
                        $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
                   if ($contrib{'version'}) {
                       for (my $id=1;$id<=$contrib{'version'};$id++) {
                           unless (($contrib{'hidden'}=~/\.$id\./) || ($contrib{'deleted'}=~/\.$id\./)) { 
                               if ($contrib{$id.':subject'}) {
                                   $result .= $contrib{$id.':subject'};
                               }
                               if ($contrib{$id.':message'}) {
                                   $result .= $contrib{$id.':message'};
                               }
                               if ($contrib{$id,':attachmenturl'}) {
                                   if ($contrib{$id,':attachmenturl'} =~ m-/([^/]+)$-) {
                                       $result .= $1;
                                   }
                               }
                               $applies = &checkwords($result,$applies,@allwords);
                           }
                       }
                   }
               }
   # Does this discussion apply?
               if ($applies) {
                   my ($map,$ind,$url)=&Apache::lonnet::decode_symb($ressymb);
                   my $disctype = &mt('resource');
                   if ($url =~ m#/bulletinboard$#) {
                       if ($url =~m#^adm/wrapper/adm/.*/bulletinboard$#) {
                           $url =~s#^adm/wrapper##;
                       }
                       $disctype = &mt('bulletin board');
                   } else {
                       $url = '/res/'.$url;
                   }
                   if ($url =~ /\?/) {
                       $url .= '&symb=';
                   } else {
                       $url .= '?symb=';
                   }
                   $url .= &Apache::lonnet::escape($resource->symb());
                   my $title = $resource->compTitle();
                   $r->print('<br /><a href="'.$url.'" target="cat">'.
                        ($title?$title:$url).'</a>&nbsp;&nbsp;-&nbsp;'.$disctype.'<br />');
                   $totaldiscussions++;
               } else {
                   $r->print(' .');
               }
           }
           unless ($totaldiscussions) {
               $r->print('<p>'.&mt('No matches found in postings').'.</p>');
           }
     }      }
    
 # =================================================== Done going through course  # =================================================== Done going through course
     $r->print('</body></html>');      $r->print('</body></html>');
 }  }
Line 484  sub course_search { Line 592  sub course_search {
 # =============================== This pulls up a resource and its dependencies  # =============================== This pulls up a resource and its dependencies
   
 sub checkonthis {  sub checkonthis {
     my ($r,$url,$level,$title,$fulltext,@allwords)=@_;      my ($r,$id,$url,$level,$title,$fulltext,$symb,@allwords)=@_;
     $alreadyseen{$url}=1;      $alreadyseen{$id}=1;
       if (&Apache::loncommon::connection_aborted($r)) { return; }
     $r->rflush();      $r->rflush();
     my $result=&Apache::lonnet::metadata($url,'title').' '.      
                &Apache::lonnet::metadata($url,'subject').' '.      my $result=$title.' ';
                &Apache::lonnet::metadata($url,'abstract').' '.      if ($ENV{'request.role.adv'} || !$hash{'encrypted_'.$id}) {
                &Apache::lonnet::metadata($url,'keywords');   $result.=&Apache::lonnet::metadata($url,'title').' '.
     if (($url) && ($fulltext)) {      &Apache::lonnet::metadata($url,'subject').' '.
  $result.=&Apache::lonnet::ssi_body($url);      &Apache::lonnet::metadata($url,'abstract').' '.
       &Apache::lonnet::metadata($url,'keywords');
       }
       my ($extension)=($url=~/\.(\w+)$/);
       if (&Apache::loncommon::fileembstyle($extension) eq 'ssi' &&
    ($url) && ($fulltext)) {
    $result.=&Apache::lonnet::ssi_body($url.'?symb='.&Apache::lonnet::escape($symb));
     }      }
     $result=~s/\s+/ /gs;      $result=~s/\s+/ /gs;
     my $applies=0;      my $applies = 0;
     foreach (@allwords) {      $applies = &checkwords($result,$applies,@allwords);
         if ($_=~/\w/) {  
    if ($result=~/$_/si) {  
       $applies++;  
            }  
        }  
     }  
 # Does this resource apply?  # Does this resource apply?
     if ($applies) {      if ($applies) {
        $r->print('<br />');         $r->print('<br />');
        for (my $i=0;$i<=$level*5;$i++) {         for (my $i=0;$i<=$level*5;$i++) {
            $r->print('&nbsp;');             $r->print('&nbsp;');
        }         }
        $r->print('<a href="'.$url.'" target="cat">'.         my $href=$url;
  ($title?$title:$url).'</a><br />');         if ($hash{'encrypted_'.$id} && !$ENV{'request.role.adv'}) {
      $href=&Apache::lonenc::encrypted($href)
          .'?symb='.&Apache::lonenc::encrypted($symb);
          } else {
      $href.='?symb='.&Apache::lonnet::escape($symb);
          }
          $r->print('<a href="'.$href.'" target="cat">'.($title?$title:$url).
    '</a><br />');
        $totalfound++;         $totalfound++;
     } elsif ($fulltext) {      } elsif ($fulltext) {
        $r->print(' .');         $r->print(' .');
Line 520  sub checkonthis { Line 636  sub checkonthis {
     my $dependencies=      my $dependencies=
                 &Apache::lonnet::metadata($url,'dependencies');                  &Apache::lonnet::metadata($url,'dependencies');
     foreach (split(/\,/,$dependencies)) {      foreach (split(/\,/,$dependencies)) {
        if (($_=~/^\/res\//) && (!$alreadyseen{$_})) {         if (($_=~/^\/res\//) && (!$alreadyseen{$id})) { 
           &checkonthis($r,$_,$level+1,'',$fulltext,@allwords);            &checkonthis($r,$id,$_,$level+1,'',$fulltext,undef,@allwords);
        }         }
     }      }
 }  }
   
   sub checkwords {
       my ($result,$applies,@allwords) = @_;
       foreach (@allwords) {
           if ($_=~/\w/) {
               if ($result=~/$_/si) {
                   $applies++;
               }
           }
       }
       return $applies;
   }
   
 sub untiehash {  sub untiehash {
     if (tied(%hash)) {      if (tied(%hash)) {
         untie(%hash);          untie(%hash);
Line 535  sub untiehash { Line 663  sub untiehash {
 } # End of course search scoping  } # End of course search scoping
   
 sub search_html_header {  sub search_html_header {
       my $html=&Apache::lonxml::xmlbegin();
     my $Str = <<ENDHEADER;      my $Str = <<ENDHEADER;
 <html>  $html
 <head>  <head>
 <title>The LearningOnline Network with CAPA</title>  <title>The LearningOnline Network with CAPA</title>
 </head>  </head>
Line 559  Prints the form for the basic search.  S Line 688  Prints the form for the basic search.  S
 ######################################################################  ######################################################################
 sub print_basic_search_form {  sub print_basic_search_form {
     my ($r,$closebutton,$hidden_fields) = @_;      my ($r,$closebutton,$hidden_fields) = @_;
       my $result = ($ENV{'form.catalogmode'} ne 'groupsearch');
     my $bodytag=&Apache::loncommon::bodytag('Search').      my $bodytag=&Apache::loncommon::bodytag('Search').
         &Apache::lonhtmlcommon::breadcrumbs(undef,'Searching','Searching',          &Apache::lonhtmlcommon::breadcrumbs(undef,'Searching','Search_Basic',
                                             undef,undef,! $ENV{'form.launch'});                                     undef,undef,
                                      $ENV{'form.catalogmode'} ne 'groupsearch');
     my $scrout = &search_html_header().$bodytag;      my $scrout = &search_html_header().$bodytag;
     if (&Apache::lonnet::allowed('bre',$ENV{'request.role.domain'})) {      if (&Apache::lonnet::allowed('bre',$ENV{'request.role.domain'})) {
         # Define interface components          # Define interface components
         my $userelatedwords=          my $userelatedwords= '<label>'.
             &mt('[_1] use related words',              &mt('[_1] use related words',
                 &Apache::lonhtmlcommon::checkbox                  &Apache::lonhtmlcommon::checkbox
                 ('related',$ENV{'form.related'},'related'));                  ('related',$ENV{'form.related'},'related')).'</label>';
         my $onlysearchdomain=          my $onlysearchdomain='<label>'.
             &mt('[_1] only search domain [_2]',              &mt('[_1] only search domain [_2]',
                 &Apache::lonhtmlcommon::checkbox('domains',                  &Apache::lonhtmlcommon::checkbox('domains',
                                                  $ENV{'form.domains'},                                                   $ENV{'form.domains'},
                                                  $r->dir_config('lonDefDomain')                                                   $r->dir_config('lonDefDomain')
                                                  ),                                                   ),
                 $r->dir_config('lonDefDomain')                  $r->dir_config('lonDefDomain')
                 );                  ).'</label>';
         my $adv_search_link =           my $adv_search_link = 
             '<a href="/adm/searchcat?'.              '<a href="/adm/searchcat?'.
             'phase=disp_adv&'.              'phase=disp_adv&'.
Line 599  sub print_basic_search_form { Line 730  sub print_basic_search_form {
         }          }
         $scrout.='<table>'.          $scrout.='<table>'.
             '<tr><td align="center" valign="top">'.              '<tr><td align="center" valign="top">'.
             &Apache::lonhtmlcommon::textbox('basicexp',              &Apache::lonhtmlcommon::textbox
                                             $ENV{'form.basicexp'},50).'<br />'.              ('basicexp',
                &HTML::Entities::encode($ENV{'form.basicexp'},'<>&"'),50
                ).
                '<br />'.
             '<font size="-1">'.&searchhelp().'</font>'.'</td>'.              '<font size="-1">'.&searchhelp().'</font>'.'</td>'.
             '<td><font size="-1">'.              '<td><font size="-1">'.
             '<nobr>'.('&nbsp;'x3).$adv_search_link.'</nobr>'.'<br />'.              '<nobr>'.('&nbsp;'x3).$adv_search_link.'</nobr>'.'<br />'.
Line 609  sub print_basic_search_form { Line 743  sub print_basic_search_form {
             '</font></td>'.              '</font></td>'.
             '</tr>'.$/;              '</tr>'.$/;
         #          #
 #        $scrout .= '<tr><td align="center">'.  
 #            '<font size="-1">'.  
 #            $userelatedwords.('&nbsp;'x3).  
 #            $onlysearchdomain.('&nbsp;'x2).$adv_search_link.  
 #            '</font>'.  
 #            '</td></tr>'.$/;  
         $scrout .= '<tr><td align="center" colspan="2">'.          $scrout .= '<tr><td align="center" colspan="2">'.
             '<font size="-1">'.              '<font size="-1">'.
             '<input type="submit" name="basicsubmit" '.              '<input type="submit" name="basicsubmit" '.
Line 630  sub print_basic_search_form { Line 758  sub print_basic_search_form {
                                            'header' => 'Course Search',                                             'header' => 'Course Search',
  'note' => 'Enter terms or phrases, then press "Search" below',   'note' => 'Enter terms or phrases, then press "Search" below',
  'use' => 'use related words',   'use' => 'use related words',
  'full' =>'fulltext search (time consuming)'   'full' =>'fulltext search (time consuming)',
            'disc' => 'search discussion postings (resources and bulletin boards)',
    );     );
         $scrout.=(<<ENDCOURSESEARCH);          $scrout.=(<<ENDCOURSESEARCH);
 <form name="loncapa_search" method="post" action="/adm/searchcat">  <form name="loncapa_search" method="post" action="/adm/searchcat">
Line 655  ENDCOURSESEARCH Line 784  ENDCOURSESEARCH
         my $relcheckbox =           my $relcheckbox = 
             &Apache::lonhtmlcommon::checkbox('crsrelated',              &Apache::lonhtmlcommon::checkbox('crsrelated',
    $ENV{'form.crsrelated'});     $ENV{'form.crsrelated'});
           my $discheckbox = 
               &Apache::lonhtmlcommon::checkbox('crsdiscuss',
                                      $ENV{'form.crsrelated'});
         $scrout.=(<<ENDENDCOURSE);          $scrout.=(<<ENDENDCOURSE);
 </td></tr>  </td></tr>
 <tr><td>$relcheckbox $lt{'use'}</td><td></td></tr>  <tr><td><label>$relcheckbox $lt{'use'}</label></td><td></td></tr>
 <tr><td>$crscheckbox $lt{'full'}</td><td></td></tr>  <tr><td><label>$crscheckbox $lt{'full'}</label></td><td></td></tr>
   <tr><td><label>$discheckbox $lt{'disc'}</label></td><td></td></tr>
 </table><p>  </table><p>
 &nbsp;<input type="submit" name="coursesubmit" value='$lt{'srch'}' />  &nbsp;<input type="submit" name="coursesubmit" value='$lt{'srch'}' />
 </p>  </p>
Line 690  sub print_advanced_search_form{ Line 823  sub print_advanced_search_form{
     my ($r,$closebutton,$hidden_fields) = @_;      my ($r,$closebutton,$hidden_fields) = @_;
     my $bodytag=&Apache::loncommon::bodytag('Advanced Catalog Search').      my $bodytag=&Apache::loncommon::bodytag('Advanced Catalog Search').
         &Apache::lonhtmlcommon::breadcrumbs(undef,'Searching',          &Apache::lonhtmlcommon::breadcrumbs(undef,'Searching',
                                             'Searching',                                              'Search_Advanced',
                                             undef,undef,                                              undef,undef,
                                             ! $ENV{'form.launch'});                                    $ENV{'form.catalogmode'} ne 'groupsearch');
   
     my %lt=&Apache::lonlocal::texthash('srch' => 'Search',      my %lt=&Apache::lonlocal::texthash('srch' => 'Search',
        'reset' => 'Reset',         'reset' => 'Reset',
        'help' => 'Help');         'help' => 'Help');
Line 774  ENDHEADER Line 906  ENDHEADER
     $scrout.='<tr><td align="right" valign="top">'.      $scrout.='<tr><td align="right" valign="top">'.
  &titlefield(&mt('Copyright/Distribution')).'</td><td colspan="2">'.   &titlefield(&mt('Copyright/Distribution')).'</td><td colspan="2">'.
         &Apache::lonmeta::selectbox('copyright',          &Apache::lonmeta::selectbox('copyright',
                                     '',,                                      $ENV{'form.copyright'},
                                     \&Apache::loncommon::copyrightdescription,                                      \&Apache::loncommon::copyrightdescription,
                                     ( undef,                                      ( undef,
                                       &Apache::loncommon::copyrightids)                                        &Apache::loncommon::copyrightids)
Line 782  ENDHEADER Line 914  ENDHEADER
     $scrout.='<tr><td align="right" valign="top">'.      $scrout.='<tr><td align="right" valign="top">'.
  &titlefield(&mt('Language')).'</td><td colspan="2">'.   &titlefield(&mt('Language')).'</td><td colspan="2">'.
         &Apache::lonmeta::selectbox('language',          &Apache::lonmeta::selectbox('language',
                                     'notset',,                                      $ENV{'form.language'},
                                     \&Apache::loncommon::languagedescription,                                      \&Apache::loncommon::languagedescription,
                                     ('any',&Apache::loncommon::languageids)                                      ('any',&Apache::loncommon::languageids)
                                     ).'</td></tr>';                                      ).'</td></tr>';
Line 997  Outputs: return little blurb on how to e Line 1129  Outputs: return little blurb on how to e
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
 sub searchhelp {  sub searchhelp {
     return &mt('Enter terms or phrases separated by AND, OR, or NOT');      return &mt('Enter words and quoted phrases');
 }  }
   
 ######################################################################  ######################################################################
Line 1188  Parse advanced search form and return th Line 1320  Parse advanced search form and return th
 sub parse_advanced_search {  sub parse_advanced_search {
     my ($r,$closebutton,$hidden_fields)=@_;      my ($r,$closebutton,$hidden_fields)=@_;
     my @BasicFields = ('title','author','subject','keywords','url','version',      my @BasicFields = ('title','author','subject','keywords','url','version',
                        'notes','abstract','extension','owner',                         'notes','abstract','extension','owner','authorspace',
 #                       'custommetadata','customshow',  #                       'custommetadata','customshow',
                        'modifyinguser','standards','mime');                         'modifyinguser','standards','mime');
     my @StatsFields = &statfields();      my @StatsFields = &statfields();
     my @EvalFields = &evalfields();      my @EvalFields = &evalfields();
     my $fillflag=0;      my $fillflag=0;
     my $pretty_search_string = "<br />\n";      my $pretty_search_string = "";
     # Clean up fields for safety      # Clean up fields for safety
     for my $field (@BasicFields,      for my $field (@BasicFields,
                    'creationdatestart_month','creationdatestart_day',                     'creationdatestart_month','creationdatestart_day',
Line 1204  sub parse_advanced_search { Line 1336  sub parse_advanced_search {
    'lastrevisiondatestart_year','lastrevisiondateend_month',     'lastrevisiondatestart_year','lastrevisiondateend_month',
    'lastrevisiondateend_day','lastrevisiondateend_year') {     'lastrevisiondateend_day','lastrevisiondateend_year') {
  $ENV{'form.'.$field}=~s/[^\w\/\s\(\)\=\-\"\']//g;   $ENV{'form.'.$field}=~s/[^\w\/\s\(\)\=\-\"\']//g;
         $ENV{'form.'.$field}=~s/(not\s*$|^\s*(and|or)|)//gi;  
     }      }
     foreach ('mode','form','element') {      foreach ('mode','form','element') {
  # is this required?  Hmmm.   # is this required?  Hmmm.
Line 1248  sub parse_advanced_search { Line 1379  sub parse_advanced_search {
     my $font = '<font color="#800000" face="helvetica">';      my $font = '<font color="#800000" face="helvetica">';
     # Evaluate logical expression AND/OR/NOT phrase fields.      # Evaluate logical expression AND/OR/NOT phrase fields.
     foreach my $field (@BasicFields) {      foreach my $field (@BasicFields) {
  if ($ENV{'form.'.$field}) {   next if (!defined($ENV{'form.'.$field}) || $ENV{'form.'.$field} eq '');
             my $searchphrase = $ENV{'form.'.$field};          my ($error,$SQLQuery) = 
             $pretty_search_string .= $font."$field</font> contains <b>".              &process_phrase_input($ENV{'form.'.$field},
                 $searchphrase."</b>";                                    $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.'</font>: '.$ENV{'form.'.$field};
             if ($ENV{'form.'.$field.'_related'}) {              if ($ENV{'form.'.$field.'_related'}) {
                 my @New_Words;                  my @Words = 
                 ($searchphrase,@New_Words) = &related_version($searchphrase);                      &Apache::loncommon::get_related_words
                 if (@New_Words) {                      ($ENV{'form.'.$field});
                     $pretty_search_string .= " with related words: ".                  if (@Words) {
                         "<b>@New_Words</b>.";                      $pretty_search_string.= ' with related words: '.
                           join(', ',@Words[0..4]);
                 } else {                  } else {
                     $pretty_search_string .= " with no related words.";                      $pretty_search_string.= ' with related words.';
                 }                  }
             }              }
             $pretty_search_string .= "<br />\n";              $pretty_search_string .= '<br />';
     push @queries,&build_SQL_query($field,$searchphrase);              push (@queries,$SQLQuery);
         }          }
     }      }
     #      #
Line 1280  sub parse_advanced_search { Line 1419  sub parse_advanced_search {
         }          }
     }      }
     if (defined($searchphrase)) {      if (defined($searchphrase)) {
         push @queries,&build_SQL_query('mime',$searchphrase);          my ($error,$SQLsearch) = &process_phrase_input($searchphrase,0,'mime');
           push @queries,$SQLsearch;
         $pretty_search_string .=$font.'mime</font> contains <b>'.          $pretty_search_string .=$font.'mime</font> contains <b>'.
             $searchphrase.'</b><br />';              $searchphrase.'</b><br />';
     }      }
Line 1312  sub parse_advanced_search { Line 1452  sub parse_advanced_search {
  push @queries,"(copyright like \"$ENV{'form.copyright'}\")";   push @queries,"(copyright like \"$ENV{'form.copyright'}\")";
         $pretty_search_string.=$font."copyright</font> = ".          $pretty_search_string.=$font."copyright</font> = ".
             &Apache::loncommon::copyrightdescription($ENV{'form.copyright'}).              &Apache::loncommon::copyrightdescription($ENV{'form.copyright'}).
                 "<br \>\n";                  "<br />\n";
     }      }
     #      #
     # Statistics      # Statistics
Line 1395  sub parse_advanced_search { Line 1535  sub parse_advanced_search {
     ##      ##
     ## Deal with restrictions to given domains      ## Deal with restrictions to given domains
     ##       ## 
     my ($libraries_to_query,$pretty_domains_string) =       my ($libraries_to_query,$pretty_domains_string,$domain_sql_restriction) = 
         &parse_domain_restrictions();          &parse_domain_restrictions();
       push(@queries,$domain_sql_restriction);
     $pretty_search_string .= $pretty_domains_string."<br />\n";      $pretty_search_string .= $pretty_domains_string."<br />\n";
     #      #
     if (@queries) {      if (@queries) {
  $query="select * from metadata where ".join(" AND ",@queries);   $query="SELECT * FROM metadata WHERE (".join(") AND (",@queries).')';
     } elsif ($customquery) {      } elsif ($customquery) {
         $query = '';          $query = '';
     }      }
 #    &Apache::lonnet::logthis('query = '.$/.$query);      &Apache::lonnet::logthis('advanced query = '.$/.$query);
     return ($query,$customquery,$customshow,$libraries_to_query,      return ($query,$customquery,$customshow,$libraries_to_query,
             $pretty_search_string);              $pretty_search_string);
 }  }
Line 1425  sub parse_domain_restrictions { Line 1566  sub parse_domain_restrictions {
     #      #
     my %domain_hash = ();      my %domain_hash = ();
     my $pretty_domains_string;      my $pretty_domains_string;
       my $domain_sql_restriction;
     foreach (@allowed_domains) {      foreach (@allowed_domains) {
         $domain_hash{$_}++;          $domain_hash{$_}++;
     }      }
     if ($domain_hash{'any'}) {      if ($domain_hash{'any'}) {
         $pretty_domains_string = "In all LON-CAPA domains.";          $pretty_domains_string = "In all LON-CAPA domains.";
           $domain_sql_restriction = undef;
     } else {      } else {
         if (@allowed_domains > 1) {          if (@allowed_domains > 1) {
             $pretty_domains_string = "In LON-CAPA domains:";              $pretty_domains_string = "In LON-CAPA domains:";
         } else {          } else {
             $pretty_domains_string = "In LON-CAPA domain ";              $pretty_domains_string = "In LON-CAPA domain ";
         }          }
           $domain_sql_restriction = 
               '(domain="'.join('" OR domain="',@allowed_domains).'")';
         foreach (sort @allowed_domains) {          foreach (sort @allowed_domains) {
             $pretty_domains_string .= "<b>".$_."</b> ";              $pretty_domains_string .= "<b>".$_."</b> ";
         }          }
Line 1445  sub parse_domain_restrictions { Line 1590  sub parse_domain_restrictions {
             }              }
         }          }
     }      }
     return ($libraries_to_query,$pretty_domains_string);      return ($libraries_to_query,
               $pretty_domains_string,
               $domain_sql_restriction);
 }  }
   
 ######################################################################  ######################################################################
Line 1466  sub parse_basic_search { Line 1613  sub parse_basic_search {
     #      #
     # Clean up fields for safety      # Clean up fields for safety
     for my $field ('basicexp') {      for my $field ('basicexp') {
  $ENV{"form.$field"}=~s/[^\w\s\(\)\-]//g;   $ENV{"form.$field"}=~s/[^\w\s\'\"\!\(\)\-]//g;
     }      }
     foreach ('mode','form','element') {      foreach ('mode','form','element') {
  # is this required?  Hmmm.   # is this required?  Hmmm.
Line 1474  sub parse_basic_search { Line 1621  sub parse_basic_search {
  $ENV{"form.$_"}=&Apache::lonnet::unescape($ENV{"form.$_"});   $ENV{"form.$_"}=&Apache::lonnet::unescape($ENV{"form.$_"});
  $ENV{"form.$_"}=~s/[^\w\/\s\(\)\=\-\"\']//g;   $ENV{"form.$_"}=~s/[^\w\/\s\(\)\=\-\"\']//g;
     }      }
     my ($libraries_to_query,$pretty_domains_string) =       my ($libraries_to_query,$pretty_domains_string,$domain_sql_restriction) = 
         &parse_domain_restrictions();          &parse_domain_restrictions();
     #      #
     # Check to see if enough of a query is filled in      # Check to see if enough of a query is filled in
     my $search_string = $ENV{'form.basicexp'};      my $search_string = $ENV{'form.basicexp'};
     $search_string =~ s/(not\s*$|^\s*(and|or)|)//gi;  
     if (! &filled($search_string)) {      if (! &filled($search_string)) {
  &output_blank_field_error($r,$closebutton,'phase=disp_basic');   &output_blank_field_error($r,$closebutton,'phase=disp_basic');
  return OK;   return OK;
     }      }
     my $pretty_search_string = '<b>'.$ENV{'form.basicexp'}.'</b>';      my $pretty_search_string=$search_string;
     if ($ENV{'form.related'}) {      my @Queries;
         my @New_Words;      my $searchfield = 'concat_ws(" ",'.join(',',
         ($search_string,@New_Words) = &related_version($ENV{'form.basicexp'});                                              ('title','author','subject',
         if (@New_Words) {                                               'notes','abstract','keywords')
             $pretty_search_string .= " with related words: <b>@New_Words</b>.";                                              ).')';
         } else {      my ($error,$SQLQuery) = &process_phrase_input($search_string,
             $pretty_search_string .= " with no related words.";                                                      $ENV{'form.related'},
         }                                                      $searchfield);
       if ($error) {
           &output_unparsed_phrase_error($r,$closebutton,'phase=disp_basic',
                                         '','basicexp');
           return;
     }      }
       push(@Queries,$SQLQuery);
       push(@Queries,$domain_sql_restriction);
       #foreach my $q (@Queries) {
       #    &Apache::lonnet::logthis('    '.$q);
       #}
       my $final_query = 'SELECT * FROM metadata WHERE '.join(" AND ",@Queries);
     #      #
     # 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_ws(" ",'.$concatarg.')',$search_string);  
     if (defined($pretty_domains_string) && $pretty_domains_string ne '') {      if (defined($pretty_domains_string) && $pretty_domains_string ne '') {
         $pretty_search_string .= ' '.$pretty_domains_string;          $pretty_search_string .= ' '.$pretty_domains_string;
     }      }
     $pretty_search_string .= "<br />\n";      $pretty_search_string .= "<br />\n";
     my $final_query = 'SELECT * FROM metadata WHERE '.$query;      $pretty_search_string =~ s:^<br /> and ::;
     # &Apache::lonnet::logthis($final_query);      &Apache::lonnet::logthis('simple search final query = '.$/.$final_query);
     return ($final_query,$pretty_search_string,      return ($final_query,$pretty_search_string,
             $libraries_to_query);              $libraries_to_query);
 }  }
   
   
   ###############################################################
   ###############################################################
   
   my @Phrases;
   
   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;
   }
   
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
   
Line 1530  Note: Using this twice on a string is pr Line 1859  Note: Using this twice on a string is pr
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
 sub related_version {  sub related_version {
     my $search_string = shift;      my ($word) = @_;
     my $result = $search_string;      return (undef) if (lc($word) =~ /\b(or|and|not)\b/);
     my %New_Words = ();      my @Words = &Apache::loncommon::get_related_words($word);
     while ($search_string =~ /(\w+)/cg) {      # Only use 4 related words
         my $word = $1;      @Words = ($#Words>4? @Words[0..4] : @Words);
         next if (lc($word) =~ /\b(or|and|not)\b/);      my $result = join " OR ", ($word,@Words);
         my @Words = &Apache::loncommon::get_related_words($word);      return $result,sort(@Words);
         @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));  
 }  }
   
 ######################################################################  
 ######################################################################  
   
 =pod   
   
 =item &build_SQL_query()   
   
 Builds a SQL query string from a logical expression with AND/OR keywords  
 using Text::Query and &recursive_SQL_query_builder()  
   
 =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;  
 }  
   
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
Line 1603  sub build_custommetadata_query { Line 1902  sub build_custommetadata_query {
     return $matchexp;      return $matchexp;
 }  }
   
 ######################################################################  
 ######################################################################  
   
 =pod   
   
 =item &recursive_SQL_query_build()   
   
 Recursively constructs an SQL query.  Takes as input $dkey and $pattern.  
   
 =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 (lc($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);  
     }  
     &recursive_SQL_query_build($dkey,$pattern);  
 }  
   
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
Line 1836  a link to change the search query. Line 2094  a link to change the search query.
 ######################################################################  ######################################################################
 sub print_sort_form {  sub print_sort_form {
     my ($r,$pretty_query_string) = @_;      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'} ne 'groupsearch');
   
     ##      ##
     my %SortableFields=&Apache::lonlocal::texthash(       my %SortableFields=&Apache::lonlocal::texthash( 
          id        => 'Default',           id        => 'Default',
Line 1868  sub print_sort_form { Line 2130  sub print_sort_form {
         return;          return;
     }      }
     my $result;      my $result;
       my $html=&Apache::lonxml::xmlbegin();
     $result.=<<END;      $result.=<<END;
 <html>  $html
 <head>  <head>
 <script>  <script>
     function change_sort() {      function change_sort() {
Line 2102  results into MySQL. Line 2365  results into MySQL.
 sub run_search {  sub run_search {
     my ($r,$query,$customquery,$customshow,$serverlist,$pretty_string) = @_;      my ($r,$query,$customquery,$customshow,$serverlist,$pretty_string) = @_;
     my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);      my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);
       $bodytag.=&Apache::lonhtmlcommon::breadcrumbs
           (undef,'Searching','Searching',undef,undef,
            $ENV{'form.catalogmode'} ne 'groupsearch');
     my $connection = $r->connection;      my $connection = $r->connection;
     #      #
     # Print run_search header      # Print run_search header
     #      #
       my $html=&Apache::lonxml::xmlbegin();
     $r->print(<<END);      $r->print(<<END);
 <html>  $html
 <head><title>Search Status</title></head>  <head>
   <title>Search Status</title></head>
 $bodytag  $bodytag
 <form name="statusform" action="" method="post">  <form name="statusform" action="" method="post">
 <input type="hidden" name="Queue" value="" />  <input type="hidden" name="Queue" value="" />
 END  END
     # Check to see if $pretty_string has more than one carriage return.      # Remove leading and trailing <br />
     # Assume \n s are following <br /> s and truncate the value.      $pretty_string =~ s:^\s*<br />::i;
     # (there is probably a better way)...      $pretty_string =~ s:(<br />)*\s*$::im;
     my @Lines = split /<br \/>/,$pretty_string;      my @Lines = split("<br />",$pretty_string);
       # I keep getting blank items at the end of the list, hence the following:
       while ($Lines[-1] =~ /^\s*$/ && @Lines) {
           pop(@Lines);
       }
     if (@Lines > 2) {      if (@Lines > 2) {
         $pretty_string = join '<br \>',(@Lines[0..2],'....<br />');          $pretty_string = join '<br />',(@Lines[0..2],'....<br />');
     }      }
     $r->print(&mt("Search: [_1]",$pretty_string));      $r->print(&mt("Search: [_1]",$pretty_string));
     $r->rflush();      $r->rflush();
Line 2211  END Line 2483  END
         #          #
         # Loop through the servers we have contacted but do not          # Loop through the servers we have contacted but do not
         # have results from yet, looking for results.          # have results from yet, looking for results.
         while (my ($server,$status) = each(%Server_status)) {          foreach my $server (keys(%Server_status)) {
             last if ($connection->aborted());              last if ($connection->aborted());
             &update_seconds($r);              &update_seconds($r);
               my $status = $Server_status{$server};
             if ($status eq 'con_lost') {              if ($status eq 'con_lost') {
                 delete ($Server_status{$server});                  delete ($Server_status{$server});
                 next;                  next;
Line 2285  END Line 2558  END
     # results to get, so let the client know the top frame needs to be      # results to get, so let the client know the top frame needs to be
     # loaded from /adm/searchcat      # loaded from /adm/searchcat
     $r->print("</body></html>");      $r->print("</body></html>");
     if ($ENV{'form.catalogmode'} ne 'groupsearch') {  #    if ($ENV{'form.catalogmode'} ne 'groupsearch') {
         $r->print("<script>".          $r->print("<script>".
                       "window.location='/adm/searchcat?".                        "window.location='/adm/searchcat?".
                       "phase=sort&".                        "phase=sort&".
                       "persistent_db_id=$ENV{'form.persistent_db_id'}';".                        "persistent_db_id=$ENV{'form.persistent_db_id'}';".
                   "</script>");                    "</script>");
     }  #    }
     return;      return;
 }  }
   
Line 2408  sub display_results { Line 2681  sub display_results {
               &hidden_field('persistent_db_id').                &hidden_field('persistent_db_id').
               &hidden_field('start')                &hidden_field('start')
               );                );
       #
       # Build sorting selector
       my @field_order =  ('default',
                           'title',
                           'author',
                           'subject',
                           'url',
                           'keywords',
                           'version',
                           'language',
                           'creationdate'=>,
                           'lastrevisiondate',
                           'owner',
                           'copyright',
                           'authorspace',
                           'lowestgradeleve',
                           'highestgradelevel',
                           'standards',
                           'count',
                           'stdno',
                           'avetries',
                           'difficulty',
                           'disc',
                           'clear',
                           'technical',
                           'correct',
                           'helpful',
                           'depth',
                           );                                              
       my %sort_fields = ('default'     => 'Default',
                          'title'       => 'Title',
                          'author'      => 'Author',
                          'subject'     => 'Subject',
                          'url'         => 'URL',
                          'keywords'    => 'Keywords',
                          'version'     => 'Version',
                          'language'    => 'Language',
                          'creationdate'=> 'Creation Date',
                          'lastrevisiondate' => 'Last Revision Date',
                          'owner'       => 'Owner',
                          'copyright'   => 'Copyright',
                          'authorspace' => 'Authorspace',
                          'lowestgradeleve' => 'Lowest Grade Level',
                          'highestgradelevel' => 'Highest Grade Level',
                          'standards'   => 'Standards',
                          'count'       => 'Number of Accesses',
                          'stdno'       => 'Students Attempting',
                          'avetries'    => 'Average Number of Tries',
                          'difficulty'  => 'Mean Degree of Difficulty',
                          'disc'        => 'Mean Degree of Discrimination',
                          'clear'       => 'Evaluation: Clear',
                          'technical'   => 'Evaluation: Technically Correct',
                          'correct'     => 'Evaluation: Material is Correct',
                          'helpful'     => 'Evaluation: Material is Helpful',
                          'depth'       => 'Evaluation: Material has Depth',
                          'select_form_order' => \@field_order,
                          );
   
       my $sortform = &mt('Sort by [_1]',
                          &Apache::loncommon::select_form($ENV{'form.sortfield'},
                                                         'sortfield',
                                                         %sort_fields));
     ##      ##
     ## Output links (if necessary) for 'prev' and 'next' pages.      ## Output links (if necessary) for 'prev' and 'next' pages.
     $r->print      $r->print
         ('<table width="100%"><tr><td width="50%" align="right">'.          ('<table width="100%"><tr><td width="25%" align="right">'.
            $sortform.
            '</td><td width="25%" align="right">'.
          &prev_next_buttons($min,$ENV{'form.show'},$total_results).           &prev_next_buttons($min,$ENV{'form.show'},$total_results).
          '</td><td align="right">'.           '</td><td align="right">'.
          &viewoptions().'</td></tr></table>'           &viewoptions().'</td></tr></table>'
Line 2422  sub display_results { Line 2759  sub display_results {
                   "</form></body></html>");                    "</form></body></html>");
         return;          return;
     } else {      } else {
         $r->print          $r->print('<center>'.
             ("<center>Results $min to $max out of $total_results</center>\n");                    mt('Results [_1] to [_2] out of [_3]',
                        $min,$max,$total_results).
                     "</center>\n");
     }      }
     ##      ##
     ## Get results from MySQL table      ## Get results from MySQL table
     my @Results = &Apache::lonmysql::get_rows($table,      my $sort_command  = 'id>='.$min.' AND id<='.$max;
                                               'id>='.$min.' AND id<='.$max);      if ($ENV{'form.sortfield'} ne 'default' && 
           exists($sort_fields{$ENV{'form.sortfield'}})) {
           $sort_command = $ENV{'form.sortfield'}.' IS NOT NULL '.
               'ORDER BY '.$ENV{'form.sortfield'}.
               '  LIMIT '.($min-1).','.($max-$min);
       }
       my @Results = &Apache::lonmysql::get_rows($table,$sort_command);
     ##      ##
     ## Loop through the results and output them.      ## Loop through the results and output them.
     foreach my $row (@Results) {      foreach my $row (@Results) {
Line 2764  END Line 3109  END
 ######################################################################  ######################################################################
 sub search_status_header {  sub search_status_header {
     my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);      my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);
       my $html=&Apache::lonxml::xmlbegin();
     return <<ENDSTATUS;      return <<ENDSTATUS;
 <html><head><title>Search Status</title></head>  $html
   <head>
   <title>Search Status</title></head>
 $bodytag  $bodytag
 <h3>Search Status</h3>  <h3>Search Status</h3>
 Sending search request to LON-CAPA servers.<br />  Sending search request to LON-CAPA servers.<br />
Line 2788  sub print_frames_interface { Line 3136  sub print_frames_interface {
         "&persistent_db_id=".$ENV{'form.persistent_db_id'};          "&persistent_db_id=".$ENV{'form.persistent_db_id'};
     my $run_search_link = $basic_link."&phase=run_search";      my $run_search_link = $basic_link."&phase=run_search";
     my $results_link = &results_link();      my $results_link = &results_link();
       my $html=&Apache::lonxml::xmlbegin();
     my $result = <<"ENDFRAMES";      my $result = <<"ENDFRAMES";
 <html>  $html
 <head>  <head>
 <script>  <script>
 var targetwin = opener;  var targetwin = opener;
Line 2811  ENDFRAMES Line 3160  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   =pod 
   
 =item Metadata Viewing Functions  =item Metadata Viewing Functions
Line 2858  sub detailed_citation_view { Line 3242  sub detailed_citation_view {
          { name=>'copyrighttag',           { name=>'copyrighttag',
            translate => '<b>Copyright/Distribution:</b>&nbsp;[_1]',},             translate => '<b>Copyright/Distribution:</b>&nbsp;[_1]',},
          { name=>'count',           { name=>'count',
              format => "%d",
            translate => '<b>Access Count:</b>&nbsp;[_1]',},             translate => '<b>Access Count:</b>&nbsp;[_1]',},
          { name=>'stdno',           { name=>'stdno',
              format => "%d",
            translate => '<b>Number of Students:</b>&nbsp;[_1]',},             translate => '<b>Number of Students:</b>&nbsp;[_1]',},
          { name=>'avetries',           { name=>'avetries',
              format => "%.2f",
            translate => '<b>Average Tries:</b>&nbsp;[_1]',},             translate => '<b>Average Tries:</b>&nbsp;[_1]',},
          { name=>'disc',           { name=>'disc',
              format => "%.2f",
            translate => '<b>Degree of Discrimination:</b>&nbsp;[_1]',},             translate => '<b>Degree of Discrimination:</b>&nbsp;[_1]',},
          { name=>'difficulty',           { name=>'difficulty',
              format => "%.2f",
            translate => '<b>Degree of Difficulty:</b>&nbsp;[_1]',},             translate => '<b>Degree of Difficulty:</b>&nbsp;[_1]',},
          { name=>'clear',           { name=>'clear',
              format => "%.2f",
            translate => '<b>Clear:</b>&nbsp;[_1]',},             translate => '<b>Clear:</b>&nbsp;[_1]',},
          { name=>'depth',           { name=>'depth',
              format => "%.2f",
            translate => '<b>Depth:</b>&nbsp;[_1]',},             translate => '<b>Depth:</b>&nbsp;[_1]',},
          { name=>'helpful',           { name=>'helpful',
              format => "%.2f",
            translate => '<b>Helpful:</b>&nbsp;[_1]',},             translate => '<b>Helpful:</b>&nbsp;[_1]',},
          { name=>'correct',           { name=>'correct',
            translate => '<b>Correcy:</b>&nbsp;[_1]',},             format => "%.2f",
              translate => '<b>Correct:</b>&nbsp;[_1]',},
          { name=>'technical',           { name=>'technical',
              format => "%.2f",
            translate => '<b>Technical:</b>&nbsp;[_1]',},             translate => '<b>Technical:</b>&nbsp;[_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',},
          ) {           ) {
         if (exists($values{$field->{'name'}}) &&          next if (! exists($values{$field->{'name'}}) ||
             $values{$field->{'name'}} ne '') {                   $values{$field->{'name'}} eq '');
           if (exists($field->{'type'}) && $field->{'type'} eq 'list') {
               $result .= '<b>'.&mt($field->{'translate'}).'</b><ul>';
               foreach my $item (split(',',$values{$field->{'name'}})){
                   $result .= '<li>'.
                       '<a target="search_preview" '.
                       'href="/res/'.$item.'">'.$item.'</a></li>';
               }
               $result .= '</ul>';
           } elsif (exists($field->{'format'}) && $field->{'format'} ne ''){
               $result.= &mt($field->{'translate'},
                             sprintf($field->{'format'},
                                     $values{$field->{'name'}}))."<br />\n";
           } else {
             if ($field->{'special'} eq 'url link') {              if ($field->{'special'} eq 'url link') {
                 $result.=                   $result.= 
                     &mt($field->{'translate'},                       &mt($field->{'translate'},
                         '<a href="'.$values{'url'}.'" '.                           '<a href="'.$values{'url'}.'" '.
                             'target="search_preview">'.                           'target="search_preview">'.
                         $values{$field->{'name'}}.                           $values{$field->{'name'}}.
                         '</a>');                           '</a>');
             } else {              } else {
                 $result.= &mt($field->{'translate'},                  $result.= &mt($field->{'translate'},
                               $values{$field->{'name'}});                                $values{$field->{'name'}});
             }              }
             $result .= "<br />\n";              $result .= "<br />\n";
             }          }
     }      }
     $result .= "</p>";      $result .= "</p>";
     if (exists($values{'extrashow'}) && $values{'extrashow'} ne '') {      if (exists($values{'extrashow'}) && $values{'extrashow'} ne '') {
Line 2908  sub detailed_citation_view { Line 3324  sub detailed_citation_view {
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
   
 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   =pod 
   
 =item &summary_view()   =item &summary_view() 
Line 3011  $prefix <img src="$icon" /> Line 3392  $prefix <img src="$icon" />
     <dd><a href="http://$ENV{'HTTP_HOST'}$values{'url'}"       <dd><a href="http://$ENV{'HTTP_HOST'}$values{'url'}" 
          target='search_preview'>$values{'url'}</a></dd>           target='search_preview'>$values{'url'}</a></dd>
 END  END
     foreach my $field ('title','author','subject','keywords','notes',      foreach my $field ('title','author','domain','subject','keywords','notes',
                        'mimetag','language','creationdate','lastrevisiondate',                         'mimetag','language','creationdate','lastrevisiondate',
                        'owner','copyrighttag','hostname','abstract') {                         'owner','copyrighttag','hostname','abstract') {
         $result .= (' 'x4).'<dt>'.$Translated{$field}.'</dt>'."\n".          $result .= (' 'x4).'<dt>'.$Translated{$field}.'</dt>'."\n".
Line 3052  sub xml_sgml_view { Line 3433  sub xml_sgml_view {
     my ($prefix,%values) = @_;      my ($prefix,%values) = @_;
     my $xml = '<LonCapaResource>'."\n";      my $xml = '<LonCapaResource>'."\n";
     # The usual suspects      # The usual suspects
     foreach my $field ('url','title','author','subject','keywords','notes') {      foreach my $field ('url','title','author','subject','keywords','notes','domain') {
         $xml .= qq{<$field>$values{$field}</$field>}."\n";          $xml .= qq{<$field>$values{$field}</$field>}."\n";
     }      }
     #      #
Line 3135  sub filled { Line 3516  sub filled {
   
 =pod   =pod 
   
   =item &output_unparsed_phrase_error()
   
   =cut
   
   ######################################################################
   ######################################################################
   sub output_unparsed_phrase_error {
       my ($r,$closebutton,$parms,$hidden_fields,$field)=@_;
       my $errorstring;
       if ($field eq 'basicexp') {
           $errorstring = &mt('Unable to understand the search phrase <i>[_1]</i>.  Please modify your search.',$ENV{'form.basicexp'});
       } else {
           $errorstring = &mt('Unable to understand the search phrase <b>[_1]</b>:<i>[_2]</i>.',$field,$ENV{'form.'.$field});
       }
       my $bodytag = &Apache::loncommon::bodytag('Search');
       my $heading = &mt('Unparsed Field');
       my $revise  = &mt('Revise search request');
       # make query information persistent to allow for subsequent revision
       my $html=&Apache::lonxml::xmlbegin();
       $r->print(<<ENDPAGE);
   $html
   <head>
   <title>The LearningOnline Network with CAPA</title>
   </head>
   $bodytag
   <form method="post" action="/adm/searchcat">
   $hidden_fields
   $closebutton
   <hr />
   <h2>$heading</h2>
   <p>
   $errorstring
   </p>
   <p>
   <a href="/adm/searchcat?$parms&persistent_db_id=$ENV{'form.persistent_db_id'}">$revise</a>
   </p>
   </body>
   </html>
   ENDPAGE
   }
   
   ######################################################################
   ######################################################################
   
   =pod 
   
 =item &output_blank_field_error()  =item &output_blank_field_error()
   
 Output a complete page that indicates the user has not filled in enough  Output a complete page that indicates the user has not filled in enough
Line 3152  $parms is extra information to include i Line 3579  $parms is extra information to include i
 ######################################################################  ######################################################################
 sub output_blank_field_error {  sub output_blank_field_error {
     my ($r,$closebutton,$parms,$hidden_fields)=@_;      my ($r,$closebutton,$parms,$hidden_fields)=@_;
     my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);      my $bodytag=&Apache::loncommon::bodytag('Search');
     # make query information persistent to allow for subsequent revision      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.');
     $r->print(<<BEGINNING);      my $revise = &mt('Revise Search Request');
 <html>      my $heading = &mt('Unactionable Search Queary');
       my $html=&Apache::lonxml::xmlbegin();
       $r->print(<<ENDPAGE);
   $html
 <head>  <head>
 <title>The LearningOnline Network with CAPA</title>  <title>The LearningOnline Network with CAPA</title>
 BEGINNING  
     $r->print(<<RESULTS);  
 </head>  </head>
 $bodytag  $bodytag
 <img align='right' src='/adm/lonIcons/lonlogos.gif' />  
 <h1>Search Catalog</h1>  
 <form method="post" action="/adm/searchcat">  <form method="post" action="/adm/searchcat">
 $hidden_fields  $hidden_fields
 <a href="/adm/searchcat?$parms&persistent_db_id=$ENV{'form.persistent_db_id'}"  
 >Revise search request</a>&nbsp;  
 $closebutton  $closebutton
 <hr />  <hr />
 <h3>Unactionable search query.</h3>  <h2>$heading</h2>
   <p>
   $errormsg
   </p>
 <p>  <p>
 You did not fill in enough information for the search to be started.  <a href="/adm/searchcat?$parms&persistent_db_id=$ENV{'form.persistent_db_id'}">$revise</a>&nbsp;
 You need to fill in relevant fields on the search page in order   
 for a query to be processed.  
 </p>  </p>
 </body>  </body>
 </html>  </html>
 RESULTS  ENDPAGE
       return;
 }  }
   
 ######################################################################  ######################################################################
Line 3204  sub output_date_error { Line 3630  sub output_date_error {
     my ($r,$message,$closebutton,$hidden_fields)=@_;      my ($r,$message,$closebutton,$hidden_fields)=@_;
     # make query information persistent to allow for subsequent revision      # make query information persistent to allow for subsequent revision
     my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);      my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);
       my $html=&Apache::lonxml::xmlbegin();
     $r->print(<<RESULTS);      $r->print(<<RESULTS);
 <html>  $html
 <head>  <head>
 <title>The LearningOnline Network with CAPA</title>  <title>The LearningOnline Network with CAPA</title>
 </head>  </head>

Removed from v.1.223  
changed lines
  Added in v.1.239


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.