Diff for /loncom/homework/grades.pm between versions 1.739 and 1.801

version 1.739, 2017/05/19 16:40:50 version 1.801, 2024/12/09 22:22:57
Line 46  use Apache::lonenc; Line 46  use Apache::lonenc;
 use Apache::lonstathelpers;  use Apache::lonstathelpers;
 use Apache::lonquickgrades;  use Apache::lonquickgrades;
 use Apache::bridgetask();  use Apache::bridgetask();
   use Apache::lontexconvert();
   use Apache::loncourserespicker;
 use String::Similarity;  use String::Similarity;
   use HTML::Parser();
   use File::MMagic;
 use LONCAPA;  use LONCAPA;
   use LONCAPA::ltiutils();
   
 use POSIX qw(floor);  use POSIX qw(floor);
   
Line 62  my $ssi_retries = 5; Line 67  my $ssi_retries = 5;
 my $ssi_error;  my $ssi_error;
 my $ssi_error_resource;  my $ssi_error_resource;
 my $ssi_error_message;  my $ssi_error_message;
   my $registered_cleanup;
   
 sub ssi_with_retries {  sub ssi_with_retries {
     my ($resource, $retries, %form) = @_;      my ($resource, $retries, %form) = @_;
Line 116  sub getpartlist { Line 121  sub getpartlist {
     my $res      = $navmap->getBySymb($symb);      my $res      = $navmap->getBySymb($symb);
     my $partlist = $res->parts();      my $partlist = $res->parts();
     my $url      = $res->src();      my $url      = $res->src();
     my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys'));      my $toolsymb;
       if ($url =~ /ext\.tool$/) {
           $toolsymb = $symb;
       }
       my @metakeys = split(/,/,&Apache::lonnet::metadata($url,'keys',$toolsymb));
   
     my @stores;      my @stores;
     foreach my $part (@{ $partlist }) {      foreach my $part (@{ $partlist }) {
Line 140  sub nameUserString { Line 149  sub nameUserString {
 }  }
   
 #--- Get the partlist and the response type for a given problem. ---  #--- Get the partlist and the response type for a given problem. ---
 #--- Indicate if a response type is coded handgraded or not. ---  #--- Count responseIDs, essayresponse items, and dropbox items ---
 #--- Sets response_error pointer to "1" if navmaps object broken ---  #--- Sets response_error pointer to "1" if navmaps object broken ---
 sub response_type {  sub response_type {
     my ($symb,$response_error) = @_;      my ($symb,$response_error) = @_;
Line 158  sub response_type { Line 167  sub response_type {
         return;          return;
     }      }
     my $partlist = $res->parts();      my $partlist = $res->parts();
       my ($numresp,$numessay,$numdropbox) = (0,0,0);
     my %vPart =       my %vPart = 
  map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart'));   map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart'));
     my (%response_types,%handgrade);      my (%response_types,%handgrade);
Line 167  sub response_type { Line 177  sub response_type {
  my @types = $res->responseType($part);   my @types = $res->responseType($part);
  my @ids = $res->responseIds($part);   my @ids = $res->responseIds($part);
  for (my $i=0; $i < scalar(@ids); $i++) {   for (my $i=0; $i < scalar(@ids); $i++) {
               $numresp ++;
     $response_types{$part}{$ids[$i]} = $types[$i];      $response_types{$part}{$ids[$i]} = $types[$i];
               if ($types[$i] eq 'essay') {
                   $numessay ++;
                   if (&Apache::lonnet::EXT("resource.$part".'_'.$ids[$i].".uploadedfiletypes",$symb)) {
                       $numdropbox ++;
                   }
               }
     $handgrade{$part.'_'.$ids[$i]} =       $handgrade{$part.'_'.$ids[$i]} = 
  &Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i].   &Apache::lonnet::EXT('resource.'.$part.'_'.$ids[$i].
      '.handgrade',$symb);       '.handgrade',$symb);
  }   }
     }      }
     return ($partlist,\%handgrade,\%response_types);      return ($partlist,\%handgrade,\%response_types,$numresp,$numessay,$numdropbox);
 }  }
   
 sub flatten_responseType {  sub flatten_responseType {
Line 200  sub get_display_part { Line 217  sub get_display_part {
     return $display;      return $display;
 }  }
   
   #--- Show parts and response type
   sub showResourceInfo {
       my ($symb,$partlist,$responseType,$formname,$checkboxes,$uploads) = @_;
       unless ((ref($partlist) eq 'ARRAY') && (ref($responseType) eq 'HASH')) {
           return '<br clear="all">';
       }
       my $coltitle = &mt('Problem Part Shown');
       if ($checkboxes) {
           $coltitle = &mt('Problem Part');
       } else {
           my $checkedparts = 0;
           foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {
               if (grep(/^\Q$partid\E$/,@{$partlist})) {
                   $checkedparts ++;
               }
           }
           if ($checkedparts == scalar(@{$partlist})) {
               return '<br clear="all">';
           }
           if ($uploads) {
               $coltitle = &mt('Problem Part Selected');
           }
       }
       my $result = '<div class="LC_left_float" style="display:inline-block;">';
       if ($checkboxes) {
           my $legend = &mt('Parts to display');
           if ($uploads) {
               $legend = &mt('Part(s) with dropbox');
           }
           $result .= '<fieldset style="display:inline-block;"><legend>'.$legend.'</legend>'.
                      '<span class="LC_nobreak">'.
                      '<label><input type="radio" name="chooseparts" value="0" onclick="toggleParts('."'$formname'".');" checked="checked" />'.
                      &mt('All parts').'</label>'.('&nbsp;'x2).
                      '<label><input type="radio" name="chooseparts" value="1" onclick="toggleParts('."'$formname'".');" />'.
                      &mt('Selected parts').'</label></span>'.
                      '<div id="LC_partselector" style="display:none">';
       }
       $result .= &Apache::loncommon::start_data_table()
                 .&Apache::loncommon::start_data_table_header_row();
       if ($checkboxes) {
           $result .= '<th>'.&mt('Display?').'</th>';
       }
       $result .= '<th>'.$coltitle.'</th>'
                 .'<th>'.&mt('Res. ID').'</th>'
                 .'<th>'.&mt('Type').'</th>'
                 .&Apache::loncommon::end_data_table_header_row();
       my %partsseen;
       foreach my $partID (sort(keys(%$responseType))) {
           foreach my $resID (sort(keys(%{ $responseType->{$partID} }))) {
               my $responsetype = $responseType->{$partID}->{$resID};
               if ($uploads) {
                   next unless ($responsetype eq 'essay');
                   next unless (&Apache::lonnet::EXT("resource.$partID".'_'."$resID.uploadedfiletypes",$symb));
               }
               my $display_part=&get_display_part($partID,$symb);
               if (exists($partsseen{$partID})) {
                   $result.=&Apache::loncommon::continue_data_table_row();
               } else {
                   $partsseen{$partID}=scalar(keys(%{$responseType->{$partID}}));
                   $result.=&Apache::loncommon::start_data_table_row().
                            '<td rowspan="'.$partsseen{$partID}.'" style="vertical-align:middle">';
                   if ($checkboxes) {
                       $result.='<input type="checkbox" name="vPart" checked="checked" value="'.$partID.'" /></td>'.
                                '<td rowspan="'.$partsseen{$partID}.'" style="vertical-align:middle">'.$display_part.'</td>';
                   } else {
                       $result.=$display_part.'</td>';
                   }
               }
               $result.='<td>'.'<span class="LC_internal_info">'.$resID.'</span></td>'
                       .'<td>'.&mt($responsetype).'</td>'
                       .&Apache::loncommon::end_data_table_row();
           }
       }
       $result.=&Apache::loncommon::end_data_table();
       if ($checkboxes) {
           $result .= '</div></fieldset>';
       }
       $result .= '</div><div style="padding:0;clear:both;margin:0;border:0"></div>';
       if (!keys(%partsseen)) {
           $result = '';
           if ($uploads) {
               return '<div style="padding:0;clear:both;margin:0;border:0"></div>'.
                      '<p class="LC_info">'.
                       &mt('No dropbox items or essayresponse items with uploadedfiletypes set.').
                      '</p>';
           } else {
               return '<br clear="all" />';
           }
       }
       return $result;
   }
   
   sub part_selector_js {
       my $js = <<"END";
   function toggleParts(formname) {
       if (document.getElementById('LC_partselector')) {
           var index = '';
           if (document.forms.length) {
               for (var i=0; i<document.forms.length; i++) {
                   if (document.forms[i].name == formname) {
                       index = i;
                       break;
                   }
               }
           }
           if ((index != '') && (document.forms[index].elements['chooseparts'].length > 1)) {
               for (var i=0; i<document.forms[index].elements['chooseparts'].length; i++) {
                   if (document.forms[index].elements['chooseparts'][i].checked) {
                      var val = document.forms[index].elements['chooseparts'][i].value;
                       if (document.forms[index].elements['chooseparts'][i].value == 1) {
                           document.getElementById('LC_partselector').style.display = 'block';
                       } else {
                           document.getElementById('LC_partselector').style.display = 'none';
                       }
                   }
               }
           }
       }
   }
   END
       return &Apache::lonhtmlcommon::scripttag($js);
   }
   
 sub reset_caches {  sub reset_caches {
     &reset_analyze_cache();      &reset_analyze_cache();
     &reset_perm();      &reset_perm();
Line 293  sub reset_caches { Line 433  sub reset_caches {
     }      }
   
     sub scantron_partids_tograde {      sub scantron_partids_tograde {
         my ($resource,$cid,$uname,$udom,$check_for_randomlist,$bubbles_per_row) = @_;          my ($resource,$cid,$uname,$udom,$check_for_randomlist,$bubbles_per_row,$scancode) = @_;
         my (%analysis,@parts);          my (%analysis,@parts);
         if (ref($resource)) {          if (ref($resource)) {
             my $symb = $resource->symb();              my $symb = $resource->symb();
Line 301  sub reset_caches { Line 441  sub reset_caches {
             if ($check_for_randomlist) {              if ($check_for_randomlist) {
                 $add_to_form = { 'check_parts_withrandomlist' => 1,};                  $add_to_form = { 'check_parts_withrandomlist' => 1,};
             }              }
             my $analyze =               if ($scancode) {
                   if (ref($add_to_form) eq 'HASH') {
                       $add_to_form->{'code_for_randomlist'} = $scancode;
                   } else {
                       $add_to_form = { 'code_for_randomlist' => $scancode,};
                   }
               }
               my $analyze =
                 &get_analyze($symb,$uname,$udom,undef,$add_to_form,                  &get_analyze($symb,$uname,$udom,undef,$add_to_form,
                              undef,undef,undef,$bubbles_per_row);                               undef,undef,undef,$bubbles_per_row);
             if (ref($analyze) eq 'HASH') {              if (ref($analyze) eq 'HASH') {
Line 331  sub cleanRecord { Line 478  sub cleanRecord {
     if ($response =~ /^(option|rank)$/) {      if ($response =~ /^(option|rank)$/) {
  my %answer=&Apache::lonnet::str2hash($answer);   my %answer=&Apache::lonnet::str2hash($answer);
         my @answer = %answer;          my @answer = %answer;
         %answer = map {&HTML::Entities::encode($_, '"<>&')}  @answer;          %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer;
  my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});   my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
  my ($toprow,$bottomrow);   my ($toprow,$bottomrow);
  foreach my $foil (@$order) {   foreach my $foil (@$order) {
Line 349  sub cleanRecord { Line 496  sub cleanRecord {
     } elsif ($response eq 'match') {      } elsif ($response eq 'match') {
  my %answer=&Apache::lonnet::str2hash($answer);   my %answer=&Apache::lonnet::str2hash($answer);
         my @answer = %answer;          my @answer = %answer;
         %answer = map {&HTML::Entities::encode($_, '"<>&')}  @answer;          %answer = map {&HTML::Entities::encode($_, '"<>&')} @answer;
  my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});   my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
  my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"});   my @items=&Apache::lonnet::str2array($record->{$version."resource.$partid.$respid.submissionitems"});
  my ($toprow,$middlerow,$bottomrow);   my ($toprow,$middlerow,$bottomrow);
Line 406  sub cleanRecord { Line 553  sub cleanRecord {
     $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';      $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
     $env{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.      $env{'form.'.$symb} = 1; # so that we don't have to read it from disk for multiple sub of the same prob.
  }   }
           $answer = &Apache::lontexconvert::msgtexconverted($answer);
  return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>';   return '<br /><br /><blockquote><tt>'.&keywords_highlight($answer).'</tt></blockquote>';
   
     } elsif ( $response eq 'organic') {      } elsif ( $response eq 'organic') {
         my $result=&mt('Smile representation: [_1]',          my $result=&mt('Smile representation: [_1]',
                            '"<tt>'.&HTML::Entities::encode($answer, '"<>&').'</tt>"');                             '"<tt>'.&HTML::Entities::encode($answer, '"<>&').'</tt>"');
Line 491  COMMONJSFUNCTIONS Line 638  COMMONJSFUNCTIONS
 #--- Dumps the class list with usernames,list of sections,  #--- Dumps the class list with usernames,list of sections,
 #--- section, ids and fullnames for each user.  #--- section, ids and fullnames for each user.
 sub getclasslist {  sub getclasslist {
     my ($getsec,$filterlist,$getgroup) = @_;      my ($getsec,$filterbyaccstatus,$getgroup,$symb,$submitonly,$filterbysubmstatus,$filterbypbid,$possibles) = @_;
     my @getsec;      my @getsec;
     my @getgroup;      my @getgroup;
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));      my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
Line 519  sub getclasslist { Line 666  sub getclasslist {
     #      #
     my %sections;      my %sections;
     my %fullnames;      my %fullnames;
       my %passback;
       my ($cdom,$cnum,$partlist);
       if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {
           $cdom = $env{"course.$env{'request.course.id'}.domain"};
           $cnum = $env{"course.$env{'request.course.id'}.num"};
           my $res_error;
           ($partlist) = &response_type($symb,\$res_error);
       } elsif ($filterbypbid) {
           $cdom = $env{"course.$env{'request.course.id'}.domain"};
           $cnum = $env{"course.$env{'request.course.id'}.num"};
       }
     foreach my $student (keys(%$classlist)) {      foreach my $student (keys(%$classlist)) {
         my $end      =           my $end      = 
             $classlist->{$student}->[&Apache::loncoursedata::CL_END()];              $classlist->{$student}->[&Apache::loncoursedata::CL_END()];
Line 535  sub getclasslist { Line 693  sub getclasslist {
         my $group   =           my $group   = 
             $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];              $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
  # filter students according to status selected   # filter students according to status selected
  if ($filterlist && (!($stu_status =~ /Any/))) {   if ($filterbyaccstatus && (!($stu_status =~ /Any/))) {
     if (!($stu_status =~ $status)) {      if (!($stu_status =~ $status)) {
  delete($classlist->{$student});   delete($classlist->{$student});
  next;   next;
Line 552  sub getclasslist { Line 710  sub getclasslist {
                }                  } 
         }          }
            if (($grp eq 'none') && !$group) {             if (($grp eq 'none') && !$group) {
                $exclude = 0;             $exclude = 0;
         }          }
     }      }
     if ($exclude) {      if ($exclude) {
         delete($classlist->{$student});          delete($classlist->{$student});
    next;
     }      }
  }   }
           if (($filterbysubmstatus) && ($submitonly ne 'all') && ($symb ne '')) {
               my $udom =
                   $classlist->{$student}->[&Apache::loncoursedata::CL_SDOM()];
               my $uname =
                   $classlist->{$student}->[&Apache::loncoursedata::CL_SNAME()];
               if (($symb ne '') && ($udom ne '') && ($uname ne '')) {
                   if ($submitonly eq 'queued') {
                       my %queue_status =
                           &Apache::bridgetask::get_student_status($symb,$cdom,$cnum,
                                                                   $udom,$uname);
                       if (!defined($queue_status{'gradingqueue'})) {
                           delete($classlist->{$student});
                           next;
                       }
                   } else {
                       my (%status) =&student_gradeStatus($symb,$udom,$uname,$partlist);
                       my $submitted = 0;
                       my $graded = 0;
                       my $incorrect = 0;
                       foreach (keys(%status)) {
                           $submitted = 1 if ($status{$_} ne 'nothing');
                           $graded = 1 if ($status{$_} =~ /^ungraded/);
                           $incorrect = 1 if ($status{$_} =~ /^incorrect/);
   
                           my ($foo,$partid,$foo1) = split(/\./,$_);
                           if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
                               $submitted = 0;
                           }
                       }
                       if (!$submitted && ($submitonly eq 'yes' ||
                                           $submitonly eq 'incorrect' ||
                                           $submitonly eq 'graded')) {
                           delete($classlist->{$student});
                           next;
                       } elsif (!$graded && ($submitonly eq 'graded')) {
                           delete($classlist->{$student});
                           next;
                       } elsif (!$incorrect && $submitonly eq 'incorrect') {
                           delete($classlist->{$student});
                           next;
                       }
                   }
               }
           }
           if ($filterbypbid) {
               if (ref($possibles) eq 'HASH') {
                   unless (exists($possibles->{$student})) {
                       delete($classlist->{$student});
                       next;
                   }
               }
               my $udom =
                   $classlist->{$student}->[&Apache::loncoursedata::CL_SDOM()];
               my $uname =
                   $classlist->{$student}->[&Apache::loncoursedata::CL_SNAME()];
               if (($udom ne '') && ($uname ne '')) {
                   my %pbinfo = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',[$filterbypbid],$udom,$uname);
                   if (ref($pbinfo{$filterbypbid}) eq 'ARRAY') {
                       $passback{$student} = $pbinfo{$filterbypbid};
                   } else {
                       delete($classlist->{$student});
                       next;
                   }
               }
           }
  $section = ($section ne '' ? $section : 'none');   $section = ($section ne '' ? $section : 'none');
  if (&canview($section)) {   if (&canview($section)) {
     if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {      if (!@getsec || grep(/^\Q$section\E$/,@getsec)) {
Line 573  sub getclasslist { Line 797  sub getclasslist {
     delete($classlist->{$student});      delete($classlist->{$student});
  }   }
     }      }
     my %seen = ();  
     my @sections = sort(keys(%sections));      my @sections = sort(keys(%sections));
     return ($classlist,\@sections,\%fullnames);      return ($classlist,\@sections,\%fullnames,\%passback);
 }  }
   
 sub canmodify {  sub canmodify {
Line 589  sub canmodify { Line 812  sub canmodify {
  #can modify the requested section   #can modify the requested section
  return 1;   return 1;
     } else {      } else {
  # can't modify the request section   # can't modify the requested section
  return 0;   return 0;
     }      }
  }   }
Line 602  sub canview { Line 825  sub canview {
     my ($sec)=@_;      my ($sec)=@_;
     if ($perm{'vgr'}) {      if ($perm{'vgr'}) {
  if (!defined($perm{'vgr_section'})) {   if (!defined($perm{'vgr_section'})) {
     # can modify whole class      # can view whole class
     return 1;      return 1;
  } else {   } else {
     if ($sec eq $perm{'vgr_section'}) {      if ($sec eq $perm{'vgr_section'}) {
  #can modify the requested section   #can view the requested section
  return 1;   return 1;
     } else {      } else {
  # can't modify the request section   # can't view the requested section
  return 0;   return 0;
     }      }
  }   }
     }      }
     #can't modify      #can't view
     return 0;      return 0;
 }  }
   
Line 755  sub initialverifyreceipt { Line 978  sub initialverifyreceipt {
   
 #--- Check whether a receipt number is valid.---  #--- Check whether a receipt number is valid.---
 sub verifyreceipt {  sub verifyreceipt {
     my ($request,$symb)  = @_;      my ($request,$symb) = @_;
   
     my $courseid = $env{'request.course.id'};      my $courseid = $env{'request.course.id'};
     my $receipt  = &Apache::lonnet::recprefix($courseid).'-'.      my $receipt  = &Apache::lonnet::recprefix($courseid).'-'.
  $env{'form.receipt'};   $env{'form.receipt'};
     $receipt     =~ s/[^\-\d]//g;      $receipt     =~ s/[^\-\d]//g;
   
     my $title.=      my $title =
  '<h3><span class="LC_info">'.   '<h3><span class="LC_info">'.
  &mt('Verifying Receipt Number [_1]',$receipt).   &mt('Verifying Receipt Number [_1]',$receipt).
  '</span></h3>'."\n";   '</span></h3>'."\n";
Line 838  sub verifyreceipt { Line 1061  sub verifyreceipt {
     return $string;      return $string;
 }  }
   
   #-------------------------------------------------------------------
   
   #------------------------------------------- Grade Passback Routines
   #
   
   sub initialpassback {
       my ($request,$symb) = @_;
       my $cdom = $env{"course.$env{'request.course.id'}.domain"};
       my $cnum = $env{"course.$env{'request.course.id'}.num"};
       my $crstype = &Apache::loncommon::course_type();
       my %passback = &Apache::lonnet::dump('nohist_linkprot_passback',$cdom,$cnum);
       my $readonly;
       unless ($perm{'mgr'}) {
           $readonly = 1;
       }
       my $formname = 'initialpassback';
       my $navmap = Apache::lonnavmaps::navmap->new();
       my $output;
       if (!defined($navmap)) {
           if ($crstype eq 'Community') {
               $output = &mt('Unable to retrieve information about community contents');
           } else {
               $output = &mt('Unable to retrieve information about course contents');
           }
           return '<p>'.$output.'</p>';
       }
       return &Apache::loncourserespicker::create_picker($navmap,'passback',$formname,$crstype,undef,
                                                         undef,undef,undef,undef,undef,undef,
                                                         \%passback,$readonly);
   }
   
   sub passback_filters {
       my ($request,$symb) = @_;
       my $cdom = $env{"course.$env{'request.course.id'}.domain"};
       my $cnum = $env{"course.$env{'request.course.id'}.num"};
       my $crstype = &Apache::loncommon::course_type();
       my ($launcher,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);
       if ($env{'form.passback'} ne '') {
           $chosen = &unescape($env{'form.passback'});
           ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
           ($launcher,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);
       }
       my $result;
       if ($launcher ne '') {
           $result = &launcher_info_box($launcher,$appname,$setter,$linkuri,$scope).
                     '<p><br />'.&mt('Set criteria to use to list students for possible passback of scores, then push Next [_1]',
                                     '&rarr;').
                     '</p>';
       }
       $result .= '<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
                  '<input type="hidden" name="passback" value="'.&escape($chosen).'" />'."\n".
                  '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
       my ($submittext,$newcommand);
       if ($launcher ne '') {
           $submittext = &mt('Next').' &rarr;';
           $newcommand = 'passbacknames';
           $result .=  &selectfield(0)."\n";
       } else {
           $submittext = '&larr; '.&mt('Previous');
           $newcommand = 'initialpassback';
           if ($env{'form.passback'}) {
               $result .= '<span class="LC_warning">'.&mt('Invalid launcher').'</span>'."\n";
           } else {
               $result .= '<span class="LC_warning">'.&mt('No launcher selected').'</span>'."\n";
           }
       }
       $result .=  '<input type="hidden" name="command" value="'.$newcommand.'" />'."\n".
                   '<div>'."\n".
                   '<input type="submit" value="'.$submittext.'" />'."\n".
                   '</div>'."\n".
                   '</form>'."\n";
       return $result;
   }
   
   sub names_for_passback {
       my ($request,$symb) = @_;
       my $cdom = $env{"course.$env{'request.course.id'}.domain"};
       my $cnum = $env{"course.$env{'request.course.id'}.num"};
       my $crstype = &Apache::loncommon::course_type();
       my ($launcher,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);
       if ($env{'form.passback'} ne '') {
           $chosen = &unescape($env{'form.passback'});
           ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
           ($launcher,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);
       }
       my ($result,$ctr,$newcommand,$submittext);
       if ($launcher ne '') {
           $result = &launcher_info_box($launcher,$appname,$setter,$linkuri,$scope);
       }
       $ctr = 0;
       my @statuses = &Apache::loncommon::get_env_multiple('form.Status');
       my $stu_status = join(':',@statuses);
       $result .= '<form action="/adm/grades" method="post" name="passbackusers">'."\n".
                  '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
       if ($launcher ne '') {
           $result .= '<input type="hidden" name="passback" value="'.&escape($chosen).'" />'."\n".
                      '<input type="hidden" name="Status" value="'.$stu_status.'" />'."\n";
           my ($sections,$groups,$group_display,$disabled) = &sections_and_groups();
           my $section_display = join(' ',@{$sections});
           my $status_display;
           if ((grep(/^Any$/,@statuses)) ||
               (@statuses == 3)) {
               $status_display = &mt('Any');
           } else {
               $status_display = join(' '.&mt('or').' ',map { &mt($_); } @statuses);
           }
           $result .= '<p>'.&mt('Student(s) with stored passback credentials for [_1], and also satisfy:',
                                '<span class="LC_cusr_emph">'.$linkuri.'</span>').
                      '<ul>'.
                      '<li>'.&mt('Section(s)').": $section_display</li>\n".
                      '<li>'.&mt('Group(s)').": $group_display</li>\n".
                      '<li>'.&mt('Status').": $status_display</li>\n".
                      '</ul>';
           my ($classlist,undef,$fullname) = &getclasslist($sections,'1',$groups,'','','',$chosen);
           if (keys(%$fullname)) {
               $newcommand = 'passbackscores';
               $result .= &build_section_inputs().
                          &checkselect_js('passbackusers').
                          '<p><br />'.
                          &mt("To send scores, check box(es) next to the student's name(s), then push 'Send Scores'.").
                          '</p>'.
                          &check_script('passbackusers', 'stuinfo')."\n".
                          '<input type="button" '."\n".
                          'onclick="javascript:checkSelect(this.form.stuinfo);" '."\n".
                          'value="'.&mt('Send Scores').'" /> <br />'."\n".
                          &check_buttons()."\n".
                          &Apache::loncommon::start_data_table().
                          &Apache::loncommon::start_data_table_header_row();
               my $loop = 0;
               while ($loop < 2) {
                   $result .= '<th>'.&mt('No.').'</th><th>'.&mt('Select').'</th>'.
                              '<th>'.&nameUserString('header').'&nbsp;'.&mt('Section/Group').'</th>';
                   $loop++;
               }
               $result .= &Apache::loncommon::end_data_table_header_row()."\n";
               foreach my $student (sort
                                    {
                                        if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
                                            return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
                                        }
                                        return $a cmp $b;
                                    }
                                    (keys(%$fullname))) {
                   $ctr++;
                   my $section = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
                   my $group = $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
                   my $udom = $classlist->{$student}->[&Apache::loncoursedata::CL_SDOM()];
                   my $uname = $classlist->{$student}->[&Apache::loncoursedata::CL_SNAME()];
                   if ( $perm{'vgr'} eq 'F' ) {
                       if ($ctr%2 ==1) {
                           $result.= &Apache::loncommon::start_data_table_row();
                       }
                       $result .= '<td align="right">'.$ctr.'&nbsp;</td>'.
                                  '<td align="center"><label><input type="checkbox" name="stuinfo" value="'.
                                  $student.':'.$$fullname{$student}.':::SECTION'.$section.
                                  ')&nbsp;" />&nbsp;&nbsp;</label></td>'."\n".'<td>'.
                                  &nameUserString(undef,$$fullname{$student},$uname,$udom).
                                  '&nbsp;'.$section.($group ne '' ?'/'.$group:'').'</td>'."\n";
   
                       if ($ctr%2 ==0) {
                           $result .= &Apache::loncommon::end_data_table_row()."\n";
                       }
                   }
               }
               if ($ctr%2 ==1) {
                   $result .= &Apache::loncommon::end_data_table_row();
               }
               $result .= &Apache::loncommon::end_data_table()."\n";
               if ($ctr) {
                   $result .= '<input type="button" '.
                              'onclick="javascript:checkSelect(this.form.stuinfo);" '.
                              'value="'.&mt('Send Scores').'" />'."\n";
               }
           } else {
               $submittext = '&larr; '.&mt('Previous');
               $newcommand = 'passback';
               $result .= '<span class="LC_warning">'.&mt('No students match the selection criteria').'</p>';
           }
       } else {
           $newcommand = 'initialpassback';
           $submittext = &mt('Start over');
           if ($env{'form.passback'}) {
               $result .= '<span class="LC_warning">'.&mt('Invalid launcher').'</span>'."\n";
           } else {
               $result .= '<span class="LC_warning">'.&mt('No launcher selected').'</span>'."\n";
           }
       }
       $result .=  '<input type="hidden" name="command" value="'.$newcommand.'" />'."\n";
       if (!$ctr) {
           $result .= '<div>'."\n".
                      '<input type="submit" value="'.$submittext.'" />'."\n".
                      '</div>'."\n";
       }
       $result .= '</form>'."\n";
       return $result;
   }
   
   sub do_passback {
       my ($request,$symb) = @_;
       my $cdom = $env{"course.$env{'request.course.id'}.domain"};
       my $cnum = $env{"course.$env{'request.course.id'}.num"};
       my $crstype = &Apache::loncommon::course_type();
       my ($launcher,$appname,$setter,$linkuri,$linkprotector,$scope,$chosen);
       if ($env{'form.passback'} ne '') {
           $chosen = &unescape($env{'form.passback'});
           ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
           ($launcher,$appname,$setter) = &get_passback_launcher($cdom,$cnum,$chosen);
       }
       if ($launcher ne '') {
           $request->print(&launcher_info_box($launcher,$appname,$setter,$linkuri,$scope));
       }
       my $error;
       if ($perm{'mgr'}) {
           if ($launcher ne '') {
               my @poss_students = &Apache::loncommon::get_env_multiple('form.stuinfo');
               if (@poss_students) {
                   my %possibles;
                   foreach my $item (@poss_students) {
                       my ($stuname,$studom) = split(/:/,$item,3);
                       $possibles{$stuname.':'.$studom} = 1;
                   }
                   my ($sections,$groups,$group_display,$disabled) = &sections_and_groups();
                   my ($classlist,undef,$fullname,$pbinfo) =
                       &getclasslist($sections,'1',$groups,'','','',$chosen,\%possibles);
                   if ((ref($classlist) eq 'HASH') && (ref($pbinfo) eq 'HASH')) {
                       my %passback = %{$pbinfo};
                       my (%tosend,%remotenotok,%scorenotok,%zeroposs,%nopbinfo);
                       foreach my $possible (keys(%possibles)) {
                           if ((exists($classlist->{$possible})) &&
                               (exists($passback{$possible})) && (ref($passback{$possible}) eq 'ARRAY')) {
                               $tosend{$possible} = 1;
                           }
                       }
                       if (keys(%tosend)) {
                           my ($lti_in_use,$crsdef);
                           my ($ltinum,$ltitype) = ($linkprotector =~ /^(\d+)(c|d)$/);
                           if ($ltitype eq 'c') {
                               my %crslti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
                               $lti_in_use = $crslti{$ltinum};
                               $crsdef = 1;
                           } else {
                               my %domlti = &Apache::lonnet::get_domain_lti($cdom,'linkprot');
                               $lti_in_use = $domlti{$ltinum};
                           }
                           if (ref($lti_in_use) eq 'HASH') {
                               my $msgformat = $lti_in_use->{'passbackformat'};
                               my $keynum = $lti_in_use->{'cipher'};
                               my $scoretype = 'decimal';
                               if ($lti_in_use->{'scoreformat'} =~ /^(decimal|ratio|percentage)$/) {
                                   $scoretype = $1;
                               }
                               my $pbsymb = &Apache::loncommon::symb_from_tinyurl($linkuri,$cnum,$cdom);
                               my $pbmap;
                               if ($pbsymb =~ /\.(page|sequence)$/) {
                                   $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pbsymb))[2]);
                               } else {
                                   $pbmap = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pbsymb))[0]);
                               }
                               $pbmap = &Apache::lonnet::clutter($pbmap);
                               my $pbscope;
                               if ($scope eq 'res') {
                                   $pbscope = 'resource';
                               } elsif ($scope eq 'map') {
                                   $pbscope = 'nonrec';
                               } elsif ($scope eq 'rec') {
                                   $pbscope = 'map';
                               }
                               my %pb = &common_passback_info();
                               my $numstudents = scalar(keys(%tosend));
                               my %prog_state = &Apache::lonhtmlcommon::Create_PrgWin($request,$numstudents);
                               my $outcome = &Apache::loncommon::start_data_table().
                                            &Apache::loncommon::start_data_table_header_row();
                               my $loop = 0;
                               while ($loop < 2) {
                                   $outcome .= '<th>'.&mt('No.').'</th>'.
                                              '<th>'.&nameUserString('header').'&nbsp;'.&mt('Section/Group').'</th>'.
                                              '<th>'.&mt('Score').'</th>';
                                    $loop++;
                               }
                               $outcome .= &Apache::loncommon::end_data_table_header_row()."\n";
                               my $ctr=0;
                               foreach my $student (sort
                                   {
                                        if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
                                            return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
                                        }
                                        return $a cmp $b;
                                   } (keys(%$fullname))) {
                                   next unless ($tosend{$student});
                                   my ($uname,$udom) = split(/:/,$student);
                                   &Apache::lonhtmlcommon::Increment_PrgWin($request,\%prog_state,'last student');
                                   my ($uname,$udom) = split(/:/,$student);
                                   my $uhome = &Apache::lonnet::homeserver($uname,$udom),
                                   my $id = $passback{$student}[0],
                                   my $url = $passback{$student}[1],
                                   my ($total,$possible,$usec);
                                   if (ref($classlist->{$student}) eq 'ARRAY') {
                                       $usec = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION];
                                   }
                                   if ($pbscope eq 'resource') {
                                       $total = 0;
                                       $possible = 0;
                                       my $navmap = Apache::lonnavmaps::navmap->new($uname,$udom);
                                       if (ref($navmap)) {
                                           my $res = $navmap->getBySymb($pbsymb);
                                           if (ref($res)) {
                                               my $partlist = $res->parts();
                                               if (ref($partlist) eq 'ARRAY') {
                                                   my %record = &Apache::lonnet::restore($pbsymb,$env{'request.course.id'},$udom,$uname);
                                                   foreach my $part (@{$partlist}) {
                                                       next if ($record{"resource.$part.solved"} =~/^excused/);
                                                       my $weight = &Apache::lonnet::EXT("resource.$part.weight",$pbsymb,$udom,$uname,$usec);
                                                       $possible += $weight;
                                                       if (($record{'version'}) && (exists($record{"resource.$part.awarded"}))) {
                                                           my $awarded = $record{"resource.$part.awarded"};
                                                           if ($awarded) {
                                                               $total += $weight * $awarded;
                                                           }
                                                       }
                                                   }
                                               }
                                           }
                                       }
                                   } elsif (($pbscope eq 'map') || ($pbscope eq 'nonrec')) {
                                       ($total,$possible) =
                                           &Apache::lonhomework::get_lti_score($uname,$udom,$pbmap,$pbscope);
                                   }
                                   if (($id ne '') && ($url ne '') && ($possible)) {
                                       my ($sent,$score,$code,$result) =
                                           &LONCAPA::ltiutils::send_grade($cdom,$cnum,$crsdef,$pb{'type'},$ltinum,$keynum,$id,
                                                                          $url,$scoretype,$pb{'sigmethod'},$msgformat,$total,$possible);
                                       my $no_passback;
                                       if ($sent) {
                                           if ($code == 200) {
                                               delete($tosend{$student});
                                               my $namespace = $cdom.'_'.$cnum.'_lp_passback';
                                               my $store = {
                                                    'score' => $score,
                                                    'ip' => $pb{'ip'},
                                                    'host' => $pb{'lonhost'},
                                                    'protector' => $linkprotector,
                                                    'deeplink' => $linkuri,
                                                    'scope' => $scope,
                                                    'url' => $url,
                                                    'id' => $id,
                                                    'clientip' => $pb{'clientip'},
                                                    'whodoneit' => $env{'user.name'}.':'.$env{'user.domain'},
                                                   };
                                               my $value='';
                                               foreach my $key (keys(%{$store})) {
                                                   $value.=&escape($key).'='.&Apache::lonnet::freeze_escape($store->{$key}).'&';
                                               }
                                               $value=~s/\&$//;
                                               &Apache::lonnet::courselog(&escape($linkuri).':'.$uname.':'.$udom.':EXPORT:'.$value);
                                               &Apache::lonnet::cstore({'score' => $score},$chosen,$namespace,$udom,$uname,'',$pb{'ip'},1);
                                               $ctr++;
                                               if ($ctr%2 ==1) {
                                                   $outcome .= &Apache::loncommon::start_data_table_row();
                                               }
                                               my $section = $classlist->{$student}->[&Apache::loncoursedata::CL_SECTION()];
                                               my $group = $classlist->{$student}->[&Apache::loncoursedata::CL_GROUP()];
                                               $outcome .= '<td align="right">'.$ctr.'&nbsp;</td>'.
                                                          '<td>'.&nameUserString(undef,$$fullname{$student},$uname,$udom).
                                                          '&nbsp;'.$section.($group ne '' ?'/'.$group:'').'</td>'.
                                                          '<td>'.$score.'</td>'."\n";
                                               if ($ctr%2 ==0) {
                                                   $outcome .= &Apache::loncommon::end_data_table_row()."\n";
                                               }
                                           } else {
                                               $remotenotok{$student} = 1;
                                               $no_passback = "Passback response for ".$linkprotector." was $code ($result)";
                                               &Apache::lonnet::logthis($no_passback." for $uname:$udom in ${cdom}_${cnum}");
                                           }
                                       } else {
                                           $scorenotok{$student} = 1;
                                           $no_passback = "Passback of grades not sent for ".$linkprotector;
                                           &Apache::lonnet::logthis($no_passback." for $uname:$udom in ${cdom}_${cnum}");
                                       }
                                       if ($no_passback) {
                                           &Apache::lonnet::log($udom,$uname,$uhome,$no_passback." score: $score; total: $total; possible: $possible");
                                           my $ltigrade = {
                                               'ltinum'   => $ltinum,
                                               'lti'      => $lti_in_use,
                                               'crsdef'   => $crsdef,
                                               'cid'      => $cdom.'_'.$cnum,
                                               'uname'    => $uname,
                                               'udom'     => $udom,
                                               'uhome'    => $uhome,
                                               'pbid'     => $id,
                                               'pburl'    => $url,
                                               'pbtype'   => $pb{'type'},
                                               'pbscope'  => $pbscope,
                                               'pbmap'    => $pbmap,
                                               'pbsymb'   => $pbsymb,
                                               'format'   => $scoretype,
                                               'scope'    => $scope,
                                               'clientip' => $pb{'clientip'},
                                               'linkprot' => $linkprotector.':'.$linkuri,
                                               'total'    => $total,
                                               'possible' => $possible,
                                               'score'    => $score,
                                           };
                                           &Apache::lonnet::put('linkprot_passback_pending',$ltigrade,$cdom,$cnum);
                                       }
                                   } else {
                                       if (($id ne '') && ($url ne '')) {
                                           $zeroposs{$student} = 1;
                                       } else {
                                           $nopbinfo{$student} = 1;
                                       }
                                   }
                               }
                               &Apache::lonhtmlcommon::Close_PrgWin($request,\%prog_state);
                               if ($ctr%2 ==1) {
                                   $outcome .= &Apache::loncommon::end_data_table_row();
                               }
                               $outcome .= &Apache::loncommon::end_data_table();
                               if ($ctr) {
                                   $request->print('<p><br />'.&mt('Scores sent to launcher CMS').'</p>'.
                                                   '<p>'.$outcome.'</p>');
                               } else {
                                   $request->print('<p>'.&mt('No scores sent to launcher CMS').'</p>');
                               }
                               if (keys(%tosend)) {
                                   $request->print('<p>'.&mt('No scores sent for following'));
                                   my ($zeros,$nopbcreds,$noconfirm,$noscore);
                                   foreach my $student (sort
                                   {
                                        if (lc($$fullname{$a}) ne lc($$fullname{$b})) {
                                            return (lc($$fullname{$a}) cmp lc($$fullname{$b}));
                                        }
                                        return $a cmp $b;
                                   } (keys(%$fullname))) {
                                       next unless ($tosend{$student});
                                       my ($uname,$udom) = split(/:/,$student);
                                       my $line = '<li>'.&nameUserString(undef,$$fullname{$student},$uname,$udom).'</li>'."\n";
                                       if ($zeroposs{$student}) {
                                           $zeros .= $line;
                                       } elsif ($nopbinfo{$student}) {
                                           $nopbcreds .= $line;
                                       } elsif ($remotenotok{$student}) {
                                           $noconfirm .= $line;
                                       } elsif ($scorenotok{$student}) {
                                           $noscore .= $line;
                                       }
                                   }
                                   if ($zeros) {
                                       $request->print('<br />'.&mt('Total points possible was 0').':'.
                                                       '<ul>'.$zeros.'</ul><br />');
                                   }
                                   if ($nopbcreds) {
                                       $request->print('<br />'.&mt('Missing unique identifier and/or passback location').':'.
                                                       '<ul>'.$nopbcreds.'</ul><br />');
                                   }
                                   if ($noconfirm) {
                                       $request->print('<br />'.&mt('Score receipt not confirmed by receiving CMS').':'.
                                                       '<ul>'.$noconfirm.'</ul><br />');
                                   }
                                   if ($noscore) {
                                       $request->print('<br />'.&mt('Score computation or transmission failed').':'.
                                                       '<ul>'.$noscore.'</ul><br />');
                                   }
                                   $request->print('</p>');
                               }
                           } else {
                               $error = &mt('Settings for deep-link launch target unavailable, so no scores were sent');
                           }
                       } else {
                           $error = &mt('No available students for whom scores can be sent.');
                       }
                   } else {
                       $error = &mt('Classlist could not be retrieved so no scores were sent.');
                   }
               } else {
                   $error = &mt('No students selected to receive scores so none were sent.');
               }
           } else {
               if ($env{'form.passback'}) {
                   $error = &mt('Deep-link launch target was invalid so no scores were sent.');
               } else {
                   $error = &mt('Deep-link launch target was missing so no scores were sent.');
               }
           }
       } else {
           $error = &mt('You do not have permission to manage grades, so no scores were sent');
       }
       if ($error) {
           $request->print('<p class="LC_info">'.$error.'</p>');
       }
       return;
   }
   
   sub get_passback_launcher {
       my ($cdom,$cnum,$chosen) = @_;
       my ($linkuri,$linkprotector,$scope) = split("\0",$chosen);
       my ($ltinum,$ltitype) = ($linkprotector =~ /^(\d+)(c|d)$/);
       my ($appname,$setter);
       if ($ltitype eq 'c') {
           my %lti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
           if (ref($lti{$ltinum}) eq 'HASH') {
               $appname = $lti{$ltinum}{'name'};
               if ($appname) {
                   $setter = ' (defined in course)';
               }
           }
       } elsif ($ltitype eq 'd') {
           my %lti = &Apache::lonnet::get_domain_lti($cdom,'linkprot');
           if (ref($lti{$ltinum}) eq 'HASH') {
               $appname = $lti{$ltinum}{'name'};
               if ($appname) {
                   $setter = ' (defined in domain)';
               }
           }
       }
       if ($linkuri =~ m{^\Q/tiny/$cdom/\E(\w+)$}) {
           my $key = $1;
           my $tinyurl;
           my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key);
           if (defined($cached)) {
               $tinyurl = $result;
           } else {
               my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
               my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname);
               if ($currtiny{$key} ne '') {
                   $tinyurl = $currtiny{$key};
                   &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600);
               }
           }
           if ($tinyurl) {
               my ($crsnum,$launchsymb) = split(/\&/,$tinyurl);
               if ($crsnum eq $cnum) {
                   my %passback = &Apache::lonnet::get('nohist_linkprot_passback',[$launchsymb],$cdom,$cnum);
                   if (ref($passback{$launchsymb}) eq 'HASH') {
                       if (exists($passback{$launchsymb}{$chosen})) {
                           return ($launchsymb,$appname,$setter);
                       }
                   }
               }
           }
       }
       return ();
   }
   
   sub sections_and_groups {
       my (@sections,@groups,$group_display);
       @groups = &Apache::loncommon::get_env_multiple('form.group');
       if (grep(/^all$/,@groups)) {
            @groups = ('all');
            $group_display = 'all';
       } elsif (grep(/^none$/,@groups)) {
            @groups = ('none');
            $group_display = 'none';
       } elsif (@groups > 0) {
            $group_display = join(', ',@groups);
       }
       if ($env{'request.course.sec'} ne '') {
           @sections = ($env{'request.course.sec'});
       } else {
           @sections = &Apache::loncommon::get_env_multiple('form.section');
       }
       my $disabled = ' disabled="disabled"';
       if ($perm{'mgr'}) {
           if (grep(/^all$/,@sections)) {
               undef($disabled);
           } else {
               foreach my $sec (@sections) {
                   if (&canmodify($sec)) {
                       undef($disabled);
                       last;
                   }
               }
           }
       }
       if (grep(/^all$/,@sections)) {
           @sections = ('all');
       }
       return(\@sections,\@groups,$group_display,$disabled);
   }
   
   sub launcher_info_box {
       my ($launcher,$appname,$setter,$linkuri,$scope) = @_;
       my $shownscope;
       if ($scope eq 'res') {
           $shownscope = &mt('Resource');
       } elsif ($scope eq 'map') {
           $shownscope = &mt('Folder');
       }  elsif ($scope eq 'rec') {
           $shownscope = &mt('Folder + sub-folders');
       }
       return '<p>'.
              &Apache::lonhtmlcommon::start_pick_box().
              &Apache::lonhtmlcommon::row_title(&mt('Launch Item Title')).
              &Apache::lonnet::gettitle($launcher).
              &Apache::lonhtmlcommon::row_closure().
              &Apache::lonhtmlcommon::row_title(&mt('Deep-link')).
              $linkuri.
              &Apache::lonhtmlcommon::row_closure().
              &Apache::lonhtmlcommon::row_title(&mt('Launcher')).
              $appname.' '.$setter.
              &Apache::lonhtmlcommon::row_closure().
              &Apache::lonhtmlcommon::row_title(&mt('Score Type')).
              $shownscope.      
              &Apache::lonhtmlcommon::row_closure(1).
              &Apache::lonhtmlcommon::end_pick_box().'</p>'."\n";
   }
   
   sub passbacks_for_symb {
       my ($cdom,$cnum,$symb) = @_;
       my %passback = &Apache::lonnet::dump('nohist_linkprot_passback',$cdom,$cnum);
       my %needpb;
       if (keys(%passback)) {
           my $checkpb = 1;
           if (exists($passback{$symb})) {
               if (keys(%passback) == 1) {
                   undef($checkpb);
               }
               if (ref($passback{$symb}) eq 'HASH') {
                   foreach my $launcher (keys(%{$passback{$symb}})) {
                       $needpb{$launcher} = 1;
                   }
               }
           }
           if ($checkpb) {
               my ($map,$id,$url) = &Apache::lonnet::decode_symb($symb);
               my $navmap = Apache::lonnavmaps::navmap->new();
               if (ref($navmap)) {
                   my $mapres = $navmap->getResourceByUrl($map);
                   if (ref($mapres)) {
                       my $mapsymb = $mapres->symb();
                       if (exists($passback{$mapsymb})) {
                           if (keys(%passback) == 1) {
                               undef($checkpb);
                           }
                           if (ref($passback{$mapsymb}) eq 'HASH') {
                               foreach my $launcher (keys(%{$passback{$mapsymb}})) {
                                   $needpb{$launcher} = 1;
                               }
                           }
                       }
                       my %posspb;
                       if ($checkpb) {
                           my @recurseup = $navmap->recurseup_maps($map,1);
                           if (@recurseup) {
                               map { $posspb{$_} = 1; } @recurseup;
                           }
                       }
                       foreach my $key (keys(%passback)) {
                           if (exists($posspb{$key})) {
                               if (ref($passback{$key}) eq 'HASH') {
                                   foreach my $launcher (keys(%{$passback{$key}})) {
                                       my ($linkuri,$linkprotector,$scope) = split("\0",$launcher);
                                       next unless ($scope eq 'rec');
                                       $needpb{$launcher} = 1;
                                   }
                               }
                           }
                       }
                   }
               }
           }
       }
       return %needpb;
   }
   
   sub process_passbacks {
       my ($context,$symbs,$cdom,$cnum,$udom,$uname,$weights,$awardeds,$excuseds,$needpb,
           $skip_passback,$pbsave,$pbids) = @_;
       if ((ref($needpb) eq 'HASH') && (ref($skip_passback) eq 'HASH') && (ref($pbsave) eq 'HASH')) {
           my (%weight,%awarded,%excused);
           if ((ref($symbs) eq 'ARRAY') && (ref($weights) eq 'HASH') && (ref($awardeds) eq 'HASH') &&
               (ref($excuseds) eq 'HASH')) {
               %weight = %{$weights};
               %awarded = %{$awardeds};
               %excused = %{$excuseds};
           }
           my $uhome = &Apache::lonnet::homeserver($uname,$udom);
           my @launchers = keys(%{$needpb});
           my %pbinfo;
           if (ref($pbids) eq 'HASH') {
               %pbinfo = %{$pbids};
           } else {
               %pbinfo = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',\@launchers,$udom,$uname);
           }
           my %pbc = &common_passback_info();
           foreach my $launcher (@launchers) {
               if (ref($pbinfo{$launcher}) eq 'ARRAY') {
                   my $pbid = $pbinfo{$launcher}[0];
                   my $pburl = $pbinfo{$launcher}[1];
                   my (%total_by_symb,%possible_by_symb);
                   if (($pbid ne '') && ($pburl ne '')) {
                       next if ($skip_passback->{$launcher});
                       my %pb = %pbc;
                       if ((exists($pbsave->{$launcher})) &&
                           (ref($pbsave->{$launcher}) eq 'HASH')) {
                           foreach my $item ('lti_in_use','crsdef','ltinum','keynum','scoretype','msgformat',
                                             'symb','map','pbscope','linkuri','linkprotector','scope') {
                               $pb{$item} = $pbsave->{$launcher}{$item};
                           }
                       } else {
                           my $ltitype;
                           ($pb{'linkuri'},$pb{'linkprotector'},$pb{'scope'}) = split("\0",$launcher);
                           ($pb{'ltinum'},$ltitype) = ($pb{'linkprotector'} =~ /^(\d+)(c|d)$/);
                           if ($ltitype eq 'c') {
                               my %crslti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
                               $pb{'lti_in_use'} = $crslti{$pb{'ltinum'}};
                               $pb{'crsdef'} = 1;
                           } else {
                               my %domlti = &Apache::lonnet::get_domain_lti($cdom,'linkprot');
                               $pb{'lti_in_use'} = $domlti{$pb{'ltinum'}};
                           }
                           if (ref($pb{'lti_in_use'}) eq 'HASH') {
                               $pb{'msgformat'} = $pb{'lti_in_use'}->{'passbackformat'};
                               $pb{'keynum'} = $pb{'lti_in_use'}->{'cipher'};
                               $pb{'scoretype'} = 'decimal';
                               if ($pb{'lti_in_use'}->{'scoreformat'} =~ /^(decimal|ratio|percentage)$/) {
                                   $pb{'scoretype'} = $1;
                               }
                               $pb{'symb'} = &Apache::loncommon::symb_from_tinyurl($pb{'linkuri'},$cnum,$cdom);
                               if ($pb{'symb'} =~ /\.(page|sequence)$/) {
                                   $pb{'map'} = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pb{'symb'}))[2]);
                               } else {
                                   $pb{'map'} = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($pb{'symb'}))[0]);
                               }
                               $pb{'map'} = &Apache::lonnet::clutter($pb{'map'});
                               if ($pb{'scope'} eq 'res') {
                                   $pb{'pbscope'} = 'resource';
                               } elsif ($pb{'scope'} eq 'map') {
                                   $pb{'pbscope'} = 'nonrec';
                               } elsif ($pb{'scope'} eq 'rec') {
                                   $pb{'pbscope'} = 'map';
                               }
                               foreach my $item ('lti_in_use','crsdef','ltinum','keynum','scoretype','msgformat',
                                                 'symb','map','pbscope','linkuri','linkprotector','scope') {
                                   $pbsave->{$launcher}{$item} = $pb{$item};
                               }
                           } else {
                               $skip_passback->{$launcher} = 1;
                           }
                       }
                       if (ref($symbs) eq 'ARRAY') {
                           foreach my $symb (@{$symbs}) {
                               if ((ref($weight{$symb}) eq 'HASH') && (ref($awarded{$symb}) eq 'HASH') &&
                                   (ref($excused{$symb}) eq 'HASH')) {
                                   foreach my $part (keys(%{$weight{$symb}})) {
                                       if ($excused{$symb}{$part}) {
                                           next;
                                       }
                                       my $partweight = $weight{$symb}{$part} eq '' ? 1 :
                                                        $weight{$symb}{$part};
                                       if ($awarded{$symb}{$part}) {
                                           $total_by_symb{$symb} += $partweight * $awarded{$symb}{$part};
                                       }
                                       $possible_by_symb{$symb} += $partweight;
                                   }
                               }
                           }
                       }
                       if ($context eq 'updatebypage') {
                           my $ltigrade = {
                                           'ltinum'     => $pb{'ltinum'},
                                           'lti'        => $pb{'lti_in_use'},
                                           'crsdef'     => $pb{'crsdef'},
                                           'cid'        => $cdom.'_'.$cnum,
                                           'uname'      => $uname,
                                           'udom'       => $udom,
                                           'uhome'      => $uhome,
                                           'pbid'       => $pbid,
                                           'pburl'      => $pburl,
                                           'pbtype'     => $pb{'type'},
                                           'pbscope'    => $pb{'pbscope'},
                                           'pbmap'      => $pb{'map'},
                                           'pbsymb'     => $pb{'symb'},
                                           'format'     => $pb{'scoretype'},
                                           'scope'      => $pb{'scope'},
                                           'clientip'   => $pb{'clientip'},
                                           'linkprot'   => $pb{'linkprotector'}.':'.$pb{'linkuri'},
                                           'total_s'    => \%total_by_symb,
                                           'possible_s' => \%possible_by_symb,
                           };
                           push(@Apache::grades::ltipassback,$ltigrade);
                           next;
                       }
                       my ($total,$possible);
                       if ($pb{'pbscope'} eq 'resource') {
                           $total = $total_by_symb{$pb{'symb'}};
                           $possible = $possible_by_symb{$pb{'symb'}};
                       } elsif (($pb{'pbscope'} eq 'map') || ($pb{'pbscope'} eq 'nonrec')) {
                           ($total,$possible) =
                               &Apache::lonhomework::get_lti_score($uname,$udom,$pb{'map'},$pb{'pbscope'},
                                                                   \%total_by_symb,\%possible_by_symb);
                       }
                       if (!$possible) {
                           $total = 0;
                           $possible = 1;
                       }
                       my ($sent,$score,$code,$result) =
                           &LONCAPA::ltiutils::send_grade($cdom,$cnum,$pb{'crsdef'},$pb{'type'},$pb{'ltinum'},
                                                          $pb{'keynum'},$pbid,$pburl,$pb{'scoretype'},$pb{'sigmethod'},
                                                          $pb{'msgformat'},$total,$possible);
                       my $no_passback;
                       if ($sent) {
                           if ($code == 200) {
                               my $namespace = $cdom.'_'.$cnum.'_lp_passback';
                               my $store = {
                                   'score' => $score,
                                   'ip' => $pb{'ip'},
                                   'host' => $pb{'lonhost'},
                                   'protector' => $pb{'linkprotector'},
                                   'deeplink' => $pb{'linkuri'},
                                   'scope' => $pb{'scope'},
                                   'url' => $pburl,
                                   'id' => $pbid,
                                   'clientip' => $pb{'clientip'},
                                   'whodoneit' => $env{'user.name'}.':'.$env{'user.domain'},
                               };
                               my $value='';
                               foreach my $key (keys(%{$store})) {
                                    $value.=&escape($key).'='.&Apache::lonnet::freeze_escape($store->{$key}).'&';
                               }
                               $value=~s/\&$//;
                               &Apache::lonnet::courselog(&escape($pb{'linkuri'}).':'.$uname.':'.$udom.':EXPORT:'.$value);
                               &Apache::lonnet::cstore({'score' => $score},$launcher,$namespace,$udom,$uname,'',$pb{'ip'},1);
                           } else {
                               $no_passback = 1;
                           }
                       } else {
                           $no_passback = 1;
                       }
                       if ($no_passback) {
                           &Apache::lonnet::log($udom,$uname,$uhome,$no_passback." score: $score; total: $total; possible: $possible");
                           my $ltigrade = {
                              'ltinum'   => $pb{'ltinum'},
                              'lti'      => $pb{'lti_in_use'},
                              'crsdef'   => $pb{'crsdef'},
                              'cid'      => $cdom.'_'.$cnum,
                              'uname'    => $uname,
                              'udom'     => $udom,
                              'uhome'    => $uhome,
                              'pbid'     => $pbid,
                              'pburl'    => $pburl,
                              'pbtype'   => $pb{'type'},
                              'pbscope'  => $pb{'pbscope'},
                              'pbmap'    => $pb{'map'},
                              'pbsymb'   => $pb{'symb'},
                              'format'   => $pb{'scoretype'},
                              'scope'    => $pb{'scope'},
                              'clientip' => $pb{'clientip'},
                              'linkprot' => $pb{'linkprotector'}.':'.$pb{'linkuri'},
                              'total'    => $total,
                              'possible' => $possible,
                              'score'    => $score,
                           };
                           &Apache::lonnet::put('linkprot_passback_pending',$ltigrade,$cdom,$cnum);
                       }
                   }
               }
           }
       }
       return;
   }
   
   sub common_passback_info {
       my %pbc = (
                  sigmethod => 'HMAC-SHA1',
                  type      => 'linkprot',
                  clientip  => &Apache::lonnet::get_requestor_ip(),
                  lonhost   => $Apache::lonnet::perlvar{'lonHostID'},
                  ip        => &Apache::lonnet::get_host_ip($Apache::lonnet::perlvar{'lonHostID'}),
                );
       return %pbc;
   }
   
 #--- This is called by a number of programs.  #--- This is called by a number of programs.
 #--- Called from the Grading Menu - View/Grade an individual student  #--- Called from the Grading Menu - View/Grade an individual student
 #--- Also called directly when one clicks on the subm button   #--- Also called directly when one clicks on the subm button 
 #    on the problem page.  #    on the problem page.
 sub listStudents {  sub listStudents {
     my ($request,$symb,$submitonly) = @_;      my ($request,$symb,$submitonly,$divforres) = @_;
   
       my $is_tool   = ($symb =~ /ext\.tool$/);
     my $cdom      = $env{"course.$env{'request.course.id'}.domain"};      my $cdom      = $env{"course.$env{'request.course.id'}.domain"};
     my $cnum      = $env{"course.$env{'request.course.id'}.num"};      my $cnum      = $env{"course.$env{'request.course.id'}.num"};
     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
     my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};      my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
     unless ($submitonly) {      unless ($submitonly) {
        $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};          $submitonly = $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
     }      }
   
     my $result='';      my $result='';
     my $res_error;      my $res_error;
     my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);      my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,\$res_error);
   
     my %js_lt = &Apache::lonlocal::texthash (      my $table;
  'multiple' => 'Please select a student or group of students before clicking on the Next button.',      if (ref($partlist) eq 'ARRAY') {
  'single'   => 'Please select the student before clicking on the Next button.',          if (scalar(@$partlist) > 1 ) {
      );              $table = &showResourceInfo($symb,$partlist,$responseType,'gradesub',1);
     &js_escape(\%js_lt);          } elsif ($divforres) {
     $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT));              $table = '<div style="padding:0;clear:both;margin:0;border:0"></div>';
     function checkSelect(checkBox) {          } else {
  var ctr=0;              $table = '<br clear="all" />';
  var sense="";          }
  if (checkBox.length > 1) {  
     for (var i=0; i<checkBox.length; i++) {  
  if (checkBox[i].checked) {  
     ctr++;  
  }  
     }  
     sense = '$js_lt{'multiple'}';  
  } else {  
     if (checkBox.checked) {  
  ctr = 1;  
     }  
     sense = '$js_lt{'single'}';  
  }  
  if (ctr == 0) {  
     alert(sense);  
     return false;  
  }  
  document.gradesub.submit();  
     }      }
   
       $request->print(&checkselect_js());
       $request->print(&Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT));
   
     function reLoadList(formname) {      function reLoadList(formname) {
  if (formname.saveStatusOld.value == pullDownSelection(formname.Status)) {return;}   if (formname.saveStatusOld.value == pullDownSelection(formname.Status)) {return;}
  formname.command.value = 'submission';   formname.command.value = 'submission';
Line 897  LISTJAVASCRIPT Line 1978  LISTJAVASCRIPT
     $request->print($result);      $request->print($result);
   
     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.      my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.
  "\n";   "\n".$table;
   
     $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();      $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))      unless ($is_tool) {
                   .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"          $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
                   .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"                        .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"
                   .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"                        .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"
                   .&Apache::lonhtmlcommon::row_closure();                        .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))                        .&Apache::lonhtmlcommon::row_closure();
                   .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"          $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))
                   .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n"                        .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"
                   .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"                        .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n"
                   .&Apache::lonhtmlcommon::row_closure();                        .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"
                         .&Apache::lonhtmlcommon::row_closure();
       }
   
     my $submission_options;  
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));      my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
     my $saveStatus = $stu_status eq '' ? 'Active' : $stu_status;      my $saveStatus = $stu_status eq '' ? 'Active' : $stu_status;
     $env{'form.Status'} = $saveStatus;      $env{'form.Status'} = $saveStatus;
     $submission_options.=      my %optiontext;
       if ($is_tool) {
           %optiontext = &Apache::lonlocal::texthash (
                             lastonly => 'last transaction',
                             last     => 'last transaction with details',
                             datesub  => 'all transactions',
                             all      => 'all transactions with details',
                         );
       } else {
           %optiontext = &Apache::lonlocal::texthash (
                             lastonly => 'last submission',
                             last     => 'last submission with details',
                             datesub  => 'all submissions',
                             all      => 'all submissions with details',
                         );
       }
       my $submission_options =
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="lastonly" /> '.          '<label><input type="radio" name="lastSub" value="lastonly" /> '.
         &mt('last submission').' </label></span>'."\n".          $optiontext{'lastonly'}.' </label></span>'."\n".
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="last" /> '.          '<label><input type="radio" name="lastSub" value="last" /> '.
         &mt('last submission with details').' </label></span>'."\n".          $optiontext{'last'}.' </label></span>'."\n".
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.          '<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.
         &mt('all submissions').'</label></span>'."\n".          $optiontext{'datesub'}.'</label></span>'."\n".
         '<span class="LC_nobreak">'.          '<span class="LC_nobreak">'.
         '<label><input type="radio" name="lastSub" value="all" /> '.          '<label><input type="radio" name="lastSub" value="all" /> '.
         &mt('all submissions with details').'</label></span>';          $optiontext{'all'}.'</label></span>';
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Submissions'))      my $viewtitle;
                   .$submission_options      if ($is_tool) {
           $viewtitle = &mt('View Transactions');
       } else {
           $viewtitle = &mt('View Submissions');
       }
       my ($compmsg,$nocompmsg);
       $nocompmsg = ' checked="checked"';
       if ($numessay) {
           $compmsg = $nocompmsg;
           $nocompmsg = '';
       }
       $gradeTable .= &Apache::lonhtmlcommon::row_title($viewtitle)
                     .$submission_options;
   # Check if any gradable
       my $showmore;
       if ($perm{'mgr'}) {
           my @sections;
           if ($env{'request.course.sec'} ne '') {
               @sections = ($env{'request.course.sec'});
           } elsif ($env{'form.section'} eq '') {
               @sections = ('all');
           } else {
               @sections = &Apache::loncommon::get_env_multiple('form.section');
           }
           if (grep(/^all$/,@sections)) {
               $showmore = 1;
           } else {
               foreach my $sec (@sections) {
                   if (&canmodify($sec)) {
                       $showmore = 1;
                       last;
                   }
               }
           }
       }
   
       if ($showmore) {
           $gradeTable .=
                      &Apache::lonhtmlcommon::row_closure()
                     .&Apache::lonhtmlcommon::row_title(&mt('Send Messages'))
                     .'<span class="LC_nobreak">'
                     .'<label><input type="radio" name="compmsg" value="0"'.$nocompmsg.' />'
                     .&mt('No').('&nbsp;'x2).'</label>'
                     .'<label><input type="radio" name="compmsg" value="1"'.$compmsg.' />'
                     .&mt('Yes').('&nbsp;'x2).'</label>'
                   .&Apache::lonhtmlcommon::row_closure();                    .&Apache::lonhtmlcommon::row_closure();
   
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments'))          $gradeTable .= 
                      &Apache::lonhtmlcommon::row_title(&mt('Grading Increments'))
                   .'<select name="increment">'                    .'<select name="increment">'
                   .'<option value="1">'.&mt('Whole Points').'</option>'                    .'<option value="1">'.&mt('Whole Points').'</option>'
                   .'<option value=".5">'.&mt('Half Points').'</option>'                    .'<option value=".5">'.&mt('Half Points').'</option>'
                   .'<option value=".25">'.&mt('Quarter Points').'</option>'                    .'<option value=".25">'.&mt('Quarter Points').'</option>'
                   .'<option value=".1">'.&mt('Tenths of a Point').'</option>'                    .'<option value=".1">'.&mt('Tenths of a Point').'</option>'
                   .'</select>'                    .'</select>';
                   .&Apache::lonhtmlcommon::row_closure();      }
   
     $gradeTable .=       $gradeTable .= 
         &build_section_inputs().          &build_section_inputs().
  '<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".   '<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".
  '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".   '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
  '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";   '<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";
   
     if (exists($env{'form.Status'})) {      if (exists($env{'form.Status'})) {
  $gradeTable .= '<input type="hidden" name="Status" value="'.$stu_status.'" />'."\n";   $gradeTable .= '<input type="hidden" name="Status" value="'.$env{'form.Status'}.'" />'."\n";
     } else {      } else {
         $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status'))          $gradeTable .= &Apache::lonhtmlcommon::row_closure()
                         .&Apache::lonhtmlcommon::row_title(&mt('Student Status'))
                       .&Apache::lonhtmlcommon::StatusOptions(                        .&Apache::lonhtmlcommon::StatusOptions(
                            $saveStatus,undef,1,'javascript:reLoadList(this.form);')                             $saveStatus,undef,1,'javascript:reLoadList(this.form);');
                       .&Apache::lonhtmlcommon::row_closure();  
     }      }
       if ($numessay) {
     $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))          $gradeTable .= &Apache::lonhtmlcommon::row_closure()
                   .'<input type="checkbox" name="checkPlag" checked="checked" />'                        .&Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))
                   .&Apache::lonhtmlcommon::row_closure(1)                        .'<input type="checkbox" name="checkPlag" checked="checked" />';
       }
       $gradeTable .= &Apache::lonhtmlcommon::row_closure(1)
                   .&Apache::lonhtmlcommon::end_pick_box();                    .&Apache::lonhtmlcommon::end_pick_box();
       my $regrademsg;
       if ($is_tool) {
           $regrademsg =&mt("To view/grade/regrade, click on the check box(es) next to the student's name(s). Then click on the Next button.");
       } else {
           $regrademsg = &mt("To view/grade/regrade a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.");
       }
     $gradeTable .= '<p>'      $gradeTable .= '<p>'
                   .&mt("To view/grade/regrade a submission or a group of submissions, click on the check box(es) next to the student's name(s). Then click on the Next button.")."\n"                    .$regrademsg."\n"
                   .'<input type="hidden" name="command" value="processGroup" />'                    .'<input type="hidden" name="command" value="processGroup" />'
                   .'</p>';                    .'</p>';
   
Line 1105  LISTJAVASCRIPT Line 2253  LISTJAVASCRIPT
     return '';      return '';
 }  }
   
 #---- Called from the listStudents routine  #---- Called from the listStudents and the names_for_passback routines.
   
   sub checkselect_js {
       my ($formname) = @_;
       if ($formname eq '') {
           $formname = 'gradesub';
       }
       my %js_lt;
       if ($formname eq 'passbackusers') {
           %js_lt = &Apache::lonlocal::texthash (
                        'multiple' => 'Please select a student or group of students before pushing the Save Scores button.',
                        'single'   => 'Please select the student before pushing the Save Scores button.',
                    );
       } else {
           %js_lt = &Apache::lonlocal::texthash (
                        'multiple' => 'Please select a student or group of students before clicking on the Next button.',
                        'single'   => 'Please select the student before clicking on the Next button.',
                    );
       }
       &js_escape(\%js_lt);
       return &Apache::lonhtmlcommon::scripttag(<<LISTJAVASCRIPT);
   
       function checkSelect(checkBox) {
           var ctr=0;
           var sense="";
           var len = checkBox.length;
           if (len == undefined) len = 1;
           if (len > 1) {
               for (var i=0; i<len; i++) {
                   if (checkBox[i].checked) {
                       ctr++;
                   }
               }
               sense = '$js_lt{'multiple'}';
           } else {
               if (checkBox.checked) {
                   ctr = 1;
               }
               sense = '$js_lt{'single'}';
           }
           if (ctr == 0) {
               alert(sense);
               return false;
           }
           document.$formname.submit();
       }
   LISTJAVASCRIPT
   
   }
   
 sub check_script {  sub check_script {
     my ($form, $type)=@_;      my ($form,$type) = @_;
     my $chkallscript= &Apache::lonhtmlcommon::scripttag('      my $chkallscript = &Apache::lonhtmlcommon::scripttag('
     function checkall() {      function checkall() {
         for (i=0; i<document.forms.'.$form.'.elements.length; i++) {          for (i=0; i<document.forms.'.$form.'.elements.length; i++) {
             ele = document.forms.'.$form.'.elements[i];              ele = document.forms.'.$form.'.elements[i];
Line 1154  sub check_buttons { Line 2350  sub check_buttons {
   
 #     Displays the submissions for one student or a group of students  #     Displays the submissions for one student or a group of students
 sub processGroup {  sub processGroup {
     my ($request,$symb)  = @_;      my ($request,$symb) = @_;
     my $ctr        = 0;      my $ctr        = 0;
     my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');      my @stuchecked = &Apache::loncommon::get_env_multiple('form.stuinfo');
     my $total      = scalar(@stuchecked)-1;      my $total      = scalar(@stuchecked)-1;
Line 1342  sub sub_page_js { Line 2538  sub sub_page_js {
 SUBJAVASCRIPT  SUBJAVASCRIPT
 }  }
   
 #--- javascript for essay type problem --  #--- javascript for grading message center
 sub sub_page_kw_js {  sub sub_grademessage_js {
     my $request = shift;      my $request = shift;
     my $iconpath = $request->dir_config('lonIconsURL');      my $iconpath = $request->dir_config('lonIconsURL');
     &commonJSfunctions($request);      &commonJSfunctions($request);
Line 1389  sub sub_page_kw_js { Line 2585  sub sub_page_kw_js {
 </script>  </script>
 INNERJS  INNERJS
   
     my $inner_js_highlight_central= (<<INNERJS);      my $start_page_msg_central =
 <script type="text/javascript">  
     function updateChoice(flag) {  
       opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);  
       opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);  
       opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);  
       opener.document.SCORE.refresh.value = "on";  
       if (opener.document.SCORE.keywords.value!=""){  
          opener.document.SCORE.submit();  
       }  
       self.close()  
     }  
 </script>  
 INNERJS  
   
     my $start_page_msg_central =   
         &Apache::loncommon::start_page('Message Central',$inner_js_msg_central,          &Apache::loncommon::start_page('Message Central',$inner_js_msg_central,
        {'js_ready'  => 1,         {'js_ready'  => 1,
  'only_body' => 1,   'only_body' => 1,
  'bgcolor'   =>'#FFFFFF',});   'bgcolor'   =>'#FFFFFF',});
     my $end_page_msg_central =       my $end_page_msg_central =
  &Apache::loncommon::end_page({'js_ready' => 1});  
   
   
     my $start_page_highlight_central =   
         &Apache::loncommon::start_page('Highlight Central',  
        $inner_js_highlight_central,  
        {'js_ready'  => 1,  
  'only_body' => 1,  
  'bgcolor'   =>'#FFFFFF',});  
     my $end_page_highlight_central =   
  &Apache::loncommon::end_page({'js_ready' => 1});   &Apache::loncommon::end_page({'js_ready' => 1});
   
     my $docopen=&Apache::lonhtmlcommon::javascript_docopen();      my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
     $docopen=~s/^document\.//;      $docopen=~s/^document\.//;
     my %js_lt = &Apache::lonlocal::texthash(  
                 keyw => 'Keywords list, separated by a space. Add/delete to list if desired.',  
                 plse => 'Please select a word or group of words from document and then click this link.',  
                 adds => 'Add selection to keyword list? Edit if desired.',  
                 col1 => 'red',  
                 col2 => 'green',  
                 col3 => 'blue',  
                 siz1 => 'normal',  
                 siz2 => '+1',  
                 siz3 => '+2',  
                 sty1 => 'normal',  
                 sty2 => 'italic',  
                 sty3 => 'bold',  
              );  
     my %html_js_lt = &Apache::lonlocal::texthash(      my %html_js_lt = &Apache::lonlocal::texthash(
                 comp => 'Compose Message for: ',                  comp => 'Compose Message for: ',
                 incl => 'Include',                  incl => 'Include',
Line 1447  INNERJS Line 2605  INNERJS
                 new  => 'New',                  new  => 'New',
                 save => 'Save',                  save => 'Save',
                 canc => 'Cancel',                  canc => 'Cancel',
                 kehi => 'Keyword Highlight Options',  
                 txtc => 'Text Color',  
                 font => 'Font Size',  
                 fnst => 'Font Style',  
              );               );
     &js_escape(\%js_lt);  
     &html_escape(\%html_js_lt);      &html_escape(\%html_js_lt);
     &js_escape(\%html_js_lt);      &js_escape(\%html_js_lt);
     $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));      $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));
   
 //===================== Show list of keywords ====================  
   function keywords(formname) {  
     var nret = prompt("$js_lt{'keyw'}",formname.keywords.value);  
     if (nret==null) return;  
     formname.keywords.value = nret;  
   
     if (formname.keywords.value != "") {  
  formname.refresh.value = "on";  
  formname.submit();  
     }  
     return;  
   }  
   
 //===================== Script to view submitted by ==================  //===================== Script to view submitted by ==================
   function viewSubmitter(submitter) {    function viewSubmitter(submitter) {
     document.SCORE.refresh.value = "on";      document.SCORE.refresh.value = "on";
Line 1479  INNERJS Line 2619  INNERJS
     return;      return;
   }    }
   
 //===================== Script to add keyword(s) ==================  
   function getSel() {  
     if (document.getSelection) txt = document.getSelection();  
     else if (document.selection) txt = document.selection.createRange().text;  
     else return;  
     var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");  
     if (cleantxt=="") {  
  alert("$js_lt{'plse'}");  
  return;  
     }  
     var nret = prompt("$js_lt{'adds'}",cleantxt);  
     if (nret==null) return;  
     document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;  
     if (document.SCORE.keywords.value != "") {  
  document.SCORE.refresh.value = "on";  
  document.SCORE.submit();  
     }  
     return;  
   }  
   
 //====================== Script for composing message ==============  //====================== Script for composing message ==============
    // preload images     // preload images
    img1 = new Image();     img1 = new Image();
Line 1609  INNERJS Line 2729  INNERJS
     pDoc.close();      pDoc.close();
 }  }
   
   SUBJAVASCRIPT
   }
   
   #--- javascript for essay type problem --
   sub sub_page_kw_js {
       my $request = shift;
   
       unless ($env{'form.compmsg'}) {
           &commonJSfunctions($request);
       }
   
       my $inner_js_highlight_central= (<<INNERJS);
   <script type="text/javascript">
       function updateChoice(flag) {
         opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);
         opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);
         opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);
         opener.document.SCORE.refresh.value = "on";
         if (opener.document.SCORE.keywords.value!=""){
            opener.document.SCORE.submit();
         }
         self.close()
       }
   </script>
   INNERJS
   
       my $start_page_highlight_central =
           &Apache::loncommon::start_page('Highlight Central',
                                          $inner_js_highlight_central,
                                          {'js_ready'  => 1,
                                           'only_body' => 1,
                                           'bgcolor'   =>'#FFFFFF',});
       my $end_page_highlight_central =
           &Apache::loncommon::end_page({'js_ready' => 1});
   
       my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
       $docopen=~s/^document\.//;
   
       my %js_lt = &Apache::lonlocal::texthash(
                   keyw => 'Keywords list, separated by a space. Add/delete to list if desired.',
                   plse => 'Please select a word or group of words from document and then click this link.',
                   adds => 'Add selection to keyword list? Edit if desired.',
                   col1 => 'red',
                   col2 => 'green',
                   col3 => 'blue',
                   siz1 => 'normal',
                   siz2 => '+1',
                   siz3 => '+2',
                   sty1 => 'normal',
                   sty2 => 'italic',
                   sty3 => 'bold',
                );
       my %html_js_lt = &Apache::lonlocal::texthash(
                   save => 'Save',
                   canc => 'Cancel',
                   kehi => 'Keyword Highlight Options',
                   txtc => 'Text Color',
                   font => 'Font Size',
                   fnst => 'Font Style',
                );
       &js_escape(\%js_lt);
       &html_escape(\%html_js_lt);
       &js_escape(\%html_js_lt);
       $request->print(&Apache::lonhtmlcommon::scripttag(<<SUBJAVASCRIPT));
   
   //===================== Show list of keywords ====================
     function keywords(formname) {
       var nret = prompt("$js_lt{'keyw'}",formname.keywords.value);
       if (nret==null) return;
       formname.keywords.value = nret;
   
       if (formname.keywords.value != "") {
           formname.refresh.value = "on";
           formname.submit();
       }
       return;
     }
   
   //===================== Script to add keyword(s) ==================
     function getSel() {
       if (document.getSelection) txt = document.getSelection();
       else if (document.selection) txt = document.selection.createRange().text;
       else return;
       if (typeof(txt) != 'string') {
           txt = String(txt);
       }
       var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
       if (cleantxt=="") {
           alert("$js_lt{'plse'}");
           return;
       }
       var nret = prompt("$js_lt{'adds'}",cleantxt);
       if (nret==null) return;
       document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;
       if (document.SCORE.keywords.value != "") {
           document.SCORE.refresh.value = "on";
           document.SCORE.submit();
       }
       return;
     }
   
 //====================== Script for keyword highlight options ==============  //====================== Script for keyword highlight options ==============
   function kwhighlight() {    function kwhighlight() {
     var kwclr    = document.SCORE.kwclr.value;      var kwclr    = document.SCORE.kwclr.value;
Line 1799  sub gradeBox { Line 3020  sub gradeBox {
   
 sub handback_box {  sub handback_box {
     my ($symb,$uname,$udom,$counter,$partid,$record,$res_error_pointer) = @_;      my ($symb,$uname,$udom,$counter,$partid,$record,$res_error_pointer) = @_;
     my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error_pointer);      my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,$res_error_pointer);
       return unless ($numessay);
     my (@respids);      my (@respids);
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id = &flatten_responseType($responseType);
     foreach my $part_response_id (@part_response_id) {      foreach my $part_response_id (@part_response_id) {
Line 1901  sub show_problem { Line 3123  sub show_problem {
 sub files_exist {  sub files_exist {
     my ($r, $symb) = @_;      my ($r, $symb) = @_;
     my @students = &Apache::loncommon::get_env_multiple('form.stuinfo');      my @students = &Apache::loncommon::get_env_multiple('form.stuinfo');
   
     foreach my $student (@students) {      foreach my $student (@students) {
         my ($uname,$udom,$fullname) = split(/:/,$student);          my ($uname,$udom,$fullname) = split(/:/,$student);
         my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},          my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},
       $udom,$uname);        $udom,$uname);
         my ($string,$timestamp)= &get_last_submission(\%record);          my ($string)= &get_last_submission(\%record);
         foreach my $submission (@$string) {          foreach my $submission (@$string) {
             my ($partid,$respid) =              my ($partid,$respid) =
  ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);   ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
Line 1921  sub files_exist { Line 3142  sub files_exist {
 sub download_all_link {  sub download_all_link {
     my ($r,$symb) = @_;      my ($r,$symb) = @_;
     unless (&files_exist($r, $symb)) {      unless (&files_exist($r, $symb)) {
        $r->print(&mt('There are currently no submitted documents.'));          $r->print(&mt('There are currently no submitted documents.'));
        return;          return;
     }      }
   
     my $all_students =       my $all_students = 
  join("\n", &Apache::loncommon::get_env_multiple('form.stuinfo'));   join("\n", &Apache::loncommon::get_env_multiple('form.stuinfo'));
   
Line 1943  sub download_all_link { Line 3163  sub download_all_link {
 sub submit_download_link {  sub submit_download_link {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
     if (!$symb) { return ''; }      if (!$symb) { return ''; }
 #FIXME: Figure out which type of problem this is and provide appropriate download      my $res_error;
     &download_all_link($request,$symb);      my ($partlist,$handgrade,$responseType,$numresp,$numessay,$numdropbox) =
           &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print(&mt('An error occurred retrieving response types'));
           return;
       }
       unless ($numessay) {
           $request->print(&mt('No essayresponse items found'));
           return;
       }
       my @chosenparts = &Apache::loncommon::get_env_multiple('form.vPart');
       if (@chosenparts) {
           $request->print(&showResourceInfo($symb,$partlist,$responseType,
                                             undef,undef,1));
       }
       if ($numessay) {
           my $submitonly= $env{'form.submitonly'} eq '' ? 'all' : $env{'form.submitonly'};
           my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
           my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
           (undef,undef,my $fullname) = &getclasslist($getsec,1,$getgroup,$symb,$submitonly,1);
           if (ref($fullname) eq 'HASH') {
               my @students = map { $_.':'.$fullname->{$_} } (keys(%{$fullname}));
               if (@students) {
                   @{$env{'form.stuinfo'}} = @students;
                   if ($numdropbox) {
                       &download_all_link($request,$symb);
                   } else {
                       $request->print(&mt('No essayrespose items with dropbox found'));
                   }
   # FIXME Need a mechanism to download essays, i.e., if $numessay > $numdropbox
   # Needs to omit user's identity if resource instance is for an anonymous survey.
               } else {
                   $request->print(&mt('No students match the criteria you selected'));
               }
           } else {
               $request->print(&mt('Could not retrieve student information'));
           }
       } else {
           $request->print(&mt('No essayresponse items found'));
       }
       return;
 }  }
   
 sub build_section_inputs {  sub build_section_inputs {
Line 1962  sub build_section_inputs { Line 3222  sub build_section_inputs {
   
 # --------------------------- show submissions of a student, option to grade   # --------------------------- show submissions of a student, option to grade 
 sub submission {  sub submission {
     my ($request,$counter,$total,$symb) = @_;      my ($request,$counter,$total,$symb,$divforres,$calledby) = @_;
     my ($uname,$udom)     = ($env{'form.student'},$env{'form.userdom'});      my ($uname,$udom)     = ($env{'form.student'},$env{'form.userdom'});
     $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?      $udom = ($udom eq '' ? $env{'user.domain'} : $udom); #has form.userdom changed for a student?
     my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});      my $usec = &Apache::lonnet::getsection($udom,$uname,$env{'request.course.id'});
     $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';      $env{'form.fullname'} = &Apache::loncommon::plainname($uname,$udom,'lastname') if $env{'form.fullname'} eq '';
   
     my $probtitle=&Apache::lonnet::gettitle($symb);   
     if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }      if ($symb eq '') { $request->print("Unable to handle ambiguous references:."); return ''; }
       my $probtitle=&Apache::lonnet::gettitle($symb);
       my $is_tool = ($symb =~ /ext\.tool$/);
       my ($essayurl,%coursedesc_by_cid);
   
     if (!&canview($usec)) {      if (!&canview($usec)) {
         $request->print(          $request->print(
Line 1981  sub submission { Line 3243  sub submission {
  return;   return;
     }      }
   
       my $res_error;
       my ($partlist,$handgrade,$responseType,$numresp,$numessay) =
           &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print(&navmap_errormsg());
           return;
       }
   
     if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }      if (!$env{'form.lastSub'}) { $env{'form.lastSub'} = 'datesub'; }
     if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }      unless ($is_tool) { 
     if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }          if (!$env{'form.vProb'}) { $env{'form.vProb'} = 'yes'; }
           if (!$env{'form.vAns'}) { $env{'form.vAns'} = 'yes'; }
       }
       if (($numessay) && ($calledby eq 'submission') && (!exists($env{'form.compmsg'}))) {
           $env{'form.compmsg'} = 1;
       }
     my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');      my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
     my $checkIcon = '<img alt="'.&mt('Check Mark').      my $checkIcon = '<img alt="'.&mt('Check Mark').
  '" src="'.$request->dir_config('lonIconsURL').   '" src="'.$request->dir_config('lonIconsURL').
Line 1991  sub submission { Line 3266  sub submission {
   
     # header info      # header info
     if ($counter == 0) {      if ($counter == 0) {
           my @chosenparts = &Apache::loncommon::get_env_multiple('form.vPart');
           if (@chosenparts) {
               $request->print(&showResourceInfo($symb,$partlist,$responseType,'gradesub'));
           } elsif ($divforres) {
               $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');
           } else {
               $request->print('<br clear="all" />');
           }
  &sub_page_js($request);   &sub_page_js($request);
  &sub_page_kw_js($request);          &sub_grademessage_js($request) if ($env{'form.compmsg'});
    &sub_page_kw_js($request) if ($numessay);
   
  # option to display problem, only once else it cause problems    # option to display problem, only once else it cause problems 
         # with the form later since the problem has a form.          # with the form later since the problem has a form.
Line 2009  sub submission { Line 3293  sub submission {
     $request->print(&show_problem($request,$symb,$uname,$udom,0,1,$mode));      $request->print(&show_problem($request,$symb,$uname,$udom,0,1,$mode));
  }   }
   
  # kwclr is the only variable that is guaranteed not to be blank   
         # if this subroutine has been called once.  
  my %keyhash = ();   my %keyhash = ();
 # if ($env{'form.kwclr'} eq '' && $env{'form.handgrade'} eq 'yes') {   if (($env{'form.kwclr'} eq '' && $numessay) || ($env{'form.compmsg'})) {
         if (1) {  
     %keyhash = &Apache::lonnet::dump('nohist_handgrade',      %keyhash = &Apache::lonnet::dump('nohist_handgrade',
      $env{'course.'.$env{'request.course.id'}.'.domain'},       $env{'course.'.$env{'request.course.id'}.'.domain'},
      $env{'course.'.$env{'request.course.id'}.'.num'});       $env{'course.'.$env{'request.course.id'}.'.num'});
    }
    # kwclr is the only variable that is guaranteed not to be blank
    # if this subroutine has been called once.
    if ($env{'form.kwclr'} eq '' && $numessay) {
     my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};      my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
     $env{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';      $env{'form.keywords'} = $keyhash{$symb.'_keywords'} ne '' ? $keyhash{$symb.'_keywords'} : '';
     $env{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';      $env{'form.kwclr'}    = $keyhash{$loginuser.'_kwclr'} ne '' ? $keyhash{$loginuser.'_kwclr'} : 'red';
     $env{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';      $env{'form.kwsize'}   = $keyhash{$loginuser.'_kwsize'} ne '' ? $keyhash{$loginuser.'_kwsize'} : '0';
     $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';      $env{'form.kwstyle'}  = $keyhash{$loginuser.'_kwstyle'} ne '' ? $keyhash{$loginuser.'_kwstyle'} : '';
     $env{'form.msgsub'}   = $keyhash{$symb.'_subject'} ne '' ?    }
    if ($env{'form.compmsg'}) {
       $env{'form.msgsub'}   = $keyhash{$symb.'_subject'} ne '' ?
  $keyhash{$symb.'_subject'} : $probtitle;   $keyhash{$symb.'_subject'} : $probtitle;
     $env{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';      $env{'form.savemsgN'} = $keyhash{$symb.'_savemsgN'} ne '' ? $keyhash{$symb.'_savemsgN'} : '0';
  }   }
   
  my $overRideScore = $env{'form.overRideScore'} eq '' ? 'no' : $env{'form.overRideScore'};   my $overRideScore = $env{'form.overRideScore'} eq '' ? 'no' : $env{'form.overRideScore'};
  my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));   my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
  $request->print('<form action="/adm/grades" method="post" name="SCORE" enctype="multipart/form-data">'."\n".   $request->print('<form action="/adm/grades" method="post" name="SCORE" enctype="multipart/form-data">'."\n".
Line 2040  sub submission { Line 3327  sub submission {
  '<input type="hidden" name="vProb"      value="'.$env{'form.vProb'}.'" />'."\n".   '<input type="hidden" name="vProb"      value="'.$env{'form.vProb'}.'" />'."\n".
  '<input type="hidden" name="vAns"       value="'.$env{'form.vAns'}.'" />'."\n".   '<input type="hidden" name="vAns"       value="'.$env{'form.vAns'}.'" />'."\n".
  '<input type="hidden" name="lastSub"    value="'.$env{'form.lastSub'}.'" />'."\n".   '<input type="hidden" name="lastSub"    value="'.$env{'form.lastSub'}.'" />'."\n".
    '<input type="hidden" name="compmsg"    value="'.$env{'form.compmsg'}.'" />'."\n".
  &build_section_inputs().   &build_section_inputs().
  '<input type="hidden" name="submitonly" value="'.$env{'form.submitonly'}.'" />'."\n".   '<input type="hidden" name="submitonly" value="'.$env{'form.submitonly'}.'" />'."\n".
  '<input type="hidden" name="NCT"'.   '<input type="hidden" name="NCT"'.
  ' value="'.($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : $total+1).'" />'."\n");   ' value="'.($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : $total+1).'" />'."\n");
 # if ($env{'form.handgrade'} eq 'yes') {   if ($env{'form.compmsg'}) {
         if (1) {      $request->print('<input type="hidden" name="msgsub"   value="'.$env{'form.msgsub'}.'" />'."\n".
       '<input type="hidden" name="shownSub" value="0" />'."\n".
       '<input type="hidden" name="savemsgN" value="'.$env{'form.savemsgN'}.'" />'."\n");
    }
    if ($numessay) {
     $request->print('<input type="hidden" name="keywords" value="'.$env{'form.keywords'}.'" />'."\n".      $request->print('<input type="hidden" name="keywords" value="'.$env{'form.keywords'}.'" />'."\n".
     '<input type="hidden" name="kwclr"    value="'.$env{'form.kwclr'}.'" />'."\n".      '<input type="hidden" name="kwclr"    value="'.$env{'form.kwclr'}.'" />'."\n".
     '<input type="hidden" name="kwsize"   value="'.$env{'form.kwsize'}.'" />'."\n".      '<input type="hidden" name="kwsize"   value="'.$env{'form.kwsize'}.'" />'."\n".
     '<input type="hidden" name="kwstyle"  value="'.$env{'form.kwstyle'}.'" />'."\n".      '<input type="hidden" name="kwstyle"  value="'.$env{'form.kwstyle'}.'" />'."\n");
     '<input type="hidden" name="msgsub"   value="'.$env{'form.msgsub'}.'" />'."\n".  
     '<input type="hidden" name="shownSub" value="0" />'."\n".  
     '<input type="hidden" name="savemsgN" value="'.$env{'form.savemsgN'}.'" />'."\n");  
     foreach my $partid (&Apache::loncommon::get_env_multiple('form.vPart')) {  
  $request->print('<input type="hidden" name="vPart" value="'.$partid.'" />'."\n");  
     }  
  }   }
   
  my ($cts,$prnmsg) = (1,'');   my ($cts,$prnmsg) = (1,'');
  while ($cts <= $env{'form.savemsgN'}) {   while ($cts <= $env{'form.savemsgN'}) {
     $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.      $prnmsg.='<input type="hidden" name="savemsg'.$cts.'" value="'.
Line 2070  sub submission { Line 3356  sub submission {
  }   }
  $request->print($prnmsg);   $request->print($prnmsg);
   
 # if ($env{'form.handgrade'} eq 'yes') {   if ($numessay) {
         if (1) {  
   
             my %lt = &Apache::lonlocal::texthash(              my %lt = &Apache::lonlocal::texthash(
                           keyh => 'Keyword Highlighting for Essays',                            keyh => 'Keyword Highlighting for Essays',
Line 2079  sub submission { Line 3364  sub submission {
                           list => 'List',                            list => 'List',
                           past => 'Paste Selection to List',                            past => 'Paste Selection to List',
                           high => 'Highlight Attribute',                            high => 'Highlight Attribute',
                      );                           );
 #  #
 # Print out the keyword options line  # Print out the keyword options line
 #  #
Line 2098  sub submission { Line 3383  sub submission {
 #  #
 # Load the other essays for similarity check  # Load the other essays for similarity check
 #  #
             my (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);              (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);
     my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);              if ($essayurl eq 'lib/templates/simpleproblem.problem') {
     $apath=&escape($apath);                  my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
     $apath=~s/\W/\_/gs;                  my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
             &init_old_essays($symb,$apath,$adom,$aname);                  if ($cdom ne '' && $cnum ne '') {
                       my ($map,$id,$res) = &Apache::lonnet::decode_symb($symb);
                       if ($map =~ m{^\Quploaded/$cdom/$cnum/\E(default(?:|_\d+)\.(?:sequence|page))$}) {
                           my $apath = $1.'_'.$id;
                           $apath=~s/\W/\_/gs;
                           &init_old_essays($symb,$apath,$cdom,$cnum);
                       }
                   }
               } else {
           my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);
           $apath=&escape($apath);
           $apath=~s/\W/\_/gs;
                   &init_old_essays($symb,$apath,$adom,$aname);
               }
         }          }
     }      }
   
Line 2146  sub submission { Line 3444  sub submission {
     }      }
   
     my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);      my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
     my $res_error;  
     my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);  
     if ($res_error) {  
         $request->print(&navmap_errormsg());  
         return;  
     }  
   
     # Display student info      # Display student info
     $request->print(($counter == 0 ? '' : '<br />'));      $request->print(($counter == 0 ? '' : '<br />'));
   
       my $boxtitle = &mt('Submissions');
       if ($is_tool) {
           $boxtitle = &mt('Transactions')
       }
     my $result='<div class="LC_Box">'      my $result='<div class="LC_Box">'
               .'<h3 class="LC_hcell">'.&mt('Submissions').'</h3>';                .'<h3 class="LC_hcell">'.$boxtitle.'</h3>';
     $result.='<input type="hidden" name="name'.$counter.      $result.='<input type="hidden" name="name'.$counter.
              '" value="'.$env{'form.fullname'}.'" />'."\n";               '" value="'.$env{'form.fullname'}.'" />'."\n";
 #    if ($env{'form.handgrade'} eq 'no') {      if (($numresp > $numessay) && !$is_tool) {
     if (1) {  
         $result.='<p class="LC_info">'          $result.='<p class="LC_info">'
                 .&mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)                  .&mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)
                 ."</p>\n";                  ."</p>\n";
     }      }
   
     # If any part of the problem is an essay-response (handgraded), then check for collaborators      # If any part of the problem is an essayresponse, then check for collaborators
     my $fullname;      my $fullname;
     my $col_fullnames = [];      my $col_fullnames = [];
 #    if ($env{'form.handgrade'} eq 'yes') {      if ($numessay) {
     if (1) {  
  (my $sub_result,$fullname,$col_fullnames)=   (my $sub_result,$fullname,$col_fullnames)=
     &check_collaborators($symb,$uname,$udom,\%record,$handgrade,      &check_collaborators($symb,$uname,$udom,\%record,$handgrade,
  $counter);   $counter);
  $result.=$sub_result;   $result.=$sub_result;
     }      }
     $request->print($result."\n");      $request->print($result."\n");
       
     # print student answer/submission  
     # Options are (1) Handgraded submission only  
     #             (2) Last submission, includes submission that is not handgraded   
     #                  (for multi-response type part)  
     #             (3) Last submission plus the parts info  
     #             (4) The whole record for this student  
       
     my ($string,$timestamp)= &get_last_submission(\%record);  
   
     my $lastsubonly;  
   
     if ($$timestamp eq '') {  
         $lastsubonly.='<div class="LC_grade_submissions_body">'.$$string[0].'</div>';   
     } else {  
         $lastsubonly =  
             '<div class="LC_grade_submissions_body">'  
            .'<b>'.&mt('Date Submitted:').'</b> '.$$timestamp."\n";  
   
  my %seenparts;  
  my @part_response_id = &flatten_responseType($responseType);  
  foreach my $part (@part_response_id) {  
     next if ($env{'form.lastSub'} eq 'hdgrade'   
  && $$handgrade{$$part[0].'_'.$$part[1]} ne 'yes');  
   
     my ($partid,$respid) = @{ $part };      # print student answer/submission
     my $display_part=&get_display_part($partid,$symb);      # Options are (1) Last submission only
     if ($env{"form.$uname:$udom:$partid:submitted_by"}) {      #             (2) Last submission (with detailed information for that submission)
  if (exists($seenparts{$partid})) { next; }      #             (3) All transactions (by date)
  $seenparts{$partid}=1;      #             (4) The whole record (with detailed information for all transactions)
                 $request->print(  
                     '<b>'.&mt('Part: [_1]',$display_part).'</b>'.      my ($lastsubonly,$partinfo) =
                     ' <b>'.&mt('Collaborative submission by: [_1]',          &show_last_submission($uname,$udom,$symb,$essayurl,$responseType,$env{'form.lastSub'},
                                '<a href="javascript:viewSubmitter(\''.                                $is_tool,$fullname,\%record,\%coursedesc_by_cid);
                                $env{"form.$uname:$udom:$partid:submitted_by"}.      $request->print($partinfo);
                                '\');" target="_self">'.  
                                $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a>').  
                     '<br />');  
  next;  
  }  
     my $responsetype = $responseType->{$partid}->{$respid};  
     if (!exists($record{"resource.$partid.$respid.submission"})) {  
                 $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.  
                     '<b>'.&mt('Part: [_1]',$display_part).'</b>'.  
                     ' <span class="LC_internal_info">'.  
                     '('.&mt('Response ID: [_1]',$respid).')'.  
                     '</span>&nbsp; &nbsp;'.  
           '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';  
  next;  
     }  
     foreach my $submission (@$string) {  
  my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);  
  if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }  
  my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4);  
  # Similarity check  
                 my $similar='';  
                 my ($type,$trial,$rndseed);  
                 if ($hide eq 'rand') {  
                     $type = 'randomizetry';  
                     $trial = $record{"resource.$partid.tries"};  
                     $rndseed = $record{"resource.$partid.rndseed"};  
                 }  
         if ($env{'form.checkPlag'}) {  
        my ($oname,$odom,$ocrsid,$oessay,$osim)=  
         &most_similar($uname,$udom,$symb,$subval);  
     if ($osim) {  
  $osim=int($osim*100.0);  
  my %old_course_desc =   
     &Apache::lonnet::coursedescription($ocrsid,  
  {'one_time' => 1});  
   
                         if ($hide eq 'anon') {  
                             $similar='<hr /><span class="LC_warning">'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'<br />'.  
                                      &mt('As the current submission is for an anonymous survey, no other details are available.').'</span><hr />';  
                         } else {  
     $similar="<hr /><h3><span class=\"LC_warning\">".  
  &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',  
     $osim,  
     &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',  
         $old_course_desc{'description'},  
         $old_course_desc{'num'},  
         $old_course_desc{'domain'}).  
     '</span></h3><blockquote><i>'.  
     &keywords_highlight($oessay).  
     '</i></blockquote><hr />';  
                         }  
             }  
  }  
  my $order=&get_order($partid,$respid,$symb,$uname,$udom,  
                                      undef,$type,$trial,$rndseed);  
                 if ($env{'form.lastSub'} eq 'lastonly' || $env{'form.lastSub'} eq 'datesub' || $env{'form.lastSub'} =~ /^(last|all)$/ || ($env{'form.lastSub'} eq 'hdgrade' &&   
  $$handgrade{$$part[0].'_'.$$part[1]} eq 'yes')) {  
     my $display_part=&get_display_part($partid,$symb);  
                     $lastsubonly.='<div class="LC_grade_submission_part">'.  
                         '<b>'.&mt('Part: [_1]',$display_part).'</b>'.  
                         ' <span class="LC_internal_info">'.  
                         '('.&mt('Response ID: [_1]',$respid).')'.  
                         '</span>&nbsp; &nbsp;';  
     my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);  
                           
     if (@$files) {  
                         if ($hide eq 'anon') {  
                             $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));  
                         } else {  
                             $lastsubonly.='<br /><br />'.'<b>'.&mt('Submitted Files:').'</b>'  
                                         .'<br /><span class="LC_warning">';  
                             if(@$files == 1) {  
                                 $lastsubonly .= &mt('Like all files provided by users, this file may contain viruses!');  
                             } else {  
                                 $lastsubonly .= &mt('Like all files provided by users, these files may contain viruses!');  
                             }  
                             $lastsubonly .= '</span>';                           
                             foreach my $file (@$files) {  
                                 &Apache::lonnet::allowuploaded('/adm/grades',$file);  
                                 $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" alt="" /> '.$file.'</a>';  
                             }  
                         }  
  $lastsubonly.='<br />';  
                     }  
                     if ($hide eq 'anon') {  
                         $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>';   
                     } else {  
                      $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>';  
                         if ($draft) {  
                             $lastsubonly.= ' <span class="LC_warning">'.&mt('Draft Copy').'</span>';  
                         }  
                         $subval =  
     &cleanRecord($subval,$responsetype,$symb,$partid,  
  $respid,\%record,$order,undef,$uname,$udom,$type,$trial,$rndseed);  
                         if ($responsetype eq 'essay') {  
                             $subval =~ s{\n}{<br />}g;  
                         }  
                         $lastsubonly.=$subval."\n";  
                     }  
             if ($similar) {$lastsubonly.="<br /><br />$similar\n";}  
     $lastsubonly.='</div>';  
  }  
             }  
  }  
  $lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body  
     }  
     $request->print($lastsubonly);      $request->print($lastsubonly);
   
     if ($env{'form.lastSub'} eq 'datesub') {      if ($env{'form.lastSub'} eq 'datesub') {
         my ($parts,$handgrade,$responseType) = &response_type($symb,\$res_error);          my ($parts,$handgrade,$responseType) = &response_type($symb,\$res_error);
  $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));   $request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));
         }
     }   
     if ($env{'form.lastSub'} =~ /^(last|all)$/) {      if ($env{'form.lastSub'} =~ /^(last|all)$/) {
         my $identifier = (&canmodify($usec)? $counter : '');          my $identifier = (&canmodify($usec)? $counter : '');
         $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,          $request->print(&Apache::loncommon::get_previous_attempt($symb,$uname,$udom,
Line 2343  sub submission { Line 3507  sub submission {
  $request->print('</div>'."\n");   $request->print('</div>'."\n");
     }      }
   
     # essay grading message center      # grading message center
 #    if ($env{'form.handgrade'} eq 'yes') {  
     if (1) {      if ($env{'form.compmsg'}) {
  my $result='<div class="LC_grade_message_center">';          my $result='<div class="LC_Box">'.
                          '<h3 class="LC_hcell">'.&mt('Send Message').'</h3>'.
  $result.='<div class="LC_grade_message_center_header">'.                     '<div class="LC_grade_message_center_body">';
     &mt('Send Message').'</div><div class="LC_grade_message_center_body">';          my ($lastname,$givenn) = split(/,/,$env{'form.fullname'});
  my ($lastname,$givenn) = split(/,/,$env{'form.fullname'});          my $msgfor = $givenn.' '.$lastname;
  my $msgfor = $givenn.' '.$lastname;          if (scalar(@$col_fullnames) > 0) {
  if (scalar(@$col_fullnames) > 0) {              my $lastone = pop(@$col_fullnames);
     my $lastone = pop(@$col_fullnames);              $msgfor .= ', '.(join ', ',@$col_fullnames).' and '.$lastone.'.';
     $msgfor .= ', '.(join ', ',@$col_fullnames).' and '.$lastone.'.';          }
  }          $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript
  $msgfor =~ s/\'/\\'/g; #' stupid emacs - no! javascript          $result.='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".
  $result.='<input type="hidden" name="includemsg'.$counter.'" value="" />'."\n".                   '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n".
     '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n";                   '&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.
  $result.='&nbsp;<a href="javascript:msgCenter(document.SCORE,'.$counter.                   ',\''.$msgfor.'\');" target="_self">'.
     ',\''.$msgfor.'\');" target="_self">'.                   &mt('Compose message to student'.(scalar(@$col_fullnames) >= 1 ? 's' : '')).'</a><label> ('.
     &mt('Compose message to student'.(scalar(@$col_fullnames) >= 1 ? 's' : '')).'</a><label> ('.                   &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.
     &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'.                   ' <img src="'.$request->dir_config('lonIconsURL').
     ' <img src="'.$request->dir_config('lonIconsURL').                   '/mailbkgrd.gif" width="14" height="10" alt="" name="mailicon'.$counter.'" />'."\n".
     '/mailbkgrd.gif" width="14" height="10" alt="" name="mailicon'.$counter.'" />'."\n".                   '<br />&nbsp;('.
     '<br />&nbsp;('.                   &mt('Message will be sent when you click on Save &amp; Next below.').")\n".
     &mt('Message will be sent when you click on Save &amp; Next below.').")\n";                   '</div></div>';
  $result.='</div></div>';          $request->print($result);
  $request->print($result);  
     }      }
   
     my %seen = ();      my %seen = ();
     my @partlist;      my @partlist;
     my @gradePartRespid;      my @gradePartRespid;
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id;
       if ($is_tool) {
           @part_response_id = ([0,'']);
       } else {
           @part_response_id = &flatten_responseType($responseType);
       }
     $request->print(      $request->print(
         '<div class="LC_Box">'          '<div class="LC_Box">'
        .'<h3 class="LC_hcell">'.&mt('Assign Grades').'</h3>'         .'<h3 class="LC_hcell">'.&mt('Assign Grades').'</h3>'
Line 2385  sub submission { Line 3553  sub submission {
  my $part_resp = join('_',@{ $part_response_id });   my $part_resp = join('_',@{ $part_response_id });
  next if ($seen{$partid} > 0);   next if ($seen{$partid} > 0);
  $seen{$partid}++;   $seen{$partid}++;
  next if ($$handgrade{$part_resp} ne 'yes'   
  && $env{'form.lastSub'} eq 'hdgrade');  
  push(@partlist,$partid);   push(@partlist,$partid);
  push(@gradePartRespid,$partid.'.'.$respid);   push(@gradePartRespid,$partid.'.'.$respid);
  $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));   $request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
Line 2442  sub submission { Line 3608  sub submission {
     return '';      return '';
 }  }
   
   sub show_last_submission {
       my ($uname,$udom,$symb,$essayurl,$responseType,$viewtype,$is_tool,$fullname,
           $record,$coursedesc_by_cid) = @_;
       my ($string,$timestamp,$lastgradetime,$lastsubmittime) =
           &get_last_submission($record,$is_tool);
   
       my ($lastsubonly,$partinfo);
       if ($timestamp eq '') {
           $lastsubonly.='<div class="LC_grade_submissions_body">'.$string->[0].'</div>';
       } elsif ($is_tool) {
           $lastsubonly =
               '<div class="LC_grade_submissions_body">'
              .'<b>'.&mt('Date Grade Passed Back:').'</b> '.$timestamp."</div>\n";
       } else {
           my ($shownsubmdate,$showngradedate);
           if ($lastsubmittime && $lastgradetime) {
               $shownsubmdate = &Apache::lonlocal::locallocaltime($lastsubmittime);
               if ($lastgradetime > $lastsubmittime) {
                    $showngradedate = &Apache::lonlocal::locallocaltime($lastgradetime);
                }
           } else {
               $shownsubmdate = $timestamp;
           }
           $lastsubonly =
               '<div class="LC_grade_submissions_body">'
              .'<b>'.&mt('Date Submitted:').'</b> '.$shownsubmdate."\n";
           if ($showngradedate) {
               $lastsubonly .= '<br /><b>'.&mt('Date Graded:').'</b> '.$showngradedate."\n";
           }
   
           my %seenparts;
           my @part_response_id = &flatten_responseType($responseType);
           foreach my $part (@part_response_id) {
               my ($partid,$respid) = @{ $part };
               my $display_part=&get_display_part($partid,$symb);
               if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
                   if (exists($seenparts{$partid})) { next; }
                   $seenparts{$partid}=1;
                   $partinfo .=
                       '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                       ' <b>'.&mt('Collaborative submission by: [_1]',
                                  '<a href="javascript:viewSubmitter(\''.
                                  $env{"form.$uname:$udom:$partid:submitted_by"}.
                                  '\');" target="_self">'.
                                  $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a>').
                       '<br />';
                   next;
               }
               my $responsetype = $responseType->{$partid}->{$respid};
               if (!exists($record->{"resource.$partid.$respid.submission"})) {
                   $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.
                       '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                       ' <span class="LC_internal_info">'.
                       '('.&mt('Response ID: [_1]',$respid).')'.
                       '</span>&nbsp; &nbsp;'.
                       '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';
                   next;
               }
               foreach my $submission (@$string) {
                   my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
                   if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
                   my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4);
                   # Similarity check
                   my $similar='';
                   my ($type,$trial,$rndseed);
                   if ($hide eq 'rand') {
                       $type = 'randomizetry';
                       $trial = $record->{"resource.$partid.tries"};
                       $rndseed = $record->{"resource.$partid.rndseed"};
                   }
                   if ($env{'form.checkPlag'}) {
                       my ($oname,$odom,$ocrsid,$oessay,$osim)=
                       &most_similar($uname,$udom,$symb,$subval);
                       if ($osim) {
                           $osim=int($osim*100.0);
                           if ($hide eq 'anon') {
                               $similar='<hr /><span class="LC_warning">'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'<br />'.
                                        &mt('As the current submission is for an anonymous survey, no other details are available.').'</span><hr />';
                           } else {
                               $similar='<hr />';
                               if ($essayurl eq 'lib/templates/simpleproblem.problem') {
                                   $similar .= '<h3><span class="LC_warning">'.
                                               &mt('Essay is [_1]% similar to an essay by [_2]',
                                                   $osim,
                                                   &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
                                               '</span></h3>';
                               } else {
                                   my %old_course_desc;
                                   if ($ocrsid ne '') {
                                       if (ref($coursedesc_by_cid->{$ocrsid}) eq 'HASH') {
                                           %old_course_desc = %{$coursedesc_by_cid->{$ocrsid}};
                                       } else {
                                           my $args;
                                           if ($ocrsid ne $env{'request.course.id'}) {
                                               $args = {'one_time' => 1};
                                           }
                                           %old_course_desc =
                                               &Apache::lonnet::coursedescription($ocrsid,$args);
                                           $coursedesc_by_cid->{$ocrsid} = \%old_course_desc;
                                       }
                                       $similar .=
                                           '<h3><span class="LC_warning">'.
                                           &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',
                                               $osim,
                                               &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',
                                               $old_course_desc{'description'},
                                               $old_course_desc{'num'},
                                               $old_course_desc{'domain'}).
                                           '</span></h3>';
                                   } else {
                                       $similar .=
                                           '<h3><span class="LC_warning">'.
                                           &mt('Essay is [_1]% similar to an essay by [_2] in an unknown course',
                                               $osim,
                                               &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
                                           '</span></h3>';
                                   }
                               }
                               $similar .= '<blockquote><i>'.
                                           &keywords_highlight($oessay).
                                           '</i></blockquote><hr />';
                           }
                       }
                   }
                   my $order=&get_order($partid,$respid,$symb,$uname,$udom,
                                        undef,$type,$trial,$rndseed);
                   if (($viewtype eq 'lastonly') ||
                       ($viewtype eq 'datesub')  ||
                       ($viewtype =~ /^(last|all)$/)) {
                       my $display_part=&get_display_part($partid,$symb);
                       $lastsubonly.='<div class="LC_grade_submission_part">'.
                           '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
                           ' <span class="LC_internal_info">'.
                           '('.&mt('Response ID: [_1]',$respid).')'.
                           '</span>&nbsp; &nbsp;';
                       my $files=&get_submitted_files($udom,$uname,$partid,$respid,$record);
                       if (@$files) {
                           if ($hide eq 'anon') {
                               $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));
                           } else {
                               $lastsubonly.='<br /><br />'.'<b>'.&mt('Submitted Files:').'</b>'
                                           .'<br /><span class="LC_warning">';
                               if(@$files == 1) {
                                   $lastsubonly .= &mt('Like all files provided by users, this file may contain viruses!');
                               } else {
                                   $lastsubonly .= &mt('Like all files provided by users, these files may contain viruses!');
                               }
                               $lastsubonly .= '</span>';
                               foreach my $file (@$files) {
                                   &Apache::lonnet::allowuploaded('/adm/grades',$file);
                                   $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" alt="" /> '.$file.'</a>';
                               }
                           }
                           $lastsubonly.='<br />';
                       }
                       if ($hide eq 'anon') {
                           $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>';
                       } else {
                           $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>';
                           if ($draft) {
                               $lastsubonly.= ' <span class="LC_warning">'.&mt('Draft Copy').'</span>';
                           }
                           $subval =
                               &cleanRecord($subval,$responsetype,$symb,$partid,
                                            $respid,$record,$order,undef,$uname,$udom,$type,$trial,$rndseed);
                           if ($responsetype eq 'essay') {
                               $subval =~ s{\n}{<br />}g;
                           }
                           $lastsubonly.=$subval."\n";
                       }
                       if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
                       $lastsubonly.='</div>';
                   }
               }
           }
           $lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body
       }
       return ($lastsubonly,$partinfo);
   }
   
 sub check_collaborators {  sub check_collaborators {
     my ($symb,$uname,$udom,$record,$handgrade,$counter) = @_;      my ($symb,$uname,$udom,$record,$handgrade,$counter) = @_;
     my ($result,@col_fullnames);      my ($result,@col_fullnames);
Line 2500  sub check_collaborators { Line 3846  sub check_collaborators {
   
 #--- Retrieve the last submission for all the parts  #--- Retrieve the last submission for all the parts
 sub get_last_submission {  sub get_last_submission {
     my ($returnhash)=@_;      my ($returnhash,$is_tool)=@_;
     my (@string,$timestamp,%lasthidden);      my (@string,$timestamp,$lastgradetime,$lastsubmittime);
     if ($$returnhash{'version'}) {      if ($$returnhash{'version'}) {
  my %lasthash=();   my %lasthash=();
  my ($version);          my %prevsolved=();
           my %solved=();
    my $version;
  for ($version=1;$version<=$$returnhash{'version'};$version++) {   for ($version=1;$version<=$$returnhash{'version'};$version++) {
               my %handgraded = ();
     foreach my $key (sort(split(/\:/,      foreach my $key (sort(split(/\:/,
  $$returnhash{$version.':keys'}))) {   $$returnhash{$version.':keys'}))) {
  $lasthash{$key}=$$returnhash{$version.':'.$key};   $lasthash{$key}=$$returnhash{$version.':'.$key};
  $timestamp =                   if ($key =~ /\.([^.]+)\.regrader$/) {
     &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});                      $handgraded{$1} = 1;
                   } elsif ($key =~ /\.portfiles$/) {
                       if (($$returnhash{$version.':'.$key} ne '') &&
                           ($$returnhash{$version.':'.$key} !~ /\.\d+\.\w+$/)) {
                           $lastsubmittime = $$returnhash{$version.':timestamp'};
                       }
                   } elsif ($key =~ /\.submission$/) {
                       if ($$returnhash{$version.':'.$key} ne '') {
                           $lastsubmittime = $$returnhash{$version.':timestamp'};
                       }
                   } elsif ($key =~ /\.([^.]+)\.solved$/) {
                       $prevsolved{$1} = $solved{$1};
                       $solved{$1} = $lasthash{$key};
                   }
               }
               foreach my $partid (keys(%handgraded)) {
                   if (($prevsolved{$partid} eq 'ungraded_attempted') &&
                       (($solved{$partid} eq 'incorrect_by_override') ||
                        ($solved{$partid} eq 'correct_by_override'))) {
                       $lastgradetime = $$returnhash{$version.':timestamp'};
                   }
                   if ($solved{$partid} ne '') {
                       $prevsolved{$partid} = $solved{$partid};
                   }
     }      }
  }   }
   #
   # Timestamp is for last transaction for this resource, which does not
   # necessarily correspond to the time of last submission for problem (or part).
   #
           if ($lasthash{'timestamp'} ne '') {
               $timestamp = &Apache::lonlocal::locallocaltime($lasthash{'timestamp'});
           }
         my (%typeparts,%randombytry);          my (%typeparts,%randombytry);
         my $showsurv =           my $showsurv = 
             &Apache::lonnet::allowed('vas',$env{'request.course.id'});              &Apache::lonnet::allowed('vas',$env{'request.course.id'});
Line 2566  sub get_last_submission { Line 3945  sub get_last_submission {
  }   }
     }      }
     if (!@string) {      if (!@string) {
           my $msg;
           if ($is_tool) {
               $msg = &mt('No grade passed back.');
           } else {
               $msg = &mt('Nothing submitted - no attempts.');
           }
  $string[0] =   $string[0] =
     '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span>';      '<span class="LC_warning">'.$msg.'</span>';
     }      }
     return (\@string,\$timestamp);      return (\@string,$timestamp,$lastgradetime,$lastsubmittime);
 }  }
   
 #--- High light keywords, with style choosen by user.  #--- High light keywords, with style choosen by user.
Line 2776  sub processHandGrade { Line 4161  sub processHandGrade {
     my $ntstu  = $env{'form.NTSTU'};      my $ntstu  = $env{'form.NTSTU'};
     my $cdom   = $env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom   = $env{'course.'.$env{'request.course.id'}.'.domain'};
     my $cnum   = $env{'course.'.$env{'request.course.id'}.'.num'};      my $cnum   = $env{'course.'.$env{'request.course.id'}.'.num'};
       my ($res_error,%queueable);
       my ($partlist,$handgrade,$responseType,$numresp,$numessay) = &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print(&navmap_errormsg());
           return;
       } else {
           foreach my $part (@{$partlist}) {
               if (ref($responseType->{$part}) eq 'HASH') {
                   foreach my $id (keys(%{$responseType->{$part}})) {
                       if (($responseType->{$part}->{$id} eq 'essay') ||
                           (lc($handgrade->{$part.'_'.$id}) eq 'yes')) {
                           $queueable{$part} = 1;
                           last;
                       }
                   }
               }
           }
       }
   
     if ($button eq 'Save & Next') {      if ($button eq 'Save & Next') {
           my %needpb = &passbacks_for_symb($cdom,$cnum,$symb);
           my (%skip_passback,%pbsave,%pbcollab);
  my $ctr = 0;   my $ctr = 0;
  while ($ctr < $ngrade) {   while ($ctr < $ngrade) {
     my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr});      my ($uname,$udom) = split(/:/,$env{'form.unamedom'.$ctr});
     my ($errorflag,$pts,$wgt,$numhidden) =       my ($errorflag,$pts,$wgt,$numhidden) = 
                 &saveHandGrade($request,$symb,$uname,$udom,$ctr);                  &saveHandGrade($request,$symb,$uname,$udom,$ctr,undef,undef,\%queueable,\%needpb,\%skip_passback,\%pbsave);
     if ($errorflag eq 'no_score') {      if ($errorflag eq 'no_score') {
  $ctr++;   $ctr++;
  next;   next;
Line 2837  sub processHandGrade { Line 4242  sub processHandGrade {
     foreach my $collaborator (@collaborators) {      foreach my $collaborator (@collaborators) {
  my ($errorflag,$pts,$wgt) =    my ($errorflag,$pts,$wgt) = 
     &saveHandGrade($request,$symb,$collaborator,$udom,$ctr,      &saveHandGrade($request,$symb,$collaborator,$udom,$ctr,
    $env{'form.unamedom'.$ctr},$part);     $env{'form.unamedom'.$ctr},$part,\%queueable,\%needpb,\%skip_passback,%pbsave);
  if ($errorflag eq 'not_allowed') {   if ($errorflag eq 'not_allowed') {
     $request->print("<span class=\"LC_error\">".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom")."</span>");      $request->print("<span class=\"LC_error\">".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom")."</span>");
     next;      next;
  } elsif ($message ne '') {   } else {
     my ($baseurl,$showsymb) =                               $pbcollab{$collaborator}{$part} = [$pts,$wgt];
  &get_feedurl_and_symb($symb,$collaborator,                              if ($message ne '') {
       $udom);          my ($baseurl,$showsymb) = 
     if ($env{'form.withgrades'.$ctr}) {      &get_feedurl_and_symb($symb,$collaborator,
  $messagetail = " for <a href=\"".            $udom);
                                     $baseurl."?symb=$showsymb\">$restitle</a>";          if ($env{'form.withgrades'.$ctr}) {
       $messagetail = " for <a href=\"".
                                           $baseurl."?symb=$showsymb\">$restitle</a>";
           }
           $msgstatus =
       &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle);
     }      }
     $msgstatus =           }
  &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message.$messagetail,undef,$baseurl,undef,undef,undef,$showsymb,$restitle);  
  }  
     }      }
  }   }
     }      }
     $ctr++;      $ctr++;
  }   }
           if ((keys(%pbcollab)) && (keys(%needpb))) {
               # FIXME passback scores for collaborators
           }
     }      }
   
 #    if ($env{'form.handgrade'} eq 'yes') {      my %keyhash = ();
     if (1) {      if ($numessay) {
  # Keywords sorted in alphabatical order   # Keywords sorted in alphabatical order
  my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};   my $loginuser = $env{'user.name'}.':'.$env{'user.domain'};
  my %keyhash = ();  
  $env{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;   $env{'form.keywords'}           =~ s/,\s{0,}|\s+/ /g;
  $env{'form.keywords'}           =~ s/^\s+|\s+$//;   $env{'form.keywords'}           =~ s/^\s+|\s+$//g;
  my (@keywords) = sort(split(/\s+/,$env{'form.keywords'}));   my (@keywords) = sort(split(/\s+/,$env{'form.keywords'}));
  $env{'form.keywords'} = join(' ',@keywords);   $env{'form.keywords'} = join(' ',@keywords);
  $keyhash{$symb.'_keywords'}     = $env{'form.keywords'};   $keyhash{$symb.'_keywords'}     = $env{'form.keywords'};
Line 2873  sub processHandGrade { Line 4283  sub processHandGrade {
  $keyhash{$loginuser.'_kwclr'}   = $env{'form.kwclr'};   $keyhash{$loginuser.'_kwclr'}   = $env{'form.kwclr'};
  $keyhash{$loginuser.'_kwsize'}  = $env{'form.kwsize'};   $keyhash{$loginuser.'_kwsize'}  = $env{'form.kwsize'};
  $keyhash{$loginuser.'_kwstyle'} = $env{'form.kwstyle'};   $keyhash{$loginuser.'_kwstyle'} = $env{'form.kwstyle'};
       }
   
       if ($env{'form.compmsg'}) {
  # message center - Order of message gets changed. Blank line is eliminated.   # message center - Order of message gets changed. Blank line is eliminated.
  # New messages are saved in env for the next student.   # New messages are saved in env for the next student.
  # All messages are saved in nohist_handgrade.db   # All messages are saved in nohist_handgrade.db
Line 2888  sub processHandGrade { Line 4300  sub processHandGrade {
  $ctr = 0;   $ctr = 0;
  while ($ctr < $ngrade) {   while ($ctr < $ngrade) {
     if ($env{'form.newmsg'.$ctr} ne '') {      if ($env{'form.newmsg'.$ctr} ne '') {
  $keyhash{$symb.'_savemsg'.$idx} = $env{'form.newmsg'.$ctr};          $keyhash{$symb.'_savemsg'.$idx} = $env{'form.newmsg'.$ctr};
  $env{'form.savemsg'.$idx} = $env{'form.newmsg'.$ctr};          $env{'form.savemsg'.$idx} = $env{'form.newmsg'.$ctr};
  $idx++;          $idx++;
     }      }
     $ctr++;      $ctr++;
  }   }
  $env{'form.savemsgN'} = --$idx;   $env{'form.savemsgN'} = --$idx;
  $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};   $keyhash{$symb.'_savemsgN'} = $env{'form.savemsgN'};
  my $putresult = &Apache::lonnet::put  
     ('nohist_handgrade',\%keyhash,$cdom,$cnum);  
     }      }
       if (($numessay) || ($env{'form.compmsg'})) {
           my $putresult = &Apache::lonnet::put
               ('nohist_handgrade',\%keyhash,$cdom,$cnum);
       }
   
     # Called by Save & Refresh from Highlight Attribute Window      # Called by Save & Refresh from Highlight Attribute Window
     my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');      my (undef,undef,$fullname) = &getclasslist($env{'form.section'},'1');
     if ($env{'form.refresh'} eq 'on') {      if ($env{'form.refresh'} eq 'on') {
Line 2938  sub processHandGrade { Line 4353  sub processHandGrade {
  }   }
  return $a cmp $b;   return $a cmp $b;
      } (keys(%$fullname))) {       } (keys(%$fullname))) {
 # FIXME: this is fishy, looks like the button label  
  if ($nextflg == 1 && $button =~ /Next$/) {   if ($nextflg == 1 && $button =~ /Next$/) {
     push(@parsedlist,$item);      push(@parsedlist,$item);
  }   }
Line 2949  sub processHandGrade { Line 4363  sub processHandGrade {
  }   }
     }      }
     $ctr = 0;      $ctr = 0;
 # FIXME: this is fishy, looks like the button label  
     @parsedlist = reverse @parsedlist if ($button eq 'Previous');      @parsedlist = reverse @parsedlist if ($button eq 'Previous');
     my $res_error;  
     my ($partlist) = &response_type($symb,\$res_error);  
     if ($res_error) {  
         $request->print(&navmap_errormsg());  
         return;  
     }  
     foreach my $student (@parsedlist) {      foreach my $student (@parsedlist) {
  my $submitonly=$env{'form.submitonly'};   my $submitonly=$env{'form.submitonly'};
  my ($uname,$udom) = split(/:/,$student);   my ($uname,$udom) = split(/:/,$student);
Line 3014  sub processHandGrade { Line 4421  sub processHandGrade {
   
 #---- Save the score and award for each student, if changed  #---- Save the score and award for each student, if changed
 sub saveHandGrade {  sub saveHandGrade {
     my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part) = @_;      my ($request,$symb,$stuname,$domain,$newflg,$submitter,$part,$queueable,$needpb,$skip_passback,$pbsave) = @_;
     my @version_parts;      my @version_parts;
     my $usec = &Apache::lonnet::getsection($domain,$stuname,      my $usec = &Apache::lonnet::getsection($domain,$stuname,
    $env{'request.course.id'});     $env{'request.course.id'});
Line 3025  sub saveHandGrade { Line 4432  sub saveHandGrade {
     my ($pts,$wgt,$totchg) = ('','',0);      my ($pts,$wgt,$totchg) = ('','',0);
     my %aggregate = ();      my %aggregate = ();
     my $aggregateflag = 0;      my $aggregateflag = 0;
       my $sendupdate;
     if ($env{'form.HIDE'.$newflg}) {      if ($env{'form.HIDE'.$newflg}) {
         my ($version,$parts) = split(/:/,$env{'form.HIDE'.$newflg},2);          my ($version,$parts) = split(/:/,$env{'form.HIDE'.$newflg},2);
         my $numchgs = &makehidden($version,$parts,\%record,$symb,$domain,$stuname,1);          my $numchgs = &makehidden($version,$parts,\%record,$symb,$domain,$stuname,1);
         $totchg += $numchgs;          $totchg += $numchgs;
           if ($numchgs) {
               $sendupdate = 1;
           }
     }      }
       my (%weights,%awardeds,%excuseds);
     my @parts = split(/:/,$env{'form.partlist'.$newflg});      my @parts = split(/:/,$env{'form.partlist'.$newflg});
     foreach my $new_part (@parts) {      foreach my $new_part (@parts) {
  #collaborator ($submi may vary for different parts   #collaborator ($submi may vary for different parts
  if ($submitter && $new_part ne $part) { next; }   if ($submitter && $new_part ne $part) { next; }
  my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$new_part};   my $dropMenu = $env{'form.GD_SEL'.$newflg.'_'.$new_part};
           if ($env{'form.WGT'.$newflg.'_'.$new_part} eq '') {
               $weights{$symb}{$new_part} = 1;
           } else {
               $weights{$symb}{$new_part} = $env{'form.WGT'.$newflg.'_'.$new_part};
           }
  if ($dropMenu eq 'excused') {   if ($dropMenu eq 'excused') {
               $excuseds{$symb}{$new_part} = 1;
               $awardeds{$symb}{$new_part} = '';
     if ($record{'resource.'.$new_part.'.solved'} ne 'excused') {      if ($record{'resource.'.$new_part.'.solved'} ne 'excused') {
  $newrecord{'resource.'.$new_part.'.solved'} = 'excused';   $newrecord{'resource.'.$new_part.'.solved'} = 'excused';
  if (exists($record{'resource.'.$new_part.'.awarded'})) {   if (exists($record{'resource.'.$new_part.'.awarded'})) {
     $newrecord{'resource.'.$new_part.'.awarded'} = '';      $newrecord{'resource.'.$new_part.'.awarded'} = '';
  }   }
         $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";          $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}";
                   $sendupdate = 1;
     }      }
  } elsif ($dropMenu eq 'reset status'   } elsif ($dropMenu eq 'reset status'
  && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts   && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts
Line 3065  sub saveHandGrade { Line 4485  sub saveHandGrade {
                 &decrement_aggs($symb,$new_part,\%aggregate,$aggtries,$totaltries,$solvedstatus);                  &decrement_aggs($symb,$new_part,\%aggregate,$aggtries,$totaltries,$solvedstatus);
                 $aggregateflag = 1;                  $aggregateflag = 1;
             }              }
               $sendupdate = 1;
               $excuseds{$symb}{$new_part} = '';
               $awardeds{$symb}{$new_part} = '';
  } elsif ($dropMenu eq '') {   } elsif ($dropMenu eq '') {
     $pts = ($env{'form.GD_BOX'.$newflg.'_'.$new_part} ne '' ?       $pts = ($env{'form.GD_BOX'.$newflg.'_'.$new_part} ne '' ? 
     $env{'form.GD_BOX'.$newflg.'_'.$new_part} :       $env{'form.GD_BOX'.$newflg.'_'.$new_part} : 
Line 3075  sub saveHandGrade { Line 4498  sub saveHandGrade {
     $wgt = $env{'form.WGT'.$newflg.'_'.$new_part} eq '' ? 1 :       $wgt = $env{'form.WGT'.$newflg.'_'.$new_part} eq '' ? 1 : 
  $env{'form.WGT'.$newflg.'_'.$new_part};   $env{'form.WGT'.$newflg.'_'.$new_part};
     my $partial= $pts/$wgt;      my $partial= $pts/$wgt;
               $awardeds{$symb}{$new_part} = $partial;
               $excuseds{$symb}{$new_part} = '';
     if ($partial eq $record{'resource.'.$new_part.'.awarded'}) {      if ($partial eq $record{'resource.'.$new_part.'.awarded'}) {
  #do not update score for part if not changed.   #do not update score for part if not changed.
                 &handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord);                  &handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord);
  next;   next;
     } else {      } else {
         push(@parts_graded,$new_part);          push(@parts_graded,$new_part);
                   $sendupdate = 1;
     }      }
     if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {      if ($record{'resource.'.$new_part.'.awarded'} ne $partial) {
  $newrecord{'resource.'.$new_part.'.awarded'}  = $partial;   $newrecord{'resource.'.$new_part.'.awarded'}  = $partial;
Line 3126  sub saveHandGrade { Line 4552  sub saveHandGrade {
  &Apache::lonnet::cstore(\%newrecord,$symb,   &Apache::lonnet::cstore(\%newrecord,$symb,
  $env{'request.course.id'},$domain,$stuname);   $env{'request.course.id'},$domain,$stuname);
  &check_and_remove_from_queue(\@parts,\%record,\%newrecord,$symb,   &check_and_remove_from_queue(\@parts,\%record,\%newrecord,$symb,
      $cdom,$cnum,$domain,$stuname);       $cdom,$cnum,$domain,$stuname,$queueable);
     }      }
     if ($aggregateflag) {      if ($aggregateflag) {
         &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,          &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate,
       $cdom,$cnum);        $cdom,$cnum);
     }      }
       if (($sendupdate) && (!$submitter)) {
           if ((ref($needpb) eq 'HASH') &&
               (keys(%{$needpb}))) {
               &process_passbacks('handgrade',[$symb],$cdom,$cnum,$domain,$stuname,\%weights,
                                  \%awardeds,\%excuseds,$needpb,$skip_passback,$pbsave);
           }
       }
     return ('',$pts,$wgt,$totchg);      return ('',$pts,$wgt,$totchg);
 }  }
   
Line 3166  sub makehidden { Line 4599  sub makehidden {
 }  }
   
 sub check_and_remove_from_queue {  sub check_and_remove_from_queue {
     my ($parts,$record,$newrecord,$symb,$cdom,$cnum,$domain,$stuname) = @_;      my ($parts,$record,$newrecord,$symb,$cdom,$cnum,$domain,$stuname,$queueable) = @_;
     my @ungraded_parts;      my @ungraded_parts;
     foreach my $part (@{$parts}) {      foreach my $part (@{$parts}) {
  if (    $record->{   'resource.'.$part.'.awarded'} eq ''   if (    $record->{   'resource.'.$part.'.awarded'} eq ''
Line 3174  sub check_and_remove_from_queue { Line 4607  sub check_and_remove_from_queue {
      && $newrecord->{'resource.'.$part.'.awarded'} eq ''       && $newrecord->{'resource.'.$part.'.awarded'} eq ''
      && $newrecord->{'resource.'.$part.'.solved' } ne 'excused'       && $newrecord->{'resource.'.$part.'.solved' } ne 'excused'
  ) {   ) {
     push(@ungraded_parts, $part);              if ($queueable->{$part}) {
           push(@ungraded_parts, $part);
               }
  }   }
     }      }
     if ( !@ungraded_parts ) {      if ( !@ungraded_parts ) {
Line 3209  sub handback_files { Line 4644  sub handback_files {
         &Apache::lonnet::file_name_version_ext($answer_file);          &Apache::lonnet::file_name_version_ext($answer_file);
     my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);      my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);
                     my $getpropath = 1;                      my $getpropath = 1;
                     my ($dir_list,$listerror) =                       my ($dir_list,$listerror) =
                         &Apache::lonnet::dirlist($portfolio_root.$portfolio_path,                          &Apache::lonnet::dirlist($portfolio_root.$portfolio_path,
                                                  $domain,$stuname,$getpropath);                                                   $domain,$stuname,$getpropath);
     my $version = &Apache::lonnet::get_next_version($answer_name,$answer_ext,$dir_list);      my $version = &Apache::lonnet::get_next_version($answer_name,$answer_ext,$dir_list);
Line 3373  sub version_portfiles { Line 4808  sub version_portfiles {
             $$record{$key} = join(',',@versioned_portfiles);              $$record{$key} = join(',',@versioned_portfiles);
             push(@returned_keys,$key);              push(@returned_keys,$key);
         }          }
     }       }
     return (@returned_keys);         return (@returned_keys);
 }  }
   
 #--------------------------------------------------------------------------------------  #--------------------------------------------------------------------------------------
Line 3554  VIEWJAVASCRIPT Line 4989  VIEWJAVASCRIPT
 #--- show scores for a section or whole class w/ option to change/update a score  #--- show scores for a section or whole class w/ option to change/update a score
 sub viewgrades {  sub viewgrades {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
       my ($is_tool,$toolsymb);
       if ($symb =~ /ext\.tool$/) {
           $is_tool = 1;
           $toolsymb = $symb;
       }
     &viewgrades_js($request);      &viewgrades_js($request);
   
     #need to make sure we have the correct data for later EXT calls,       #need to make sure we have the correct data for later EXT calls, 
Line 3588  sub viewgrades { Line 5028  sub viewgrades {
     }      }
   
     my ($common_header,$specific_header,@sections,$section_display);      my ($common_header,$specific_header,@sections,$section_display);
     @sections = &Apache::loncommon::get_env_multiple('form.section');      if ($env{'request.course.sec'} ne '') {
           @sections = ($env{'request.course.sec'});
       } else {
           @sections = &Apache::loncommon::get_env_multiple('form.section');
       }
   
   # Check if Save button should be usable
       my $disabled = ' disabled="disabled"';
       if ($perm{'mgr'}) {
           if (grep(/^all$/,@sections)) {
               undef($disabled);
           } else {
               foreach my $sec (@sections) {
                   if (&canmodify($sec)) {
                       undef($disabled);
                       last;
                   }
               }
           }
       }
     if (grep(/^all$/,@sections)) {      if (grep(/^all$/,@sections)) {
         @sections = ('all');          @sections = ('all');
         if ($group_display) {          if ($group_display) {
Line 3634  sub viewgrades { Line 5093  sub viewgrades {
     if ($env{'form.submitonly'} eq 'all') {      if ($env{'form.submitonly'} eq 'all') {
         $result.= '<h3>'.$common_header.'</h3>';          $result.= '<h3>'.$common_header.'</h3>';
     } else {      } else {
         $result.= '<h3>'.$common_header.'&nbsp;'.&mt('(submission status: "[_1]")',$submission_status).'</h3>';          my $text;
           if ($is_tool) {
               $text = &mt('(transaction status: "[_1]")',$submission_status);
           } else {
               $text = &mt('(submission status: "[_1]")',$submission_status);
           }
           $result.= '<h3>'.$common_header.'&nbsp;'.$text.'</h3>';
     }      }
     $result .= &Apache::loncommon::start_data_table();      $result .= &Apache::loncommon::start_data_table();
     #radio buttons/text box for assigning points for a section or class.      #radio buttons/text box for assigning points for a section or class.
Line 3647  sub viewgrades { Line 5112  sub viewgrades {
     my %weight = ();      my %weight = ();
     my $ctsparts = 0;      my $ctsparts = 0;
     my %seen = ();      my %seen = ();
     my @part_response_id = &flatten_responseType($responseType);      my @part_response_id;
       if ($is_tool) {
           @part_response_id = ([0,'']);
       } else {
           @part_response_id = &flatten_responseType($responseType);
       }
     foreach my $part_response_id (@part_response_id) {      foreach my $part_response_id (@part_response_id) {
     my ($partid,$respid) = @{ $part_response_id };      my ($partid,$respid) = @{ $part_response_id };
  my $part_resp = join('_',@{ $part_response_id });   my $part_resp = join('_',@{ $part_response_id });
  next if $seen{$partid};   next if $seen{$partid};
  $seen{$partid}++;   $seen{$partid}++;
  my $handgrade=$$handgrade{$part_resp};  
  my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);   my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb);
  $weight{$partid} = $wgt eq '' ? '1' : $wgt;   $weight{$partid} = $wgt eq '' ? '1' : $wgt;
   
Line 3702  sub viewgrades { Line 5171  sub viewgrades {
     if ($env{'form.submitonly'} eq 'all') {      if ($env{'form.submitonly'} eq 'all') {
         $result.= '<h3>'.$specific_header.'</h3>';          $result.= '<h3>'.$specific_header.'</h3>';
     } else {      } else {
         $result.= '<h3>'.$specific_header.'&nbsp;'.&mt('(submission status: "[_1]")',$submission_status).'</h3>';          my $text;
           if ($is_tool) {
               $text = &mt('(transaction status: "[_1]")',$submission_status);
           } else {
               $text = &mt('(submission status: "[_1]")',$submission_status);
           }
           $result.= '<h3>'.$specific_header.'&nbsp;'.$text.'</h3>';
     }      }
     $result.= &Apache::loncommon::start_data_table().      $result.= &Apache::loncommon::start_data_table().
       &Apache::loncommon::start_data_table_header_row().        &Apache::loncommon::start_data_table_header_row().
Line 3716  sub viewgrades { Line 5191  sub viewgrades {
     my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);      my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
     my @partids = ();      my @partids = ();
     foreach my $part (@parts) {      foreach my $part (@parts) {
  my $display=&Apache::lonnet::metadata($url,$part.'.display');   my $display=&Apache::lonnet::metadata($url,$part.'.display',$toolsymb);
         my $narrowtext = &mt('Tries');          my $narrowtext = &mt('Tries');
  $display =~ s|^Number of Attempts|$narrowtext <br />|; # makes the column narrower   $display =~ s|^Number of Attempts|$narrowtext <br />|; # makes the column narrower
  if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }   if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name',$toolsymb); }
  my ($partid) = &split_part_type($part);   my ($partid) = &split_part_type($part);
         push(@partids,$partid);          push(@partids,$partid);
 #  #
Line 3760  sub viewgrades { Line 5235  sub viewgrades {
  return $a cmp $b;   return $a cmp $b;
      } (keys(%$fullname))) {       } (keys(%$fullname))) {
  $result.=&viewstudentgrade($symb,$env{'request.course.id'},   $result.=&viewstudentgrade($symb,$env{'request.course.id'},
    $_,$$fullname{$_},\@parts,\%weight,\$ctr,\%last_resets);     $_,$$fullname{$_},\@parts,\%weight,\$ctr,\%last_resets,$is_tool);
     }      }
     $result.=&Apache::loncommon::end_data_table();      $result.=&Apache::loncommon::end_data_table();
     $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";      $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";
     $result.='<input type="button" value="'.&mt('Save').'" '.      $result.='<input type="button" value="'.&mt('Save').'"'.$disabled.' '.
  'onclick="javascript:submit();" target="_self" /></form>'."\n";   'onclick="javascript:submit();" target="_self" /></form>'."\n";
     if ($ctr == 0) {      if ($ctr == 0) {
         my $stu_status = join(' or ',&Apache::loncommon::get_env_multiple('form.Status'));          my $stu_status = join(' or ',&Apache::loncommon::get_env_multiple('form.Status'));
Line 3848  sub viewgrades { Line 5323  sub viewgrades {
   
 #--- call by previous routine to display each student who satisfies submission filter.   #--- call by previous routine to display each student who satisfies submission filter. 
 sub viewstudentgrade {  sub viewstudentgrade {
     my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets) = @_;      my ($symb,$courseid,$student,$fullname,$parts,$weight,$ctr,$last_resets,$is_tool) = @_;
     my ($uname,$udom) = split(/:/,$student);      my ($uname,$udom) = split(/:/,$student);
     my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);      my %record=&Apache::lonnet::restore($symb,$courseid,$udom,$uname);
     my $submitonly = $env{'form.submitonly'};      my $submitonly = $env{'form.submitonly'};
Line 3954  sub viewstudentgrade { Line 5429  sub viewstudentgrade {
 #    record does not get update if unchanged  #    record does not get update if unchanged
 sub editgrades {  sub editgrades {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
       my $toolsymb;
       if ($symb =~ /ext\.tool$/) {
           $toolsymb = $symb;
       }
   
     my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));      my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
     my $title='<h2>'.&mt('Current Grade Status').'</h2>';      my $title='<h2>'.&mt('Current Grade Status').'</h2>';
     $title.='<h4>'.&mt('<b>Section: </b>[_1]',$section_display).'</h4>'."\n";      $title.='<h4><b>'.&mt('Section:').'</b> '.$section_display.'</h4>'."\n";
   
     my $result= &Apache::loncommon::start_data_table().      my $result= &Apache::loncommon::start_data_table().
  &Apache::loncommon::start_data_table_header_row().   &Apache::loncommon::start_data_table_header_row().
Line 3973  sub editgrades { Line 5452  sub editgrades {
     );      );
     my ($classlist,undef,$fullname) = &getclasslist($env{'form.section'},'0');      my ($classlist,undef,$fullname) = &getclasslist($env{'form.section'},'0');
   
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
       my %needpb = &passbacks_for_symb($cdom,$cnum,$symb);
   
     my (@partid);      my (@partid);
     my %weight = ();      my %weight = ();
     my %columns = ();      my %columns = ();
Line 3991  sub editgrades { Line 5474  sub editgrades {
  $ctr++;   $ctr++;
     }      }
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
       my $totcolspan = 0;
     foreach my $partid (@partid) {      foreach my $partid (@partid) {
  $header .= '<th align="center">'.&mt('Old Score').'</th>'.   $header .= '<th align="center">'.&mt('Old Score').'</th>'.
     '<th align="center">'.&mt('New Score').'</th>';      '<th align="center">'.&mt('New Score').'</th>';
Line 3999  sub editgrades { Line 5483  sub editgrades {
     my ($part,$type) = &split_part_type($stores);      my ($part,$type) = &split_part_type($stores);
     if ($part !~ m/^\Q$partid\E/) { next;}      if ($part !~ m/^\Q$partid\E/) { next;}
     if ($type eq 'awarded' || $type eq 'solved') { next; }      if ($type eq 'awarded' || $type eq 'solved') { next; }
     my $display=&Apache::lonnet::metadata($url,$stores.'.display');      my $display=&Apache::lonnet::metadata($url,$stores.'.display',$toolsymb);
     $display =~ s/\[Part: \Q$part\E\]//;      $display =~ s/\[Part: \Q$part\E\]//;
             my $narrowtext = &mt('Tries');              my $narrowtext = &mt('Tries');
     $display =~ s/Number of Attempts/$narrowtext/;      $display =~ s/Number of Attempts/$narrowtext/;
Line 4007  sub editgrades { Line 5491  sub editgrades {
  '<th align="center">'.&mt('New').' '.$display.'</th>';   '<th align="center">'.&mt('New').' '.$display.'</th>';
     $columns{$partid}+=2;      $columns{$partid}+=2;
  }   }
           $totcolspan += $columns{$partid};
     }      }
     foreach my $partid (@partid) {      foreach my $partid (@partid) {
  my $display_part=&get_display_part($partid,$symb);   my $display_part=&get_display_part($partid,$symb);
Line 4021  sub editgrades { Line 5506  sub editgrades {
  &Apache::loncommon::end_data_table_header_row();   &Apache::loncommon::end_data_table_header_row();
     my @noupdate;      my @noupdate;
     my ($updateCtr,$noupdateCtr) = (1,1);      my ($updateCtr,$noupdateCtr) = (1,1);
       my ($got_types,%queueable,%pbsave,%skip_passback);
     for ($i=0; $i<$env{'form.total'}; $i++) {      for ($i=0; $i<$env{'form.total'}; $i++) {
  my $line;  
  my $user = $env{'form.ctr'.$i};   my $user = $env{'form.ctr'.$i};
  my ($uname,$udom)=split(/:/,$user);   my ($uname,$udom)=split(/:/,$user);
  my %newrecord;   my %newrecord;
  my $updateflag = 0;   my $updateflag = 0;
  $line .= '<td>'.&nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';  
  my $usec=$classlist->{"$uname:$udom"}[5];   my $usec=$classlist->{"$uname:$udom"}[5];
  if (!&canmodify($usec)) {   my $canmodify = &canmodify($usec);
     my $numcols=scalar(@partid)*4+2;   my $line = '<td'.($canmodify?'':' colspan="2"').'>'.
      &nameUserString(undef,$$fullname{$user},$uname,$udom).'</td>';
    if (!$canmodify) {
     push(@noupdate,      push(@noupdate,
  $line."<td colspan=\"$numcols\"><span class=\"LC_warning\">".   $line."<td colspan=\"$totcolspan\"><span class=\"LC_warning\">".
  &mt('Not allowed to modify student')."</span></td></tr>");   &mt('Not allowed to modify student')."</span></td>");
     next;      next;
  }   }
         my %aggregate = ();          my %aggregate = ();
         my $aggregateflag = 0;          my $aggregateflag = 0;
  $user=~s/:/_/; # colon doen't work in javascript for names   $user=~s/:/_/; # colon doen't work in javascript for names
           my (%weights,%awardeds,%excuseds);
  foreach (@partid) {   foreach (@partid) {
     my $old_aw    = $env{'form.GD_'.$user.'_'.$_.'_awarded_s'};      my $old_aw    = $env{'form.GD_'.$user.'_'.$_.'_awarded_s'};
     my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);      my $old_part_pcr = $old_aw/($weight{$_} ne '0' ? $weight{$_}:1);
Line 4047  sub editgrades { Line 5534  sub editgrades {
     my $awarded   = $env{'form.GD_'.$user.'_'.$_.'_awarded'};      my $awarded   = $env{'form.GD_'.$user.'_'.$_.'_awarded'};
     my $pcr       = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1);      my $pcr       = $awarded/($weight{$_} ne '0' ? $weight{$_} : 1);
     my $partial   = $awarded eq '' ? '' : $pcr;      my $partial   = $awarded eq '' ? '' : $pcr;
               $awardeds{$symb}{$_} = $partial;
     my $score;      my $score;
     if ($partial eq '') {      if ($partial eq '') {
  $score = $scoreptr{$env{'form.GD_'.$user.'_'.$_.'_solved_s'}};   $score = $scoreptr{$env{'form.GD_'.$user.'_'.$_.'_solved_s'}};
Line 4087  sub editgrades { Line 5575  sub editgrades {
   
   
     my $partid=$_;      my $partid=$_;
               if ($score eq 'excused') {
                   $excuseds{$symb}{$partid} = 1;
               } else {
                   $excuseds{$symb}{$partid} = '';
               }
     foreach my $stores (@parts) {      foreach my $stores (@parts) {
  my ($part,$type) = &split_part_type($stores);   my ($part,$type) = &split_part_type($stores);
  if ($part !~ m/^\Q$partid\E/) { next;}   if ($part !~ m/^\Q$partid\E/) { next;}
Line 4104  sub editgrades { Line 5597  sub editgrades {
  }   }
  $line.="\n";   $line.="\n";
   
  my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};  
  my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};  
   
  if ($updateflag) {   if ($updateflag) {
     $count++;      $count++;
     &Apache::lonnet::cstore(\%newrecord,$symb,$env{'request.course.id'},      &Apache::lonnet::cstore(\%newrecord,$symb,$env{'request.course.id'},
Line 4120  sub editgrades { Line 5610  sub editgrades {
      $udom,$uname);       $udom,$uname);
  my $all_graded = 1;   my $all_graded = 1;
  my $none_graded = 1;   my $none_graded = 1;
                   unless ($got_types) {
                       my $error;
                       my ($plist,$handgrd,$resptype) = &response_type($symb,\$error);
                       unless ($error) {
                           foreach my $part (@parts) {
                               if (ref($resptype->{$part}) eq 'HASH') {
                                   foreach my $id (keys(%{$resptype->{$part}})) {
                                       if (($resptype->{$part}->{$id} eq 'essay') ||
                                           (lc($handgrd->{$part.'_'.$id}) eq 'yes')) {
                                           $queueable{$part} = 1;
                                           last;
                                       }
                                   }
                               }
                           }
                       }
                       $got_types = 1;
                   }
  foreach my $part (@parts) {   foreach my $part (@parts) {
     if ( $record{'resource.'.$part.'.awarded'} eq '' ) {                      if ($queueable{$part}) {
  $all_graded = 0;          if ( $record{'resource.'.$part.'.awarded'} eq '' ) {
     } else {      $all_graded = 0;
  $none_graded = 0;          } else {
       $none_graded = 0;
           }
     }      }
  }                  }
   
  if ($all_graded || $none_graded) {   if ($all_graded || $none_graded) {
     &Apache::bridgetask::remove_from_queue('gradingqueue',      &Apache::bridgetask::remove_from_queue('gradingqueue',
    $symb,$cdom,$cnum,     $symb,$cdom,$cnum,
Line 4139  sub editgrades { Line 5648  sub editgrades {
  '<td align="right">&nbsp;'.$updateCtr.'&nbsp;</td>'.$line.   '<td align="right">&nbsp;'.$updateCtr.'&nbsp;</td>'.$line.
  &Apache::loncommon::end_data_table_row();   &Apache::loncommon::end_data_table_row();
     $updateCtr++;      $updateCtr++;
               if (keys(%needpb)) {
                   $weights{$symb} = \%weight;
                   &process_passbacks('editgrades',[$symb],$cdom,$cnum,$udom,$uname,\%weights,
                                      \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave);
               }
  } else {   } else {
     push(@noupdate,      push(@noupdate,
  '<td align="right">&nbsp;'.$noupdateCtr.'&nbsp;</td>'.$line);   '<td align="right">&nbsp;'.$noupdateCtr.'&nbsp;</td>'.$line);
Line 4150  sub editgrades { Line 5664  sub editgrades {
         }          }
     }      }
     if (@noupdate) {      if (@noupdate) {
 # my $numcols=(scalar(@partid)*(scalar(@parts)-1)*2)+3;          my $numcols=$totcolspan+2;
  my $numcols=scalar(@partid)*4+2;  
  $result .= &Apache::loncommon::start_data_table_row('LC_empty_row').   $result .= &Apache::loncommon::start_data_table_row('LC_empty_row').
     '<td align="center" colspan="'.$numcols.'">'.      '<td align="center" colspan="'.$numcols.'">'.
     &mt('No Changes Occurred For the Students Below').      &mt('No Changes Occurred For the Students Below').
Line 4192  sub split_part_type { Line 5705  sub split_part_type {
 #  #
 #--- Javascript to handle csv upload  #--- Javascript to handle csv upload
 sub csvupload_javascript_reverse_associate {  sub csvupload_javascript_reverse_associate {
     my $error1=&mt('You need to specify the username or the student/employee ID');      my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
     my $error2=&mt('You need to specify at least one grading field');      my $error2=&mt('You need to specify at least one grading field');
   &js_escape(\$error1);    &js_escape(\$error1);
   &js_escape(\$error2);    &js_escape(\$error2);
Line 4201  sub csvupload_javascript_reverse_associa Line 5714  sub csvupload_javascript_reverse_associa
     var foundsomething=0;      var foundsomething=0;
     var founduname=0;      var founduname=0;
     var foundID=0;      var foundID=0;
       var foundclicker=0;
     for (i=0;i<=vf.nfields.value;i++) {      for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');        tw=eval('vf.f'+i+'.selectedIndex');
       if (i==0 && tw!=0) { foundID=1; }        if (i==0 && tw!=0) { foundID=1; }
       if (i==1 && tw!=0) { founduname=1; }        if (i==1 && tw!=0) { founduname=1; }
       if (i!=0 && i!=1 && i!=2 && tw!=0) { foundsomething=1; }        if (i==2 && tw!=0) { foundclicker=1; }
         if (i!=0 && i!=1 && i!=2 && i!=3 && tw!=0) { foundsomething=1; }
     }      }
     if (founduname==0 && foundID==0) {      if (founduname==0 && foundID==0 && foundclicker==0) {
  alert('$error1');   alert('$error1');
  return;   return;
     }      }
Line 4234  ENDPICK Line 5749  ENDPICK
 }  }
   
 sub csvupload_javascript_forward_associate {  sub csvupload_javascript_forward_associate {
     my $error1=&mt('You need to specify the username or the student/employee ID');      my $error1=&mt('You need to specify the username, the student/employee ID, or the clicker ID');
     my $error2=&mt('You need to specify at least one grading field');      my $error2=&mt('You need to specify at least one grading field');
   &js_escape(\$error1);    &js_escape(\$error1);
   &js_escape(\$error2);    &js_escape(\$error2);
Line 4243  sub csvupload_javascript_forward_associa Line 5758  sub csvupload_javascript_forward_associa
     var foundsomething=0;      var foundsomething=0;
     var founduname=0;      var founduname=0;
     var foundID=0;      var foundID=0;
       var foundclicker=0;
     for (i=0;i<=vf.nfields.value;i++) {      for (i=0;i<=vf.nfields.value;i++) {
       tw=eval('vf.f'+i+'.selectedIndex');        tw=eval('vf.f'+i+'.selectedIndex');
       if (tw==1) { foundID=1; }        if (tw==1) { foundID=1; }
       if (tw==2) { founduname=1; }        if (tw==2) { founduname=1; }
       if (tw>3) { foundsomething=1; }        if (tw==3) { foundclicker=1; }
         if (tw>4) { foundsomething=1; }
     }      }
     if (founduname==0 && foundID==0) {      if (founduname==0 && foundID==0 && Æ’oundclicker==0) {
  alert('$error1');   alert('$error1');
  return;   return;
     }      }
Line 4307  ENDPICK Line 5824  ENDPICK
   
 sub csvupload_fields {  sub csvupload_fields {
     my ($symb,$errorref) = @_;      my ($symb,$errorref) = @_;
       my $toolsymb;
       if ($symb =~ /ext\.tool$/) {
           $toolsymb = $symb;
       }
     my (@parts) = &getpartlist($symb,$errorref);      my (@parts) = &getpartlist($symb,$errorref);
     if (ref($errorref)) {      if (ref($errorref)) {
         if ($$errorref) {          if ($$errorref) {
Line 4315  sub csvupload_fields { Line 5836  sub csvupload_fields {
     }      }
   
     my @fields=(['ID','Student/Employee ID'],      my @fields=(['ID','Student/Employee ID'],
                 ['clicker','Clicker ID'],  
  ['username','Student Username'],   ['username','Student Username'],
    ['clicker','Clicker ID'],
  ['domain','Student Domain']);   ['domain','Student Domain']);
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);      my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
     foreach my $part (sort(@parts)) {      foreach my $part (sort(@parts)) {
  my @datum;   my @datum;
  my $display=&Apache::lonnet::metadata($url,$part.'.display');   my $display=&Apache::lonnet::metadata($url,$part.'.display',$toolsymb);
  my $name=$part;   my $name=$part;
  if  (!$display) { $display = $name; }   if (!$display) { $display = $name; }
  @datum=($name,$display);   @datum=($name,$display);
  if ($name=~/^stores_(.*)_awarded/) {   if ($name=~/^stores_(.*)_awarded/) {
     push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);      push(@fields,['stores_'.$1.'_points',"Points [Part: $1]"]);
Line 4391  ENDUPFORM Line 5912  ENDUPFORM
   
   
 sub csvuploadmap {  sub csvuploadmap {
     my ($request,$symb)= @_;      my ($request,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
     my $datatoken;      my $datatoken;
     if (!$env{'form.datatoken'}) {      if (!$env{'form.datatoken'}) {
  $datatoken=&Apache::loncommon::upfile_store($request);   $datatoken=&Apache::loncommon::upfile_store($request);
     } else {      } else {
  $datatoken=$env{'form.datatoken'};   $datatoken=&Apache::loncommon::valid_datatoken($env{'form.datatoken'});
  &Apache::loncommon::load_tmp_file($request);          if ($datatoken ne '') {
       &Apache::loncommon::load_tmp_file($request,$datatoken);
           }
     }      }
     my @records=&Apache::loncommon::upfile_record_sep();      my @records=&Apache::loncommon::upfile_record_sep();
     &csvuploadmap_header($request,$symb,$datatoken,$#records+1);      &csvuploadmap_header($request,$symb,$datatoken,$#records+1);
Line 4485  sub get_fields { Line 6008  sub get_fields {
 }  }
   
 sub csvuploadassign {  sub csvuploadassign {
     my ($request,$symb)= @_;      my ($request,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $error_msg = '';      my $error_msg = '';
     &Apache::loncommon::load_tmp_file($request);      my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'});
       if ($datatoken ne '') { 
           &Apache::loncommon::load_tmp_file($request,$datatoken);
       }
     my @gradedata = &Apache::loncommon::upfile_record_sep();      my @gradedata = &Apache::loncommon::upfile_record_sep();
     my %fields=&get_fields();      my %fields=&get_fields();
     my $courseid=$env{'request.course.id'};      my $courseid=$env{'request.course.id'};
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
     my ($classlist) = &getclasslist('all',0);      my ($classlist) = &getclasslist('all',0);
     my @notallowed;      my @notallowed;
     my @skipped;      my @skipped;
     my @warnings;      my @warnings;
     my $countdone=0;      my $countdone=0;
       my @parts;
       my %needpb = &passbacks_for_symb($cdom,$cnum,$symb);
       my $passback;
       if (keys(%needpb)) {
           $passback = 1;
           my $navmap = Apache::lonnavmaps::navmap->new();
           if (ref($navmap)) {
               my $res = $navmap->getBySymb($symb);
               if (ref($res)) {
                   my $partlist = $res->parts();
                   if (ref($partlist) eq 'ARRAY') {
                       @parts = sort(@{$partlist});
                   }
               }
           } else {
               return &navmap_errormsg();
           }
       }
       my (%skip_passback,%pbsave,%weights,%awardeds,%excuseds);
   
     foreach my $grade (@gradedata) {      foreach my $grade (@gradedata) {
  my %entries=&Apache::loncommon::record_sep($grade);   my %entries=&Apache::loncommon::record_sep($grade);
  my $domain;   my $domain;
Line 4571  sub csvuploadassign { Line 6119  sub csvuploadassign {
  my $part=$1;   my $part=$1;
  my $wgt =&Apache::lonnet::EXT('resource.'.$part.'.weight',   my $wgt =&Apache::lonnet::EXT('resource.'.$part.'.weight',
       $symb,$domain,$username);        $symb,$domain,$username);
                   $weights{$symb}{$part} = $wgt;
                 if ($wgt) {                  if ($wgt) {
                     $entries{$fields{$dest}}=~s/\s//g;                      $entries{$fields{$dest}}=~s/\s//g;
                     my $pcr=$entries{$fields{$dest}} / $wgt;                      my $pcr=$entries{$fields{$dest}} / $wgt;
                       if ($passback) {
                           $awardeds{$symb}{$part} = $pcr;
                           $excuseds{$symb}{$part} = '';
                       }
                     my $award=($pcr == 0) ? 'incorrect_by_override'                      my $award=($pcr == 0) ? 'incorrect_by_override'
                                           : 'correct_by_override';                                            : 'correct_by_override';
                     if ($pcr>1) {                      if ($pcr>1) {
Line 4593  sub csvuploadassign { Line 6146  sub csvuploadassign {
  if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }   if ($dest=~/stores_(.*)_awarded/) { if ($points{$1}) {next;} }
  if ($dest=~/stores_(.*)_solved/)  { if ($points{$1}) {next;} }   if ($dest=~/stores_(.*)_solved/)  { if ($points{$1}) {next;} }
  my $store_key=$dest;   my $store_key=$dest;
                   if ($passback) {
                       if ($store_key=~/stores_(.*)_(awarded|solved)/) {
                           my ($part,$key) = ($1,$2);
                           unless ((ref($weights{$symb}) eq 'HASH') && (exists($weights{$symb}{$part}))) {
                               $weights{$symb}{$part} = &Apache::lonnet::EXT('resource.'.$part.'.weight',
                                                                             $symb,$domain,$username);
                           }
                           if ($key eq 'awarded') {
                               $awardeds{$symb}{$part} = $entries{$fields{$dest}};
                           } elsif ($key eq 'solved') {
                               if ($entries{$fields{$dest}} =~ /^excused/) {
                                   $excuseds{$symb}{$part} = 1;
                               }
                           }
                       }
                   }
  $store_key=~s/^stores/resource/;   $store_key=~s/^stores/resource/;
  $store_key=~s/_/\./g;   $store_key=~s/_/\./g;
  $grades{$store_key}=$entries{$fields{$dest}};   $grades{$store_key}=$entries{$fields{$dest}};
     }      }
  }   }
  if (! %grades) {    if (! %grades) {
            push(@skipped,&mt("[_1]: no data to save","$username:$domain"));              push(@skipped,&mt("[_1]: no data to save","$username:$domain")); 
         } else {          } else {
    $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";     $grades{"resource.regrader"}="$env{'user.name'}:$env{'user.domain'}";
Line 4609  sub csvuploadassign { Line 6178  sub csvuploadassign {
 # Successfully stored  # Successfully stored
       $request->print('.');        $request->print('.');
 # Remove from grading queue  # Remove from grading queue
               &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,                &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,$cdom,$cnum,
                                              $env{'course.'.$env{'request.course.id'}.'.domain'},       $domain,$username);
                                              $env{'course.'.$env{'request.course.id'}.'.num'},  
                                              $domain,$username);  
               $countdone++;                $countdone++;
                 if ($passback) {
                     my @parts_in_upload;
                     if (ref($weights{$symb}) eq 'HASH') {
                         @parts_in_upload = sort(keys(%{$weights{$symb}}));
                     }
                     my @diffs = &Apache::loncommon::compare_arrays(\@parts_in_upload,\@parts);
                     if (@diffs > 0) {
                         my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$domain,$username);
                         foreach my $part (@parts) {
                             next if (grep(/^\Q$part\E$/,@parts_in_upload));
                             $weights{$symb}{$part} = &Apache::lonnet::EXT('resource.'.$part.'.weight',
                                                                           $symb,$domain,$username);
                             if ($record{"resource.$part.solved"} =~/^excused/) {
                                 $excuseds{$symb}{$part} = 1;
                             } else {
                                 $excuseds{$symb}{$part} = '';
                             }
                             $awardeds{$symb}{$part} = $record{"resource.$part.awarded"};
                         }
                     }
                     &process_passbacks('csvupload',[$symb],$cdom,$cnum,$domain,$username,\%weights,
                                        \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave);
                 }
            } else {             } else {
       $request->print("<p><span class=\"LC_error\">".        $request->print("<p><span class=\"LC_error\">".
                               &mt("Failed to save data for student [_1]. Message when trying to save was: [_2]",                                &mt("Failed to save data for student [_1]. Message when trying to save was: [_2]",
Line 4669  LISTJAVASCRIPT Line 6259  LISTJAVASCRIPT
     my $cdom      = $env{"course.$env{'request.course.id'}.domain"};      my $cdom      = $env{"course.$env{'request.course.id'}.domain"};
     my $cnum      = $env{"course.$env{'request.course.id'}.num"};      my $cnum      = $env{"course.$env{'request.course.id'}.num"};
     my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};      my $getsec    = $env{'form.section'} eq '' ? 'all' : $env{'form.section'};
       my $getgroup  = $env{'form.group'} eq '' ? 'all' : $env{'form.group'};
   
     my $result='<h3><span class="LC_info">&nbsp;'.      my $result='<h3><span class="LC_info">&nbsp;'.
  &mt('Manual Grading by Page or Sequence').'</span></h3>';   &mt('Manual Grading by Page or Sequence').'</span></h3>';
Line 4758  LISTJAVASCRIPT Line 6349  LISTJAVASCRIPT
  '<th>'.&nameUserString('header').'</th>'.   '<th>'.&nameUserString('header').'</th>'.
  &Apache::loncommon::end_data_table_header_row();   &Apache::loncommon::end_data_table_header_row();
     
     my (undef,undef,$fullname) = &getclasslist($getsec,'1');      my (undef,undef,$fullname) = &getclasslist($getsec,'1',$getgroup);
     my $ptr = 1;      my $ptr = 1;
     foreach my $student (sort       foreach my $student (sort 
  {   {
Line 4808  sub getSymbMap { Line 6399  sub getSymbMap {
     my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },      my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map(); },
        1,0,1);         1,0,1);
     for my $sequence ($navmap->getById('0.0'), @sequences) {      for my $sequence ($navmap->getById('0.0'), @sequences) {
  if ($navmap->hasResource($sequence, sub { shift->is_problem(); }, 0) ) {   if ($navmap->hasResource($sequence, sub { shift->is_gradable(); }, 0) ) {
     my $title = $minder.'.'.      my $title = $minder.'.'.
  &HTML::Entities::encode($sequence->compTitle(),'"\'&');   &HTML::Entities::encode($sequence->compTitle(),'"\'&');
     push(@titles, $title); # minder in case two titles are identical      push(@titles, $title); # minder in case two titles are identical
Line 4905  sub displayPage { Line 6496  sub displayPage {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }          if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }          if($curRes == $iterator->END_MAP) { $depth--; }
   
         if (ref($curRes) && $curRes->is_problem()) {          if (ref($curRes) && $curRes->is_gradable()) {
     my $parts = $curRes->parts();      my $parts = $curRes->parts();
             my $title = $curRes->compTitle();              my $title = $curRes->compTitle();
     my $symbx = $curRes->symb();      my $symbx = $curRes->symb();
               my $is_tool = ($symbx =~ /ext\.tool$/);
     $studentTable.=      $studentTable.=
  &Apache::loncommon::start_data_table_row().   &Apache::loncommon::start_data_table_row().
  '<td align="center" valign="top" >'.$prob.   '<td align="center" valign="top" >'.$prob.
Line 4919  sub displayPage { Line 6511  sub displayPage {
  '</td>';   '</td>';
     $studentTable.='<td valign="top">';      $studentTable.='<td valign="top">';
     my %form = ('CODE' => $env{'form.CODE'},);      my %form = ('CODE' => $env{'form.CODE'},);
     if ($env{'form.vProb'} eq 'yes' ) {              if ($is_tool) {
  $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,                  $studentTable.='&nbsp;<b>'.$title.'</b><br />';
      undef,'both',\%form);              } else {
     } else {          if ($env{'form.vProb'} eq 'yes' ) {
  my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);      $studentTable.=&show_problem($request,$symbx,$uname,$udom,1,
  $companswer =~ s|<form(.*?)>||g;           undef,'both',\%form);
  $companswer =~ s|</form>||g;          } else {
 # while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>      my $companswer = &Apache::loncommon::get_student_answers($symbx,$uname,$udom,$env{'request.course.id'},%form);
 #    $companswer =~ s/$1/ /ms;      $companswer =~ s|<form(.*?)>||g;
 #    $request->print('match='.$1."<br />\n");      $companswer =~ s|</form>||g;
 # }  #    while ($companswer =~ /(<a href\=\"javascript:newWindow.*?Script Vars<\/a>)/s) { #<a href="javascript:newWindow</a>
 # $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;  #        $companswer =~ s/$1/ /ms;
  $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>'.&mt('Correct answer').':</b><br />'.$companswer;  #        $request->print('match='.$1."<br />\n");
   #    }
   #    $companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
       $studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;<b>'.&mt('Correct answer').':</b><br />'.$companswer;
    }
     }      }
   
     my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);      my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
   
     if ($env{'form.lastSub'} eq 'datesub') {      if ($env{'form.lastSub'} eq 'datesub') {
  if ($record{'version'} eq '') {   if ($record{'version'} eq '') {
     $studentTable.='<br />&nbsp;<span class="LC_warning">'.&mt('No recorded submission for this problem.').'</span><br />';                      my $msg = &mt('No recorded submission for this problem.');
                       if ($is_tool) {
                           $msg = &mt('No recorded transactions for this external tool');
                       }
       $studentTable.='<br />&nbsp;<span class="LC_warning">'.$msg.'</span><br />';
  } else {   } else {
     my %responseType = ();      my %responseType = ();
     foreach my $partid (@{$parts}) {      foreach my $partid (@{$parts}) {
Line 4951  sub displayPage { Line 6551  sub displayPage {
  $responseType{$partid} = \%responseIds;   $responseType{$partid} = \%responseIds;
     }      }
     $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);      $studentTable.= &displaySubByDates($symbx,\%record,$parts,\%responseType,$checkIcon,$uname,$udom);
   
  }   }
     } elsif ($env{'form.lastSub'} eq 'all') {      } elsif ($env{'form.lastSub'} eq 'all') {
  my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');   my $last = ($env{'form.lastSub'} eq 'last' ? 'last' : '');
Line 4977  sub displayPage { Line 6576  sub displayPage {
  }   }
         $curRes = $iterator->next();          $curRes = $iterator->next();
     }      }
       my $disabled;
       unless (&canmodify($usec)) {
           $disabled = ' disabled="disabled"';
       }
   
     $studentTable.=      $studentTable.=
         '</table>'."\n".          '</table>'."\n".
         '<input type="button" value="'.&mt('Save').'" '.          '<input type="button" value="'.&mt('Save').'"'.$disabled.' '.
         'onclick="javascript:checkSubmitPage(this.form,'.$question.');" />'.          'onclick="javascript:checkSubmitPage(this.form,'.$question.');" />'.
         '</form>'."\n";          '</form>'."\n";
     $request->print($studentTable);      $request->print($studentTable);
Line 4992  sub displaySubByDates { Line 6595  sub displaySubByDates {
     my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;      my ($symb,$record,$parts,$responseType,$checkIcon,$uname,$udom) = @_;
     my $isCODE=0;      my $isCODE=0;
     my $isTask = ($symb =~/\.task$/);      my $isTask = ($symb =~/\.task$/);
       my $is_tool = ($symb =~/\.tool$/);
     if (exists($record->{'resource.CODE'})) { $isCODE=1; }      if (exists($record->{'resource.CODE'})) { $isCODE=1; }
     my $studentTable=&Apache::loncommon::start_data_table().      my $studentTable=&Apache::loncommon::start_data_table().
  &Apache::loncommon::start_data_table_header_row().   &Apache::loncommon::start_data_table_header_row().
  '<th>'.&mt('Date/Time').'</th>'.   '<th>'.&mt('Date/Time').'</th>'.
  ($isCODE?'<th>'.&mt('CODE').'</th>':'').   ($isCODE?'<th>'.&mt('CODE').'</th>':'').
         ($isTask?'<th>'.&mt('Version').'</th>':'').          ($isTask?'<th>'.&mt('Version').'</th>':'').
  '<th>'.&mt('Submission').'</th>'.   '<th>'.($is_tool?&mt('Grade'):&mt('Submission')).'</th>'.
  '<th>'.&mt('Status').'</th>'.   '<th>'.&mt('Status').'</th>'.
  &Apache::loncommon::end_data_table_header_row();   &Apache::loncommon::end_data_table_header_row();
     my ($version);      my ($version);
Line 5006  sub displaySubByDates { Line 6610  sub displaySubByDates {
     my %orders;      my %orders;
     $mark{'correct_by_student'} = $checkIcon;      $mark{'correct_by_student'} = $checkIcon;
     if (!exists($$record{'1:timestamp'})) {      if (!exists($$record{'1:timestamp'})) {
  return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';          if ($is_tool) {
               return '<br />&nbsp;<span class="LC_warning">'.&mt('No grade passed back.').'</span><br />';
           } else {
               return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';
           }
     }      }
   
     my $interaction;      my $interaction;
Line 5039  sub displaySubByDates { Line 6647  sub displaySubByDates {
             if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {              if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {
                 $hidden = 1;                  $hidden = 1;
             }              }
     my @matchKey = ($isTask ? sort(grep /^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys)              my @matchKey;
             : sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));              if ($isTask) {
                       @matchKey = sort(grep(/^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys));
               } elsif ($is_tool) {
                   @matchKey = sort(grep(/^resource\.\Q$partid\E\.awarded$/,@versionKeys));
               } else {
                   @matchKey = sort(grep(/^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));
               }
 #    next if ($$record{"$version:resource.$partid.solved"} eq '');  #    next if ($$record{"$version:resource.$partid.solved"} eq '');
     my $display_part=&get_display_part($partid,$symb);      my $display_part=&get_display_part($partid,$symb);
     foreach my $matchKey (@matchKey) {      foreach my $matchKey (@matchKey) {
  if (exists($$record{$version.':'.$matchKey}) &&   if (exists($$record{$version.':'.$matchKey}) &&
     $$record{$version.':'.$matchKey} ne '') {      $$record{$version.':'.$matchKey} ne '') {
                                           if ($is_tool) {
     my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)                          $displaySub[0].=$$record{"$version:resource.$partid.awarded"};
                : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));  
                     $displaySub[0].='<span class="LC_nobreak">';  
                     $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'  
                                    .' <span class="LC_internal_info">'  
                                    .'('.&mt('Response ID: [_1]',$responseId).')'  
                                    .'</span>'  
                                    .' <b>';  
                     if ($hidden) {  
                         $displaySub[0].= &mt('Anonymous Survey').'</b>';  
                     } else {                      } else {
                         my ($trial,$rndseed,$newvariation);          my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
                         if ($type eq 'randomizetry') {                     : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
                             $trial = $$record{"$where.$partid.tries"};                          $displaySub[0].='<span class="LC_nobreak">';
                             $rndseed = $$record{"$where.$partid.rndseed"};                          $displaySub[0].='<b>'.&mt('Part: [_1]',$display_part).'</b>'
                         }                                         .' <span class="LC_internal_info">'
         if ($$record{"$where.$partid.tries"} eq '') {                                         .'('.&mt('Response ID: [_1]',$responseId).')'
     $displaySub[0].=&mt('Trial not counted');                                         .'</span>'
         } else {                                         .' <b>';
     $displaySub[0].=&mt('Trial: [_1]',                          if ($hidden) {
     $$record{"$where.$partid.tries"});                              $displaySub[0].= &mt('Anonymous Survey').'</b>';
                             if (($rndseed ne '') && ($lastrndseed{$partid} ne '')) {                          } else {
                                 if (($rndseed ne $lastrndseed{$partid}) &&                              my ($trial,$rndseed,$newvariation);
                                     (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) {                              if ($type eq 'randomizetry') {
                                     $newvariation = '&nbsp;('.&mt('New variation this try').')';                                  $trial = $$record{"$where.$partid.tries"};
                                 }                                  $rndseed = $$record{"$where.$partid.rndseed"};
                             }                              }
                             $lastrndseed{$partid} = $rndseed;              if ($$record{"$where.$partid.tries"} eq '') {
                             $lasttype{$partid} = $type;          $displaySub[0].=&mt('Trial not counted');
         }              } else {
         my $responseType=($isTask ? 'Task'          $displaySub[0].=&mt('Trial: [_1]',
           $$record{"$where.$partid.tries"});
                                   if (($rndseed ne '') && ($lastrndseed{$partid} ne '')) {
                                       if (($rndseed ne $lastrndseed{$partid}) &&
                                           (($type eq 'randomizetry') || ($lasttype{$partid} eq 'randomizetry'))) {
                                           $newvariation = '&nbsp;('.&mt('New variation this try').')';
                                       }
                                   }
                                   $lastrndseed{$partid} = $rndseed;
                                   $lasttype{$partid} = $type;
               }
               my $responseType=($isTask ? 'Task'
                                               : $responseType->{$partid}->{$responseId});                                                : $responseType->{$partid}->{$responseId});
         if (!exists($orders{$partid})) { $orders{$partid}={}; }              if (!exists($orders{$partid})) { $orders{$partid}={}; }
         if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {              if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {
     $orders{$partid}->{$responseId}=          $orders{$partid}->{$responseId}=
         &get_order($partid,$responseId,$symb,$uname,$udom,              &get_order($partid,$responseId,$symb,$uname,$udom,
                                            $no_increment,$type,$trial,$rndseed);                                                 $no_increment,$type,$trial,$rndseed);
         }              }
         $displaySub[0].='</b>'.$newvariation.'</span>'; # /nobreak              $displaySub[0].='</b>'.$newvariation.'</span>'; # /nobreak
         $displaySub[0].='&nbsp; '.              $displaySub[0].='&nbsp; '.
     &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).'<br />';          &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).'<br />';
                           }
                     }                      }
  }   }
     }      }
Line 5103  sub displaySubByDates { Line 6719  sub displaySubByDates {
     lc($$record{"$where.$partid.award"}).' '.      lc($$record{"$where.$partid.award"}).' '.
     $mark{$$record{"$where.$partid.solved"}}.      $mark{$$record{"$where.$partid.solved"}}.
     '<br />';      '<br />';
       } elsif (($is_tool) && (exists($$record{"$version:resource.$partid.solved"}))) {
    if ($$record{"$version:resource.$partid.solved"} =~ /^(in|)correct_by_passback$/) {
       $displaySub[1].=&mt('Grade passed back by external tool');
    }
     }      }
     if (exists $$record{"$where.$partid.regrader"}) {      if (exists $$record{"$where.$partid.regrader"}) {
  $displaySub[2].=$$record{"$where.$partid.regrader"}.   $displaySub[2].=$$record{"$where.$partid.regrader"};
     ' (<b>'.&mt('Part').':</b> '.$display_part.')';   unless ($is_tool) {
       $displaySub[2].=' (<b>'.&mt('Part').':</b> '.$display_part.')';
    }
     } elsif ($$record{"$version:resource.$partid.regrader"} =~ /\S/) {      } elsif ($$record{"$version:resource.$partid.regrader"} =~ /\S/) {
  $displaySub[2].=   $displaySub[2].=
     $$record{"$version:resource.$partid.regrader"}.      $$record{"$version:resource.$partid.regrader"};
     ' (<b>'.&mt('Part').':</b> '.$display_part.')';                  unless ($is_tool) {
       $displaySub[2].=' (<b>'.&mt('Part').':</b> '.$display_part.')';
                   }
     }      }
  }   }
  # needed because old essay regrader has not parts info   # needed because old essay regrader has not parts info
Line 5175  sub updateGradeByPage { Line 6799  sub updateGradeByPage {
     $iterator->next(); # skip the first BEGIN_MAP      $iterator->next(); # skip the first BEGIN_MAP
     my $curRes = $iterator->next(); # for "current resource"      my $curRes = $iterator->next(); # for "current resource"
     my ($depth,$question,$prob,$changeflag,$hideflag)= (1,1,1,0,0);      my ($depth,$question,$prob,$changeflag,$hideflag)= (1,1,1,0,0);
       my (@updates,%weights,%excuseds,%awardeds,@symbs_in_map);
     while ($depth > 0) {      while ($depth > 0) {
         if($curRes == $iterator->BEGIN_MAP) { $depth++; }          if($curRes == $iterator->BEGIN_MAP) { $depth++; }
         if($curRes == $iterator->END_MAP) { $depth--; }          if($curRes == $iterator->END_MAP) { $depth--; }
Line 5183  sub updateGradeByPage { Line 6808  sub updateGradeByPage {
     my $parts = $curRes->parts();      my $parts = $curRes->parts();
             my $title = $curRes->compTitle();              my $title = $curRes->compTitle();
     my $symbx = $curRes->symb();      my $symbx = $curRes->symb();
               push(@symbs_in_map,$symbx);
     $studentTable.=      $studentTable.=
  &Apache::loncommon::start_data_table_row().   &Apache::loncommon::start_data_table_row().
  '<td align="center" valign="top" >'.$prob.   '<td align="center" valign="top" >'.$prob.
Line 5195  sub updateGradeByPage { Line 6821  sub updateGradeByPage {
     my @displayPts=();      my @displayPts=();
             my %aggregate = ();              my %aggregate = ();
             my $aggregateflag = 0;              my $aggregateflag = 0;
               my %queueable;
             if ($env{'form.HIDE'.$prob}) {              if ($env{'form.HIDE'.$prob}) {
                 my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);                  my %record = &Apache::lonnet::restore($symbx,$env{'request.course.id'},$udom,$uname);
                 my ($version,$parts) = split(/:/,$env{'form.HIDE'.$prob},2);                  my ($version,$parts) = split(/:/,$env{'form.HIDE'.$prob},2);
                 my $numchgs = &makehidden($version,$parts,\%record,$symbx,$udom,$uname,1);                  my $numchgs = &makehidden($version,$parts,\%record,$symbx,$udom,$uname,1);
                   if ($numchgs) {
                       push(@updates,$symbx);
                   }
                 $hideflag += $numchgs;                  $hideflag += $numchgs;
             }              }
     foreach my $partid (@{$parts}) {      foreach my $partid (@{$parts}) {
  my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid};   my $newpts = $env{'form.GD_BOX'.$question.'_'.$partid};
  my $oldpts = $env{'form.oldpts'.$question.'_'.$partid};   my $oldpts = $env{'form.oldpts'.$question.'_'.$partid};
                   my @types = $curRes->responseType($partid);
                   if (grep(/^essay$/,@types)) {
                       $queueable{$partid} = 1;
                   } else {
                       my @ids = $curRes->responseIds($partid);
                       for (my $i=0; $i < scalar(@ids); $i++) {
                           my $hndgrd = &Apache::lonnet::EXT('resource.'.$partid.'_'.$ids[$i].
                                                             '.handgrade',$symb);
                           if (lc($hndgrd) eq 'yes') {
                               $queueable{$partid} = 1;
                               last;
                           }
                       }
                   }
  my $wgt = $env{'form.WGT'.$question.'_'.$partid} != 0 ?    my $wgt = $env{'form.WGT'.$question.'_'.$partid} != 0 ? 
     $env{'form.WGT'.$question.'_'.$partid} : 1;      $env{'form.WGT'.$question.'_'.$partid} : 1;
                   $weights{$symbx}{$partid} = $wgt;
                   $excuseds{$symbx}{$partid} = '';
  my $partial = $newpts/$wgt;   my $partial = $newpts/$wgt;
  my $score;   my $score;
  if ($partial > 0) {   if ($partial > 0) {
Line 5218  sub updateGradeByPage { Line 6863  sub updateGradeByPage {
  if ($dropMenu eq 'excused') {   if ($dropMenu eq 'excused') {
     $partial = '';      $partial = '';
     $score = 'excused';      $score = 'excused';
                       $excuseds{$symbx}{$partid} = 1;
  } elsif ($dropMenu eq 'reset status'   } elsif ($dropMenu eq 'reset status'
  && $env{'form.solved'.$question.'_'.$partid} ne '') { #update only if previous record exists   && $env{'form.solved'.$question.'_'.$partid} ne '') { #update only if previous record exists
     $newrecord{'resource.'.$partid.'.tries'} = 0;      $newrecord{'resource.'.$partid.'.tries'} = 0;
Line 5245  sub updateGradeByPage { Line 6891  sub updateGradeByPage {
      (($score eq 'excused') ? 'excused' : $newpts).       (($score eq 'excused') ? 'excused' : $newpts).
     '&nbsp;<br />';      '&nbsp;<br />';
  $question++;   $question++;
                   if (($newpts eq '') || ($partial eq '')) {
                       $awardeds{$symbx}{$partid} = 0;
                   } else {
                       $awardeds{$symbx}{$partid} = $partial;
                   }
  next if ($dropMenu eq 'reset status' || ($newpts eq $oldpts && $score ne 'excused'));   next if ($dropMenu eq 'reset status' || ($newpts eq $oldpts && $score ne 'excused'));
   
  $newrecord{'resource.'.$partid.'.awarded'}  = $partial if $partial ne '';   $newrecord{'resource.'.$partid.'.awarded'}  = $partial if $partial ne '';
Line 5270  sub updateGradeByPage { Line 6921  sub updateGradeByPage {
    $env{'request.course.id'},     $env{'request.course.id'},
    $udom,$uname);     $udom,$uname);
  &check_and_remove_from_queue($parts,\%record,undef,$symbx,   &check_and_remove_from_queue($parts,\%record,undef,$symbx,
      $cdom,$cnum,$udom,$uname);       $cdom,$cnum,$udom,$uname,\%queueable);
     }      }
           
             if ($aggregateflag) {              if ($aggregateflag) {
Line 5284  sub updateGradeByPage { Line 6935  sub updateGradeByPage {
  &Apache::loncommon::end_data_table_row();   &Apache::loncommon::end_data_table_row();
   
     $prob++;      $prob++;
               if ($changeflag) {
                   push(@updates,$symbx);
               }
  }   }
         $curRes = $iterator->next();          $curRes = $iterator->next();
     }      }
Line 5297  sub updateGradeByPage { Line 6951  sub updateGradeByPage {
                      $hideflag).'<br />');                       $hideflag).'<br />');
     $request->print($hidemsg.$grademsg.$studentTable);      $request->print($hidemsg.$grademsg.$studentTable);
   
       if (@updates) {
           my (@allsymbs,$mapsymb,@recurseup,%parentmapsymbs,%possmappb,%possrespb);
           @allsymbs = @updates;
           if (ref($map)) {
               $mapsymb = $map->symb();
               push(@allsymbs,$mapsymb);
               @recurseup = $navmap->recurseup_maps($map->src,1);
           }
           if (@recurseup) {
               push(@allsymbs,@recurseup);
               map { $parentmapsymbs{$_} = 1; } @recurseup;
           }
           my %passback = &Apache::lonnet::get('nohist_linkprot_passback',\@allsymbs,$cdom,$cnum);
           my (%uniqsymbs,$use_symbs_in_map);
           if (keys(%passback)) {
               foreach my $possible (keys(%passback)) {
                   if (ref($passback{$possible}) eq 'HASH') {
                       if ($possible eq $mapsymb) {
                           foreach my $launcher (keys(%{$passback{$possible}})) {
                               $possmappb{$launcher} = 1;
                           }
                           $use_symbs_in_map = 1;
                       } elsif (exists($parentmapsymbs{$possible})) {
                           foreach my $launcher (keys(%{$passback{$possible}})) {
                               my ($linkuri,$linkprotector,$scope) = split(/\0/,$launcher);
                               if ($scope eq 'rec') {
                                   $possmappb{$launcher} = 1;
                                   $use_symbs_in_map = 1;
                               }
                           }
                       } elsif (grep(/^\Q$possible$\E$/,@updates)) {
                           foreach my $launcher (keys(%{$passback{$possible}})) {
                               $possrespb{$launcher} = 1;
                           }
                           $uniqsymbs{$possible} = 1;
                       }
                   }
               }
           }
           if ($use_symbs_in_map) {
               map { $uniqsymbs{$_} = 1; } @symbs_in_map;
           }
           my @posslaunchers;
           if (keys(%possmappb)) {
               push(@posslaunchers,keys(%possmappb));
           }
           if (keys(%possrespb)) {
               push(@posslaunchers,keys(%possrespb));
           }
           if (@posslaunchers) {
               my (%pbsave,%skip_passback,%needpb);
               my %pbids = &Apache::lonnet::get('nohist_'.$cdom.'_'.$cnum.'_linkprot_pb',\@posslaunchers,$udom,$uname);
               foreach my $key (keys(%pbids)) {
                   if (ref($pbids{$key}) eq 'ARRAY') {
                       $needpb{$key} = 1;
                   }
               }
               my @symbs = keys(%uniqsymbs);
               &process_passbacks('updatebypage',\@symbs,$cdom,$cnum,$udom,$uname,\%weights,
                                  \%awardeds,\%excuseds,\%needpb,\%skip_passback,\%pbsave,\%pbids);
               if (@Apache::grades::ltipassback) {
                   unless ($registered_cleanup) {
                       my $handlers = $request->get_handlers('PerlCleanupHandler');
                       $request->set_handlers('PerlCleanupHandler' =>
                                              [\&Apache::grades::make_passback,@{$handlers}]);
                       $registered_cleanup=1;
                   }
               }
           }
       }
     return '';      return '';
 }  }
   
   sub make_passback {
       if (@Apache::grades::ltipassback) {
           my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
           my $ip = &Apache::lonnet::get_host_ip($lonhost);
           foreach my $item (@Apache::grades::ltipassback) {
               &Apache::lonhomework::run_passback($item,$lonhost,$ip);
           }
           undef(@Apache::grades::ltipassback);
       }
   }
   
 #-------- end of section for handling grading by page/sequence ---------  #-------- end of section for handling grading by page/sequence ---------
 #  #
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
Line 5369  the homework problem. Line 7104  the homework problem.
   
 sub defaultFormData {  sub defaultFormData {
     my ($symb)=@_;      my ($symb)=@_;
     return '<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />';      return '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />';
 }  }
   
   
Line 5522  sub scantron_uploads { Line 7257  sub scantron_uploads {
 sub scantron_scantab {  sub scantron_scantab {
     my $result='<select name="scantron_format">'."\n";      my $result='<select name="scantron_format">'."\n";
     $result.='<option></option>'."\n";      $result.='<option></option>'."\n";
     my @lines = &get_scantronformat_file();      my @lines = &Apache::lonnet::get_scantronformat_file();
     if (@lines > 0) {      if (@lines > 0) {
         foreach my $line (@lines) {          foreach my $line (@lines) {
             next if (($line =~ /^\#/) || ($line eq ''));              next if (($line =~ /^\#/) || ($line eq ''));
Line 5534  sub scantron_scantab { Line 7269  sub scantron_scantab {
     return $result;      return $result;
 }  }
   
 =pod  
   
 =item get_scantronformat_file  
   
   Returns an array containing lines from the scantron format file for  
   the domain of the course.  
   
   If a url for a custom.tab file is listed in domain's configuration.db,   
   lines are from this file.  
   
   Otherwise, if a default.tab has been published in RES space by the   
   domainconfig user, lines are from this file.  
   
   Otherwise, fall back to getting lines from the legacy file on the  
   local server:  /home/httpd/lonTabs/default_scantronformat.tab      
   
 =cut  
   
 sub get_scantronformat_file {  
     my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};  
     my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$cdom);  
     my $gottab = 0;  
     my @lines;  
     if (ref($domconfig{'scantron'}) eq 'HASH') {  
         if ($domconfig{'scantron'}{'scantronformat'} ne '') {  
             my $formatfile = &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.$domconfig{'scantron'}{'scantronformat'});  
             if ($formatfile ne '-1') {  
                 @lines = split("\n",$formatfile,-1);  
                 $gottab = 1;  
             }  
         }  
     }  
     if (!$gottab) {  
         my $confname = $cdom.'-domainconfig';  
         my $default = $Apache::lonnet::perlvar{'lonDocRoot'}.'/res/'.$cdom.'/'.$confname.'/default.tab';  
         my $formatfile =  &Apache::lonnet::getfile($default);  
         if ($formatfile ne '-1') {  
             @lines = split("\n",$formatfile,-1);  
             $gottab = 1;  
         }  
     }  
     if (!$gottab) {  
         my @domains = &Apache::lonnet::current_machine_domains();  
         if (grep(/^\Q$cdom\E$/,@domains)) {  
             my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab');  
             @lines = <$fh>;  
             close($fh);  
         } else {  
             my $fh=Apache::File->new($Apache::lonnet::perlvar{'lonTabDir'}.'/default_scantronformat.tab');  
             @lines = <$fh>;  
             close($fh);  
         }  
     }  
     return @lines;  
 }  
   
 =pod   =pod 
   
 =item scantron_CODElist  =item scantron_CODElist
Line 5668  sub scantron_selectphase { Line 7347  sub scantron_selectphase {
   
     $ssi_error = 0;      $ssi_error = 0;
   
     if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||      if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'}) {
         &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {  
   
  # Chunk of form to prompt for a scantron file upload.   # Chunk of form to prompt for a scantron file upload.
   
         $r->print('          $r->print('
     <br />      <br />');
     '.&Apache::loncommon::start_data_table('LC_scantron_action').'  
        '.&Apache::loncommon::start_data_table_header_row().'  
             <th>  
               &nbsp;'.&mt('Specify a bubblesheet data file to upload.').'  
             </th>  
        '.&Apache::loncommon::end_data_table_header_row().'  
        '.&Apache::loncommon::start_data_table_row().'  
             <td>  
 ');  
     my $default_form_data=&defaultFormData($symb);  
     my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
     my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};      my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
       my $csec= $env{'request.course.sec'};
     my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');      my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');
     &js_escape(\$alertmsg);      &js_escape(\$alertmsg);
       my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($cdom);
     $r->print(&Apache::lonhtmlcommon::scripttag('      $r->print(&Apache::lonhtmlcommon::scripttag('
     function checkUpload(formname) {      function checkUpload(formname) {
  if (formname.upfile.value == "") {   if (formname.upfile.value == "") {
Line 5696  sub scantron_selectphase { Line 7366  sub scantron_selectphase {
     return false;      return false;
  }   }
  formname.submit();   formname.submit();
     }'));      }'."\n".$formatjs));
     $r->print('      $r->print('
               <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">                <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
                 '.$default_form_data.'                  '.$default_form_data.'
                 <input name="courseid" type="hidden" value="'.$cnum.'" />                  <input name="courseid" type="hidden" value="'.$cnum.'" />
                   <input name="coursesec" type="hidden" value="'.$csec.'" />
                 <input name="domainid" type="hidden" value="'.$cdom.'" />                  <input name="domainid" type="hidden" value="'.$cdom.'" />
                 <input name="command" value="scantronupload_save" type="hidden" />                  <input name="command" value="scantronupload_save" type="hidden" />
                 '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'                '.&Apache::loncommon::start_data_table('LC_scantron_action').'
                 <br />                '.&Apache::loncommon::start_data_table_header_row().'
                 <input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />                  <th>
               </form>                  &nbsp;'.&mt('Specify a bubblesheet data file to upload.').'
 ');                  </th>
                 '.&Apache::loncommon::end_data_table_header_row().'
                 '.&Apache::loncommon::start_data_table_row().'
               <td>
                   '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'<br />'."\n");
       if ($formatoptions) {
           $r->print('</td>
                    '.&Apache::loncommon::end_data_table_row().'
                    '.&Apache::loncommon::start_data_table_row().'
                    <td>'.$formattitle.('&nbsp;'x2).$formatoptions.'
                    </td>
                    '.&Apache::loncommon::end_data_table_row().'
                    '.&Apache::loncommon::start_data_table_row().'
                    <td>'
           );
       } else {
           $r->print(' <br />');
       }
       $r->print('<input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
                 </td>
                '.&Apache::loncommon::end_data_table_row().'
                '.&Apache::loncommon::end_data_table().'
                </form>'
       );
   
         $r->print('  
             </td>  
        '.&Apache::loncommon::end_data_table_row().'  
        '.&Apache::loncommon::end_data_table().'  
 ');  
     }      }
   
     # Chunk of form to prompt for a file to grade and how:      # Chunk of form to prompt for a file to grade and how:
Line 5763  sub scantron_selectphase { Line 7452  sub scantron_selectphase {
         
     $r->print($result);      $r->print($result);
   
   
   
     # Chunk of the form that prompts to view a scoring office file,      # Chunk of the form that prompts to view a scoring office file,
     # corrected file, skipped records in a file.      # corrected file, skipped records in a file.
   
Line 5826  sub scantron_selectphase { Line 7513  sub scantron_selectphase {
     return;      return;
 }  }
   
 =pod  
   
 =item get_scantron_config  
   
    Parse and return the bubblesheet configuration line selected as a  
    hash of configuration file fields.  
   
  Arguments:  
     which - the name of the configuration to parse from the file.  
   
   
  Returns:  
             If the named configuration is not in the file, an empty  
             hash is returned.  
     a hash with the fields  
       name         - internal name for the this configuration setup  
       description  - text to display to operator that describes this config  
       CODElocation - if 0 or the string 'none'  
                           - no CODE exists for this config  
                      if -1 || the string 'letter'  
                           - a CODE exists for this config and is  
                             a string of letters  
                      Unsupported value (but planned for future support)  
                           if a positive integer  
                                - The CODE exists as the first n items from  
                                  the question section of the form  
                           if the string 'number'  
                                - The CODE exists for this config and is  
                                  a string of numbers  
       CODEstart   - (only matter if a CODE exists) column in the line where  
                      the CODE starts  
       CODElength  - length of the CODE  
       IDstart     - column where the student/employee ID starts  
       IDlength    - length of the student/employee ID info  
       Qstart      - column where the information from the bubbled  
                     'questions' start  
       Qlength     - number of columns comprising a single bubble line from  
                     the sheet. (usually either 1 or 10)  
       Qon         - either a single character representing the character used  
                     to signal a bubble was chosen in the positional setup, or  
                     the string 'letter' if the letter of the chosen bubble is  
                     in the final, or 'number' if a number representing the  
                     chosen bubble is in the file (1->A 0->J)  
       Qoff        - the character used to represent that a bubble was  
                     left blank  
       PaperID     - if the scanning process generates a unique number for each  
                     sheet scanned the column that this ID number starts in  
       PaperIDlength - number of columns that comprise the unique ID number  
                       for the sheet of paper  
       FirstName   - column that the first name starts in  
       FirstNameLength - number of columns that the first name spans  
    
       LastName    - column that the last name starts in  
       LastNameLength - number of columns that the last name spans  
       BubblesPerRow - number of bubbles available in each row used to   
                       bubble an answer. (If not specified, 10 assumed).  
   
 =cut  
   
 sub get_scantron_config {  
     my ($which) = @_;  
     my @lines = &get_scantronformat_file();  
     my %config;  
     #FIXME probably should move to XML it has already gotten a bit much now  
     foreach my $line (@lines) {  
  my ($name,$descrip)=split(/:/,$line);  
  if ($name ne $which ) { next; }  
  chomp($line);  
  my @config=split(/:/,$line);  
  $config{'name'}=$config[0];  
  $config{'description'}=$config[1];  
  $config{'CODElocation'}=$config[2];  
  $config{'CODEstart'}=$config[3];  
  $config{'CODElength'}=$config[4];  
  $config{'IDstart'}=$config[5];  
  $config{'IDlength'}=$config[6];  
  $config{'Qstart'}=$config[7];  
   $config{'Qlength'}=$config[8];  
  $config{'Qoff'}=$config[9];  
  $config{'Qon'}=$config[10];  
  $config{'PaperID'}=$config[11];  
  $config{'PaperIDlength'}=$config[12];  
  $config{'FirstName'}=$config[13];  
  $config{'FirstNamelength'}=$config[14];  
  $config{'LastName'}=$config[15];  
  $config{'LastNamelength'}=$config[16];  
         $config{'BubblesPerRow'}=$config[17];  
  last;  
     }  
     return %config;  
 }  
   
 =pod   =pod 
   
 =item username_to_idmap  =item username_to_idmap
Line 5965  sub username_to_idmap { Line 7560  sub username_to_idmap {
    Process a requested correction to a scanline.     Process a requested correction to a scanline.
   
   Arguments:    Arguments:
     $scantron_config   - hash from &get_scantron_config()      $scantron_config   - hash from &Apache::lonnet::get_scantron_config()
     $scan_data         - hash of correction information       $scan_data         - hash of correction information 
                           (see &scantron_getfile())                            (see &scantron_getfile())
     $line              - existing scanline      $line              - existing scanline
Line 6322  sub scantron_parse_scanline { Line 7917  sub scantron_parse_scanline {
 }  }
   
 sub get_master_seq {  sub get_master_seq {
     my ($resources,$master_seq,$symb_to_resource) = @_;      my ($resources,$master_seq,$symb_to_resource,$need_symb_in_map,$symb_for_examcode) = @_;
     return unless ((ref($resources) eq 'ARRAY') && (ref($master_seq) eq 'ARRAY') &&       return unless ((ref($resources) eq 'ARRAY') && (ref($master_seq) eq 'ARRAY') && 
                    (ref($symb_to_resource) eq 'HASH'));                     (ref($symb_to_resource) eq 'HASH'));
       if ($need_symb_in_map) {
           return unless (ref($symb_for_examcode) eq 'HASH');
       }
     my $resource_error;      my $resource_error;
     foreach my $resource (@{$resources}) {      foreach my $resource (@{$resources}) {
         my $ressymb;          my $ressymb;
Line 6332  sub get_master_seq { Line 7930  sub get_master_seq {
             $ressymb = $resource->symb();              $ressymb = $resource->symb();
             push(@{$master_seq},$ressymb);              push(@{$master_seq},$ressymb);
             $symb_to_resource->{$ressymb} = $resource;              $symb_to_resource->{$ressymb} = $resource;
               if ($need_symb_in_map) {
                   unless ($resource->is_map()) {
                       my $map=(&Apache::lonnet::decode_symb($ressymb))[0];
                       unless (exists($symb_for_examcode->{$map})) {
                           $symb_for_examcode->{$map} = $ressymb;
                       }
                   }
               }
         } else {          } else {
             $resource_error = 1;              $resource_error = 1;
             last;              last;
Line 6648  sub scantron_filter { Line 8254  sub scantron_filter {
   
 sub scantron_process_corrections {  sub scantron_process_corrections {
     my ($r) = @_;      my ($r) = @_;
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my $which=$env{'form.scantron_line'};      my $which=$env{'form.scantron_line'};
Line 6817  sub check_for_error { Line 8423  sub check_for_error {
 sub scantron_warning_screen {  sub scantron_warning_screen {
     my ($button_text,$symb)=@_;      my ($button_text,$symb)=@_;
     my $title=&Apache::lonnet::gettitle($env{'form.selectpage'});      my $title=&Apache::lonnet::gettitle($env{'form.selectpage'});
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my $CODElist;      my $CODElist;
     if ($scantron_config{'CODElocation'} &&      if ($scantron_config{'CODElocation'} &&
  $scantron_config{'CODEstart'} &&   $scantron_config{'CODEstart'} &&
Line 6834  sub scantron_warning_screen { Line 8440  sub scantron_warning_screen {
             '<tr><td><b>'.&mt('Hand-graded items: points from last bubble in row').'</b></td><td><tt>'.              '<tr><td><b>'.&mt('Hand-graded items: points from last bubble in row').'</b></td><td><tt>'.
             $env{'form.scantron_lastbubblepoints'}.'</tt></td></tr>';              $env{'form.scantron_lastbubblepoints'}.'</tt></td></tr>';
     }      }
     return ('      return '
 <p>  <p>
 <span class="LC_warning">  <span class="LC_warning">
 '.&mt("Please double check the information below before clicking on '[_1]'",&mt($button_text)).'</span>  '.&mt("Please double check the information below before clicking on '[_1]'",&mt($button_text)).'</span>
Line 6846  sub scantron_warning_screen { Line 8452  sub scantron_warning_screen {
 </table>  </table>
 <p> '.&mt("If this information is correct, please click on '[_1]'.",&mt($button_text)).'<br />  <p> '.&mt("If this information is correct, please click on '[_1]'.",&mt($button_text)).'<br />
 '.&mt('If something is incorrect, please return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','<a href="/adm/grades?symb='.$symb.'&command=scantron_selectphase" class="LC_info">','</a>').'</p>  '.&mt('If something is incorrect, please return to [_1]Grade/Manage/Review Bubblesheets[_2] to start over.','<a href="/adm/grades?symb='.$symb.'&command=scantron_selectphase" class="LC_info">','</a>').'</p>
   ';
 <br />  
 ');  
 }  }
   
 =pod  =pod
Line 6874  sub scantron_do_warning { Line 8478  sub scantron_do_warning {
  }    } 
  if ( $env{'form.scantron_selectfile'} eq '') {   if ( $env{'form.scantron_selectfile'} eq '') {
     $r->print('<p><span class="LC_error">'.&mt("You have not selected a file that contains the student's response data.").'</span></p>');      $r->print('<p><span class="LC_error">'.&mt("You have not selected a file that contains the student's response data.").'</span></p>');
  }    }
  if ( $env{'form.scantron_format'} eq '') {   if ( $env{'form.scantron_format'} eq '') {
     $r->print('<p><span class="LC_error">'.&mt("You have not selected the format of the student's response data.").'</span></p>');      $r->print('<p><span class="LC_error">'.&mt("You have not selected the format of the student's response data.").'</span></p>');
  }    }
     } else {      } else {
  my $warning=&scantron_warning_screen('Grading: Validate Records',$symb);   my $warning=&scantron_warning_screen('Grading: Validate Records',$symb);
           my ($checksec,@possibles) = &gradable_sections();
           my $gradesections;
           if ($checksec) {
               my $file=$env{'form.scantron_selectfile'};
               if (&valid_file($file)) {
                   my %bysec = &scantron_get_sections();
                   my $table;
                   if ((keys(%bysec) > 1) || ((keys(%bysec) == 1) && ((keys(%bysec))[0] ne $checksec))) {
                       $gradesections = &mt('Your current role is for section [_1].','<i>'.$checksec.'</i>').'<br />';
                       $table = &Apache::loncommon::start_data_table()."\n".
                                &Apache::loncommon::start_data_table_header_row().
                                '<th>'.&mt('Section').'</th><th>'.&mt('Number of records').'</th>'.
                                 &Apache::loncommon::end_data_table_header_row()."\n";
                       if ($bysec{'none'}) {
                           $table .= &Apache::loncommon::start_data_table_row().
                                     '<td>'.&mt('None').'</td><td>'.$bysec{'none'}.'</td>'.
                                     &Apache::loncommon::end_data_table_row()."\n";
                       }
                       foreach my $sec (sort { $a <=> $b } keys(%bysec)) {
                           next if ($sec eq 'none');
                           $table .= &Apache::loncommon::start_data_table_row().
                                     '<td>'.$sec.'</td><td>'.$bysec{$sec}.'</td>'.
                                     &Apache::loncommon::end_data_table_row()."\n";
                       }
                       $table .= &Apache::loncommon::end_data_table()."\n";
                       $gradesections .= &mt('Sections represented in the bubblesheet data file (based on bubbled student IDs) are as follows:').
                                         '<p>'.$table.'</p>';
                       if (@possibles) {
                           $gradesections .= '<p>'.
                                             &mt('You have role(s) in [quant,_1,other section,other sections] with privileges to manage grades.',
                                                 scalar(@possibles)).'<br />'.
                                             &mt('Check which of those section(s), in addition to section [_1], you wish to grade using this bubblesheet file:',
                                                 '<i>'.$checksec.'</i>').' ';
                           foreach my $sec (sort {$a <=> $b } @possibles) {
                               $gradesections .= '<label><input type="checkbox" name="scantron_othersections" value="'.$sec.'" />'.$sec.'</label>'.('&nbsp;'x2);
                           }
                           $gradesections .= '</p>';
                       }
                   }
               } else {
                   $gradesections = '<p class="LC_error">'.&mt('The selected file is unavailable').'</p>';
               }
           }
         my $bubbledbyhand=&hand_bubble_option();          my $bubbledbyhand=&hand_bubble_option();
  $r->print('   $r->print('
 '.$warning.$bubbledbyhand.'  '.$warning.$gradesections.$bubbledbyhand.'
 <input type="submit" name="submit" value="'.&mt('Grading: Validate Records').'" />  <input type="submit" name="submit" value="'.&mt('Grading: Validate Records').'" />
 <input type="hidden" name="command" value="scantron_validate" />  <input type="hidden" name="command" value="scantron_validate" />
 ');  ');
Line 6969  sub scantron_validate_file { Line 8616  sub scantron_validate_file {
     if ($env{'form.scantron_corrections'}) {      if ($env{'form.scantron_corrections'}) {
  &scantron_process_corrections($r);   &scantron_process_corrections($r);
     }      }
     $r->print('<p>'.&mt('Gathering necessary information.').'</p>');$r->rflush();  
       $r->print('<p>'.&mt('Gathering necessary information.').'</p>');
       my ($checksec,@gradable);
       if ($env{'request.course.sec'}) {
           ($checksec,my @possibles) = &gradable_sections();
           if ($checksec) {
               if (@possibles) {
                   my @chosensecs = &Apache::loncommon::get_env_multiple('form.scantron_othersections');
                   if (@chosensecs) {
                       foreach my $sec (@chosensecs) {
                           if (grep(/^\Q$sec\E$/,@possibles)) {
                               unless (grep(/^\Q$sec\E$/,@gradable)) {
                                   push(@gradable,$sec);
                               }
                           }
                       }
                   }
               }
               $r->print('<p><table>');
               if (@gradable) {
                   my @showsections = sort { $a <=> $b } (@gradable,$checksec);
                   $r->print(
                       '<tr><td><b>'.&mt('Sections to be Graded:').'</b></td><td>'.join(', ',@showsections).'</td></tr>');
               } else {
                   $r->print(
                       '<tr><td><b>'.&mt('Section to be Graded:').'</b></td><td>'.$checksec.'</td></tr>');
               }
               $r->print('</table></p>');
           }
       }
       $r->rflush();
   
     #get the student pick code ready      #get the student pick code ready
     $r->print(&Apache::loncommon::studentbrowser_javascript());      $r->print(&Apache::loncommon::studentbrowser_javascript());
     my $nav_error;      my $nav_error;
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);      my $max_bubble=&scantron_get_maxbubble(\$nav_error,\%scantron_config);
     if ($nav_error) {      if ($nav_error) {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
Line 6994  sub scantron_validate_file { Line 8672  sub scantron_validate_file {
  $env{'form.validatepass'} = 0;   $env{'form.validatepass'} = 0;
     }      }
     my $currentphase=$env{'form.validatepass'};      my $currentphase=$env{'form.validatepass'};
       my %skipbysec=();
   
     my $stop=0;      my $stop=0;
     while (!$stop && $currentphase < scalar(@validate_phases)) {      while (!$stop && $currentphase < scalar(@validate_phases)) {
Line 7004  sub scantron_validate_file { Line 8682  sub scantron_validate_file {
  my $which="scantron_validate_".$validate_phases[$currentphase];   my $which="scantron_validate_".$validate_phases[$currentphase];
  {   {
     no strict 'refs';      no strict 'refs';
     ($stop,$currentphase)=&$which($r,$currentphase);              my @extras=();
               if ($validate_phases[$currentphase] eq 'ID') {
                   @extras = (\%skipbysec,$checksec,@gradable);
               }
       ($stop,$currentphase)=&$which($r,$currentphase,@extras);
  }   }
     }      }
     if (!$stop) {      if (!$stop) {
  my $warning=&scantron_warning_screen('Start Grading',$symb);   my $warning=&scantron_warning_screen('Start Grading',$symb);
           my $secinfo;
           if (keys(%skipbysec) > 0) {
               my $seclist = '<ul>';
               foreach my $sec (sort { $a <=> $b } keys(%skipbysec)) {
                   $seclist .= '<li>'.&mt('section [_1]: [_2]',$sec,$skipbysec{$sec}).'</li>';
               }
               $seclist .= '</ul>';
               $secinfo = '<p class="LC_info">'.
                          &mt('Numbers of records for students in sections not being graded [_1]',
                              $seclist).
                          '</p>';
           }
  $r->print(&mt('Validation process complete.').'<br />'.   $r->print(&mt('Validation process complete.').'<br />'.
                   $warning.                    $secinfo.$warning.
                   &mt('Perform verification for each student after storage of submissions?').                    &mt('Perform verification for each student after storage of submissions?').
                   '&nbsp;<span class="LC_nobreak"><label>'.                    '&nbsp;<span class="LC_nobreak"><label>'.
                   '<input type="radio" name="verifyrecord" value="1" />'.&mt('Yes').'</label>'.                    '<input type="radio" name="verifyrecord" value="1" />'.&mt('Yes').'</label>'.
Line 7426  sub scantron_validate_sequence { Line 9120  sub scantron_validate_sequence {
   
   
 sub scantron_validate_ID {  sub scantron_validate_ID {
     my ($r,$currentphase) = @_;      my ($r,$currentphase,$skipbysec,$checksec,@gradable) = @_;
           
     #get student info      #get student info
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);      my %idmap=&username_to_idmap($classlist);
       my $secidx = &Apache::loncoursedata::CL_SECTION();
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
     my $nav_error;      my $nav_error;
Line 7444  sub scantron_validate_ID { Line 9139  sub scantron_validate_ID {
     }      }
   
     my %found=('ids'=>{},'usernames'=>{});      my %found=('ids'=>{},'usernames'=>{});
       my $unsavedskips = 0;
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {      for (my $i=0;$i<=$scanlines->{'count'};$i++) {
  my $line=&scantron_get_line($scanlines,$scan_data,$i);   my $line=&scantron_get_line($scanlines,$scan_data,$i);
  if ($line=~/^[\s\cz]*$/) { next; }   if ($line=~/^[\s\cz]*$/) { next; }
Line 7456  sub scantron_validate_ID { Line 9152  sub scantron_validate_ID {
  }   }
  if ($found) {   if ($found) {
     my $username=$idmap{$found};      my $username=$idmap{$found};
               if ($checksec) {
                   if (ref($classlist->{$username}) eq 'ARRAY') {
                       my $stusec = $classlist->{$username}->[$secidx];
                       if ($stusec ne $checksec) {
                           unless ((@gradable > 0) && (grep(/^\Q$stusec\E$/,@gradable))) {
                               my $skip=1;
                               &scantron_put_line($scanlines,$scan_data,$i,$line,$skip);
                               if (ref($skipbysec) eq 'HASH') {
                                   if ($stusec eq '') {
                                       $skipbysec->{'none'} ++;
                                   } else {
                                       $skipbysec->{$stusec} ++;
                                   }
                               }
                               $unsavedskips ++;
                               next;
                           }
                       }
                   }
               }
     if ($found{'ids'}{$found}) {      if ($found{'ids'}{$found}) {
  &scantron_get_correction($r,$i,$scan_record,\%scantron_config,   &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
  $line,'duplicateID',$found);   $line,'duplicateID',$found);
                   if ($unsavedskips) {
                       &scantron_putfile($scanlines,$scan_data);
                       $unsavedskips = 0;
                   }
  return(1,$currentphase);   return(1,$currentphase);
     } elsif ($found{'usernames'}{$username}) {      } elsif ($found{'usernames'}{$username}) {
  &scantron_get_correction($r,$i,$scan_record,\%scantron_config,   &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
  $line,'duplicateID',$username);   $line,'duplicateID',$username);
                   if ($unsavedskips) {
                       &scantron_putfile($scanlines,$scan_data);
                       $unsavedskips = 0;
                   }
  return(1,$currentphase);   return(1,$currentphase);
     }      }
     #FIXME store away line we previously saw the ID on to use above      #FIXME store away line we previously saw the ID on to use above
Line 7471  sub scantron_validate_ID { Line 9195  sub scantron_validate_ID {
  } else {   } else {
     if ($id =~ /^\s*$/) {      if ($id =~ /^\s*$/) {
  my $username=&scan_data($scan_data,"$i.user");   my $username=&scan_data($scan_data,"$i.user");
  if (defined($username) && $found{'usernames'}{$username}) {                  if (($checksec && $username ne '')) {
                       if (ref($classlist->{$username}) eq 'ARRAY') {
                           my $stusec = $classlist->{$username}->[$secidx];
                           if ($stusec ne $checksec) {
                               unless ((@gradable > 0) && (grep(/^\Q$stusec\E$/,@gradable))) {
                                   my $skip=1;
                                   &scantron_put_line($scanlines,$scan_data,$i,$line,$skip);
                                   if (ref($skipbysec) eq 'HASH') {
                                       if ($stusec eq '') {
                                           $skipbysec->{'none'} ++;
                                       } else {
                                           $skipbysec->{$stusec} ++;
                                       }
                                   }
                                   $unsavedskips ++;
                                   next;
                               }
                           }
                       }
    } elsif (defined($username) && $found{'usernames'}{$username}) {
     &scantron_get_correction($r,$i,$scan_record,      &scantron_get_correction($r,$i,$scan_record,
      \%scantron_config,       \%scantron_config,
      $line,'duplicateID',$username);       $line,'duplicateID',$username);
                       if ($unsavedskips) {
                           &scantron_putfile($scanlines,$scan_data);
                           $unsavedskips = 0;
                       }
     return(1,$currentphase);      return(1,$currentphase);
  } elsif (!defined($username)) {   } elsif (!defined($username)) {
     &scantron_get_correction($r,$i,$scan_record,      &scantron_get_correction($r,$i,$scan_record,
      \%scantron_config,       \%scantron_config,
      $line,'incorrectID');       $line,'incorrectID');
                       if ($unsavedskips) {
                           &scantron_putfile($scanlines,$scan_data);
                           $unsavedskips = 0;
                       }
     return(1,$currentphase);      return(1,$currentphase);
  }   }
  $found{'usernames'}{$username}++;   $found{'usernames'}{$username}++;
     } else {      } else {
  &scantron_get_correction($r,$i,$scan_record,\%scantron_config,   &scantron_get_correction($r,$i,$scan_record,\%scantron_config,
  $line,'incorrectID');   $line,'incorrectID');
                   if ($unsavedskips) {
                       &scantron_putfile($scanlines,$scan_data);
                       $unsavedskips = 0;
                   }
  return(1,$currentphase);   return(1,$currentphase);
     }      }
  }   }
     }      }
       if ($unsavedskips) {
           &scantron_putfile($scanlines,$scan_data);
           $unsavedskips = 0;
       }
     return (0,$currentphase+1);      return (0,$currentphase+1);
 }  }
   
   sub scantron_get_sections {
       my %bysec;
       if ($env{'form.scantron_format'} ne '') {
           my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
           my ($scanlines,$scan_data)=&scantron_getfile();
           my $classlist=&Apache::loncoursedata::get_classlist();
           my %idmap=&username_to_idmap($classlist);
           foreach my $key (keys(%idmap)) {
               my $lckey = lc($key);
               $idmap{$lckey} = $idmap{$key};
           }
           my $secidx = &Apache::loncoursedata::CL_SECTION();
           for (my $i=0;$i<=$scanlines->{'count'};$i++) {
               my $line=&scantron_get_line($scanlines,$scan_data,$i);
               if ($line=~/^[\s\cz]*$/) { next; }
               my $scan_record=&scantron_parse_scanline($line,$i,\%scantron_config,
                                                        $scan_data);
               my $id=lc($$scan_record{'scantron.ID'});
               if (exists($idmap{$id})) {
                   if (ref($classlist->{$idmap{$id}}) eq 'ARRAY') {
                       my $stusec = $classlist->{$idmap{$id}}->[$secidx];
                       if ($stusec eq '') {
                           $bysec{'none'} ++;
                       } else {
                           $bysec{$stusec} ++;
                       }
                   }
               }
           }
       }
       return %bysec;
   }
   
 sub scantron_get_correction {  sub scantron_get_correction {
     my ($r,$i,$scan_record,$scan_config,$line,$error,$arg,      my ($r,$i,$scan_record,$scan_config,$line,$error,$arg,
Line 7680  sub verify_bubbles_checked { Line 9470  sub verify_bubbles_checked {
     my $ansnumstr = join('","',@ansnums);      my $ansnumstr = join('","',@ansnums);
     my $warning = &mt("A bubble or 'No bubble' selection has not been made for one or more lines.");      my $warning = &mt("A bubble or 'No bubble' selection has not been made for one or more lines.");
     &js_escape(\$warning);      &js_escape(\$warning);
     my $output = &Apache::lonhtmlcommon::scripttag((<<ENDSCRIPT));      my $output = &Apache::lonhtmlcommon::scripttag(<<ENDSCRIPT);
 function verify_bubble_radio(form) {  function verify_bubble_radio(form) {
     var ansnumArray = new Array ("$ansnumstr");      var ansnumArray = new Array ("$ansnumstr");
     var need_bubble_count = 0;      var need_bubble_count = 0;
Line 7903  sub prompt_for_corrections { Line 9693  sub prompt_for_corrections {
   
  Arguments:   Arguments:
     $r           - Apache request object      $r           - Apache request object
     $scan_config - hash from &get_scantron_config()      $scan_config - hash from &Apache::lonnet::get_scantron_config()
     $line        - Number of the line being displayed.      $line        - Number of the line being displayed.
     $questionnum - Question number (may include subquestion)      $questionnum - Question number (may include subquestion)
     $error       - Type of error.      $error       - Type of error.
Line 8067  sub get_codes { Line 9857  sub get_codes {
   
 sub scantron_validate_CODE {  sub scantron_validate_CODE {
     my ($r,$currentphase) = @_;      my ($r,$currentphase) = @_;
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     if ($scantron_config{'CODElocation'} &&      if ($scantron_config{'CODElocation'} &&
  $scantron_config{'CODEstart'} &&   $scantron_config{'CODEstart'} &&
  $scantron_config{'CODElength'}) {   $scantron_config{'CODElength'}) {
Line 8141  sub scantron_validate_doublebubble { Line 9931  sub scantron_validate_doublebubble {
         &Apache::lonnet::decode_symb($env{'form.selectpage'});          &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
Line 8159  sub scantron_validate_doublebubble { Line 9949  sub scantron_validate_doublebubble {
     if (ref($map)) {      if (ref($map)) {
         $randomorder = $map->randomorder();          $randomorder = $map->randomorder();
         $randompick = $map->randompick();          $randompick = $map->randompick();
           unless ($randomorder || $randompick) {
               foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
                   if ($res->randomorder()) {
                       $randomorder = 1;
                   }
                   if ($res->randompick()) {
                       $randompick = 1;
                   }
                   last if ($randomorder || $randompick);
               }
           }
         if ($randomorder || $randompick) {          if ($randomorder || $randompick) {
             $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);              $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
             if ($nav_error) {              if ($nav_error) {
Line 8323  sub scantron_validate_missingbubbles { Line 10124  sub scantron_validate_missingbubbles {
         &Apache::lonnet::decode_symb($env{'form.selectpage'});          &Apache::lonnet::decode_symb($env{'form.selectpage'});
   
     #get scantron line setup      #get scantron line setup
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
   
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
Line 8342  sub scantron_validate_missingbubbles { Line 10143  sub scantron_validate_missingbubbles {
     if (ref($map)) {      if (ref($map)) {
         $randomorder = $map->randomorder();          $randomorder = $map->randomorder();
         $randompick = $map->randompick();          $randompick = $map->randompick();
           unless ($randomorder || $randompick) {
               foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
                   if ($res->randomorder()) {
                       $randomorder = 1;
                   }
                   if ($res->randompick()) {
                       $randompick = 1;
                   }
                   last if ($randomorder || $randompick);
               }
           }
         if ($randomorder || $randompick) {          if ($randomorder || $randompick) {
             $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);              $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
             if ($nav_error) {              if ($nav_error) {
Line 8452  sub hand_bubble_option { Line 10264  sub hand_bubble_option {
         }          }
     }      }
     if ($needs_hand_bubbles) {      if ($needs_hand_bubbles) {
         my %scantron_config=&get_scantron_config($env{'form.scantron_format'});          my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
         my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);          my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
         return &mt('The sequence to be graded contains response types which are handgraded.').'<p>'.          return &mt('The sequence to be graded contains response types which are handgraded.').'<p>'.
                &mt('If you have already graded these by bubbling sheets to indicate points awarded, [_1]what point value is assigned to a filled last bubble in each row?','<br />').                 &mt('If you have already graded these by bubbling sheets to indicate points awarded, [_1]what point value is assigned to a filled last bubble in each row?','<br />').
Line 8471  sub scantron_process_students { Line 10283  sub scantron_process_students {
     }      }
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
   
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});      my %scantron_config=&Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);       my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config); 
     my ($scanlines,$scan_data)=&scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
Line 8483  sub scantron_process_students { Line 10295  sub scantron_process_students {
     }      }
     my $map=$navmap->getResourceByUrl($sequence);      my $map=$navmap->getResourceByUrl($sequence);
     my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,      my ($randomorder,$randompick,@master_seq,%symb_to_resource,%grader_partids_by_symb,
         %grader_randomlists_by_symb);          %grader_randomlists_by_symb,%symb_for_examcode);
     if (ref($map)) {      if (ref($map)) {
         $randomorder = $map->randomorder();          $randomorder = $map->randomorder();
         $randompick = $map->randompick();          $randompick = $map->randompick();
           unless ($randomorder || $randompick) {
               foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
                   if ($res->randomorder()) {
                       $randomorder = 1;
                   }
                   if ($res->randompick()) {
                       $randompick = 1;
                   }
                   last if ($randomorder || $randompick);
               }
           }
     } else {      } else {
         $r->print(&navmap_errormsg());          $r->print(&navmap_errormsg());
         return '';          return '';
Line 8494  sub scantron_process_students { Line 10317  sub scantron_process_students {
     my $nav_error;      my $nav_error;
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
     if ($randomorder || $randompick) {      if ($randomorder || $randompick) {
         $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);          $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource,1,\%symb_for_examcode);
         if ($nav_error) {          if ($nav_error) {
             $r->print(&navmap_errormsg());              $r->print(&navmap_errormsg());
             return '';              return '';
Line 8511  sub scantron_process_students { Line 10334  sub scantron_process_students {
 SCANTRONFORM  SCANTRONFORM
     $r->print($result);      $r->print($result);
   
       my ($checksec,@possibles)=&gradable_sections();
     my @delayqueue;      my @delayqueue;
     my (%completedstudents,%scandata);      my (%completedstudents,%scandata);
       
     my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));      my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));
     my $count=&get_todo_count($scanlines,$scan_data);      my $count=&get_todo_count($scanlines,$scan_data);
     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);      my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,$count);
Line 8539  SCANTRONFORM Line 10363  SCANTRONFORM
  return ''; # Dunno why the other returns return '' rather than just returning.   return ''; # Dunno why the other returns return '' rather than just returning.
     }      }
   
     my %lettdig = &letter_to_digits();      my %lettdig = &Apache::lonnet::letter_to_digits();
     my $numletts = scalar(keys(%lettdig));      my $numletts = scalar(keys(%lettdig));
     my %orderedforcode;      my %orderedforcode;
   
Line 8573  SCANTRONFORM Line 10397  SCANTRONFORM
      next;       next;
   }    }
         my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION];          my $usec = $classlist->{$uname}->[&Apache::loncoursedata::CL_SECTION];
           if (($checksec ne '') && ($checksec ne $usec)) {
               unless (grep(/^\Q$usec\E$/,@possibles)) {
                   &scantron_add_delay(\@delayqueue,$line,
                                       "No role with manage grades privilege in student's section ($usec)",3);
                   next;
               }
           }
         my $user = $uname.':'.$usec;          my $user = $uname.':'.$usec;
   ($uname,$udom)=split(/:/,$uname);    ($uname,$udom)=split(/:/,$uname);
   
Line 8601  SCANTRONFORM Line 10432  SCANTRONFORM
             }              }
             if ((exists($grader_randomlists_by_symb{$ressymb})) ||              if ((exists($grader_randomlists_by_symb{$ressymb})) ||
                 (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {                  (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
                   my $currcode;
                   if (exists($grader_randomlists_by_symb{$ressymb})) {
                       $currcode = $scancode;
                   }
                 my ($analysis,$parts) =                  my ($analysis,$parts) =
                     &scantron_partids_tograde($resource,$env{'request.course.id'},                      &scantron_partids_tograde($resource,$env{'request.course.id'},
                                               $uname,$udom,undef,$bubbles_per_row);                                                $uname,$udom,undef,$bubbles_per_row,
                                                 $currcode);
                 $partids_by_symb{$ressymb} = $parts;                  $partids_by_symb{$ressymb} = $parts;
             } else {              } else {
                 $partids_by_symb{$ressymb} = $grader_partids_by_symb{$ressymb};                  $partids_by_symb{$ressymb} = $grader_partids_by_symb{$ressymb};
Line 8636  SCANTRONFORM Line 10472  SCANTRONFORM
         }          }
   
         if (($scancode) && ($randomorder || $randompick)) {          if (($scancode) && ($randomorder || $randompick)) {
             my $parmresult =              foreach my $key (keys(%symb_for_examcode)) {
                 &Apache::lonparmset::storeparm_by_symb($symb,                  my $symb_in_map = $symb_for_examcode{$key};
                                                        '0_examcode',2,$scancode,                  if ($symb_in_map ne '') {
                                                        'string_examcode',$uname,                      my $parmresult =
                                                        $udom);                          &Apache::lonparmset::storeparm_by_symb($symb_in_map,
                                                                  '0_examcode',2,$scancode,
                                                                  'string_examcode',$uname,
                                                                  $udom);
                   }
               }
         }          }
  $completedstudents{$uname}={'line'=>$line};   $completedstudents{$uname}={'line'=>$line};
         if ($env{'form.verifyrecord'}) {          if ($env{'form.verifyrecord'}) {
Line 8859  sub grade_student_bubbles { Line 10700  sub grade_student_bubbles {
 }  }
   
 sub scantron_upload_scantron_data {  sub scantron_upload_scantron_data {
     my ($r,$symb)=@_;      my ($r,$symb) = @_;
     my $dom = $env{'request.role.domain'};      my $dom = $env{'request.role.domain'};
       my ($formatoptions,$formattitle,$formatjs) = &scantron_upload_dataformat($dom);
     my $domdesc = &Apache::lonnet::domain($dom,'description');      my $domdesc = &Apache::lonnet::domain($dom,'description');
     $r->print(&Apache::loncommon::coursebrowser_javascript($dom));      $r->print(&Apache::loncommon::coursebrowser_javascript($dom));
     my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',      my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',
Line 8900  sub scantron_upload_scantron_data { Line 10742  sub scantron_upload_scantron_data {
         return;          return;
     }      }
   
       '.$formatjs.'
 '));  '));
     $r->print('      $r->print('
 <h3>'.&mt('Send bubblesheet data to a course').'</h3>  <h3>'.&mt('Send bubblesheet data to a course').'</h3>
Line 8915  sub scantron_upload_scantron_data { Line 10758  sub scantron_upload_scantron_data {
   &Apache::lonhtmlcommon::row_closure().    &Apache::lonhtmlcommon::row_closure().
   &Apache::lonhtmlcommon::row_title(&mt('Domain')).    &Apache::lonhtmlcommon::row_title(&mt('Domain')).
   '<input name="domainid" type="hidden" />'.$domdesc.    '<input name="domainid" type="hidden" />'.$domdesc.
   &Apache::lonhtmlcommon::row_closure().    &Apache::lonhtmlcommon::row_closure());
       if ($formatoptions) {
           $r->print(&Apache::lonhtmlcommon::row_title($formattitle).$formatoptions.
                     &Apache::lonhtmlcommon::row_closure());
       }
       $r->print(
   &Apache::lonhtmlcommon::row_title(&mt('File to upload')).    &Apache::lonhtmlcommon::row_title(&mt('File to upload')).
   '<input type="file" name="upfile" size="50" />'.    '<input type="file" name="upfile" size="50" />'.
   &Apache::lonhtmlcommon::row_closure(1).    &Apache::lonhtmlcommon::row_closure(1).
Line 8928  sub scantron_upload_scantron_data { Line 10776  sub scantron_upload_scantron_data {
     return '';      return '';
 }  }
   
   sub scantron_upload_dataformat {
       my ($dom) = @_;
       my ($formatoptions,$formattitle,$formatjs);
       $formatjs = <<'END';
   function toggleScantab(form) {
      return;
   }
   END
       my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$dom);
       if (ref($domconfig{'scantron'}) eq 'HASH') {
           if (ref($domconfig{'scantron'}{'config'}) eq 'HASH') {
               if (keys(%{$domconfig{'scantron'}{'config'}}) > 1) {
                   if (($domconfig{'scantron'}{'config'}{'dat'}) &&
                       (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH')) {
                       if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {  
                           if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {
                               my ($onclick,$formatextra,$singleline);
                               my @lines = &Apache::lonnet::get_scantronformat_file();
                               my $count = 0;
                               foreach my $line (@lines) {
                                   next if (($line =~ /^\#/) || ($line eq ''));
                                   $singleline = $line;
                                   $count ++;
                               }
                               if ($count > 1) {
                                   $formatextra = '<div style="display:none" id="bubbletype">'.
                                                  '<span class="LC_nobreak">'.
                                                  &mt('Bubblesheet type').':&nbsp;'.
                                                  &scantron_scantab().'</span></div>';
                                   $onclick = ' onclick="toggleScantab(this.form);"';
                                   $formatjs = <<"END";
   function toggleScantab(form) {
       var divid = 'bubbletype';
       if (document.getElementById(divid)) {
           var radioname = 'fileformat';
           var num = form.elements[radioname].length;
           if (num) {
               for (var i=0; i<num; i++) {
                   if (form.elements[radioname][i].checked) {
                       var chosen = form.elements[radioname][i].value;
                       if (chosen == 'dat') {
                           document.getElementById(divid).style.display = 'none';
                       } else if (chosen == 'csv') {
                           document.getElementById(divid).style.display = 'block';
                       }
                   }
               }
           }
       }
       return;
   }
   
   END
                               } elsif ($count == 1) {
                                   my $formatname = (split(/:/,$singleline,2))[0];
                                   $formatextra = '<input type="hidden" name="scantron_format" value="'.$formatname.'" />';
                               }
                               $formattitle = &mt('File format');
                               $formatoptions = '<label><input name="fileformat" type="radio" value="dat" checked="checked"'.$onclick.' />'.
                                                &mt('Plain Text (no delimiters)').
                                                '</label>'.('&nbsp;'x2).
                                                '<label><input name="fileformat" type="radio" value="csv"'.$onclick.' />'.
                                                &mt('Comma separated values').'</label>'.$formatextra;
                           }
                       }
                   }
               } elsif (keys(%{$domconfig{'scantron'}{'config'}}) == 1) {
                   if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
                       if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}})) {
                           $formattitle = &mt('Bubblesheet type');
                           $formatoptions = &scantron_scantab();
                       }
                   }
               }
           }
       }
       return ($formatoptions,$formattitle,$formatjs);
   }
   
 sub scantron_upload_scantron_data_save {  sub scantron_upload_scantron_data_save {
     my($r,$symb)=@_;      my ($r,$symb) = @_;
     my $doanotherupload=      my $doanotherupload=
  '<br /><form action="/adm/grades" method="post">'."\n".   '<br /><form action="/adm/grades" method="post">'."\n".
  '<input type="hidden" name="command" value="scantronupload" />'."\n".   '<input type="hidden" name="command" value="scantronupload" />'."\n".
Line 8938  sub scantron_upload_scantron_data_save { Line 10864  sub scantron_upload_scantron_data_save {
  '</form>'."\n";   '</form>'."\n";
     if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&      if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&
  !&Apache::lonnet::allowed('usc',   !&Apache::lonnet::allowed('usc',
     $env{'form.domainid'}.'_'.$env{'form.courseid'})) {      $env{'form.domainid'}.'_'.$env{'form.courseid'}) &&
           !&Apache::lonnet::allowed('usc',
                               $env{'form.domainid'}.'_'.$env{'form.courseid'}.'/'.$env{'form.coursesec'})) {
  $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."<br />");   $r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."<br />");
  unless ($symb) {   unless ($symb) {
     $r->print($doanotherupload);      $r->print($doanotherupload);
Line 8954  sub scantron_upload_scantron_data_save { Line 10882  sub scantron_upload_scantron_data_save {
                 &mt('The file: [_1] you attempted to upload contained no information. Please check that you entered the correct filename.',                  &mt('The file: [_1] you attempted to upload contained no information. Please check that you entered the correct filename.',
                         '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'),1));                          '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'),1));
     } else {      } else {
         my $result =           my %domconfig = &Apache::lonnet::get_dom('configuration',['scantron'],$env{'form.domainid'});
             &Apache::lonnet::userfileupload('upfile','','scantron','','','',          my $parser;
           if (ref($domconfig{'scantron'}) eq 'HASH') {
               if (ref($domconfig{'scantron'}{'config'}) eq 'HASH') {
                   my $is_csv;
                   my @possibles = keys(%{$domconfig{'scantron'}{'config'}});
                   if (@possibles > 1) {
                       if ($env{'form.fileformat'} eq 'csv') {
                           if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') {
                               if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
                                   if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}}) > 1) {
                                       $is_csv = 1;
                                   }
                               }
                           }
                       }
                   } elsif (@possibles == 1) {
                       if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') {
                           if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
                               if (keys(%{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}}) > 1) {
                                   $is_csv = 1;
                               }
                           }
                       }
                   }
                   if ($is_csv) {
                      $parser = $domconfig{'scantron'}{'config'}{'csv'};
                   }
               }
           }
           my $result =
               &Apache::lonnet::userfileupload('upfile','scantron','scantron',$parser,'','',
                                             $env{'form.courseid'},$env{'form.domainid'});                                              $env{'form.courseid'},$env{'form.domainid'});
         if ($result =~ m{^/uploaded/}) {          if ($result =~ m{^/uploaded/}) {
             $r->print(              $r->print(
Line 8964  sub scantron_upload_scantron_data_save { Line 10922  sub scantron_upload_scantron_data_save {
                         (length($env{'form.upfile'})-1),                          (length($env{'form.upfile'})-1),
                         '<span class="LC_filename">'.$result.'</span>'));                          '<span class="LC_filename">'.$result.'</span>'));
             ($uploadedfile) = ($result =~ m{/([^/]+)$});              ($uploadedfile) = ($result =~ m{/([^/]+)$});
               if ($uploadedfile =~ /^scantron_orig_/) {
                   my $logname = $uploadedfile;
                   $logname =~ s/^scantron_orig_//;
                   if ($logname ne '') {
                       my $now = time;
                       my %info = ($logname => { $now => $env{'user.name'}.':'.$env{'user.domain'} });  
                       &Apache::lonnet::put('scantronupload',\%info,$env{'form.domainid'},$env{'form.courseid'});
                   }
               }
             $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},              $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},
                                                        $env{'form.courseid'},$uploadedfile));                                                         $env{'form.courseid'},$symb,$uploadedfile));
         } else {          } else {
             $r->print(              $r->print(
                 &Apache::lonhtmlcommon::confirm_success(&mt('Upload failed'),1).'<br />'.                  &Apache::lonhtmlcommon::confirm_success(&mt('Upload failed'),1).'<br />'.
Line 8983  sub scantron_upload_scantron_data_save { Line 10950  sub scantron_upload_scantron_data_save {
 }  }
   
 sub validate_uploaded_scantron_file {  sub validate_uploaded_scantron_file {
     my ($cdom,$cname,$fname) = @_;      my ($cdom,$cname,$symb,$fname,$context,$countsref) = @_;
   
     my $scanlines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.$fname);      my $scanlines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.$fname);
     my @lines;      my @lines;
     if ($scanlines ne '-1') {      if ($scanlines ne '-1') {
         @lines=split("\n",$scanlines,-1);          @lines=split("\n",$scanlines,-1);
     }      }
     my $output;      my ($output,$secidx,$checksec,$priv,%crsroleshash,@possibles);
       $secidx = &Apache::loncoursedata::CL_SECTION();
       if ($context eq 'download') {
           $priv = 'mgr';
       } else {
           $priv = 'usc';
       }
       unless ((&Apache::lonnet::allowed($priv,$env{'request.role.domain'})) ||
               (($env{'request.course.id'}) &&
                (&Apache::lonnet::allowed($priv,$env{'request.course.id'})))) {
           if ($env{'request.course.sec'} ne '') {
               unless (&Apache::lonnet::allowed($priv,
                                            "$env{'request.course.id'}/$env{'request.course.sec'}")) {
                   unless ($context eq 'download') {
                       $output = '<p class="LC_warning">'.&mt('You do not have permission to upload bubblesheet data').'</p>';
                   }
                   return $output;
               }
               ($checksec,@possibles)=&gradable_sections();
           }
       }
     if (@lines) {      if (@lines) {
         my (%counts,$max_match_format);          my (%counts,$max_match_format);
         my ($found_match_count,$max_match_count,$max_match_pct) = (0,0,0);          my ($found_match_count,$max_match_count,$max_match_pct) = (0,0,0);
Line 9000  sub validate_uploaded_scantron_file { Line 10988  sub validate_uploaded_scantron_file {
             $idmap{$lckey} = $idmap{$key};              $idmap{$lckey} = $idmap{$key};
         }          }
         my %unique_formats;          my %unique_formats;
         my @formatlines = &get_scantronformat_file();          my @formatlines = &Apache::lonnet::get_scantronformat_file();
         foreach my $line (@formatlines) {          foreach my $line (@formatlines) {
             chomp($line);              next if (($line =~ /^\#/) || ($line eq ''));
             my @config = split(/:/,$line);              my @config = split(/:/,$line);
             my $idstart = $config[5];              my $idstart = $config[5];
             my $idlength = $config[6];              my $idlength = $config[6];
Line 9019  sub validate_uploaded_scantron_file { Line 11007  sub validate_uploaded_scantron_file {
             %{$counts{$key}} = (              %{$counts{$key}} = (
                                'found'   => 0,                                 'found'   => 0,
                                'total'   => 0,                                 'total'   => 0,
                                  'totalanysec' => 0,
                                  'othersec' => 0,
                               );                                );
             foreach my $line (@lines) {              foreach my $line (@lines) {
                 next if ($line =~ /^#/);                  next if ($line =~ /^#/);
Line 9026  sub validate_uploaded_scantron_file { Line 11016  sub validate_uploaded_scantron_file {
                 my $id = substr($line,$idstart-1,$idlength);                  my $id = substr($line,$idstart-1,$idlength);
                 $id = lc($id);                  $id = lc($id);
                 if (exists($idmap{$id})) {                  if (exists($idmap{$id})) {
                       if ($checksec ne '') {
                           $counts{$key}{'totalanysec'} ++;
                           if (ref($classlist->{$idmap{$id}}) eq 'ARRAY') {
                               my $stusec = $classlist->{$idmap{$id}}->[$secidx];
                               if ($stusec ne $checksec) {
                                   if (@possibles) {
                                       unless (grep(/^\Q$stusec\E$/,@possibles)) {
                                           $counts{$key}{'othersec'} ++;
                                           next;
                                       }
                                   } else {
                                       $counts{$key}{'othersec'} ++;
                                       next;
                                   }
                               }
                           }
                       }
                     $counts{$key}{'found'} ++;                      $counts{$key}{'found'} ++;
                 }                  }
                 $counts{$key}{'total'} ++;                  $counts{$key}{'total'} ++;
Line 9040  sub validate_uploaded_scantron_file { Line 11047  sub validate_uploaded_scantron_file {
                 }                  }
             }              }
         }          }
         if (ref($unique_formats{$max_match_format}) eq 'ARRAY') {          if ((ref($unique_formats{$max_match_format}) eq 'ARRAY') && ($context ne 'download')) {
             my $format_descs;              my $format_descs;
             my $numwithformat = @{$unique_formats{$max_match_format}};              my $numwithformat = @{$unique_formats{$max_match_format}};
             for (my $i=0; $i<$numwithformat; $i++) {              for (my $i=0; $i<$numwithformat; $i++) {
Line 9085  sub validate_uploaded_scantron_file { Line 11092  sub validate_uploaded_scantron_file {
                     '<li>'.&mt('The course roster is not up to date.').'</li>'.                      '<li>'.&mt('The course roster is not up to date.').'</li>'.
                     '</ul>';                      '</ul>';
             }              }
               if (($checksec ne '') && (ref($counts{$max_match_format}) eq 'HASH')) {
                   if ($counts{$max_match_format}{'othersec'}) {
                       my $percent_nongrade = (100*$counts{$max_match_format}{'othersec'})/($counts{$max_match_format}{'totalanysec'});
                       my $showpct = sprintf("%.0f",$percent_nongrade).'%';
                       my $confirmdel = &mt('Are you sure you want to permanently delete this file?');
                       &js_escape(\$confirmdel);
                       $output .= '<p class="LC_warning">'.
                                  &mt('Comparison of student IDs in the uploaded file with the course roster found [_1][quant,_2,match,matches][_3] for students in section(s) for which none of your role(s) have privileges to modify grades',
                                      '<b>',$counts{$max_match_format}{'othersec'},'</b>').
                                  '<br />'.
                                  &mt('Unless you are assigned role(s) which allow modification of grades in additional sections, [_1] of the records in this file will be automatically excluded when you perform bubblesheet grading.','<b>'.$showpct.'</b>').
                                  '</p><p>'.
                                  &mt('If you prefer to delete the file now, use: [_1]').
                                  '<form method="post" name="delupload" action="/adm/grades">'.
                                  '<input type="hidden" name="symb" value="'.$symb.'" />'.
                                  '<input type="hidden" name="domainid" value="'.$cdom.'" />'.
                                  '<input type="hidden" name="courseid" value="'.$cname.'" />'.
                                  '<input type="hidden" name="coursesec" value="'.$env{'request.course.sec'}.'" />'. 
                                  '<input type="hidden" name="uploadedfile" value="'.$fname.'" />'. 
                                  '<input type="hidden" name="command" value="scantronupload_delete" />'.
                                  '<input type="button" name="delbutton" value="'.&mt('Delete Uploaded File').'" onclick="javascript:if (confirm('."'$confirmdel'".')) { document.delupload.submit(); }" />'.
                                  '</form></p>';
                   }
               }
         }          }
     } else {          if (($context eq 'download') && ($checksec ne '')) {
               if ((ref($countsref) eq 'HASH') && (ref($counts{$max_match_format}) eq 'HASH')) {
                   $countsref->{'totalanysec'} = $counts{$max_match_format}{'totalanysec'};
                   $countsref->{'othersec'} = $counts{$max_match_format}{'othersec'};
               }
           } 
       } elsif ($context ne 'download') {
         $output = '<p class="LC_warning">'.&mt('Uploaded file contained no data').'</p>';          $output = '<p class="LC_warning">'.&mt('Uploaded file contained no data').'</p>';
     }      }
     return $output;      return $output;
 }  }
   
   sub gradable_sections {
       my $checksec = $env{'request.course.sec'};
       my @oksecs;
       if ($checksec) {
           my %availablesecs = &sections_grade_privs();
           if (ref($availablesecs{'mgr'}) eq 'ARRAY') {
               foreach my $sec (@{$availablesecs{'mgr'}}) {
                   unless (grep(/^\Q$sec\E$/,@oksecs)) {
                       push(@oksecs,$sec);
                   }
               }
               if (grep(/^all$/,@oksecs)) {
                   undef($checksec);
               }
           }
       }
       return($checksec,@oksecs);
   }
   
   sub sections_grade_privs {
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
       my %availablesecs = (
                             mgr => [],
                             vgr => [],
                             usc => [],
                           );
       my $ccrole = 'cc';
       if ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Community') {
           $ccrole = 'co';
       }
       my %crsroleshash = &Apache::lonnet::get_my_roles($env{'user.name'},$env{'user.domain'},
                                                        'userroles',['active'],
                                                        [$ccrole,'in','cr'],$cdom,1);
       my $crsid = $cnum.':'.$cdom;
       foreach my $item (keys(%crsroleshash)) {
           next unless ($item =~ /^$crsid\:/);
           my ($crsnum,$crsdom,$role,$sec) = split(/\:/,$item);
           my $suffix = "/$cdom/$cnum./$cdom/$cnum";
           if ($sec ne '') {
               $suffix = "/$cdom/$cnum/$sec./$cdom/$cnum/$sec";
           }
           if (($role eq $ccrole) || ($role eq 'in')) {
               foreach my $priv ('mgr','vgr','usc') { 
                   unless (grep(/^all$/,@{$availablesecs{$priv}})) {
                       if ($sec eq '') {
                           $availablesecs{$priv} = ['all'];
                       } elsif ($sec ne $env{'request.course.sec'}) {
                           unless (grep(/^\Q$sec\E$/,@{$availablesecs{$priv}})) {
                               push(@{$availablesecs{$priv}},$sec);
                           }
                       }
                   }
               }
           } elsif ($role =~ m{^cr/}) {
               foreach my $priv ('mgr','vgr','usc') {
                   unless (grep(/^all$/,@{$availablesecs{$priv}})) {
                       if ($env{"user.priv.$role.$suffix"} =~ /:$priv&/) {
                           if ($sec eq '') {
                               $availablesecs{$priv} = ['all'];
                           } elsif ($sec ne $env{'request.course.sec'}) {
                               unless (grep(/^\Q$sec\E$/,@{$availablesecs{$priv}})) {
                                   push(@{$availablesecs{$priv}},$sec);
                               }
                           }
                       }
                   }
               }
           }
       }
       return %availablesecs;
   }
   
   sub scantron_upload_delete {
       my ($r,$symb) = @_;
       my $filename = $env{'form.uploadedfile'};
       if ($filename =~ /^scantron_orig_/) {
           if (&Apache::lonnet::allowed('usc',$env{'form.domainid'}) ||
               &Apache::lonnet::allowed('usc',
                                        $env{'form.domainid'}.'_'.$env{'form.courseid'}) ||
               &Apache::lonnet::allowed('usc',
                                        $env{'form.domainid'}.'_'.$env{'form.courseid'}.'/'.$env{'form.coursesec'})) {
               my $uploadurl = '/uploaded/'.$env{'form.domainid'}.'/'.$env{'form.courseid'}.'/'.$env{'form.uploadedfile'};
               my $retrieval = &Apache::lonnet::getfile($uploadurl);
               if ($retrieval eq '-1') {
                   $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                             &mt('File requested for deletion not found.'));
               } else {
                   $filename =~ s/^scantron_orig_//;
                   if ($filename ne '') {
                       my ($is_valid,$numleft);
                       my %info = &Apache::lonnet::get('scantronupload',[$filename],$env{'form.domainid'},$env{'form.courseid'});
                       if (keys(%info)) {
                           if (ref($info{$filename}) eq 'HASH') {
                               foreach my $timestamp (sort(keys(%{$info{$filename}}))) {
                                   if ($info{$filename}{$timestamp} eq $env{'user.name'}.':'.$env{'user.domain'}) {
                                       $is_valid = 1;
                                       delete($info{$filename}{$timestamp}); 
                                   }
                               }
                               $numleft = scalar(keys(%{$info{$filename}}));
                           }
                       }
                       if ($is_valid) {
                           my $result = &Apache::lonnet::removeuploadedurl($uploadurl);
                           if ($result eq 'ok') {
                               $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion successful')).'<br />');
                               if ($numleft) {
                                   &Apache::lonnet::put('scantronupload',\%info,$env{'form.domainid'},$env{'form.courseid'});
                               } else {
                                   &Apache::lonnet::del('scantronupload',[$filename],$env{'form.domainid'},$env{'form.courseid'});
                               }
                           } else {
                               $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                                         &mt('Result was [_1]',$result));
                           }
                       } else {
                           $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                                     &mt('File requested for deletion was uploaded by a different user.'));
                       }
                   } else {
                       $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                                 &mt('Filename of bubblesheet data file requested for deletion is invalid.'));
                   }
               }
           } else {
               $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'. 
                         &mt('You are not permitted to delete bubblesheet data files from the requested course.'));
           }
       } else {
           $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('File deletion failed'),1).'<br />'.
                             &mt('Filename of bubblesheet data file requested for deletion is invalid.'));
       }
       return;
   }
   
 sub valid_file {  sub valid_file {
     my ($requested_file)=@_;      my ($requested_file)=@_;
     foreach my $filename (sort(&scantron_filenames())) {      foreach my $filename (sort(&scantron_filenames())) {
Line 9101  sub valid_file { Line 11274  sub valid_file {
 }  }
   
 sub scantron_download_scantron_data {  sub scantron_download_scantron_data {
     my ($r,$symb)=@_;      my ($r,$symb) = @_;
     my $default_form_data=&defaultFormData($symb);      my $default_form_data=&defaultFormData($symb);
     my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};      my $cname=$env{'course.'.$env{'request.course.id'}.'.num'};
     my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};      my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'};
Line 9114  sub scantron_download_scantron_data { Line 11287  sub scantron_download_scantron_data {
 ');  ');
  return;   return;
     }      }
       my (%uploader,$is_owner,%counts,$percent);
       my %uploader = &Apache::lonnet::get('scantronupload',[$file],$cdom,$cname);
       if (ref($uploader{$file}) eq 'HASH') {
           foreach my $timestamp (sort { $a <=> $b } keys(%{$uploader{$file}})) {
               if ($uploader{$file}{$timestamp} eq $env{'user.name'}.':'.$env{'user.domain'}) {
                   $is_owner = 1;
                   last;
               }
           }
       }
       unless ($is_owner) {
           &validate_uploaded_scantron_file($cdom,$cname,$symb,'scantron_orig_'.$file,'download',\%counts);
           if ($counts{'totalanysec'}) {
               my $percent_othersec = (100*$counts{'othersec'})/($counts{'totalanysec'});
               if ($percent_othersec >= 10) {
                   my $showpct = sprintf("%.0f",$percent_othersec).'%';
                   $r->print('<p class="LC_warning">'.
                             &mt('The original uploaded file includes [_1] or more of records for students for which none of your roles have rights to modify grades, so files are unavailable for download.',$showpct).
                             '</p>');
                   return;
               }
           }
       }
     my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;      my $orig='/uploaded/'.$cdom.'/'.$cname.'/scantron_orig_'.$file;
     my $corrected='/uploaded/'.$cdom.'/'.$cname.'/scantron_corrected_'.$file;      my $corrected='/uploaded/'.$cdom.'/'.$cname.'/scantron_corrected_'.$file;
     my $skipped='/uploaded/'.$cdom.'/'.$cname.'/scantron_skipped_'.$file;      my $skipped='/uploaded/'.$cdom.'/'.$cname.'/scantron_skipped_'.$file;
Line 9141  sub checkscantron_results { Line 11337  sub checkscantron_results {
     my ($r,$symb) = @_;      my ($r,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
     my $cid = $env{'request.course.id'};      my $cid = $env{'request.course.id'};
     my %lettdig = &letter_to_digits();      my %lettdig = &Apache::lonnet::letter_to_digits();
     my $numletts = scalar(keys(%lettdig));      my $numletts = scalar(keys(%lettdig));
     my $cnum = $env{'course.'.$cid.'.num'};      my $cnum = $env{'course.'.$cid.'.num'};
     my $cdom = $env{'course.'.$cid.'.domain'};      my $cdom = $env{'course.'.$cid.'.domain'};
     my (undef, undef, $sequence) = &Apache::lonnet::decode_symb($env{'form.selectpage'});      my (undef, undef, $sequence) = &Apache::lonnet::decode_symb($env{'form.selectpage'});
     my %record;      my %record;
     my %scantron_config =      my %scantron_config =
         &Apache::grades::get_scantron_config($env{'form.scantron_format'});          &Apache::lonnet::get_scantron_config($env{'form.scantron_format'});
     my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);      my $bubbles_per_row = &bubblesheet_bubbles_per_row(\%scantron_config);
     my ($scanlines,$scan_data)=&Apache::grades::scantron_getfile();      my ($scanlines,$scan_data)=&scantron_getfile();
     my $classlist=&Apache::loncoursedata::get_classlist();      my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&Apache::grades::username_to_idmap($classlist);      my %idmap=&Apache::grades::username_to_idmap($classlist);
     my $navmap=Apache::lonnavmaps::navmap->new();      my $navmap=Apache::lonnavmaps::navmap->new();
Line 9164  sub checkscantron_results { Line 11360  sub checkscantron_results {
     if (ref($map)) {       if (ref($map)) { 
         $randomorder=$map->randomorder();          $randomorder=$map->randomorder();
         $randompick=$map->randompick();          $randompick=$map->randompick();
           unless ($randomorder || $randompick) {
               foreach my $res ($navmap->retrieveResources($map,sub { $_[0]->is_map() },1,0,1)) {
                   if ($res->randomorder()) {
                       $randomorder = 1;
                   }
                   if ($res->randompick()) {
                       $randompick = 1;
                   }
                   last if ($randomorder || $randompick);
               }
           }
     }      }
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);      my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
     my $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);      my $nav_error = &get_master_seq(\@resources,\@master_seq,\%symb_to_resource);
Line 9258  sub checkscantron_results { Line 11465  sub checkscantron_results {
             my $ressymb = $resource->symb();              my $ressymb = $resource->symb();
             if ((exists($grader_randomlists_by_symb{$ressymb})) ||              if ((exists($grader_randomlists_by_symb{$ressymb})) ||
                 (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {                  (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
                   my $currcode;
                   if (exists($grader_randomlists_by_symb{$ressymb})) {
                       $currcode = $scancode;
                   }
                 (my $analysis,$parts) =                  (my $analysis,$parts) =
                     &scantron_partids_tograde($resource,$env{'request.course.id'},                      &scantron_partids_tograde($resource,$env{'request.course.id'},
                                               $username,$domain,undef,                                                $username,$domain,undef,
                                               $bubbles_per_row);                                                $bubbles_per_row,$currcode);
             } else {              } else {
                 $parts = $grader_partids_by_symb{$ressymb};                  $parts = $grader_partids_by_symb{$ressymb};
             }              }
Line 9468  sub verify_scantron_grading { Line 11679  sub verify_scantron_grading {
     return ($counter,$record);      return ($counter,$record);
 }  }
   
 sub letter_to_digits {  
     my %lettdig = (  
                     A => 1,  
                     B => 2,  
                     C => 3,  
                     D => 4,  
                     E => 5,  
                     F => 6,  
                     G => 7,  
                     H => 8,  
                     I => 9,  
                     J => 0,  
                   );  
     return %lettdig;  
 }  
   
   
 #-------- end of section for handling grading scantron forms -------  #-------- end of section for handling grading scantron forms -------
 #  #
 #-------------------------------------------------------------------  #-------------------------------------------------------------------
Line 9495  sub letter_to_digits { Line 11689  sub letter_to_digits {
   
 sub href_symb_cmd {  sub href_symb_cmd {
     my ($symb,$cmd)=@_;      my ($symb,$cmd)=@_;
     return '/adm/grades?symb='.&HTML::Entities::encode(&Apache::lonenc::check_encrypt($symb),'<>&"').'&amp;command='.$cmd;      return '/adm/grades?symb='.&HTML::Entities::encode(&Apache::lonenc::check_encrypt($symb),'<>&"').'&amp;command='.
              &HTML::Entities::encode($cmd,'<>&"');
 }  }
   
 sub grading_menu {  sub grading_menu {
Line 9530  sub grading_menu { Line 11725  sub grading_menu {
   
     $fields{'command'} = 'initialverifyreceipt';      $fields{'command'} = 'initialverifyreceipt';
     my $url5 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);      my $url5 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
       
       my %permissions;
       if ($perm{'mgr'}) {
           $permissions{'either'} = 'F';
           $permissions{'mgr'} = 'F';
       }
       if ($perm{'vgr'}) {
           $permissions{'either'} = 'F';
           $permissions{'vgr'} = 'F';
       }
   
     my @menu = ({ categorytitle=>'Hand Grading',      my @menu = ({ categorytitle=>'Hand Grading',
             items =>[              items =>[
                         { linktext => 'Select individual students to grade',                          { linktext => 'Select individual students to grade',
                     url => $url1a,                      url => $url1a,
                     permission => 'F',                      permission => $permissions{'either'},
                     icon => 'grade_students.png',                      icon => 'grade_students.png',
                     linktitle => 'Grade current resource for a selection of students.'                      linktitle => 'Grade current resource for a selection of students.'
                         },                           }, 
                         {       linktext => 'Grade ungraded submissions.',                          {       linktext => 'Grade ungraded submissions',
                                 url => $url1b,                                  url => $url1b,
                                 permission => 'F',                                  permission => $permissions{'either'},
                                 icon => 'ungrade_sub.png',                                  icon => 'ungrade_sub.png',
                                 linktitle => 'Grade all submissions that have not been graded yet.'                                  linktitle => 'Grade all submissions that have not been graded yet.'
                         },                          },
   
                         {       linktext => 'Grading table',                          {       linktext => 'Grading table',
                                 url => $url1c,                                  url => $url1c,
                                 permission => 'F',                                  permission => $permissions{'either'},
                                 icon => 'grading_table.png',                                  icon => 'grading_table.png',
                                 linktitle => 'Grade current resource for all students.'                                  linktitle => 'Grade current resource for all students.'
                         },                          },
                         {       linktext => 'Grade page/folder for one student',                          {       linktext => 'Grade page/folder for one student',
                                 url => $url1d,                                  url => $url1d,
                                 permission => 'F',                                  permission => $permissions{'either'},
                                 icon => 'grade_PageFolder.png',                                  icon => 'grade_PageFolder.png',
                                 linktitle => 'Grade all resources in current page/sequence/folder for one student.'                                  linktitle => 'Grade all resources in current page/sequence/folder for one student.'
                         },                          },
                         {       linktext => 'Download submissions',                          {       linktext => 'Download submissions',
                                 url => $url1e,                                  url => $url1e,
                                 permission => 'F',                                  permission => $permissions{'either'},
                                 icon => 'download_sub.png',                                  icon => 'download_sub.png',
                                 linktitle => 'Download all students submissions.'                                  linktitle => 'Download all students submissions.'
                         }]},                          }]},
Line 9569  sub grading_menu { Line 11774  sub grading_menu {
   
                    { linktext => 'Upload Scores',                     { linktext => 'Upload Scores',
                     url => $url2,                      url => $url2,
                     permission => 'F',                      permission => $permissions{'mgr'},
                     icon => 'uploadscores.png',                      icon => 'uploadscores.png',
                     linktitle => 'Specify a file containing the class scores for current resource.'                      linktitle => 'Specify a file containing the class scores for current resource.'
                    },                     },
                    { linktext => 'Process Clicker',                     { linktext => 'Process Clicker',
                     url => $url3,                      url => $url3,
                     permission => 'F',                      permission => $permissions{'mgr'},
                     icon => 'addClickerInfoFile.png',                      icon => 'addClickerInfoFile.png',
                     linktitle => 'Specify a file containing the clicker information for this resource.'                      linktitle => 'Specify a file containing the clicker information for this resource.'
                    },                     },
                    { linktext => 'Grade/Manage/Review Bubblesheets',                     { linktext => 'Grade/Manage/Review Bubblesheets',
                     url => $url4,                      url => $url4,
                     permission => 'F',                      permission => $permissions{'mgr'},
                     icon => 'bubblesheet.png',                      icon => 'bubblesheet.png',
                     linktitle => 'Grade bubblesheet exams, upload/download bubblesheet data files, and review previously graded bubblesheet exams.'                      linktitle => 'Grade bubblesheet exams, upload/download bubblesheet data files, and review previously graded bubblesheet exams.'
                    },                     },
                             {   linktext => 'Verify Receipt Number',                              {   linktext => 'Verify Receipt Number',
                                 url => $url5,                                  url => $url5,
                                 permission => 'F',                                  permission => $permissions{'either'},
                                 icon => 'receipt_number.png',                                  icon => 'receipt_number.png',
                                 linktitle => 'Verify a system-generated receipt number for correct problem solution.'                                  linktitle => 'Verify a system-generated receipt number for correct problem solution.'
                             }                              }
   
                     ]                      ]
             });              });
       my $cdom = $env{"course.$env{'request.course.id'}.domain"};
       my $cnum = $env{"course.$env{'request.course.id'}.num"};
       my %passback = &Apache::lonnet::dump('nohist_linkprot_passback',$cdom,$cnum);
       if (keys(%passback)) {
           $fields{'command'} = 'initialpassback';
           my $url6 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
           push (@{$menu[1]{items}},
                     { linktext => 'Passback of Scores',
                       url => $url6,
                       permission => $permissions{'either'},
                       icon => 'passback.png',
                       linktitle => 'Passback scores to launcher CMS for resources accessed via LTI-mediated deep-linking',
                     });
       }
     # Create the menu      # Create the menu
     my $Str;      my $Str;
     $Str .= '<form method="post" action="" name="gradingMenu">';      $Str .= '<form method="post" action="" name="gradingMenu">';
Line 9605  sub grading_menu { Line 11823  sub grading_menu {
     return $Str;          return $Str;    
 }  }
   
   
 sub ungraded {  sub ungraded {
     my ($request)=@_;      my ($request)=@_;
     &submit_options($request);      &submit_options($request);
Line 9633  sub submit_options_table { Line 11850  sub submit_options_table {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
     &commonJSfunctions($request);      &commonJSfunctions($request);
       my $is_tool = ($symb =~ /ext\.tool$/);
     my $result;      my $result;
   
     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".      $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
         '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";          '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
   
     $result.=&selectfield(1).      $result.=&selectfield(1,$is_tool).
             '<input type="hidden" name="command" value="viewgrades" />              '<input type="hidden" name="command" value="viewgrades" />
             <div>              <div>
               <input type="submit" value="'.&mt('Next').' &rarr;" />                <input type="submit" value="'.&mt('Next').' &rarr;" />
Line 9652  sub submit_options_download { Line 11870  sub submit_options_download {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
       my $res_error;
       my ($partlist,$handgrade,$responseType,$numresp,$numessay,$numdropbox) =
           &response_type($symb,\$res_error);
       if ($res_error) {
           $request->print(&mt('An error occurred retrieving response types'));
           return;
       }
       unless ($numessay) {
           $request->print(&mt('No essayresponse items found'));
           return;
       }
       my $table;
       if (ref($partlist) eq 'ARRAY') {
           if (scalar(@$partlist) > 1 ) {
               $table = &showResourceInfo($symb,$partlist,$responseType,'gradingMenu',1,1);
           }
       }
   
       my $is_tool = ($symb =~ /ext\.tool$/);
     &commonJSfunctions($request);      &commonJSfunctions($request);
   
     my $result='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".      my $result='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
         '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";                 $table."\n".
                  '<input type="hidden" name="symb" value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
     $result.='      $result.='
 <h2>  <h2>
   '.&mt('Select Students for Which to Download Submissions').'    '.&mt('Select Students for whom to Download Submissions').'
 </h2>'.&selectfield(1).'  </h2>'.&selectfield(1,$is_tool).'
                 <input type="hidden" name="command" value="downloadfileslink" />                   <input type="hidden" name="command" value="downloadfileslink" /> 
               <input type="submit" value="'.&mt('Next').' &rarr;" />                <input type="submit" value="'.&mt('Next').' &rarr;" />
             </div>              </div>
Line 9675  sub submit_options { Line 11913  sub submit_options {
     my ($request,$symb) = @_;      my ($request,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
       my $is_tool = ($symb =~ /ext\.tool$/);
     &commonJSfunctions($request);      &commonJSfunctions($request);
     my $result;      my $result;
   
     $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".      $result.='<form action="/adm/grades" method="post" name="gradingMenu">'."\n".
  '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";   '<input type="hidden" name="symb"        value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n";
     $result.=&selectfield(1).'      $result.=&selectfield(1,$is_tool).'
                 <input type="hidden" name="command" value="submission" />                   <input type="hidden" name="command" value="submission" /> 
       <input type="submit" value="'.&mt('Next').' &rarr;" />        <input type="submit" value="'.&mt('Next').' &rarr;" />
             </div>              </div>
           </div>            </div>
   
   
   </form>';    </form>';
     return $result;      return $result;
 }  }
   
 sub selectfield {  sub selectfield {
    my ($full)=@_;     my ($full,$is_tool)=@_;
    my %options =      my %options;
           (&substatus_options,     if ($is_tool) {
            'select_form_order' => ['yes','queued','graded','incorrect','all']);         %options =
              (&transtatus_options,
               'select_form_order' => ['yes','incorrect','all']);
      } else {
          %options = 
              (&substatus_options,
               'select_form_order' => ['yes','queued','graded','incorrect','all']);
      }
   
     #
     # PrepareClasslist() needs to be called to avoid getting a sections list
     # for a different course from the @Sections global in lonstatistics.pm, 
     # populated by an earlier request.
     #
      &Apache::lonstatistics::PrepareClasslist();
   
    my $result='<div class="LC_columnSection">     my $result='<div class="LC_columnSection">
       
     <fieldset>      <fieldset>
Line 9719  sub selectfield { Line 11971  sub selectfield {
       '.&Apache::lonhtmlcommon::StatusOptions(undef,undef,5,undef,'mult').'        '.&Apache::lonhtmlcommon::StatusOptions(undef,undef,5,undef,'mult').'
     </fieldset>';      </fieldset>';
     if ($full) {      if ($full) {
        $result.='          my $heading = &mt('Submission Status');
           if ($is_tool) {
               $heading = &mt('Transaction Status');
           }
           $result.='
     <fieldset>      <fieldset>
       <legend>        <legend>
         '.&mt('Submission Status').'          '.$heading.'
       </legend>'.        </legend>'.
        &Apache::loncommon::select_form('all','submitonly',\%options).         &Apache::loncommon::select_form('all','submitonly',\%options).
    '</fieldset>';     '</fieldset>';
Line 9737  sub substatus_options { Line 11993  sub substatus_options {
                                       'queued'    => 'in grading queue',                                        'queued'    => 'in grading queue',
                                       'graded'    => 'with ungraded submissions',                                        'graded'    => 'with ungraded submissions',
                                       'incorrect' => 'with incorrect submissions',                                        'incorrect' => 'with incorrect submissions',
                                       'all'       => 'with any status');                                        'all'       => 'with any status',
                                         );
   }
   
   sub transtatus_options {
       return &Apache::lonlocal::texthash(
                                          'yes'       => 'with score transactions',
                                          'incorrect' => 'with less than full credit',
                                          'all'       => 'with any status',
                                         );
 }  }
   
 sub reset_perm {  sub reset_perm {
Line 9746  sub reset_perm { Line 12011  sub reset_perm {
   
 sub init_perm {  sub init_perm {
     &reset_perm();      &reset_perm();
     foreach my $test_perm ('vgr','mgr','opa') {      foreach my $test_perm ('vgr','mgr','opa','usc') {
   
  my $scope = $env{'request.course.id'};   my $scope = $env{'request.course.id'};
  if (!($perm{$test_perm}=&Apache::lonnet::allowed($test_perm,$scope))) {   if (!($perm{$test_perm}=&Apache::lonnet::allowed($test_perm,$scope))) {
Line 9934  ENDUPFORM Line 12199  ENDUPFORM
 <input type="text" name="givenanswer" size="50" />  <input type="text" name="givenanswer" size="50" />
 <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />  <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />
 ENDGRADINGFORM  ENDGRADINGFORM
          $result.='</td>'.&Apache::loncommon::end_data_table_row().      $result.='</td>'.&Apache::loncommon::end_data_table_row().
                      &Apache::loncommon::start_data_table_row().'<td>'.(<<ENDPERCFORM);                       &Apache::loncommon::start_data_table_row().'<td>'.(<<ENDPERCFORM);
       <label>$pcorrect: <input type="text" name="pcorrect" size="4" value="$env{'form.pcorrect'}" onchange="sanitycheck()" /></label>        <label>$pcorrect: <input type="text" name="pcorrect" size="4" value="$env{'form.pcorrect'}" onchange="sanitycheck()" /></label>
 <br /><label>$pincorrect: <input type="text" name="pincorrect" size="4" value="$env{'form.pincorrect'}" onchange="sanitycheck()" /></label>  <br /><label>$pincorrect: <input type="text" name="pincorrect" size="4" value="$env{'form.pincorrect'}" onchange="sanitycheck()" /></label>
 <br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />  <br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />
 </form>'  </form>
 ENDPERCFORM  ENDPERCFORM
     $result.='</td>'.      $result.='</td>'.
              &Apache::loncommon::end_data_table_row().               &Apache::loncommon::end_data_table_row().
Line 9948  ENDPERCFORM Line 12213  ENDPERCFORM
 }  }
   
 sub process_clicker_file {  sub process_clicker_file {
     my ($r,$symb)=@_;      my ($r,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
   
     my %Saveable_Parameters=&clicker_grading_parameters();      my %Saveable_Parameters=&clicker_grading_parameters();
Line 10020  sub process_clicker_file { Line 12285  sub process_clicker_file {
                         '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'),1);                          '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'),1);
         return $result;          return $result;
     }      }
       my $mimetype;
       if ($env{'form.upfiletype'} eq 'iclicker') {
           my $mm = new File::MMagic;
           $mimetype = $mm->checktype_contents($env{'form.upfile'});
           unless (($mimetype eq 'text/plain') || ($mimetype eq 'text/html')) {
               $result.= '<p>'.
                   &Apache::lonhtmlcommon::confirm_success(
                       &mt('File format is neither csv (iclicker 6) nor xml (iclicker 7)'),1).'</p>';
               return $result;
           }
       } elsif (($env{'form.upfiletype'} ne 'interwrite') && ($env{'form.upfiletype'} ne 'turning')) {
           $result .= '<p>'.
               &Apache::lonhtmlcommon::confirm_success(
                   &mt('Invalid clicker type: choose one of: i>clicker, Interwrite PRS, or Turning Technologies.'),1).'</p>';
           return $result;
       }
   
 # Were able to get all the info needed, now analyze the file  # Were able to get all the info needed, now analyze the file
   
Line 10046  ENDHEADER Line 12327  ENDHEADER
     my $errormsg='';      my $errormsg='';
     my $number=0;      my $number=0;
     if ($env{'form.upfiletype'} eq 'iclicker') {      if ($env{'form.upfiletype'} eq 'iclicker') {
  ($errormsg,$number)=&iclicker_eval(\@questiontitles,\%responses);          if ($mimetype eq 'text/plain') {
     }              ($errormsg,$number)=&iclicker_eval(\@questiontitles,\%responses);
     if ($env{'form.upfiletype'} eq 'interwrite') {          } elsif ($mimetype eq 'text/html') {
               ($errormsg,$number)=&iclickerxml_eval(\@questiontitles,\%responses);
           }
       } elsif ($env{'form.upfiletype'} eq 'interwrite') {
         ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);          ($errormsg,$number)=&interwrite_eval(\@questiontitles,\%responses);
     }      } elsif ($env{'form.upfiletype'} eq 'turning') {
     if ($env{'form.upfiletype'} eq 'turning') {  
         ($errormsg,$number)=&turning_eval(\@questiontitles,\%responses);          ($errormsg,$number)=&turning_eval(\@questiontitles,\%responses);
     }      }
     $result.='<br />'.&mt('Found [_1] question(s)',$number).'<br />'.      $result.='<br />'.&mt('Found [_1] question(s)',$number).'<br />'.
Line 10104  ENDHEADER Line 12387  ENDHEADER
                    "\n".&mt("Username").": <input type='text' name='uname".$id."' />&nbsp;".                     "\n".&mt("Username").": <input type='text' name='uname".$id."' />&nbsp;".
                    "\n".&mt("Domain").": ".                     "\n".&mt("Domain").": ".
                    &Apache::loncommon::select_dom_form($env{'course.'.$env{'request.course.id'}.'.domain'},'udom'.$id).'&nbsp;'.                     &Apache::loncommon::select_dom_form($env{'course.'.$env{'request.course.id'}.'.domain'},'udom'.$id).'&nbsp;'.
                    &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id,0,$id);                     &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id,'',$id);
           $unknown_count++;            $unknown_count++;
        }         }
     }      }
Line 10159  sub iclicker_eval { Line 12442  sub iclicker_eval {
     return ($errormsg,$number);      return ($errormsg,$number);
 }  }
   
   sub iclickerxml_eval {
       my ($questiontitles,$responses)=@_;
       my $number=0;
       my $errormsg='';
       my @state;
       my %respbyid;
       my $p = HTML::Parser->new
       (
           xml_mode => 1,
           start_h =>
               [sub {
                    my ($tagname,$attr) = @_;
                    push(@state,$tagname);
                    if ("@state" eq "ssn p") {
                        my $title = $attr->{qn};
                        $title =~ s/(^\s+|\s+$)//g;
                        $questiontitles->[$number]=$title;
                    } elsif ("@state" eq "ssn p v") {
                        my $id = $attr->{id};
                        my $entry = $attr->{ans};
                        $id=~s/^[\#0]+//;
                        $entry =~s/[^a-zA-Z0-9\.\*\-\+]+//g;
                        $respbyid{$id}[$number] = $entry;
                    }
               }, "tagname, attr"],
            end_h =>
                  [sub {
                      my ($tagname) = @_;
                      if ("@state" eq "ssn p") {
                          $number++;
                      }
                      pop(@state);
                   }, "tagname"],
       );
   
       $p->parse($env{'form.upfile'});
       $p->eof;
       foreach my $id (keys(%respbyid)) {
           $responses->{$id}=join(',',@{$respbyid{$id}});
       }
       return ($errormsg,$number);
   }
   
 sub interwrite_eval {  sub interwrite_eval {
     my ($questiontitles,$responses)=@_;      my ($questiontitles,$responses)=@_;
     my $number=0;      my $number=0;
Line 10217  sub turning_eval { Line 12543  sub turning_eval {
   
   
 sub assign_clicker_grades {  sub assign_clicker_grades {
     my ($r,$symb)=@_;      my ($r,$symb) = @_;
     if (!$symb) {return '';}      if (!$symb) {return '';}
 # See which part we are saving to  # See which part we are saving to
     my $res_error;      my $res_error;
Line 10228  sub assign_clicker_grades { Line 12554  sub assign_clicker_grades {
 # FIXME: This should probably look for the first handgradeable part  # FIXME: This should probably look for the first handgradeable part
     my $part=$$partlist[0];      my $part=$$partlist[0];
 # Start screen output  # Start screen output
     my $result=&Apache::loncommon::start_data_table().      my $result = &Apache::loncommon::start_data_table().
              &Apache::loncommon::start_data_table_header_row().                   &Apache::loncommon::start_data_table_header_row().
              '<th>'.&mt('Assigning grades based on clicker file').'</th>'.                   '<th>'.&mt('Assigning grades based on clicker file').'</th>'.
              &Apache::loncommon::end_data_table_header_row().                   &Apache::loncommon::end_data_table_header_row().
              &Apache::loncommon::start_data_table_row().'<td>';                   &Apache::loncommon::start_data_table_row().'<td>';
 # Get correct result  # Get correct result
 # FIXME: Possibly need delimiter other than ":"  # FIXME: Possibly need delimiter other than ":"
     my @correct=();      my @correct=();
Line 10294  sub assign_clicker_grades { Line 12620  sub assign_clicker_grades {
           for (my $i=0;$i<$number;$i++) {            for (my $i=0;$i<$number;$i++) {
              if  ($correct[$i] eq '-') {               if  ($correct[$i] eq '-') {
                 $realnumber--;                  $realnumber--;
              } elsif (($answer[$i]) || ($answer[$i]=~/^[0\.]+$/))  {               } elsif (($answer[$i]) || ($answer[$i]=~/^[0\.]+$/)) {
                 if ($gradingmechanism eq 'attendance') {                  if ($gradingmechanism eq 'attendance') {
                    $sum+=$pcorrect;                     $sum+=$pcorrect;
                 } elsif ($correct[$i] eq '*') {                  } elsif ($correct[$i] eq '*') {
Line 10334  sub assign_clicker_grades { Line 12660  sub assign_clicker_grades {
              $result.="<br /><span class=\"LC_error\">Failed to save student $username:$domain. Message when trying to save was ($returncode)</span>";               $result.="<br /><span class=\"LC_error\">Failed to save student $username:$domain. Message when trying to save was ($returncode)</span>";
           } else {            } else {
              $storecount++;               $storecount++;
                #FIXME Do passback for $user if required
           }            }
        }         }
     }      }
Line 10353  sub navmap_errormsg { Line 12680  sub navmap_errormsg {
 }  }
   
 sub startpage {  sub startpage {
     my ($r,$symb,$crumbs,$onlyfolderflag,$nodisplayflag,$stuvcurrent,$stuvdisp,$nomenu,$js) = @_;      my ($r,$symb,$crumbs,$onlyfolderflag,$nodisplayflag,$stuvcurrent,$stuvdisp,$nomenu,$head_extra,$onload,$divforres) = @_;
       my %args;
       if ($onload) {
            my %loaditems = (
                           'onload' => $onload,
                         );
            $args{'add_entries'} = \%loaditems;
       }
     if ($nomenu) {      if ($nomenu) {
         $r->print(&Apache::loncommon::start_page("Student's Version",$js,{'only_body' => '1'}));          $args{'only_body'} = 1; 
           $r->print(&Apache::loncommon::start_page("Student's Version",$head_extra,\%args));
     } else {      } else {
         unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});          if ($env{'request.course.id'}) { 
         $r->print(&Apache::loncommon::start_page('Grading',$js,              unshift(@$crumbs,{href=>&href_symb_cmd($symb,'gradingmenu'),text=>"Grading"});
                                                  {'bread_crumbs' => $crumbs}));          }
         &Apache::lonquickgrades::startGradeScreen($r,($env{'form.symb'}?'probgrading':'grading'));          $args{'bread_crumbs'} = $crumbs;
           $r->print(&Apache::loncommon::start_page('Grading',$head_extra,\%args));
           if ($env{'request.course.id'}) {
               &Apache::lonquickgrades::startGradeScreen($r,($env{'form.symb'}?'probgrading':'grading'));
           }
     }      }
     unless ($nodisplayflag) {      unless ($nodisplayflag) {
        $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp));          $r->print(&Apache::lonhtmlcommon::resource_info_box($symb,$onlyfolderflag,$stuvcurrent,$stuvdisp,$divforres));
     }      }
 }  }
   
 sub select_problem {  sub select_problem {
     my ($r)=@_;      my ($r)=@_;
     $r->print('<h3>'.&mt('Select the problem or one of the problems you want to grade').'</h3><form action="/adm/grades">');      $r->print('<h3>'.&mt('Select the problem or one of the problems you want to grade').'</h3><form action="/adm/grades">');
     $r->print(&Apache::lonstathelpers::problem_selector('.',undef,1));      $r->print(&Apache::lonstathelpers::problem_selector('.',undef,1,undef,undef,1,1));
     $r->print('<input type="hidden" name="command" value="gradingmenu" />');      $r->print('<input type="hidden" name="command" value="gradingmenu" />');
     $r->print('<input type="submit" value="'.&mt('Next').' &rarr;" /></form>');      $r->print('<input type="submit" value="'.&mt('Next').' &rarr;" /></form>');
 }  }
   
   #----- display problem, answer, and submissions for a single student (no grading)
   
   sub view_as_user {
       my ($symb,$vuname,$vudom,$hasperm) = @_;
       my $plainname = &Apache::loncommon::plainname($vuname,$vudom,'lastname');
       my $displayname = &nameUserString('',$plainname,$vuname,$vudom);
       my $output = &Apache::loncommon::get_student_view($symb,$vuname,$vudom,
                                                         $env{'request.course.id'},
                                                         undef,{'disable_submit' => 1}).
                    "\n\n".
                    '<div class="LC_grade_show_user">'.
                    '<h2>'.$displayname.'</h2>'.
                    "\n".
                    &Apache::loncommon::track_student_link('View recent activity',
                                                           $vuname,$vudom,'check').' '.
                    "\n";
       if (&Apache::lonnet::allowed('opa',$env{'request.course.id'}) ||
           (($env{'request.course.sec'} ne '') &&
            &Apache::lonnet::allowed('opa',$env{'request.course.id'}.'/'.$env{'request.course.sec'}))) {
           $output .= &Apache::loncommon::pprmlink(&mt('Set/Change parameters'),
                                                  $vuname,$vudom,$symb,'check');
       }
       $output .= "\n";
       my $companswer = &Apache::loncommon::get_student_answers($symb,$vuname,$vudom,
                                                                $env{'request.course.id'});
       $companswer=~s|<form(.*?)>||g;
       $companswer=~s|</form>||g;
       $companswer=~s|name="submit"|name="would_have_been_submit"|g;
       $output .= '<div class="LC_Box">'.
                  '<h3 class="LC_hcell">'.&mt('Correct answer for[_1]',$displayname).'</h3>'.
                  $companswer.
                  '</div>'."\n";
       my $is_tool = ($symb =~ /ext\.tool$/);
       my ($essayurl,%coursedesc_by_cid);
       (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);
       my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$vudom,$vuname);
       my $res_error;
       my ($partlist,$handgrade,$responseType,$numresp,$numessay) =
           &response_type($symb,\$res_error);
       my $fullname;
       my $collabinfo;
       if ($numessay) {
           unless ($hasperm) {
               &init_perm();
           }
           ($collabinfo,$fullname)=
               &check_collaborators($symb,$vuname,$vudom,\%record,$handgrade,0);
           unless ($hasperm) {
               &reset_perm();
           }
       }
       my $checkIcon = '<img alt="'.&mt('Check Mark').
                       '" src="'.$Apache::lonnet::perlvar{'lonIconsURL'}.
                       '/check.gif" height="16" border="0" />';
       my ($lastsubonly,$partinfo) =
           &show_last_submission($vuname,$vudom,$symb,$essayurl,$responseType,'datesub',
                                 '',$fullname,\%record,\%coursedesc_by_cid);
       $output .= '<div class="LC_Box">'.
                  '<h3 class="LC_hcell">'.&mt('Submissions').'</h3>'."\n".$collabinfo."\n";
       if (($numresp > $numessay) & !$is_tool) {
           $output .='<p class="LC_info">'.
                     &mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon).
                     "</p>\n";
       }
       $output .= $partinfo;
       $output .= $lastsubonly;
       $output .= &displaySubByDates($symb,\%record,$partlist,$responseType,$checkIcon,$vuname,$vudom);
       $output .= '</div></div>'."\n";
       return $output;
   }
   
 sub handler {  sub handler {
     my $request=$_[0];      my $request=$_[0];
     &reset_caches();      &reset_caches();
Line 10409  sub handler { Line 12819  sub handler {
  &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));   &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands));
     }      }
   
   # -------------------------------------- Flag and buffer for registered cleanup
       $registered_cleanup=0;
       undef(@Apache::grades::ltipassback);
   
 # see what the symb is  # see what the symb is
   
     my $symb=$env{'form.symb'};      my $symb=$env{'form.symb'};
Line 10427  sub handler { Line 12841  sub handler {
         &select_problem($request);          &select_problem($request);
     } else {      } else {
  if ($command eq 'submission' && $perm{'vgr'}) {   if ($command eq 'submission' && $perm{'vgr'}) {
             my ($stuvcurrent,$stuvdisp,$versionform,$js);              my ($stuvcurrent,$stuvdisp,$versionform,$js,$onload);
             if (($env{'form.student'} ne '') && ($env{'form.userdom'} ne '')) {              if (($env{'form.student'} ne '') && ($env{'form.userdom'} ne '')) {
                 ($stuvcurrent,$stuvdisp,$versionform,$js) =                  ($stuvcurrent,$stuvdisp,$versionform,$js) =
                     &choose_task_version_form($symb,$env{'form.student'},                      &choose_task_version_form($symb,$env{'form.student'},
                                               $env{'form.userdom'});                                                $env{'form.userdom'});
             }              }
             &startpage($request,$symb,[{href=>"", text=>"Student Submissions"}],undef,undef,$stuvcurrent,$stuvdisp,undef,$js);              my $divforres;
               if ($env{'form.student'} eq '') {
                   $js .= &part_selector_js();
                   $onload = "toggleParts('gradesub');";
               } else {
                   $divforres = 1;
               }
               my $head_extra = $js;
               unless ($env{'form.vProb'} eq 'no') {
                   my $csslinks = &Apache::loncommon::css_links($symb);
                   if ($csslinks) {
                       $head_extra .= "\n$csslinks";
                   }
               }
               &startpage($request,$symb,[{href=>"", text=>"Student Submissions"}],undef,undef,
                          $stuvcurrent,$stuvdisp,undef,$head_extra,$onload,$divforres);
             if ($versionform) {              if ($versionform) {
                   if ($divforres) {
                       $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');
                   }
                 $request->print($versionform);                  $request->print($versionform);
             }              }
             $request->print('<br clear="all" />');      ($env{'form.student'} eq '' ? &listStudents($request,$symb,'',$divforres) : &submission($request,0,0,$symb,$divforres,$command));
     ($env{'form.student'} eq '' ? &listStudents($request,$symb) : &submission($request,0,0,$symb));  
         } elsif ($command eq 'versionsub' && $perm{'vgr'}) {          } elsif ($command eq 'versionsub' && $perm{'vgr'}) {
             my ($stuvcurrent,$stuvdisp,$versionform,$js) =              my ($stuvcurrent,$stuvdisp,$versionform,$js) =
                 &choose_task_version_form($symb,$env{'form.student'},                  &choose_task_version_form($symb,$env{'form.student'},
                                           $env{'form.userdom'},                                            $env{'form.userdom'},
                                           $env{'form.inhibitmenu'});                                            $env{'form.inhibitmenu'});
             &startpage($request,$symb,[{href=>"", text=>"Previous Student Version"}],undef,undef,$stuvcurrent,$stuvdisp,$env{'form.inhibitmenu'},$js);              my $head_extra = $js;
               unless ($env{'form.vProb'} eq 'no') {
                   my $csslinks = &Apache::loncommon::css_links($symb);
                   if ($csslinks) {
                       $head_extra .= "\n$csslinks";
                   }
               }
               &startpage($request,$symb,[{href=>"", text=>"Previous Student Version"}],undef,undef,
                          $stuvcurrent,$stuvdisp,$env{'form.inhibitmenu'},$head_extra);
             if ($versionform) {              if ($versionform) {
                 $request->print($versionform);                  $request->print($versionform);
             }              }
Line 10455  sub handler { Line 12894  sub handler {
                                        {href=>'',text=>'Select student'}],1,1);                                         {href=>'',text=>'Select student'}],1,1);
     &pickStudentPage($request,$symb);      &pickStudentPage($request,$symb);
  } elsif ($command eq 'displayPage' && $perm{'vgr'}) {   } elsif ($command eq 'displayPage' && $perm{'vgr'}) {
               my $csslinks;
               unless ($env{'form.vProb'} eq 'no') {
                   $csslinks = &Apache::loncommon::css_links($symb,'map');
               }
             &startpage($request,$symb,              &startpage($request,$symb,
                                       [{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},                                        [{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
                                        {href=>'',text=>'Select student'},                                         {href=>'',text=>'Select student'},
                                        {href=>'',text=>'Grade student'}],1,1);                                         {href=>'',text=>'Grade student'}],1,1,undef,undef,undef,$csslinks);
     &displayPage($request,$symb);      &displayPage($request,$symb);
  } elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {   } elsif ($command eq 'gradeByPage' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},              &startpage($request,$symb,[{href=>&href_symb_cmd($symb,'all_for_one'),text=>'Grade page/folder for one student'},
Line 10467  sub handler { Line 12910  sub handler {
                                        {href=>'',text=>'Store grades'}],1,1);                                         {href=>'',text=>'Store grades'}],1,1);
     &updateGradeByPage($request,$symb);      &updateGradeByPage($request,$symb);
  } elsif ($command eq 'processGroup' && $perm{'vgr'}) {   } elsif ($command eq 'processGroup' && $perm{'vgr'}) {
               my $csslinks;
               unless ($env{'form.vProb'} eq 'no') {
                   $csslinks = &Apache::loncommon::css_links($symb);
               }
             &startpage($request,$symb,[{href=>'',text=>'...'},              &startpage($request,$symb,[{href=>'',text=>'...'},
                                        {href=>'',text=>'Modify grades'}]);                                         {href=>'',text=>'Modify grades'}],undef,undef,undef,undef,undef,$csslinks,undef,1);
     &processGroup($request,$symb);      &processGroup($request,$symb);
  } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {   } elsif ($command eq 'gradingmenu' && $perm{'vgr'}) {
             &startpage($request,$symb);              &startpage($request,$symb);
Line 10477  sub handler { Line 12924  sub handler {
             &startpage($request,$symb,[{href=>'',text=>'Select individual students to grade'}]);              &startpage($request,$symb,[{href=>'',text=>'Select individual students to grade'}]);
     $request->print(&submit_options($request,$symb));      $request->print(&submit_options($request,$symb));
         } elsif ($command eq 'ungraded' && $perm{'vgr'}) {          } elsif ($command eq 'ungraded' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>'',text=>'Grade ungraded submissions'}]);              my $js = &part_selector_js();
               my $onload = "toggleParts('gradesub');";
               &startpage($request,$symb,[{href=>'',text=>'Grade ungraded submissions'}],
                          undef,undef,undef,undef,undef,$js,$onload);
             $request->print(&listStudents($request,$symb,'graded'));              $request->print(&listStudents($request,$symb,'graded'));
         } elsif ($command eq 'table' && $perm{'vgr'}) {          } elsif ($command eq 'table' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>"", text=>"Grading table"}]);              &startpage($request,$symb,[{href=>"", text=>"Grading table"}]);
Line 10543  sub handler { Line 12993  sub handler {
             &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Upload Scores'}],1,1);
     $request->print(&csvuploadassign($request,$symb));      $request->print(&csvuploadassign($request,$symb));
  } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {   } elsif ($command eq 'scantron_selectphase' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,
                          undef,undef,undef,undef,'toggleScantab(document.rules);');
     $request->print(&scantron_selectphase($request,undef,$symb));      $request->print(&scantron_selectphase($request,undef,$symb));
   } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {    } elsif ($command eq 'scantron_warning' && $perm{'mgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
Line 10555  sub handler { Line 13006  sub handler {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
     $request->print(&scantron_process_students($request,$symb));      $request->print(&scantron_process_students($request,$symb));
   } elsif ($command eq 'scantronupload' &&     } elsif ($command eq 'scantronupload' && 
   (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||    (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
   &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1,
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);                         undef,undef,undef,undef,'toggleScantab(document.rules);');
      $request->print(&scantron_upload_scantron_data($request,$symb));        $request->print(&scantron_upload_scantron_data($request,$symb)); 
   } elsif ($command eq 'scantronupload_save' &&    } elsif ($command eq 'scantronupload_save' &&
   (&Apache::lonnet::allowed('usc',$env{'request.role.domain'})||    (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
   &Apache::lonnet::allowed('usc',$env{'request.course.id'}))) {  
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
      $request->print(&scantron_upload_scantron_data_save($request,$symb));       $request->print(&scantron_upload_scantron_data_save($request,$symb));
   } elsif ($command eq 'scantron_download' &&    } elsif ($command eq 'scantron_download' && ($perm{'usc'} || $perm{'mgr'})) {
  &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {  
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
      $request->print(&scantron_download_scantron_data($request,$symb));       $request->print(&scantron_download_scantron_data($request,$symb));
           } elsif ($command eq 'scantronupload_delete' &&
                    (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) || $perm{'usc'})) {
               &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
               &scantron_upload_delete($request,$symb);
         } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {          } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);              &startpage($request,$symb,[{href=>'', text=>'Grade/Manage/Review Bubblesheets'}],1,1);
             $request->print(&checkscantron_results($request,$symb));              $request->print(&checkscantron_results($request,$symb));
         } elsif ($command eq 'downloadfilesselect' && $perm{'vgr'}) {          } elsif ($command eq 'downloadfilesselect' && $perm{'vgr'}) {
             &startpage($request,$symb,[{href=>'', text=>'Select which submissions to download'}]);              my $js = &part_selector_js();
               my $onload = "toggleParts('gradingMenu');";
               &startpage($request,$symb,[{href=>'', text=>'Select which submissions to download'}],
                          undef,undef,undef,undef,undef,$js,$onload);
             $request->print(&submit_options_download($request,$symb));              $request->print(&submit_options_download($request,$symb));
          } elsif ($command eq 'downloadfileslink' && $perm{'vgr'}) {           } elsif ($command eq 'downloadfileslink' && $perm{'vgr'}) {
             &startpage($request,$symb,              &startpage($request,$symb,
    [{href=>&href_symb_cmd($symb,'downloadfilesselect'), text=>'Select which submissions to download'},     [{href=>&href_symb_cmd($symb,'downloadfilesselect'), text=>'Select which submissions to download'},
     {href=>'', text=>'Download submissions'}]);      {href=>'', text=>'Download submitted files'}],
                  undef,undef,undef,undef,undef,undef,undef,1);
               $request->print('<div style="padding:0;clear:both;margin:0;border:0"></div>');
             &submit_download_link($request,$symb);              &submit_download_link($request,$symb);
           } elsif ($command eq 'initialpassback') {
               &startpage($request,$symb,[{href=>'', text=>'Choose Launcher'}],undef,1);
               $request->print(&initialpassback($request,$symb));
           } elsif ($command eq 'passback') {
               &startpage($request,$symb,
                          [{href=>&href_symb_cmd($symb,'initialpassback'), text=>'Choose Launcher'},
                           {href=>'', text=>'Types of User'}],undef,1);
               $request->print(&passback_filters($request,$symb));
           } elsif ($command eq 'passbacknames') {
               my $chosen;
               if ($env{'form.passback'} ne '') {
                   if ($env{'form.passback'} eq &unescape($env{'form.passback'})) {
                       $env{'form.passback'} = &escape($env{'form.passback'} );
                   }
                   $chosen = &HTML::Entities::encode($env{'form.passback'},'<>"&');
               }
               &startpage($request,$symb,
                          [{href=>&href_symb_cmd($symb,'initialpassback'), text=>'Choose Launcher'},
                           {href=>&href_symb_cmd($symb,'passback').'&amp;passback='.$chosen, text=>'Types of User'},
                           {href=>'', text=>'Select Users'}],undef,1);
               $request->print(&names_for_passback($request,$symb));
           } elsif ($command eq 'passbackscores') {
               my ($chosen,$stu_status);
               if ($env{'form.passback'} ne '') {
                   if ($env{'form.passback'} eq &unescape($env{'form.passback'})) {
                       $env{'form.passback'} = &escape($env{'form.passback'} );
                   }
                   $chosen = &HTML::Entities::encode($env{'form.passback'},'<>"&');
               }
               if ($env{'form.Status'}) {
                   $stu_status = &HTML::Entities::encode($env{'form.Status'});
               }
               &startpage($request,$symb,
                          [{href=>&href_symb_cmd($symb,'initialpassback'), text=>'Choose Launcher'},
                           {href=>&href_symb_cmd($symb,'passback').'&amp;passback='.$chosen, text=>'Types of User'},
                           {href=>&href_symb_cmd($symb,'passbacknames').'&amp;Status='.$stu_status.'&amp;passback='.$chosen, text=>'Select Users'},
                           {href=>'', text=>'Execute Passback'}],undef,1);
               $request->print(&do_passback($request,$symb));
  } elsif ($command) {   } elsif ($command) {
             &startpage($request,$symb,[{href=>'', text=>'Access denied'}]);              &startpage($request,$symb,[{href=>'', text=>'Access denied'}]);
     $request->print('<p class="LC_error">'.&mt('Access Denied ([_1])',$command).'</p>');      $request->print('<p class="LC_error">'.&mt('Access Denied ([_1])',$command).'</p>');
Line 10589  sub handler { Line 13085  sub handler {
     }      }
     if ($env{'form.inhibitmenu'}) {      if ($env{'form.inhibitmenu'}) {
         $request->print(&Apache::loncommon::end_page());          $request->print(&Apache::loncommon::end_page());
     } else {      } elsif ($env{'request.course.id'}) {
         &Apache::lonquickgrades::endGradeScreen($request);          &Apache::lonquickgrades::endGradeScreen($request);
     }      }
     &reset_caches();      &reset_caches();
Line 10740  Side Effects: None. Line 13236  Side Effects: None.
     $r           - Apache request object      $r           - Apache request object
     $i           - number of the current scanline      $i           - number of the current scanline
     $scan_record - hash ref as returned from &scantron_parse_scanline()      $scan_record - hash ref as returned from &scantron_parse_scanline()
     $scan_config - hash ref as returned from &get_scantron_config()      $scan_config - hash ref as returned from &Apache::lonnet::get_scantron_config()
     $line        - full contents of the current scanline      $line        - full contents of the current scanline
     $error       - error condition, valid values are      $error       - error condition, valid values are
                    'incorrectCODE', 'duplicateCODE',                     'incorrectCODE', 'duplicateCODE',
Line 10757  Side Effects: None. Line 13253  Side Effects: None.
          - missingbubble - array ref of the bubble lines that have missing           - missingbubble - array ref of the bubble lines that have missing
                            bubble errors                             bubble errors
   
    $randomorder - True if exam folder has randomorder set     $randomorder - True if exam folder (or a sub-folder) has randomorder set
    $randompick  - True if exam folder has randompick set     $randompick  - True if exam folder (or a sub-folder) has randompick set
    $respnumlookup - Reference to HASH mapping question numbers in bubble lines     $respnumlookup - Reference to HASH mapping question numbers in bubble lines
                      for current line to question number used for same question                       for current line to question number used for same question
                      in "Master Seqence" (as seen by Course Coordinator).                       in "Master Seqence" (as seen by Course Coordinator).
Line 10827  Side Effects: None. Line 13323  Side Effects: None.
 =item  scantron_upload_scantron_data_save() :   =item  scantron_upload_scantron_data_save() : 
   
    Adds a provided bubble information data file to the course if user     Adds a provided bubble information data file to the course if user
    has the correct privileges to do so.      has the correct privileges to do so.
   
   = item scantron_upload_delete() :
   
      Deletes a previously uploaded bubble information data file, if user
      was the one who uploaded the file, and has the privileges to do so.
   
 =item  valid_file() :  =item  valid_file() :
   

Removed from v.1.739  
changed lines
  Added in v.1.801


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>