--- loncom/homework/grades.pm	2008/12/17 13:06:17	1.537
+++ loncom/homework/grades.pm	2012/08/09 23:25:48	1.596.2.13
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.537 2008/12/17 13:06:17 harmsja Exp $
+# $Id: grades.pm,v 1.596.2.13 2012/08/09 23:25:48 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -40,9 +40,10 @@ use Apache::lonhomework;
 use Apache::lonpickcode;
 use Apache::loncoursedata;
 use Apache::lonmsg();
-use Apache::Constants qw(:common);
+use Apache::Constants qw(:common :http);
 use Apache::lonlocal;
 use Apache::lonenc;
+use Apache::bridgetask();
 use String::Similarity;
 use LONCAPA;
 
@@ -51,6 +52,7 @@ use POSIX qw(floor);
 
 
 my %perm=();
+my %old_essays=();
 
 #  These variables are used to recover from ssi errors
 
@@ -97,9 +99,15 @@ sub ssi_print_error {
 #
 # --- Retrieve the parts from the metadata file.---
 sub getpartlist {
-    my ($symb) = @_;
+    my ($symb,$errorref) = @_;
 
     my $navmap   = Apache::lonnavmaps::navmap->new();
+    unless (ref($navmap)) {
+        if (ref($errorref)) { 
+            $$errorref = 'navmap';
+            return;
+        }
+    }
     my $res      = $navmap->getBySymb($symb);
     my $partlist = $res->parts();
     my $url      = $res->src();
@@ -121,7 +129,7 @@ sub get_symb {
     my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
     if ($symb eq '') { 
 	if (!$silent) {
-	    $request->print("Unable to handle ambiguous references:$url:.");
+            $request->print(&mt("Unable to handle ambiguous references: [_1].",$url));
 	    return ();
 	}
     }
@@ -144,10 +152,20 @@ sub nameUserString {
 #--- Get the partlist and the response type for a given problem. ---
 #--- Indicate if a response type is coded handgraded or not. ---
 sub response_type {
-    my ($symb) = shift;
+    my ($symb,$response_error) = @_;
 
     my $navmap = Apache::lonnavmaps::navmap->new();
+    unless (ref($navmap)) {
+        if (ref($response_error)) {
+            $$response_error = 1;
+        }
+        return;
+    }
     my $res = $navmap->getBySymb($symb);
+    unless (ref($res)) {
+        $$response_error = 1;
+        return;
+    }
     my $partlist = $res->parts();
     my %vPart = 
 	map { $_ => 1 } (&Apache::loncommon::get_env_multiple('form.vPart'));
@@ -183,7 +201,8 @@ sub get_display_part {
     my ($partID,$symb)=@_;
     my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb);
     if (defined($display) and $display ne '') {
-	$display.= " (<span class=\"LC_internal_info\">id $partID</span>)";
+        $display.= ' (<span class="LC_internal_info">'
+                  .&mt('Part ID: [_1]',$partID).'</span>)';
     } else {
 	$display=$partID;
     }
@@ -193,87 +212,166 @@ sub get_display_part {
 #--- Show resource title
 #--- and parts and response type
 sub showResourceInfo {
-    my ($symb,$probTitle,$checkboxes) = @_;
-    my $col=3;
-    if ($checkboxes) { $col=4; }
+    my ($symb,$probTitle,$checkboxes,$res_error) = @_;
     my $result = '<h3>'.&mt('Current Resource').': '.$probTitle.'</h3>'."\n";
-    $result .='<table border="0">';
-    my ($partlist,$handgrade,$responseType) = &response_type($symb);
+    my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error);
+    if (ref($res_error)) {
+        if ($$res_error) {
+            return;
+        }
+    }
+    $result.=&Apache::loncommon::start_data_table()
+            .&Apache::loncommon::start_data_table_header_row();
+    if ($checkboxes) {
+        $result.='<th>&nbsp;</th>';
+    }
+    $result.='<th>'.&mt('Problem Part').'</th>'
+            .'<th>'.&mt('Res. ID').'</th>'
+            .'<th>'.&mt('Type').'</th>'
+            .&Apache::loncommon::end_data_table_header_row();
     my %resptype = ();
     my $hdgrade='no';
     my %partsseen;
     foreach my $partID (sort(keys(%$responseType))) {
-	foreach my $resID (sort(keys(%{ $responseType->{$partID} }))) {
-	    my $handgrade=$$handgrade{$partID.'_'.$resID};
-	    my $responsetype = $responseType->{$partID}->{$resID};
-	    $hdgrade = $handgrade if ($handgrade eq 'yes');
-	    $result.='<tr>';
-	    if ($checkboxes) {
-		if (exists($partsseen{$partID})) {
-		    $result.="<td>&nbsp;</td>";
-		} else {
-		    $result.="<td><input type='checkbox' name='vPart' value='$partID' checked='checked' /></td>";
-		}
-		$partsseen{$partID}=1;
-	    }
-	    my $display_part=&get_display_part($partID,$symb);
-	    $result.='<td>'.&mt('<b>Part: </b>[_1]',$display_part).' <span class="LC_internal_info">'.
-		$resID.'</span></td>'.
-		'<td>'.&mt('<b>Type: </b>[_1]',$responsetype).'</td></tr>';
-#	    '<td>'.&mt('<b>Handgrade: </b>[_1]',$handgrade).'</td></tr>';
-	}
+        foreach my $resID (sort(keys(%{ $responseType->{$partID} }))) {
+            my $handgrade=$$handgrade{$partID.'_'.$resID};
+            my $responsetype = $responseType->{$partID}->{$resID};
+            $hdgrade = $handgrade if ($handgrade eq 'yes');
+            $result.=&Apache::loncommon::start_data_table_row();
+            if ($checkboxes) {
+                if (exists($partsseen{$partID})) {
+                    $result.="<td>&nbsp;</td>";
+                } else {
+                    $result.="<td><input type='checkbox' name='vPart' value='$partID' checked='checked' /></td>";
+                }
+                $partsseen{$partID}=1;
+            }
+            my $display_part=&get_display_part($partID,$symb);
+            $result.='<td>'.$display_part.'</td>'
+                    .'<td>'.'<span class="LC_internal_info">'.$resID.'</span></td>'
+                    .'<td>'.&mt($responsetype).'</td>'
+#                   .'<td>'.&mt('<b>Handgrade: </b>[_1]',$handgrade).'</td>'
+                    .&Apache::loncommon::end_data_table_row();
+        }
     }
-    $result.='</table>'."\n";
+    $result.=&Apache::loncommon::end_data_table();
     return $result,$responseType,$hdgrade,$partlist,$handgrade;
 }
 
 sub reset_caches {
     &reset_analyze_cache();
     &reset_perm();
+    &reset_old_essays();
 }
 
 {
     my %analyze_cache;
+    my %analyze_cache_formkeys;
 
     sub reset_analyze_cache {
 	undef(%analyze_cache);
+        undef(%analyze_cache_formkeys);
     }
 
     sub get_analyze {
-	my ($symb,$uname,$udom,$no_increment)=@_;
+	my ($symb,$uname,$udom,$no_increment,$add_to_hash,$type,$trial,$rndseed)=@_;
 	my $key = "$symb\0$uname\0$udom";
-	return $analyze_cache{$key} if (exists($analyze_cache{$key}));
+        if ($type eq 'randomizetry') {
+            if ($trial ne '') {
+                $key .= "\0".$trial;
+            }
+        }
+	if (exists($analyze_cache{$key})) {
+            my $getupdate = 0;
+            if (ref($add_to_hash) eq 'HASH') {
+                foreach my $item (keys(%{$add_to_hash})) {
+                    if (ref($analyze_cache_formkeys{$key}) eq 'HASH') {
+                        if (!exists($analyze_cache_formkeys{$key}{$item})) {
+                            $getupdate = 1;
+                            last;
+                        }
+                    } else {
+                        $getupdate = 1;
+                    }
+                }
+            }
+            if (!$getupdate) {
+                return $analyze_cache{$key};
+            }
+        }
 
 	my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
 	$url=&Apache::lonnet::clutter($url);
-	my $subresult=&ssi_with_retries($url, $ssi_retries,
-					   ('grade_target' => 'analyze',
-					    'grade_domain' => $udom,
-					    'grade_symb' => $symb,
-					    'grade_courseid' => 
-					    $env{'request.course.id'},
-					    'grade_username' => $uname,
-                                            'grade_noincrement' => $no_increment));
+        my %form = ('grade_target'      => 'analyze',
+                    'grade_domain'      => $udom,
+                    'grade_symb'        => $symb,
+                    'grade_courseid'    =>  $env{'request.course.id'},
+                    'grade_username'    => $uname,
+                    'grade_noincrement' => $no_increment);
+        if ($type eq 'randomizetry') {
+            $form{'grade_questiontype'} = $type;
+            if ($rndseed ne '') {
+                $form{'grade_rndseed'} = $rndseed;
+            }
+        }
+        if (ref($add_to_hash)) {
+            %form = (%form,%{$add_to_hash});
+        }
+	my $subresult=&ssi_with_retries($url, $ssi_retries,%form);
 	(undef,$subresult)=split(/_HASH_REF__/,$subresult,2);
 	my %analyze=&Apache::lonnet::str2hash($subresult);
+        if (ref($add_to_hash) eq 'HASH') {
+            $analyze_cache_formkeys{$key} = $add_to_hash;
+        } else {
+            $analyze_cache_formkeys{$key} = {};
+        }
 	return $analyze_cache{$key} = \%analyze;
     }
 
     sub get_order {
-	my ($partid,$respid,$symb,$uname,$udom,$no_increment)=@_;
-	my $analyze = &get_analyze($symb,$uname,$udom,$no_increment);
+	my ($partid,$respid,$symb,$uname,$udom,$no_increment,$type,$trial,$rndseed)=@_;
+	my $analyze = &get_analyze($symb,$uname,$udom,$no_increment,undef,$type,$trial,$rndseed);
 	return $analyze->{"$partid.$respid.shown"};
     }
 
     sub get_radiobutton_correct_foil {
-	my ($partid,$respid,$symb,$uname,$udom)=@_;
-	my $analyze = &get_analyze($symb,$uname,$udom);
-	foreach my $foil (@{&get_order($partid,$respid,$symb,$uname,$udom)}) {
-	    if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') {
-		return $foil;
+	my ($partid,$respid,$symb,$uname,$udom,$type,$trial,$rndseed)=@_;
+	my $analyze = &get_analyze($symb,$uname,$udom,undef,undef,$type,$trial,$rndseed);
+        my $foils = &get_order($partid,$respid,$symb,$uname,$udom,undef,$type,$trial,$rndseed);
+        if (ref($foils) eq 'ARRAY') {
+	    foreach my $foil (@{$foils}) {
+	        if ($analyze->{"$partid.$respid.foil.value.$foil"} eq 'true') {
+		    return $foil;
+	        }
 	    }
 	}
     }
+
+    sub scantron_partids_tograde {
+        my ($resource,$cid,$uname,$udom,$check_for_randomlist) = @_;
+        my (%analysis,@parts);
+        if (ref($resource)) {
+            my $symb = $resource->symb();
+            my $add_to_form;
+            if ($check_for_randomlist) {
+                $add_to_form = { 'check_parts_withrandomlist' => 1,};
+            }
+            my $analyze = &get_analyze($symb,$uname,$udom,undef,$add_to_form);
+            if (ref($analyze) eq 'HASH') {
+                %analysis = %{$analyze};
+            }
+            if (ref($analysis{'parts'}) eq 'ARRAY') {
+                foreach my $part (@{$analysis{'parts'}}) {
+                    my ($id,$respid) = split(/\./,$part);
+                    if (!&Apache::loncommon::check_if_partid_hidden($id,$symb,$udom,$uname)) {
+                        push(@parts,$part);
+                    }
+                }
+            }
+        }
+        return (\%analysis,\@parts);
+    }
+
 }
 
 #--- Clean response type for display
@@ -281,7 +379,7 @@ sub reset_caches {
 #        response types only.
 sub cleanRecord {
     my ($answer,$response,$symb,$partid,$respid,$record,$order,$version,
-	$uname,$udom) = @_;
+	$uname,$udom,$type,$trial,$rndseed) = @_;
     my $grayFont = '<span class="LC_internal_info">';
     if ($response =~ /^(option|rank)$/) {
 	my %answer=&Apache::lonnet::str2hash($answer);
@@ -298,7 +396,7 @@ sub cleanRecord {
 	return '<blockquote><table border="1">'.
 	    '<tr valign="top"><td>'.&mt('Answer').'</td>'.$toprow.'</tr>'.
 	    '<tr valign="top"><td>'.$grayFont.&mt('Option ID').'</span></td>'.
-	    $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
+	    $bottomrow.'</tr></table></blockquote>';
     } elsif ($response eq 'match') {
 	my %answer=&Apache::lonnet::str2hash($answer);
 	my %grading=&Apache::lonnet::str2hash($record->{$version."resource.$partid.$respid.submissiongrading"});
@@ -320,12 +418,12 @@ sub cleanRecord {
 	    '<tr valign="top"><td>'.$grayFont.&mt('Item ID').'</span></td>'.
 	    $middlerow.'</tr>'.
 	    '<tr valign="top"><td>'.$grayFont.&mt('Option ID').'</span></td>'.
-	    $bottomrow.'</tr>'.'</table></blockquote>';
+	    $bottomrow.'</tr></table></blockquote>';
     } elsif ($response eq 'radiobutton') {
 	my %answer=&Apache::lonnet::str2hash($answer);
 	my ($toprow,$bottomrow);
 	my $correct = 
-	    &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom);
+	    &get_radiobutton_correct_foil($partid,$respid,$symb,$uname,$udom,$type,$trial,$rndseed);
 	foreach my $foil (@$order) {
 	    if (exists($answer{$foil})) {
 		if ($foil eq $correct) {
@@ -341,7 +439,7 @@ sub cleanRecord {
 	return '<blockquote><table border="1">'.
 	    '<tr valign="top"><td>'.&mt('Answer').'</td>'.$toprow.'</tr>'.
 	    '<tr valign="top"><td>'.$grayFont.&mt('Option ID').'</span></td>'.
-	    $grayFont.$bottomrow.'</tr>'.'</table></blockquote>';
+	    $bottomrow.'</tr></table></blockquote>';
     } elsif ($response eq 'essay') {
 	if (! exists ($env{'form.'.$symb})) {
 	    my (%keyhash) = &Apache::lonnet::dump('nohist_handgrade',
@@ -642,7 +740,11 @@ sub compute_points {
 #
 
 sub most_similar {
-    my ($uname,$udom,$uessay,$old_essays)=@_;
+    my ($uname,$udom,$symb,$uessay)=@_;
+
+    unless ($symb) { return ''; }
+
+    unless (ref($old_essays{$symb}) eq 'HASH') { return ''; }
 
 # ignore spaces and punctuation
 
@@ -650,7 +752,7 @@ sub most_similar {
 
 # ignore empty submissions (occuring when only files are sent)
 
-    unless ($uessay=~/\w+/) { return ''; }
+    unless ($uessay=~/\w+/s) { return ''; }
 
 # these will be returned. Do not care if not at least 50 percent similar
     my $limit=0.6;
@@ -659,11 +761,11 @@ sub most_similar {
     my $scrsid='';
     my $sessay='';
 # go through all essays ...
-    foreach my $tkey (keys(%$old_essays)) {
+    foreach my $tkey (keys(%{$old_essays{$symb}})) {
 	my ($tname,$tdom,$tcrsid)=map {&unescape($_)} (split(/\./,$tkey));
 # ... except the same student
         next if (($tname eq $uname) && ($tdom eq $udom));
-	my $tessay=$old_essays->{$tkey};
+	my $tessay=$old_essays{$symb}{$tkey};
 	$tessay=~s/\W+/ /gs;
 # String similarity gives up if not even limit
 	my $tsimilar=&String::Similarity::similarity($uessay,$tessay,$limit);
@@ -673,7 +775,7 @@ sub most_similar {
 	    $sname=$tname;
 	    $sdom=$tdom;
 	    $scrsid=$tcrsid;
-	    $sessay=$old_essays->{$tkey};
+	    $sessay=$old_essays{$symb}{$tkey};
 	}
     }
     if ($limit>0.6) {
@@ -699,7 +801,7 @@ sub verifyreceipt {
 
     my $title.=
 	'<h3><span class="LC_info">'.
-	&mt('Verifying Submission Receipt [_1]',$receipt).
+	&mt('Verifying Receipt No. [_1]',$receipt).
 	'</span></h3>'."\n".
 	'<h4>'.&mt('<b>Resource: </b>[_1]',$env{'form.probTitle'}).
 	'</h4>'."\n";
@@ -711,7 +813,13 @@ sub verifyreceipt {
     if ($env{"course.$courseid.receiptalg"} eq 'receipt2' ||
 	$env{"course.$courseid.receiptalg"} eq 'receipt3') { $receiptparts=1; }
     my $parts=['0'];
-    if ($receiptparts) { ($parts)=&response_type($symb); }
+    if ($receiptparts) {
+        my $res_error; 
+        ($parts)=&response_type($symb,\$res_error);
+        if ($res_error) {
+            return &navmap_errormsg();
+        } 
+    }
     
     my $header = 
 	&Apache::loncommon::start_data_table().
@@ -753,11 +861,14 @@ sub verifyreceipt {
 	}
     }
     if ($matches == 0) {
-	$string = $title.&mt('No match found for the above receipt.');
+        $string = $title
+                 .'<p class="LC_warning">'
+                 .&mt('No match found for the above receipt number.')
+                 .'</p>';
     } else {
 	$string = &jscriptNform($symb).$title.
 	    '<p>'.
-	    &mt('The above receipt matches the following [numerate,_1,student].',$matches).
+	    &mt('The above receipt number matches the following [quant,_1,student].',$matches).
 	    '</p>'.
 	    $header.
 	    $contents.
@@ -783,18 +894,16 @@ sub listStudents {
     $env{'form.probTitle'} = $env{'form.probTitle'} eq '' ? 
 	&Apache::lonnet::gettitle($symb) : $env{'form.probTitle'};
 
-    my $result='<h3><span class="LC_info">&nbsp;'.
-	&mt($viewgrade.' Submissions for a Student or a Group of Students')
+    my $result='<h3><span class="LC_info">&nbsp;'
+	.&mt("$viewgrade Submissions for a Student or a Group of Students")
 	.'</span></h3>';
 
     my ($table,undef,$hdgrade,$partlist,$handgrade) = &showResourceInfo($symb,$env{'form.probTitle'},($env{'form.showgrading'} eq 'yes'));
 
-    my %lt = ( '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.",
-	       );
-    %lt = &Apache::lonlocal::texthash(%lt);
+    my %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.',
+	     );
     $request->print(<<LISTJAVASCRIPT);
 <script type="text/javascript" language="javascript">
     function checkSelect(checkBox) {
@@ -836,18 +945,17 @@ LISTJAVASCRIPT
     my $gradeTable='<form action="/adm/grades" method="post" name="gradesub">'.
 	"\n".$table;
 	
-    $gradeTable .= 
-	'&nbsp;'.
-	&mt('<b>View Problem Text: </b>[_1]',
-	    '<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n".
-	    '<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n".
-	    '<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label>').'<br />'."\n";
-    $gradeTable .= 
-	'&nbsp;'.
-	&mt('<b>View Answer: </b>[_1]',
-	    '<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n".
-	    '<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n".
-	    '<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label>').'<br />'."\n";
+    $gradeTable .= &Apache::lonhtmlcommon::start_pick_box();
+    $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Problem Text'))
+                  .'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n"
+                  .'<label><input type="radio" name="vProb" value="yes" /> '.&mt('one student').' </label>'."\n"
+                  .'<label><input type="radio" name="vProb" value="all" /> '.&mt('all students').' </label><br />'."\n"
+                  .&Apache::lonhtmlcommon::row_closure();
+    $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('View Answer'))
+                  .'<label><input type="radio" name="vAns" value="no"  /> '.&mt('no').' </label>'."\n"
+                  .'<label><input type="radio" name="vAns" value="yes" /> '.&mt('one student').' </label>'."\n"
+                  .'<label><input type="radio" name="vAns" value="all" checked="checked" /> '.&mt('all students').' </label><br />'."\n"
+                  .&Apache::lonhtmlcommon::row_closure();
 
     my $submission_options;
     if ($env{'form.handgrade'} eq 'yes' && scalar(@$partlist) > 1) {
@@ -858,25 +966,32 @@ LISTJAVASCRIPT
     my $saveStatus = $stu_status eq '' ? 'Active' : $stu_status;
     $env{'form.Status'} = $saveStatus;
     $submission_options.=
-	'<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> '.&mt('last submission only').' </label>'."\n".
-	'<label><input type="radio" name="lastSub" value="last" /> '.&mt('last submission &amp; parts info').' </label>'."\n".
-	'<label><input type="radio" name="lastSub" value="datesub" /> '.&mt('by dates and submissions').' </label>'."\n".
-	'<label><input type="radio" name="lastSub" value="all" /> '.&mt('all details').'</label>';
-    $gradeTable .= 
-	'&nbsp;'.
-	&mt('<b>Submissions: </b>[_1]',$submission_options).'<br />'."\n";
+        '<span class="LC_nobreak">'.
+        '<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> '.
+        &mt('last submission only').' </label></span>'."\n".
+        '<span class="LC_nobreak">'.
+        '<label><input type="radio" name="lastSub" value="last" /> '.
+        &mt('last submission &amp; parts info').' </label></span>'."\n".
+        '<span class="LC_nobreak">'.
+        '<label><input type="radio" name="lastSub" value="datesub" /> '.
+        &mt('by dates and submissions').'</label></span>'."\n".
+        '<span class="LC_nobreak">'.
+        '<label><input type="radio" name="lastSub" value="all" /> '.
+        &mt('all details').'</label></span>';
+    $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Submissions'))
+                  .$submission_options
+                  .&Apache::lonhtmlcommon::row_closure();
+
+    $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Grading Increments'))
+                  .'<select name="increment">'
+                  .'<option value="1">'.&mt('Whole Points').'</option>'
+                  .'<option value=".5">'.&mt('Half Points').'</option>'
+                  .'<option value=".25">'.&mt('Quarter Points').'</option>'
+                  .'<option value=".1">'.&mt('Tenths of a Point').'</option>'
+                  .'</select>'
+                  .&Apache::lonhtmlcommon::row_closure();
 
     $gradeTable .= 
-        '&nbsp;'.
-	&mt('<b>Grading Increments:</b> [_1]',
-	    '<select name="increment">'.
-	    '<option value="1">'.&mt('Whole Points').'</option>'.
-	    '<option value=".5">'.&mt('Half Points').'</option>'.
-	    '<option value=".25">'.&mt('Quarter Points').'</option>'.
-	    '<option value=".1">'.&mt('Tenths of a Point').'</option>'.
-	    '</select>');
-    
-    $gradeTable .= 
         &build_section_inputs().
 	'<input type="hidden" name="submitonly"  value="'.$submitonly.'" />'."\n".
 	'<input type="hidden" name="handgrade"   value="'.$env{'form.handgrade'}.'" /><br />'."\n".
@@ -887,23 +1002,30 @@ LISTJAVASCRIPT
 	'<input type="hidden" name="saveStatusOld" value="'.$saveStatus.'" />'."\n";
 
     if (exists($env{'form.gradingMenu'}) && exists($env{'form.Status'})) {
-	$gradeTable.='<input type="hidden" name="Status"   value="'.$stu_status.'" />'."\n";
+	$gradeTable .= '<input type="hidden" name="Status" value="'.$stu_status.'" />'."\n";
     } else {
-	$gradeTable.=&mt('<b>Student Status:</b> [_1]',
-			 &Apache::lonhtmlcommon::StatusOptions($saveStatus,undef,1,'javascript:reLoadList(this.form);')).'<br />';
+        $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Student Status'))
+                      .&Apache::lonhtmlcommon::StatusOptions(
+                           $saveStatus,undef,1,'javascript:reLoadList(this.form);')
+                      .&Apache::lonhtmlcommon::row_closure();
     }
 
-    $gradeTable.=&mt('To '.lc($viewgrade).' 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.').'<br />'."\n".
-	'<input type="hidden" name="command" value="processGroup" />'."\n";
+    $gradeTable .= &Apache::lonhtmlcommon::row_title(&mt('Check For Plagiarism'))
+                  .'<input type="checkbox" name="checkPlag" checked="checked" />'
+                  .&Apache::lonhtmlcommon::row_closure(1)
+                  .&Apache::lonhtmlcommon::end_pick_box();
+
+    $gradeTable .= '<p>'
+                  .&mt('To '.lc($viewgrade)." 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"
+                  .'<input type="hidden" name="command" value="processGroup" />'
+                  .'</p>';
 
 # checkall buttons
     $gradeTable.=&check_script('gradesub', 'stuinfo');
     $gradeTable.='<input type="button" '."\n".
-	'onClick="javascript:checkSelect(this.form.stuinfo);" '."\n".
-	'value="'.&mt('Next-&gt;').'" /> <br />'."\n";
+        'onclick="javascript:checkSelect(this.form.stuinfo);" '."\n".
+        'value="'.&mt('Next').' &rarr;" /> <br />'."\n";
     $gradeTable.=&check_buttons();
-    $gradeTable.='<label><input type="checkbox" name="checkPlag" checked="checked" />'.&mt('Check For Plagiarism').'</label>';
     my ($classlist, undef, $fullname) = &getclasslist($getsec,'1',$getgroup);
     $gradeTable.= &Apache::loncommon::start_data_table().
 	&Apache::loncommon::start_data_table_header_row();
@@ -986,7 +1108,7 @@ LISTJAVASCRIPT
 		$gradeTable.= &Apache::loncommon::start_data_table_row();
 	    }
 	    $gradeTable.='<td align="right">'.$ctr.'&nbsp;</td>'.
-               '<td align="center"><label><input type=checkbox name="stuinfo" value="'.
+               '<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).
@@ -1019,9 +1141,9 @@ LISTJAVASCRIPT
     }
 
     $gradeTable.=&Apache::loncommon::end_data_table()."\n".
-	'<input type="button" '.
-	'onClick="javascript:checkSelect(this.form.stuinfo);" '.
-	'value="'.&mt('Next-&gt;').'" /></form>'."\n";
+        '<input type="button" '.
+        'onclick="javascript:checkSelect(this.form.stuinfo);" '.
+        'value="'.&mt('Next').' &rarr;" /></form>'."\n";
     if ($ctr == 0) {
 	my $num_students=(scalar(keys(%$fullname)));
 	if ($num_students eq 0) {
@@ -1117,6 +1239,7 @@ sub processGroup {
 #--- Javascript to handle the submission page functionality ---
 sub sub_page_js {
     my $request = shift;
+	    my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = ');
     $request->print(<<SUBJAVASCRIPT);
 <script type="text/javascript" language="javascript">
     function updateRadio(formname,id,weight) {
@@ -1127,7 +1250,7 @@ sub sub_page_js {
 	gradeBox.value = pts;
 	var resetbox = false;
 	if (isNaN(pts) || pts < 0) {
-	    alert("A number equal or greater than 0 is expected. Entered value = "+pts);
+	    alert("$alertmsg"+pts);
 	    for (var i=0; i<radioButton.length; i++) {
 		if (radioButton[i].checked) {
 		    gradeBox.value = i;
@@ -1372,12 +1495,29 @@ INNERJS
 
     my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
     $docopen=~s/^document\.//;
+    my %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.',
+                comp => 'Compose Message for: ',
+                incl => 'Include',
+                type => 'Type',
+                subj => 'Subject',
+                mesa => 'Message',
+                new  => 'New',
+                save => 'Save',
+                canc => 'Cancel',
+                kehi => 'Keyword Highlight Options',
+                txtc => 'Text Color',
+                font => 'Font Size',
+                fnst => 'Font Style',
+             );
     $request->print(<<SUBJAVASCRIPT);
 <script type="text/javascript" language="javascript">
 
 //===================== Show list of keywords ====================
   function keywords(formname) {
-    var nret = prompt("Keywords list, separated by a space. Add/delete to list if desired.",formname.keywords.value);
+    var nret = prompt("$lt{'keyw'}",formname.keywords.value);
     if (nret==null) return;
     formname.keywords.value = nret;
 
@@ -1404,10 +1544,10 @@ INNERJS
     else return;
     var cleantxt = txt.replace(new RegExp('([\\f\\n\\r\\t\\v ])+', 'g')," ");
     if (cleantxt=="") {
-	alert("Please select a word or group of words from document and then click this link.");
+	alert("$lt{'plse'}");
 	return;
     }
-    var nret = prompt("Add selection to keyword list? Edit if desired.",cleantxt);
+    var nret = prompt("$lt{'adds'}",cleantxt);
     if (nret==null) return;
     document.SCORE.keywords.value = document.SCORE.keywords.value+" "+nret;
     if (document.SCORE.keywords.value != "") {
@@ -1481,7 +1621,7 @@ INNERJS
     var ypos = (screen.height-height)/2-30;
     ypos = (ypos < 0) ? '0' : ypos;
 
-    pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=600,height='+height);
+    pWin = window.open('', 'MessageCenter', 'resizable=yes,toolbar=no,location=no,scrollbars='+scrollbar+',screenx='+xpos+',screeny='+ypos+',width=700,height='+height);
     pWin.focus();
     pDoc = pWin.document;
     pDoc.$docopen;
@@ -1489,16 +1629,16 @@ INNERJS
 
     pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">");
     pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">");
-    pDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Compose Message for \"+fullname+\"<\\/span><\\/h3><br /><br />");
+    pDoc.write("<h3><span class=\\"LC_info\\">&nbsp;$lt{'comp'}\"+fullname+\"<\\/span><\\/h3><br /><br />");
 
-    pDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
-    pDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
-    pDoc.write("<td><b>Type<\\/b><\\/td><td><b>Include<\\/b><\\/td><td><b>Message<\\/td><\\/tr>");
+    pDoc.write('<table border="0" width="100%"><tr><td bgcolor="#777777">');
+    pDoc.write('<table border="0" width="100%"><tr bgcolor="#DDFFFF">');
+    pDoc.write("<td><b>$lt{'type'}<\\/b><\\/td><td><b>$lt{'incl'}<\\/b><\\/td><td><b>$lt{'mesa'}<\\/td><\\/tr>");
 }
     function displaySubject(msg,shwsel) {
     pDoc = pWin.document;
     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
-    pDoc.write("<td>Subject<\\/td>");
+    pDoc.write("<td>$lt{'subj'}<\\/td>");
     pDoc.write("<td align=\\"center\\"><input name=\\"subchk\\" type=\\"checkbox\\"" +shwsel+"><\\/td>");
     pDoc.write("<td><input name=\\"msgsub\\" type=\\"text\\" value=\\""+msg+"\\"size=\\"60\\" maxlength=\\"80\\"><\\/td><\\/tr>");
 }
@@ -1514,7 +1654,7 @@ INNERJS
   function newMsg(newmsg,shwsel) {
     pDoc = pWin.document;
     pDoc.write("<tr bgcolor=\\"#ffffdd\\">");
-    pDoc.write("<td align=\\"center\\">New<\\/td>");
+    pDoc.write("<td align=\\"center\\">$lt{'new'}<\\/td>");
     pDoc.write("<td align=\\"center\\"><input name=\\"newmsgchk\\" type=\\"checkbox\\"" +shwsel+"><\\/td>");
     pDoc.write("<td><textarea name=\\"newmsg\\" cols=\\"60\\" rows=\\"3\\" onchange=\\"javascript:this.form.newmsgchk.checked=true\\" >"+newmsg+"<\\/textarea><\\/td><\\/tr>");
 }
@@ -1523,8 +1663,8 @@ INNERJS
     pDoc = pWin.document;
     pDoc.write("<\\/table>");
     pDoc.write("<\\/td><\\/tr><\\/table>&nbsp;");
-    pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
-    pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");
+    pDoc.write("<input type=\\"button\\" value=\\"$lt{'save'}\\" onclick=\\"javascript:checkInput()\\">&nbsp;&nbsp;");
+    pDoc.write("<input type=\\"button\\" value=\\"$lt{'canc'}\\" onclick=\\"self.close()\\"><br /><br />");
     pDoc.write("<\\/form>");
     pDoc.write('$end_page_msg_central');
     pDoc.close();
@@ -1574,11 +1714,11 @@ INNERJS
     hDoc.$docopen;
     hDoc.write('$start_page_highlight_central');
     hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">");
-    hDoc.write("<h3><span class=\\"LC_info\\">&nbsp;Keyword Highlight Options<\\/span><\\/h3><br /><br />");
+    hDoc.write("<h3><span class=\\"LC_info\\">&nbsp;$lt{'kehi'}<\\/span><\\/h3><br /><br />");
 
-    hDoc.write("<table border=0 width=100%><tr><td bgcolor=\\"#777777\\">");
-    hDoc.write("<table border=0 width=100%><tr bgcolor=\\"#ddffff\\">");
-    hDoc.write("<td><b>Text Color<\\/b><\\/td><td><b>Font Size<\\/b><\\/td><td><b>Font Style<\\/td><\\/tr>");
+    hDoc.write('<table border="0" width="100%"><tr><td bgcolor="#777777">');
+    hDoc.write('<table border="0" width="100%"><tr bgcolor="#DDFFFF">');
+    hDoc.write("<td><b>$lt{'txtc'}<\\/b><\\/td><td><b>$lt{'font'}<\\/b><\\/td><td><b>$lt{'fnst'}<\\/td><\\/tr>");
   }
 
   function highlightbody(clrval,clrtxt,clrsel,szval,sztxt,szsel,syval,sytxt,sysel) { 
@@ -1597,8 +1737,8 @@ INNERJS
     var hDoc = hwdWin.document;
     hDoc.write("<\\/table>");
     hDoc.write("<\\/td><\\/tr><\\/table>&nbsp;");
-    hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");
-    hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />");
+    hDoc.write("<input type=\\"button\\" value=\\"$lt{'save'}\\" onclick=\\"javascript:updateChoice(1)\\">&nbsp;&nbsp;");
+    hDoc.write("<input type=\\"button\\" value=\\"$lt{'canc'}\\" onclick=\\"self.close()\\"><br /><br />");
     hDoc.write("<\\/form>");
     hDoc.write('$end_page_highlight_central');
     hDoc.close();
@@ -1617,6 +1757,25 @@ sub get_increment {
     return $increment;
 }
 
+sub gradeBox_start {
+    return (
+        &Apache::loncommon::start_data_table()
+       .&Apache::loncommon::start_data_table_header_row()
+       .'<th>'.&mt('Part').'</th>'
+       .'<th>'.&mt('Points').'</th>'
+       .'<th>&nbsp;</th>'
+       .'<th>'.&mt('Assign Grade').'</th>'
+       .'<th>'.&mt('Weight').'</th>'
+       .'<th>'.&mt('Grade Status').'</th>'
+       .&Apache::loncommon::end_data_table_header_row()
+    );
+}
+
+sub gradeBox_end {
+    return (
+        &Apache::loncommon::end_data_table()
+    );
+}
 #--- displays the grading box, used in essay type problem and grading by page/sequence
 sub gradeBox {
     my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_;
@@ -1636,7 +1795,7 @@ sub gradeBox {
     if ($last_resets{$partid}) {
         $aggtries = &get_num_tries($record,$last_resets{$partid},$partid);
     }
-    $result.='<table border="0"><tr>';
+    $result.=&Apache::loncommon::start_data_table_row();
     my $ctr = 0;
     my $thisweight = 0;
     my $increment = &get_increment();
@@ -1644,7 +1803,7 @@ sub gradeBox {
     my $radio.='<table border="0"><tr>'."\n";  # display radio buttons in a nice table 10 across
     while ($thisweight<=$wgt) {
 	$radio.= '<td><span class="LC_nobreak"><label><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '.
-	    'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.
+        'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','.
 	    $thisweight.')" value="'.$thisweight.'" '.
 	    ($score eq $thisweight ? 'checked="checked"':'').' /> '.$thisweight."</label></span></td>\n";
 	$radio.=(($ctr+1)%10 == 0 ? '</tr><tr>' : '');
@@ -1655,13 +1814,13 @@ sub gradeBox {
 
     my $line.='<input type="text" name="GD_BOX'.$counter.'_'.$partid.'"'.
 	($score ne ''? ' value = "'.$score.'"':'').' size="4" '.
-	'onChange="javascript:updateRadio(this.form,\''.$counter.'_'.$partid.'\','.
+	'onchange="javascript:updateRadio(this.form,\''.$counter.'_'.$partid.'\','.
 	$wgt.')" /></td>'."\n";
     $line.='<td>/'.$wgt.' '.$wgtmsg.
 	($$record{'resource.'.$partid.'.solved'} eq 'correct_by_student' ? '&nbsp;'.$checkIcon : '').
-	' </td><td>'."\n";
-    $line.='<select name="GD_SEL'.$counter.'_'.$partid.'" '.
-	'onChange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";
+	' </td>'."\n";
+    $line.='<td><select name="GD_SEL'.$counter.'_'.$partid.'" '.
+	'onchange="javascript:clearRadBox(this.form,\''.$counter.'_'.$partid.'\')" >'."\n";
     if ($$record{'resource.'.$partid.'.solved'} eq 'excused') {
 	$line.='<option></option>'.
 	    '<option value="excused" selected="selected">'.&mt('excused').'</option>';
@@ -1672,11 +1831,10 @@ sub gradeBox {
     $line.='<option value="reset status">'.&mt('reset status').'</option></select>'."\n";
 
 
+	#&mt('<td><b>Part:</b></td><td>[_1]</td><td><b>Points:</b></td><td>[_2]</td><td>or</td><td>[_3]</td>',$display_part,$radio,$line);
     $result .= 
-	&mt('<td><b>Part:</b></td><td>[_1]</td><td><b>Points:</b></td><td>[_2]</td><td>or</td><td>[_3]</td>',$display_part,$radio,$line);
-
-    
-    $result.='</tr></table>'."\n";
+	    '<td>'.$display_part.'</td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>';
+    $result.=&Apache::loncommon::end_data_table_row();
     $result.='<input type="hidden" name="stores'.$counter.'_'.$partid.'" value="" />'."\n".
 	'<input type="hidden" name="oldpts'.$counter.'_'.$partid.'" value="'.$score.'" />'."\n".
 	'<input type="hidden" name="solved'.$counter.'_'.$partid.'" value="'.
@@ -1685,15 +1843,19 @@ sub gradeBox {
         $$record{'resource.'.$partid.'.tries'}.'" />'."\n".
         '<input type="hidden" name="aggtries'.$counter.'_'.$partid.'" value="'.
         $aggtries.'" />'."\n";
-    $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record);
+    my $res_error;
+    $result.=&handback_box($symb,$uname,$udom,$counter,$partid,$record,\$res_error);
+    if ($res_error) {
+        return &navmap_errormsg();
+    }
     return $result;
 }
 
 sub handback_box {
-    my ($symb,$uname,$udom,$counter,$partid,$record) = @_;
-    my ($partlist,$handgrade,$responseType) = &response_type($symb);
+    my ($symb,$uname,$udom,$counter,$partid,$record,$res_error) = @_;
+    my ($partlist,$handgrade,$responseType) = &response_type($symb,$res_error);
     my (@respids);
-     my @part_response_id = &flatten_responseType($responseType);
+    my @part_response_id = &flatten_responseType($responseType);
     foreach my $part_response_id (@part_response_id) {
     	my ($part,$resp) = @{ $part_response_id };
         if ($part eq $partid) {
@@ -1705,9 +1867,10 @@ sub handback_box {
 	my $prefix = $counter.'_'.$partid.'_'.$respid.'_';
 	my $files=&get_submitted_files($udom,$uname,$partid,$respid,$record);
 	next if (!@$files);
-	my $file_counter = 1;
+	my $file_counter = 0;
 	foreach my $file (@$files) {
 	    if ($file =~ /\/portfolio\//) {
+                $file_counter++;
     	        my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|);
     	        my ($name,$version,$ext) = &file_name_version_ext($file_disp);
     	        $file_disp = "$name.$ext";
@@ -1715,11 +1878,14 @@ sub handback_box {
     	        $result.=&mt('Return commented version of [_1] to student.',
     			 '<span class="LC_filename">'.$file_disp.'</span>');
     	        $result.='<input type="file"   name="'.$prefix.'returndoc'.$file_counter.'" />'."\n";
-    	        $result.='<input type="hidden" name="'.$prefix.'origdoc'.$file_counter.'" value="'.$file.'" /><br />';
-    	        $result.='('.&mt('File will be uploaded when you click on Save &amp; Next below.').')<br />';
-    	        $file_counter++;
+    	        $result.='<input type="hidden" name="'.$prefix.'origdoc'.$file_counter.'" value="'.$file.'" /><br />'."\n";
 	    }
 	}
+        if ($file_counter) {
+            $result .= '<input type="hidden" name="'.$prefix.'countreturndoc" value="'.$file_counter.'" />'."\n".
+                       '<span class="LC_info">'.
+                       '('.&mt('File(s) will be uploaded when you click on Save &amp; Next below.',$file_counter).')</span><br /><br />';
+        }
     }
     return $result;    
 }
@@ -1753,26 +1919,23 @@ sub show_problem {
 	$companswer=~s|name="submit"|name="would_have_been_submit"|g;
     }
     $rendered=
-	'<div class="LC_grade_show_problem_header">'.
-	&mt('View of the problem').
-	'</div><div class="LC_grade_show_problem_problem">'.
-	$rendered.
-	'</div>';
+        '<div class="LC_Box">'
+       .'<h3 class="LC_hcell">'.&mt('View of the problem').'</h3>'
+       .$rendered
+       .'</div>';
     $companswer=
-	'<div class="LC_grade_show_problem_header">'.
-	&mt('Correct answer').
-	'</div><div class="LC_grade_show_problem_problem">'.
-	$companswer.
-	'</div>';
+        '<div class="LC_Box">'
+       .'<h3 class="LC_hcell">'.&mt('Correct answer').'</h3>'
+       .$companswer
+       .'</div>';
     my $result;
     if ($mode eq 'both') {
-	$result=$rendered.$companswer;
+        $result=$rendered.$companswer;
     } elsif ($mode eq 'text') {
-	$result=$rendered;
+        $result=$rendered;
     } elsif ($mode eq 'answer') {
-	$result=$companswer;
+        $result=$companswer;
     }
-    $result='<div class="LC_grade_show_problem">'.$result.'</div>';
     return $result;
 }
 
@@ -1852,7 +2015,6 @@ sub submission {
 	'" src="'.$request->dir_config('lonIconsURL').
 	'/check.gif" height="16" border="0" />';
 
-    my %old_essays;
     # header info
     if ($counter == 0) {
 	&sub_page_js($request);
@@ -1944,15 +2106,22 @@ sub submission {
 	$request->print($prnmsg);
 
 	if ($env{'form.handgrade'} eq 'yes' && $env{'form.showgrading'} eq 'yes') {
+
+            my %lt = &Apache::lonlocal::texthash(
+                          keyw => 'Keyword Options',
+                          list => 'List',
+                          past => 'Paste Selection to List',
+                          high => 'Highlight Attribute',
+                     );
 #
 # Print out the keyword options line
 #
 	    $request->print(<<KEYWORDS);
-&nbsp;<b>Keyword Options:</b>&nbsp;
-<a href="javascript:keywords(document.SCORE);" target="_self">List</a>&nbsp; &nbsp;
-<a href="#" onMouseDown="javascript:getSel(); return false"
- CLASS="page">Paste Selection to List</a>&nbsp; &nbsp;
-<a href="javascript:kwhighlight();" target="_self">Highlight Attribute</a><br /><br />
+&nbsp;<b>$lt{'keyw'}:</b>&nbsp;
+<a href="javascript:keywords(document.SCORE);" target="_self">$lt{'list'}</a>&nbsp; &nbsp;
+<a href="#" onmousedown="javascript:getSel(); return false"
+ CLASS="page">$lt{'past'}</a>&nbsp; &nbsp;
+<a href="javascript:kwhighlight();" target="_self">$lt{'high'}</a><br /><br />
 KEYWORDS
 #
 # Load the other essays for similarity check
@@ -1961,17 +2130,36 @@ KEYWORDS
 	    my ($adom,$aname,$apath)=($essayurl=~/^($LONCAPA::domain_re)\/($LONCAPA::username_re)\/(.*)$/);
 	    $apath=&escape($apath);
 	    $apath=~s/\W/\_/gs;
-	    %old_essays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);
+	    &init_old_essays($symb,$apath,$adom,$aname);
         }
     }
 
 # This is where output for one specific student would start
-    my $add_class = ($counter%2) ? 'LC_grade_show_user_odd_row' : '';
-    $request->print("\n\n".
-                    '<div class="LC_grade_show_user '.$add_class.'">'.
-		    '<div class="LC_grade_user_name">'.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).'</div>'.
-		    '<div class="LC_grade_show_user_body">'."\n");
+    my $add_class = ($counter%2) ? ' LC_grade_show_user_odd_row' : '';
+    $request->print(
+        "\n\n"
+       .'<div class="LC_grade_show_user'.$add_class.'">'
+       .'<h2>'.&nameUserString(undef,$env{'form.fullname'},$uname,$udom).'</h2>'
+       ."\n"
+    );
 
+    # Show additional functions if allowed
+    if ($perm{'vgr'}) {
+        $request->print(
+            &Apache::loncommon::track_student_link(
+                &mt('View recent activity'),
+                $uname,$udom,'check')
+           .' '
+        );
+    }
+    if ($perm{'opa'}) {
+        $request->print(
+            &Apache::loncommon::pprmlink(
+                &mt('Set/Change parameters'),
+                $uname,$udom,$symb,'check'));
+    }
+
+    # Show Problem
     if ($env{'form.vProb'} eq 'all' or $env{'form.vAns'} eq 'all') {
 	my $mode;
 	if ($env{'form.vProb'} eq 'all' && $env{'form.vAns'} eq 'all') {
@@ -1986,24 +2174,26 @@ KEYWORDS
     }
 
     my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$udom,$uname);
-    my ($partlist,$handgrade,$responseType) = &response_type($symb);
+    my $res_error;
+    my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
+    if ($res_error) {
+        $request->print(&navmap_errormsg());
+        return;
+    }
 
     # Display student info
     $request->print(($counter == 0 ? '' : '<br />'));
-    my $result='<div class="LC_grade_submissions">';
-    
-    $result.='<div class="LC_grade_submissions_header">';
-    $result.= &mt('Submissions');
+
+    my $result='<div class="LC_Box">'
+              .'<h3 class="LC_hcell">'.&mt('Submissions').'</h3>';
     $result.='<input type="hidden" name="name'.$counter.
-	'" value="'.$env{'form.fullname'}.'" />'."\n";
+             '" value="'.$env{'form.fullname'}.'" />'."\n";
     if ($env{'form.handgrade'} eq 'no') {
-	$result.='<span class="LC_grade_check_note">'.
-	    &mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)."</span>\n";
-
+        $result.='<p class="LC_info">'
+                .&mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon)
+                ."</p>\n";
     }
 
-
-
     # If any part of the problem is an essay-response (handgraded), then check for collaborators
     my $fullname;
     my $col_fullnames = [];
@@ -2014,9 +2204,9 @@ KEYWORDS
 	$result.=$sub_result;
     }
     $request->print($result."\n");
-    $request->print('</div>'."\n");
+
     # print student answer/submission
-    # Options are (1) Handgaded submission only
+    # 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
@@ -2026,10 +2216,12 @@ KEYWORDS
 	
 	my $lastsubonly;
 
-	if ($$timestamp eq '') {
-	    $lastsubonly.='<div class="LC_grade_submissions_body">'.$$string[0].'</div>'; 
-	} else {
-	    $lastsubonly = '<div class="LC_grade_submissions_body"> <b>Date Submitted:</b> '.$$timestamp."\n";
+        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);
@@ -2053,68 +2245,89 @@ KEYWORDS
 		}
 		my $responsetype = $responseType->{$partid}->{$respid};
 		if (!exists($record{"resource.$partid.$respid.submission"})) {
-		    $lastsubonly.="\n".'<div class="LC_grade_submission_part"><b>Part:</b> '.
-			$display_part.' <span class="LC_internal_info">( ID '.$respid.
-			' )</span>&nbsp; &nbsp;'.
-			'<span class="LC_warning">'.&mt('Nothing submitted - no attempts').'</span><br /><br /></div>';
+                    $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,$subval) = split(/:/,$submission,2);
+		    my ($ressub,$hide,$subval) = split(/:/,$submission,3);
 		    # 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,$subval,\%old_essays);
+			    &most_similar($uname,$udom,$symb,$subval);
 			if ($osim) {
 			    $osim=int($osim*100.0);
 			    my %old_course_desc = 
 				&Apache::lonnet::coursedescription($ocrsid,
 								   {'one_time' => 1});
 
-			    $similar="<hr /><h3><span class=\"LC_warning\">".
-				&mt('Essay is [_1]% similar to an essay by [_2] ([_3]:[_4]) in course [_5] (course id [_6]:[_7])',
-				    $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 />';
+                            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);
+		    my $order=&get_order($partid,$respid,$symb,$uname,$udom,
+                                         undef,$type,$trial,$rndseed);
 		    if ($env{'form.lastSub'} eq 'lastonly' || 
 			($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>Part:</b> '.
-			    $display_part.' <span class="LC_internal_info">( ID '.$respid.
-			    ' )</span>&nbsp; &nbsp;';
+                        $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) {
-			    $lastsubonly.='<br /><span class="LC_warning">'.&mt('Like all files provided by users, this file may contain virusses').'</span><br />';
-			    my $file_counter = 0;
-			    foreach my $file (@$files) {
-			        $file_counter++;
-				&Apache::lonnet::allowuploaded('/adm/grades',$file);
-				$lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border=0"> '.$file.'</a>';
-			    }
+                            if ($hide eq 'anon') {
+                                $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));
+                            } else {
+                                $lastsubonly.='<br /><span class="LC_warning">'.&mt('Like all files provided by users, this file may contain viruses').'</span><br />';
+                                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" /> '.$file.'</a>';
+                                }
+                            }
 			    $lastsubonly.='<br />';
 			}
-			$lastsubonly.='<b>'.&mt('Submitted Answer:').' </b>'.
-			    &cleanRecord($subval,$responsetype,$symb,$partid,
-					 $respid,\%record,$order);
+                        if ($hide eq 'anon') {
+                            $lastsubonly.='<b>'.&mt('Anonymous Survey').'</b>'; 
+                        } else {
+			    $lastsubonly.='<b>'.&mt('Submitted Answer:').' </b>'.
+			        &cleanRecord($subval,$responsetype,$symb,$partid,
+					     $respid,\%record,$order,undef,$uname,$udom,$type,$trial,$rndseed);
+                        }
 			if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
 			$lastsubonly.='</div>';
 		    }
 		}
 	    }
-	    $lastsubonly.='</div>'."\n";
+	    $lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body
 	}
 	$request->print($lastsubonly);
    } elsif ($env{'form.lastSub'} eq 'datesub') {
@@ -2132,7 +2345,7 @@ KEYWORDS
     # return if view submission with no grading option
     if ($env{'form.showgrading'} eq '' || (!&canmodify($usec))) {
 	my $toGrade.='<input type="button" value="Grade Student" '.
-	    'onClick="javascript:checksubmit(this.form,\'Grade Student\',\''
+	    'onclick="javascript:checksubmit(this.form,\'Grade Student\',\''
 	    .$counter.'\');" target="_self" /> &nbsp;'."\n" if (&canmodify($usec));
 	$toGrade.='</div>'."\n";
 	if (($env{'form.command'} eq 'submission') || 
@@ -2176,11 +2389,11 @@ KEYWORDS
     my @partlist;
     my @gradePartRespid;
     my @part_response_id = &flatten_responseType($responseType);
-    $request->print('<div class="LC_grade_assign">'.
-		    
-		    '<div class="LC_grade_assign_header">'.
-		    &mt('Assign Grades').'</div>'.
-		    '<div class="LC_grade_assign_body">');
+    $request->print(
+        '<div class="LC_Box">'
+       .'<h3 class="LC_hcell">'.&mt('Assign Grades').'</h3>'
+    );
+    $request->print(&gradeBox_start());
     foreach my $part_response_id (@part_response_id) {
     	my ($partid,$respid) = @{ $part_response_id };
 	my $part_resp = join('_',@{ $part_response_id });
@@ -2192,19 +2405,10 @@ KEYWORDS
 	push(@gradePartRespid,$partid.'.'.$respid);
 	$request->print(&gradeBox($request,$symb,$uname,$udom,$counter,$partid,\%record));
     }
-    $request->print('</div></div>');
+    $request->print(&gradeBox_end()); # </div>
+    $request->print('</div>');
 
     $request->print('<div class="LC_grade_info_links">');
-    if ($perm{'vgr'}) {
-	$request->print(
-	    &Apache::loncommon::track_student_link(&mt('View recent activity'),
-						   $uname,$udom,'check'));
-    }
-    if ($perm{'opa'}) {
-	$request->print(
-	    &Apache::loncommon::pprmlink(&mt('Set/Change parameters'),
-					 $uname,$udom,$symb,'check'));
-    }
     $request->print('</div>');
 
     $result='<input type="hidden" name="partlist'.$counter.
@@ -2221,15 +2425,14 @@ KEYWORDS
 
 # Done with printing info for one student
 
-    $request->print('</div>');#LC_grade_show_user_body
     $request->print('</div>');#LC_grade_show_user
 
 
     # print end of form
     if ($counter == $total) {
-	my $endform='<table border="0"><tr><td>'."\n";
+        my $endform='<br /><hr /><table border="0"><tr><td>'."\n";
 	$endform.='<input type="button" value="'.&mt('Save &amp; Next').'" '.
-	    'onClick="javascript:checksubmit(this.form,\'Save & Next\','.
+	    'onclick="javascript:checksubmit(this.form,\'Save & Next\','.
 	    $total.','.scalar(@partlist).');" target="_self" /> &nbsp;'."\n";
 	my $ntstu ='<select name="NTSTU">'.
 	    '<option>1</option><option>2</option>'.
@@ -2237,12 +2440,14 @@ KEYWORDS
 	    '<option>7</option><option>10</option></select>'."\n";
 	my $nsel = ($env{'form.NTSTU'} ne '' ? $env{'form.NTSTU'} : '1');
 	$ntstu =~ s/<option>$nsel</<option selected="selected">$nsel</;
-	$endform.=&mt('[_1]student(s)',$ntstu);
+        $endform.=&mt('[_1]student(s)',$ntstu);
 	$endform.='&nbsp;&nbsp;<input type="button" value="'.&mt('Previous').'" '.
-	    'onClick="javascript:checksubmit(this.form,\'Previous\');" target="_self" /> &nbsp;'."\n".
+	    'onclick="javascript:checksubmit(this.form,\'Previous\');" target="_self" /> &nbsp;'."\n".
 	    '<input type="button" value="'.&mt('Next').'" '.
-	    'onClick="javascript:checksubmit(this.form,\'Next\');" target="_self" /> &nbsp;';
-	$endform.=&mt('(Next and Previous (student) do not save the scores.)')."\n" ;
+	    'onclick="javascript:checksubmit(this.form,\'Next\');" target="_self" /> &nbsp;';
+        $endform.='<span class="LC_warning">'.
+                  &mt('(Next and Previous (student) do not save the scores.)').
+                  '</span>'."\n" ;
         $endform.="<input type='hidden' value='".&get_increment().
             "' name='increment' />";
 	$endform.='</td></tr></table></form>';
@@ -2265,10 +2470,10 @@ sub check_collaborators {
 	next if ($record->{'resource.'.$part.'.collaborators'} eq '');
 	my (@good_collaborators, @bad_collaborators);
 	foreach my $possible_collaborator
-	    (split(/,?\s+/,$record->{'resource.'.$part.'.collaborators'})) { 
+	    (split(/[,;\s]+/,$record->{'resource.'.$part.'.collaborators'})) { 
 	    $possible_collaborator =~ s/[\$\^\(\)]//g;
 	    next if ($possible_collaborator eq '');
-	    my ($co_name,$co_dom) = split(/\@|:/,$possible_collaborator);
+	    my ($co_name,$co_dom) = split(/:/,$possible_collaborator);
 	    $co_dom = $udom if (! defined($co_dom) || $co_dom =~ /^domain$/i);
 	    next if ($co_name eq $uname && $co_dom eq $udom);
 	    # Doing this grep allows 'fuzzy' specification
@@ -2281,13 +2486,13 @@ sub check_collaborators {
 	    }
 	}
 	if (scalar(@good_collaborators) != 0) {
-	    $result.='<br />'.&mt('Collaborators: ');
+	    $result.='<br />'.&mt('Collaborators:').'<ol>';
 	    foreach my $name (@good_collaborators) {
 		my ($lastname,$givenn) = split(/,/,$$fullname{$name});
 		push(@col_fullnames, $givenn.' '.$lastname);
-		$result.=$fullname->{$name}.'&nbsp; &nbsp; &nbsp;';
+		$result.='<li>'.$fullname->{$name}.'</li>';
 	    }
-	    $result.='<br />'."\n";
+	    $result.='</ol><br />'."\n";
 	    my ($part)=split(/\./,$part);
 	    $result.='<input type="hidden" name="collaborator'.$counter.
 		'" value="'.$part.':'.(join ':',@good_collaborators).'" />'.
@@ -2311,7 +2516,7 @@ sub check_collaborators {
 #--- Retrieve the last submission for all the parts
 sub get_last_submission {
     my ($returnhash)=@_;
-    my (@string,$timestamp);
+    my (@string,$timestamp,%lasthidden);
     if ($$returnhash{'version'}) {
 	my %lasthash=();
 	my ($version);
@@ -2320,21 +2525,63 @@ sub get_last_submission {
 					$$returnhash{$version.':keys'}))) {
 		$lasthash{$key}=$$returnhash{$version.':'.$key};
 		$timestamp = 
-		    scalar(localtime($$returnhash{$version.':timestamp'}));
+		    &Apache::lonlocal::locallocaltime($$returnhash{$version.':timestamp'});
 	    }
 	}
+        my (%typeparts,%randombytry);
+        my $showsurv = 
+            &Apache::lonnet::allowed('vas',$env{'request.course.id'});
+        foreach my $key (sort(keys(%lasthash))) {
+            if ($key =~ /\.type$/) {
+                if (($lasthash{$key} eq 'anonsurvey') || 
+                    ($lasthash{$key} eq 'anonsurveycred') ||
+                    ($lasthash{$key} eq 'randomizetry')) {
+                    my ($ign,@parts) = split(/\./,$key);
+                    pop(@parts);
+                    my $id = join('.',@parts);
+                    if ($lasthash{$key} eq 'randomizetry') {
+                        $randombytry{$ign.'.'.$id} = $lasthash{$key};
+                    } else {
+                        unless ($showsurv) {
+                            $typeparts{$ign.'.'.$id} = $lasthash{$key};
+                        }
+                    }
+                    delete($lasthash{$key});
+                }
+            }
+        }
+        my @hidden = keys(%typeparts);
+        my @randomize = keys(%randombytry);
 	foreach my $key (keys(%lasthash)) {
 	    next if ($key !~ /\.submission$/);
-
+            my $hide;
+            if (@hidden) {
+                foreach my $id (@hidden) {
+                    if ($key =~ /^\Q$id\E/) {
+                        $hide = 'anon';
+                        last;
+                    }
+                }
+            }
+            unless ($hide) {
+                if (@randomize) {
+                    foreach my $id (@hidden) {
+                        if ($key =~ /^\Q$id\E/) {
+                            $hide = 'rand';
+                            last;
+                        }
+                    }
+                }
+            }
 	    my ($partid,$foo) = split(/submission$/,$key);
 	    my $draft  = $lasthash{$partid.'awarddetail'} eq 'DRAFT' ?
 		'<span class="LC_warning">Draft Copy</span> ' : '';
-	    push(@string, join(':', $key, $draft.$lasthash{$key}));
+	    push(@string, join(':', $key, $hide, $draft.$lasthash{$key}));
 	}
     }
     if (!@string) {
 	$string[0] =
-	    '<span class="LC_warning">Nothing submitted - no attempts.</span>';
+	    '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span>';
     }
     return (\@string,\$timestamp);
 }
@@ -2403,8 +2650,8 @@ sub processHandGrade {
                                                      undef,$feedurl,undef,
                                                      undef,undef,$showsymb,
                                                      $restitle);
-		$request->print('<br />'.&mt('Sending message to [_1]:[_2]',$uname,$udom).': '.
-				$msgstatus);
+		$request->print('<br />'.&mt('Sending message to [_1]',$uname.':'.$udom).': '.
+				$msgstatus.'<br />');
 	    }
 	    if ($env{'form.collaborator'.$ctr}) {
 		my @collabstrs=&Apache::loncommon::get_env_multiple("form.collaborator$ctr");
@@ -2534,7 +2781,12 @@ sub processHandGrade {
     }
     $ctr = 0;
     @parsedlist = reverse @parsedlist if ($button eq 'Previous');
-    my ($partlist) = &response_type($symb);
+    my $res_error;
+    my ($partlist) = &response_type($symb,\$res_error);
+    if ($res_error) {
+        $request->print(&navmap_errormsg());
+        return;
+    }
     foreach my $student (@parsedlist) {
 	my $submitonly=$env{'form.submitonly'};
 	my ($uname,$udom) = split(/:/,$student);
@@ -2585,7 +2837,7 @@ sub processHandGrade {
     }
     if ($total < 0) {
 	my $the_end = '<h3><span class="LC_info">'.&mt('LON-CAPA User Message').'</span></h3><br />'."\n";
-	$the_end.=&mt('<b>Message: </b> No more students for this section or class.').'<br /><br />'."\n";
+	$the_end.='<p>'.&mt('[_1]Message:[_2] No more students for this section or class.','<b>','</b>').'</p>'."\n";
 	$the_end.=&mt('Click on the button below to return to the grading menu.').'<br /><br />'."\n";
 	$the_end.=&show_grading_menu_form($symb);
 	$request->print($the_end);
@@ -2732,20 +2984,25 @@ sub check_and_remove_from_queue {
 sub handback_files {
     my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_;
     my $portfolio_root = '/userfiles/portfolio';
-    my ($partlist,$handgrade,$responseType) = &response_type($symb);
-
+    my $res_error;
+    my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
+    if ($res_error) {
+        $request->print('<br />'.&navmap_errormsg().'<br />');
+        return;
+    }
+    my @handedback;
+    my $file_msg;
     my @part_response_id = &flatten_responseType($responseType);
     foreach my $part_response_id (@part_response_id) {
     	my ($part_id,$resp_id) = @{ $part_response_id };
 	my $part_resp = join('_',@{ $part_response_id });
-            if (($env{'form.'.$newflg.'_'.$part_resp.'_returndoc1'}) && ($new_part == $part_id)) {
+        if (($env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'} =~ /^\d+$/) & ($new_part eq $part_id)) {
+            for (my $counter=1; $counter<=$env{'form.'.$newflg.'_'.$part_resp.'_countreturndoc'}; $counter++) {
                 # if multiple files are uploaded names will be 'returndoc2','returndoc3'
-                my $file_counter = 1;
-		my $file_msg;
-                while ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter}) {
-                    my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'.filename'};
+		if ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter}) {
+                    my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$counter.'.filename'};
                     my ($directory,$answer_file) = 
-                        ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter} =~ /^(.*?)([^\/]*)$/);
+                        ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter} =~ /^(.*?)([^\/]*)$/);
                     my ($answer_name,$answer_ver,$answer_ext) =
 		        &file_name_version_ext($answer_file);
 		    my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/);
@@ -2755,43 +3012,55 @@ sub handback_files {
                     # fix file name
                     my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/);
                     my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain,
-            	                                $newflg.'_'.$part_resp.'_returndoc'.$file_counter,
+            	                                $newflg.'_'.$part_resp.'_returndoc'.$counter,
             	                                $save_file_name);
                     if ($result !~ m|^/uploaded/|) {
                         $request->print('<br /><span class="LC_error">'.
                             &mt('An error occurred ([_1]) while trying to upload [_2].',
-                                $result,$newflg.'_'.$part_resp.'_returndoc'.$file_counter).
+                                $result,$newflg.'_'.$part_resp.'_returndoc'.$counter).
                                         '</span>');
                     } else {
                         # mark the file as read only
-                        my @files = ($save_file_name);
-                        my @what = ($symb,$env{'request.course.id'},'handback');
-                        &Apache::lonnet::mark_as_readonly($domain,$stuname,\@files,\@what);
+                        push(@handedback,$save_file_name);
 			if (exists($$newrecord{"resource.$new_part.$resp_id.handback"})) {
 			    $$newrecord{"resource.$new_part.$resp_id.handback"}.=',';
 			}
                         $$newrecord{"resource.$new_part.$resp_id.handback"} .= $save_file_name;
-			$file_msg.= "\n".'<br /><span class="LC_filename"><a href="/uploaded/'."$domain/$stuname/".$save_file_name.'">'.$save_file_name."</a></span><br />";
+			$file_msg.='<span class="LC_filename"><a href="/uploaded/'."$domain/$stuname/".$save_file_name.'">'.$save_file_name."</a></span> <br />";
 
                     }
-                    $request->print("<br />".$fname." will be the uploaded file name");
-                    $request->print(" ".$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter});
-                    $file_counter++;
+                    $request->print('<br />'.&mt('[_1] will be the uploaded file name [_2]','<span class="LC_info">'.$fname.'</span>','<span class="LC_filename">'.$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$counter}.'</span>'));
                 }
-		my $subject = "File Handed Back by Instructor ";
-		my $message = "A file has been returned that was originally submitted in reponse to: <br />";
-		$message .= "<strong>".&Apache::lonnet::gettitle($symb)."</strong><br />";
-		$message .= ' The returned file(s) are named: '. $file_msg;
-		$message .= " and can be found in your portfolio space.";
-		my ($feedurl,$showsymb) = 
-		    &get_feedurl_and_symb($symb,$domain,$stuname);
-                my $restitle = &Apache::lonnet::gettitle($symb);
-		my $msgstatus = 
-                   &Apache::lonmsg::user_normal_msg($stuname,$domain,$subject.
-			 ' (File Returned) ['.$restitle.']',$message,undef,
-                         $feedurl,undef,undef,undef,$showsymb,$restitle);
             }
         }
+    }
+    if (@handedback > 0) {
+        $request->print('<br />');
+        my @what = ($symb,$env{'request.course.id'},'handback');
+        &Apache::lonnet::mark_as_readonly($domain,$stuname,\@handedback,\@what);
+        my $user_lh = &Apache::loncommon::user_lang($stuname,$domain,$env{'request.course.id'});
+        my ($subject,$message);
+        if (scalar(@handedback) == 1) {
+            $subject = &mt_user($user_lh,'File Handed Back by Instructor');
+        } else {
+            $subject = &mt_user($user_lh,'Files Handed Back by Instructor');
+            $message = &mt_user($user_lh,'Files have been returned that were originally submitted in response to: ');
+        }
+        $message .= "<p><strong>".&Apache::lonnet::gettitle($symb)." </strong></p>";
+        $message .= &mt_user($user_lh,'The returned file(s) are named: [_1]',"<br />$file_msg <br />").
+                    &mt_user($user_lh,'The file(s) can be found in your [_1]portfolio[_2].','<a href="/adm/portfolio">','</a>');
+        my ($feedurl,$showsymb) =
+            &get_feedurl_and_symb($symb,$domain,$stuname);
+        my $restitle = &Apache::lonnet::gettitle($symb);
+        $subject .= ' '.&mt_user($user_lh,'(File Returned)').' ['.$restitle.']';
+        my $msgstatus =
+             &Apache::lonmsg::user_normal_msg($stuname,$domain,$subject,
+                 $message,undef,$feedurl,undef,undef,undef,$showsymb,
+                 $restitle);
+        if ($msgstatus) {
+            $request->print(&mt('Notification message status: [_1]','<span class="LC_info">'.$msgstatus.'</span>').'<br />');
+        }
+    }
     return;
 }
 
@@ -2980,6 +3249,7 @@ sub file_name_version_ext {
 sub viewgrades_js {
     my ($request) = shift;
 
+    my $alertmsg = &mt('A number equal or greater than 0 is expected. Entered value = ');
     $request->print(<<VIEWJAVASCRIPT);
 <script type="text/javascript" language="javascript">
    function writePoint(partid,weight,point) {
@@ -2988,7 +3258,7 @@ sub viewgrades_js {
 	if (point == "textval") {
 	    point = document.classgrade["TEXTVAL_"+partid].value;
 	    if (isNaN(point) || parseFloat(point) < 0) {
-		alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));
+		alert("$alertmsg"+parseFloat(point));
 		var resetbox = false;
 		for (var i=0; i<radioButton.length; i++) {
 		    if (radioButton[i].checked) {
@@ -3086,7 +3356,7 @@ sub viewgrades_js {
 	var weight = document.classgrade["weight_"+partid].value;
 
 	if (isNaN(point) || parseFloat(point) < 0) {
-	    alert("A number equal or greater than 0 is expected. Entered value = "+parseFloat(point));
+	    alert("$alertmsg"+parseFloat(point));
 	    textbox.value = "";
 	    return;
 	}
@@ -3175,22 +3445,26 @@ sub viewgrades {
 	'<input type="hidden" name="Status" value="'.$env{'stu_status'}.'" />'."\n".
 	'<input type="hidden" name="probTitle" value="'.$env{'form.probTitle'}.'" />'."\n";
 
-    my $sectionClass;
-    my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
+    my ($common_header,$specific_header);
     if ($env{'form.section'} eq 'all') {
-	$sectionClass='Class';
+	$common_header = &mt('Assign Common Grade to Class');
+        $specific_header = &mt('Assign Grade to Specific Students in Class');
     } elsif ($env{'form.section'} eq 'none') {
-	$sectionClass='Students in no Section';
+        $common_header = &mt('Assign Common Grade to Students in no Section');
+	$specific_header = &mt('Assign Grade to Specific Students in no Section');
     } else {
-	$sectionClass='Students in Section(s) [_1]';
+        my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
+        $common_header = &mt('Assign Common Grade to Students in Section(s) [_1]',$section_display);
+	$specific_header = &mt('Assign Grade to Specific Students in Section(s) [_1]',$section_display);
     }
-    $result.=
-	'<h3>'.
-	&mt("Assign Common Grade To $sectionClass",$section_display).'</h3>';
-    $result.= &Apache::loncommon::start_data_table();
+    $result.= '<h3>'.$common_header.'</h3>'.&Apache::loncommon::start_data_table();
     #radio buttons/text box for assigning points for a section or class.
     #handles different parts of a problem
-    my ($partlist,$handgrade,$responseType) = &response_type($symb);
+    my $res_error;
+    my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
+    if ($res_error) {
+        return &navmap_errormsg();
+    }
     my %weight = ();
     my $ctsparts = 0;
     my %seen = ();
@@ -3216,11 +3490,11 @@ sub viewgrades {
 	}
 	$radio.='</tr></table>';
 	my $line = '<input type="text" name="TEXTVAL_'.
-	    $partid.'" size="4" '.'onChange="javascript:writePoint(\''.
+	    $partid.'" size="4" '.'onchange="javascript:writePoint(\''.
 		$partid.'\','.$weight{$partid}.',\'textval\')" /> /'.
-	    $weight{$partid}.' (problem weight)</td>'."\n";
-	$line.= '<td><select name="SELVAL_'.$partid.'"'.
-	    'onChange="javascript:writeRadText(\''.$partid.'\','.
+	    $weight{$partid}.' '.&mt('(problem weight)').'</td>'."\n";
+	$line.= '<td><b>'.&mt('Grade Status').':</b><select name="SELVAL_'.$partid.'"'.
+	    'onchange="javascript:writeRadText(\''.$partid.'\','.
 		$weight{$partid}.')"> '.
 	    '<option selected="selected"> </option>'.
 	    '<option value="excused">'.&mt('excused').'</option>'.
@@ -3234,29 +3508,33 @@ sub viewgrades {
 
 	$result.=
 	    &Apache::loncommon::start_data_table_row()."\n".
-	    &mt('<td><b>Part:</b></td><td>[_1]</td><td><b>Points:</b></td><td>[_2]</td><td>or</td><td>[_3]</td>',$display_part,$radio,$line).
+	    '<td><b>'.&mt('Part:').'</b></td><td>'.$display_part.'</td><td><b>'.&mt('Points:').'</b></td><td>'.$radio.'</td><td>'.&mt('or').'</td><td>'.$line.'</td>'.
 	    &Apache::loncommon::end_data_table_row()."\n";
 	$ctsparts++;
     }
     $result.=&Apache::loncommon::end_data_table()."\n".
 	'<input type="hidden" name="totalparts" value="'.$ctsparts.'" />';
     $result.='<input type="button" value="'.&mt('Revert to Default').'" '.
-	'onClick="javascript:resetEntry('.$ctsparts.');" />';
+	'onclick="javascript:resetEntry('.$ctsparts.');" />';
 
     #table listing all the students in a section/class
     #header of table
-    $result.= '<h3>'.&mt('Assign Grade to Specific Students in '.$sectionClass,
-			 $section_display).'</h3>';
-    $result.= &Apache::loncommon::start_data_table().
-	&Apache::loncommon::start_data_table_header_row().
-	'<th>'.&mt('No.').'</th>'.
-	'<th>'.&nameUserString('header')."</th>\n";
-    my (@parts) = sort(&getpartlist($symb));
+    $result.= '<h3>'.$specific_header.'</h3>'.
+              &Apache::loncommon::start_data_table().
+	      &Apache::loncommon::start_data_table_header_row().
+	      '<th>'.&mt('No.').'</th>'.
+	      '<th>'.&nameUserString('header')."</th>\n";
+    my $partserror;
+    my (@parts) = sort(&getpartlist($symb,\$partserror));
+    if ($partserror) {
+        return &navmap_errormsg();
+    }
     my (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
     my @partids = ();
     foreach my $part (@parts) {
 	my $display=&Apache::lonnet::metadata($url,$part.'.display');
-	$display =~ s|^Number of Attempts|Tries<br />|; # makes the column narrower
+        my $narrowtext = &mt('Tries');
+	$display =~ s|^Number of Attempts|$narrowtext <br />|; # makes the column narrower
 	if  (!$display) { $display = &Apache::lonnet::metadata($url,$part.'.name'); }
 	my ($partid) = &split_part_type($part);
         push(@partids,$partid);
@@ -3301,7 +3579,7 @@ sub viewgrades {
     $result.=&Apache::loncommon::end_data_table();
     $result.='<input type="hidden" name="total" value="'.$ctr.'" />'."\n";
     $result.='<input type="button" value="'.&mt('Save').'" '.
-	'onClick="javascript:submit();" target="_self" /></form>'."\n";
+	'onclick="javascript:submit();" target="_self" /></form>'."\n";
     if (scalar(%$fullname) eq 0) {
 	my $colspan=3+scalar(@parts);
 	my $section_display = join (", ",&Apache::loncommon::get_env_multiple('form.section'));
@@ -3353,7 +3631,7 @@ sub viewstudentgrade {
 		'GD_'.$student.'_'.$part.'_awarded_s" value="'.$pts.'" />'."\n";
 	    $result.='<input type="text" name="'.
 		'GD_'.$student.'_'.$part.'_awarded" '.
-		'onChange="javascript:changeSelect(\''.$part.'\',\''.$student.
+                'onchange="javascript:changeSelect(\''.$part.'\',\''.$student.
 		'\')" value="'.$pts.'" size="4" /></td>'."\n";
 	} elsif ($type eq 'solved') {
 	    my ($status,$foo)=split(/_/,$score,2);
@@ -3362,7 +3640,7 @@ sub viewstudentgrade {
 		$part.'_solved_s" value="'.$status.'" />'."\n";
 	    $result.='&nbsp;<select name="'.
 		'GD_'.$student.'_'.$part.'_solved" '.
-		'onChange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n";
+                'onchange="javascript:changeOneScore(\''.$part.'\',\''.$student.'\')" >'."\n";
 	    $result.= (($status eq 'excused') ? '<option> </option><option selected="selected" value="excused">'.&mt('excused').'</option>' 
 		: '<option selected="selected"> </option><option value="excused">'.&mt('excused').'</option>')."\n";
 	    $result.='<option value="reset status">'.&mt('reset status').'</option>';
@@ -3400,6 +3678,7 @@ sub editgrades {
 		    'incorrect'=>'incorrect_by_override',
 		    'excused'  =>'excused',
 		    'ungraded' =>'ungraded_attempted',
+                    'credited' =>'credit_attempted',
 		    'nothing'  => '',
 		    );
     my ($classlist,undef,$fullname) = &getclasslist($env{'form.section'},'0');
@@ -3409,7 +3688,11 @@ sub editgrades {
     my %columns = ();
     my ($i,$ctr,$count,$rec_update) = (0,0,0,0);
 
-    my (@parts) = sort(&getpartlist($symb));
+    my $partserror;
+    my (@parts) = sort(&getpartlist($symb,\$partserror));
+    if ($partserror) {
+        return &navmap_errormsg();
+    }
     my $header;
     while ($ctr < $env{'form.totalparts'}) {
 	my $partid = $env{'form.partid_'.$ctr};
@@ -3427,10 +3710,11 @@ sub editgrades {
 	    if ($part !~ m/^\Q$partid\E/) { next;}
 	    if ($type eq 'awarded' || $type eq 'solved') { next; }
 	    my $display=&Apache::lonnet::metadata($url,$stores.'.display');
-	    $display =~ s/\[Part: (\w)+\]//;
-	    $display =~ s/Number of Attempts/Tries/;
-	    $header .= '<th align="center">'.&mt('Old '.$display).'</th>'.
-		'<th align="center">'.&mt('New '.$display).'</th>';
+	    $display =~ s/\[Part: \Q$part\E\]//;
+            my $narrowtext = &mt('Tries');
+	    $display =~ s/Number of Attempts/$narrowtext/;
+	    $header .= '<th align="center">'.&mt('Old').' '.$display.'</th>'.
+		'<th align="center">'.&mt('New').' '.$display.'</th>';
 	    $columns{$partid}+=2;
 	}
     }
@@ -3619,7 +3903,7 @@ sub split_part_type {
 #
 #--- Javascript to handle csv upload
 sub csvupload_javascript_reverse_associate {
-    my $error1=&mt('You need to specify the username or ID');
+    my $error1=&mt('You need to specify the username or the student/employee ID');
     my $error2=&mt('You need to specify at least one grading field');
   return(<<ENDPICK);
   function verify(vf) {
@@ -3659,7 +3943,7 @@ ENDPICK
 }
 
 sub csvupload_javascript_forward_associate {
-    my $error1=&mt('You need to specify the username or ID');
+    my $error1=&mt('You need to specify the username or the student/employee ID');
     my $error2=&mt('You need to specify at least one grading field');
   return(<<ENDPICK);
   function verify(vf) {
@@ -3717,7 +4001,7 @@ $result
 Total number of records found in file: $distotal <hr />
 Enter as many fields as you can. The system will inform you and bring you back
 to this page if the data selected is insufficient to run your class.<hr />
-<input type="button" value="Reverse Association" onClick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
+<input type="button" value="Reverse Association" onclick="javascript:this.form.associate.value='Reverse Association';submit(this.form);" />
 <label><input type="checkbox" name="noFirstLine" $checked />$ignore</label>
 <input type="hidden" name="associate"  value="" />
 <input type="hidden" name="phase"      value="three" />
@@ -3740,9 +4024,15 @@ ENDPICK
 }
 
 sub csvupload_fields {
-    my ($symb) = @_;
-    my (@parts) = &getpartlist($symb);
-    my @fields=(['ID','Student ID'],
+    my ($symb,$errorref) = @_;
+    my (@parts) = &getpartlist($symb,$errorref);
+    if (ref($errorref)) {
+        if ($$errorref) {
+            return;
+        }
+    }
+
+    my @fields=(['ID','Student/Employee ID'],
 		['username','Student Username'],
 		['domain','Student Domain']);
     my (undef,undef,$url) = &Apache::lonnet::decode_symb($symb);
@@ -3766,17 +4056,18 @@ sub csvuploadmap_footer {
 </table>
 <input type="hidden" name="nfields" value="$i" />
 <input type="hidden" name="keyfields" value="$keyfields" />
-<input type="button" onClick="javascript:verify(this.form)" value="Assign Grades" /><br />
+<input type="button" onclick="javascript:verify(this.form)" value="Assign Grades" /><br />
 </form>
 ENDPICK
 }
 
 sub checkforfile_js {
+    my $alertmsg = &mt('Please use the browse button to select a file from your local directory.');
     my $result =<<CSVFORMJS;
 <script type="text/javascript" language="javascript">
     function checkUpload(formname) {
 	if (formname.upfile.value == "") {
-	    alert("Please use the browse button to select a file from your local directory.");
+	    alert("$alertmsg");
 	    return false;
 	}
 	formname.submit();
@@ -3796,9 +4087,9 @@ sub upcsvScores_form {
     $result.=$table;
     $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";
     $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";
-    $result.='&nbsp;<b>'.&mt('Specify a file containing the class scores for current resource').
-	'.</b></td></tr>'."\n";
-    $result.='<tr bgcolor=#ffffe6><td>'."\n";
+    $result.='&nbsp;<b>'.&mt('Specify a file containing the class scores for current resource.').
+	'</b></td></tr>'."\n";
+    $result.='<tr bgcolor="#ffffe6"><td>'."\n";
     my $upload=&mt("Upload Scores");
     my $upfile_select=&Apache::loncommon::upfile_select_html();
     my $ignore=&mt('Ignore First Line');
@@ -3810,7 +4101,7 @@ sub upcsvScores_form {
 <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" />
 <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
 $upfile_select
-<br /><input type="button" onClick="javascript:checkUpload(this.form);" value="$upload" />
+<br /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />
 <label><input type="checkbox" name="noFirstLine" />$ignore</label>
 </form>
 ENDUPFORM
@@ -3840,8 +4131,12 @@ sub csvuploadmap {
     &csvuploadmap_header($request,$symb,$datatoken,$#records+1);
     my ($i,$keyfields);
     if (@records) {
-	my @fields=&csvupload_fields($symb);
-
+        my $fieldserror;
+	my @fields=&csvupload_fields($symb,\$fieldserror);
+        if ($fieldserror) {
+            $request->print(&navmap_errormsg());
+            return;
+        }
 	if ($env{'form.upfile_associate'} eq 'reverse') {	
 	    &Apache::loncommon::csv_print_samples($request,\@records);
 	    $i=&Apache::loncommon::csv_print_select_table($request,\@records,
@@ -3942,6 +4237,7 @@ sub csvuploadassign {
     my ($classlist) = &getclasslist('all',0);
     my @notallowed;
     my @skipped;
+    my @warnings;
     my $countdone=0;
     foreach my $grade (@gradedata) {
 	my %entries=&Apache::loncommon::record_sep($grade);
@@ -3990,6 +4286,9 @@ sub csvuploadassign {
                     my $pcr=$entries{$fields{$dest}} / $wgt;
                     my $award=($pcr == 0) ? 'incorrect_by_override'
                                           : 'correct_by_override';
+                    if ($pcr>1) {
+                        push(@warnings,&mt("[_1]: point value larger than weight","$username:$domain"));
+                    }
                     $grades{"resource.$part.awarded"}=$pcr;
                     $grades{"resource.$part.solved"}=$award;
                     $points{$part}=1;
@@ -4018,6 +4317,11 @@ sub csvuploadassign {
 					   $domain,$username);
 	   if ($result eq 'ok') {
 	      $request->print('.');
+# Remove from grading queue
+              &Apache::bridgetask::remove_from_queue('gradingqueue',$symb,
+                                             $env{'course.'.$env{'request.course.id'}.'.domain'},
+                                             $env{'course.'.$env{'request.course.id'}.'.num'},
+                                             $domain,$username);
 	   } else {
 	      $request->print("<p><span class=\"LC_error\">".
                               &mt("Failed to save data for student [_1]. Message when trying to save was: [_2]",
@@ -4027,14 +4331,18 @@ sub csvuploadassign {
 	   $countdone++;
         }
     }
-    $request->print('<br /><span class="LC_info">'.&mt("Saved [_1] students",$countdone)."</span>\n");
+    $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt("Saved scores for [quant,_1,student]",$countdone),$countdone==0));
+    if (@warnings) {
+        $request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('Warnings generated for the following saved scores:'),1).'<br />');
+        $request->print(join(', ',@warnings));
+    }
     if (@skipped) {
-	$request->print('<p><span class="LC_warning">'.&mt('Skipped Students').'</span></p>');
-	foreach my $student (@skipped) { $request->print("$student<br />\n"); }
+	$request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('No scores stored for the following username(s):'),1).'<br />');
+        $request->print(join(', ',@skipped));
     }
     if (@notallowed) {
-	$request->print('<p><span class="LC_error">'.&mt('Students Not Allowed to Modify').'</span></p>');
-	foreach my $student (@notallowed) { $request->print("$student<br />\n"); }
+	$request->print('<br />'.&Apache::lonhtmlcommon::confirm_success(&mt('Modification of scores not allowed for the following username(s):'),1).'<br />');
+	$request->print(join(', ',@notallowed));
     }
     $request->print("<br />\n");
     $request->print(&show_grading_menu_form($symb));
@@ -4050,12 +4358,13 @@ sub csvuploadassign {
 sub pickStudentPage {
     my ($request) = shift;
 
+    my $alertmsg = &mt('Please select the student you wish to grade.');
     $request->print(<<LISTJAVASCRIPT);
 <script type="text/javascript" language="javascript">
 
 function checkPickOne(formname) {
     if (radioSelection(formname.student) == null) {
-	alert("Please select the student you wish to grade.");
+	alert("$alertmsg");
 	return;
     }
     ptr = pullDownSelection(formname.selectpage);
@@ -4076,7 +4385,12 @@ LISTJAVASCRIPT
 	&mt('Manual Grading by Page or Sequence').'</span></h3>';
 
     $result.='<form action="/adm/grades" method="post" name="displayPage">'."\n";
-    my ($titles,$symbx) = &getSymbMap();
+    my $map_error;
+    my ($titles,$symbx) = &getSymbMap($map_error);
+    if ($map_error) {
+        $request->print(&navmap_errormsg());
+        return; 
+    }
     my ($curpage) =&Apache::lonnet::decode_symb($symb); 
 #    my ($curpage,$mapId) =&Apache::lonnet::decode_symb($symb); 
 #    my $type=($curpage =~ /\.(page|sequence)/);
@@ -4090,7 +4404,7 @@ LISTJAVASCRIPT
 	$ctr++;
     }
     $select.= '</select>';
-    $result.=&mt('&nbsp;<b>Problems from:</b> [_1]',$select)."<br />\n";
+    $result.='&nbsp;<b>'.&mt('Problems from').':</b> '.$select."<br />\n";
 
     $ctr=0;
     foreach (@$titles) {
@@ -4105,13 +4419,13 @@ LISTJAVASCRIPT
     my $options =
 	'<label><input type="radio" name="vProb" value="no" checked="checked" /> '.&mt('no').' </label>'."\n".
 	'<label><input type="radio" name="vProb" value="yes" /> '.&mt('yes').' </label>'."<br />\n";
-    $result.='&nbsp;'.&mt('<b>View Problems Text: </b> [_1]',$options);
+    $result.='&nbsp;<b>'.&mt('View Problem Text').': </b>'.$options;
 
     $options =
 	'<label><input type="radio" name="lastSub" value="none" /> '.&mt('none').' </label>'."\n".
 	'<label><input type="radio" name="lastSub" value="datesub" checked="checked" /> '.&mt('by dates and submissions').'</label>'."\n".
 	'<label><input type="radio" name="lastSub" value="all" /> '.&mt('all details').' </label>'."\n";
-    $result.='&nbsp;'.&mt('<b>Submission Details: </b>[_1]',$options);
+    $result.='&nbsp;<b>'.&mt('Submissions').': </b>'.$options;
     
     $result.=&build_section_inputs();
     my $stu_status = join(':',&Apache::loncommon::get_env_multiple('form.Status'));
@@ -4120,12 +4434,10 @@ LISTJAVASCRIPT
 	'<input type="hidden" name="symb"    value="'.&Apache::lonenc::check_encrypt($symb).'" />'."\n".
 	'<input type="hidden" name="saveState" value="'.$env{'form.saveState'}.'" />'."<br />\n";
 
-    $result.='&nbsp;'.&mt('<b>Use CODE: [_1] </b>',
-			  '<input type="text" name="CODE" value="" />').
-			      '<br />'."\n";
+    $result.='&nbsp;<b>'.&mt('Use CODE').': </b> <input type="text" name="CODE" value="" /> <br />'."\n";
 
     $result.='&nbsp;<input type="button" '.
-	'onClick="javascript:checkPickOne(this.form);" value="'.&mt('Next-&gt;').'" /><br />'."\n";
+             'onclick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /><br />'."\n";
 
     $request->print($result);
 
@@ -4164,7 +4476,7 @@ LISTJAVASCRIPT
     }
     $studentTable.=&Apache::loncommon::end_data_table()."\n";
     $studentTable.='<input type="button" '.
-	'onClick="javascript:checkPickOne(this.form);" value="'.&mt('Next-&gt;').'" /></form>'."\n";
+                   'onclick="javascript:checkPickOne(this.form);" value="'.&mt('Next').' &rarr;" /></form>'."\n";
 
     $studentTable.=&show_grading_menu_form($symb);
     $request->print($studentTable);
@@ -4173,8 +4485,14 @@ LISTJAVASCRIPT
 }
 
 sub getSymbMap {
+    my ($map_error) = @_;
     my $navmap = Apache::lonnavmaps::navmap->new();
-
+    unless (ref($navmap)) {
+        if (ref($map_error)) {
+            $$map_error = 'navmap';
+        }
+        return;
+    }
     my %symbx = ();
     my @titles = ();
     my $minder = 0;
@@ -4233,6 +4551,11 @@ sub displayPage {
     $request->print($result);
 
     my $navmap = Apache::lonnavmaps::navmap->new();
+    unless (ref($navmap)) {
+        $request->print(&navmap_errormsg());
+        $request->print(&show_grading_menu_form($symb));
+        return;
+    }
     my ($mapUrl, $id, $resUrl)=&Apache::lonnet::decode_symb($env{'form.page'});
     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
     if (!$map) {
@@ -4260,7 +4583,9 @@ sub displayPage {
     my $checkIcon = '<img alt="'.&mt('Check Mark').
 	'" src="'.&Apache::loncommon::lonhttpdurl($request->dir_config('lonIconsURL').'/check.gif').'" height="16" border="0" />';
 
-    $studentTable.='&nbsp;'.&mt('<b>Note:</b> Problems graded correct by the computer are marked with a [_1] symbol.',$checkIcon)."\n".
+    $studentTable.='&nbsp;<span class="LC_info">'.
+        &mt('Problems graded correct by the computer are marked with a [_1] symbol.',$checkIcon).
+        '</span>'."\n".
 	&Apache::loncommon::start_data_table().
 	&Apache::loncommon::start_data_table_header_row().
 	'<th align="center">&nbsp;Prob.&nbsp;</th>'.
@@ -4283,8 +4608,8 @@ sub displayPage {
 		&Apache::loncommon::start_data_table_row().
 		'<td align="center" valign="top" >'.$prob.
 		(scalar(@{$parts}) == 1 ? '' 
-		                        : '<br />('.&mt('[_1]&nbsp;parts)',
-							scalar(@{$parts}))
+		                        : '<br />('.&mt('[_1]parts)',
+							scalar(@{$parts}).'&nbsp;')
 		 ).
 		 '</td>';
 	    $studentTable.='<td valign="top">';
@@ -4301,7 +4626,7 @@ sub displayPage {
 #		    $request->print('match='.$1."<br />\n");
 #		}
 #		$companswer =~ s|<table border=\"1\">|<table border=\"0\">|g;
-		$studentTable.='&nbsp;<b>'.$title.'</b>&nbsp;<br />&nbsp;'.&mt('<b>Correct answer:</b><br />[_1]',$companswer);
+		$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);
@@ -4331,11 +4656,13 @@ sub displayPage {
  
 	    }
 	    if (&canmodify($usec)) {
+            $studentTable.=&gradeBox_start();
 		foreach my $partid (@{$parts}) {
 		    $studentTable.=&gradeBox($request,$symbx,$uname,$udom,$question,$partid,\%record);
 		    $studentTable.='<input type="hidden" name="q_'.$question.'" value="'.$partid.'" />'."\n";
 		    $question++;
 		}
+            $studentTable.=&gradeBox_end();
 		$prob++;
 	    }
 	    $studentTable.='</td></tr>';
@@ -4344,10 +4671,11 @@ sub displayPage {
         $curRes = $iterator->next();
     }
 
-    $studentTable.='</table>'."\n".
-	'<input type="button" value="'.&mt('Save').'" '.
-	'onClick="javascript:checkSubmitPage(this.form,'.$question.');" />'.
-	'</form>'."\n";
+    $studentTable.=
+        '</table>'."\n".
+        '<input type="button" value="'.&mt('Save').'" '.
+        'onclick="javascript:checkSubmitPage(this.form,'.$question.');" />'.
+        '</form>'."\n";
     $studentTable.=&show_grading_menu_form($symb);
     $request->print($studentTable);
 
@@ -4371,11 +4699,12 @@ sub displaySubByDates {
     my %orders;
     $mark{'correct_by_student'} = $checkIcon;
     if (!exists($$record{'1:timestamp'})) {
-	return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts').'</span><br />';
+	return '<br />&nbsp;<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br />';
     }
 
     my $interaction;
     my $no_increment = 1;
+    my %lastrndseed;
     for ($version=1;$version<=$$record{'version'};$version++) {
 	my $timestamp = 
 	    &Apache::lonlocal::locallocaltime($$record{$version.':timestamp'});
@@ -4393,37 +4722,59 @@ sub displaySubByDates {
 	my @versionKeys = split(/\:/,$$record{$version.':keys'});
 	my @displaySub = ();
 	foreach my $partid (@{$parts}) {
+            my ($hidden,$type);
+            $type = $$record{$version.':resource.'.$partid.'.type'};
+            if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) {
+                $hidden = 1;
+            }
 	    my @matchKey = ($isTask ? sort(grep /^resource\.\d+\.\Q$partid\E\.award$/,@versionKeys)
 			            : sort(grep /^resource\.\Q$partid\E\..*?\.submission$/,@versionKeys));
 	    
-
 #	    next if ($$record{"$version:resource.$partid.solved"} eq '');
 	    my $display_part=&get_display_part($partid,$symb);
 	    foreach my $matchKey (@matchKey) {
 		if (exists($$record{$version.':'.$matchKey}) &&
 		    $$record{$version.':'.$matchKey} ne '') {
-
+                    
 		    my ($responseId)= ($isTask ? ($matchKey=~ /^resource\.(.*?)\.\Q$partid\E\.award$/)
 				               : ($matchKey=~ /^resource\.\Q$partid\E\.(.*?)\.submission$/));
-		    $displaySub[0].='<b>'.&mt('Part:').'</b>&nbsp;'.$display_part.'&nbsp;';
-		    $displaySub[0].='<span class="LC_internal_info">('.&mt('ID').'&nbsp;'.
-			$responseId.')</span>&nbsp;<b>';
-		    if ($$record{"$where.$partid.tries"} eq '') {
-			$displaySub[0].=&mt('Trial&nbsp;not&nbsp;counted');
-		    } else {
-			$displaySub[0].=&mt('Trial&nbsp;[_1]',
+                    $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 {
+                        my ($trial,$rndseed,$newvariation);
+                        if ($type eq 'randomizetry') {
+                            $trial = $$record{"$where.$partid.tries"};
+                            $rndseed = $$record{"$where.$partid.rndseed"};
+                        }
+		        if ($$record{"$where.$partid.tries"} eq '') {
+			    $displaySub[0].=&mt('Trial not counted');
+		        } else {
+			    $displaySub[0].=&mt('Trial: [_1]',
 					    $$record{"$where.$partid.tries"});
-		    }
-		    my $responseType=($isTask ? 'Task'
+                            if ($rndseed || $lastrndseed{$partid}) {
+                                if ($rndseed ne $lastrndseed{$partid}) {
+                                    $newvariation = '&nbsp;('.&mt('New variation this try').')';
+                                }
+                            }
+		        }
+		        my $responseType=($isTask ? 'Task'
                                               : $responseType->{$partid}->{$responseId});
-		    if (!exists($orders{$partid})) { $orders{$partid}={}; }
-		    if (!exists($orders{$partid}->{$responseId})) {
-			$orders{$partid}->{$responseId}=
-			    &get_order($partid,$responseId,$symb,$uname,$udom,
-                                       $no_increment);
-		    }
-		    $displaySub[0].='</b>&nbsp; '.
-			&cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom).'<br />';
+		        if (!exists($orders{$partid})) { $orders{$partid}={}; }
+		        if ((!exists($orders{$partid}->{$responseId})) || ($trial)) {
+			    $orders{$partid}->{$responseId}=
+			        &get_order($partid,$responseId,$symb,$uname,$udom,
+                                           $no_increment,$type,$trial,$rndseed);
+		        }
+		        $displaySub[0].='</b>'.$newvariation.'</span>'; # /nobreak
+		        $displaySub[0].='&nbsp; '.
+			    &cleanRecord($$record{$version.':'.$matchKey},$responseType,$symb,$partid,$responseId,$record,$orders{$partid}->{$responseId},"$version:",$uname,$udom,$type,$trial,$rndseed).'<br />';
+                    }
 		}
 	    }
 	    if (exists($$record{"$where.$partid.checkedin"})) {
@@ -4483,7 +4834,12 @@ sub updateGradeByPage {
 
     $request->print($result);
 
+
     my $navmap = Apache::lonnavmaps::navmap->new();
+    unless (ref($navmap)) {
+        $request->print(&navmap_errormsg());
+        return;
+    }
     my ($mapUrl, $id, $resUrl) = &Apache::lonnet::decode_symb( $env{'form.page'});
     my $map = $navmap->getResourceByUrl($resUrl); # add to navmaps
     if (!$map) {
@@ -4519,7 +4875,7 @@ sub updateGradeByPage {
 		&Apache::loncommon::start_data_table_row().
 		'<td align="center" valign="top" >'.$prob.
 		(scalar(@{$parts}) == 1 ? '' 
-                                        : '<br />('.&mt('[quant,_1,&nbsp;part]',scalar(@{$parts}))
+                                        : '<br />('.&mt('[quant,_1,part]',scalar(@{$parts}))
 		.')').'</td>';
 	    $studentTable.='<td valign="top">&nbsp;<b>'.$title.'</b>&nbsp;</td>';
 
@@ -4628,7 +4984,7 @@ sub updateGradeByPage {
 #
 #-------------------------------------------------------------------
 
-#--------------------Scantron Grading-----------------------------------
+#-------------------- Bubblesheet (Scantron) Grading -------------------
 #
 #------ start of section for handling grading by page/sequence ---------
 
@@ -4642,12 +4998,12 @@ sub updateGradeByPage {
    from the file that we are parsing that represents one entire sheet
 
    'bubble line' refers to the data
-   representing the line of bubbles that are on the physical bubble sheet
+   representing the line of bubbles that are on the physical bubblesheet
 
 
-The overall process is that a scanned in bubble sheet data is uploaded
+The overall process is that a scanned in bubblesheet data is uploaded
 into a course. When a user wants to grade, they select a
-sequence/folder of resources, a file of bubble sheet info, and pick
+sequence/folder of resources, a file of bubblesheet info, and pick
 one of the predefined configurations for what each scanline looks
 like.
 
@@ -4655,22 +5011,22 @@ Next each scanline is checked for any er
 bubbles' (it's an error because it may have been mis-scanned
 because too light bubbling), 'double bubble' (each bubble line should
 have no more that one letter picked), invalid or duplicated CODE,
-invalid student ID
+invalid student/employee ID
 
 If the CODE option is used that determines the randomization of the
-homework problems, either way the student ID is looked up into a
+homework problems, either way the student/employee ID is looked up into a
 username:domain.
 
 During the validation phase the instructor can choose to skip scanlines. 
 
-After the validation phase, there are now 3 bubble sheet files
+After the validation phase, there are now 3 bubblesheet files
 
   scantron_original_filename (unmodified original file)
   scantron_corrected_filename (file where the corrected information has replaced the original information)
   scantron_skipped_filename (contains the exact text of scanlines that where skipped)
 
 Also there is a separate hash nohist_scantrondata that contains extra
-correction information that isn't representable in the bubble sheet
+correction information that isn't representable in the bubblesheet
 file (see &scantron_getfile() for more information)
 
 After all scanlines are either valid, marked as valid or skipped, then
@@ -4706,14 +5062,19 @@ sub defaultFormData {
    Return html dropdown of possible sequences to grade
  
  Arguments:
-   $symb - $symb of the current resource 
+   $symb - $symb of the current resource
+   $map_error - ref to scalar which will container error if
+                $navmap object is unavailable in &getSymbMap().
 
 =cut
 
 sub getSequenceDropDown {
-    my ($symb)=@_;
+    my ($symb,$map_error)=@_;
     my $result='<select name="selectpage">'."\n";
-    my ($titles,$symbx) = &getSymbMap();
+    my ($titles,$symbx) = &getSymbMap($map_error);
+    if (ref($map_error)) {
+        return if ($$map_error);
+    }
     my ($curpage)=&Apache::lonnet::decode_symb($symb); 
     my $ctr=0;
     foreach (@$titles) {
@@ -4728,7 +5089,7 @@ sub getSequenceDropDown {
 }
 
 my %bubble_lines_per_response;     # no. bubble lines for each response.
-                                   # index is "symb.part_id"
+                                   # key is zero-based index - 0, 1, 2 ...
 
 my %first_bubble_line;             # First bubble line no. for each bubble.
 
@@ -4769,7 +5130,6 @@ sub restore_bubble_lines {
             $env{"form.scantron.responsetype.$line"};
 	$line++;
     }
-
 }
 
 #  Given the parsed scanline, get the response for 
@@ -4778,7 +5138,6 @@ sub restore_bubble_lines {
 sub get_response_bubbles {
     my ($parsed_line, $response)  = @_;
 
-
     my $bubble_line = $first_bubble_line{$response-1} +1;
     my $bubble_lines= $bubble_lines_per_response{$response-1};
     
@@ -4964,7 +5323,7 @@ sub scantron_CODEunique {
 
 =item scantron_selectphase
 
-  Generates the initial screen to start the bubble sheet process.
+  Generates the initial screen to start the bubblesheet process.
   Allows for - starting a grading run.
              - downloading existing scan data (original, corrected
                                                 or skipped info)
@@ -4981,7 +5340,12 @@ sub scantron_selectphase {
     my ($r,$file2grade) = @_;
     my ($symb)=&get_symb($r);
     if (!$symb) {return '';}
-    my $sequence_selector=&getSequenceDropDown($symb);
+    my $map_error;
+    my $sequence_selector=&getSequenceDropDown($symb,\$map_error);
+    if ($map_error) {
+        $r->print('<br />'.&navmap_errormsg().'<br />');
+        return;
+    }
     my $default_form_data=&defaultFormData($symb);
     my $grading_menu_button=&show_grading_menu_form($symb);
     my $file_selector=&scantron_uploads($file2grade);
@@ -4992,6 +5356,54 @@ sub scantron_selectphase {
 
     $ssi_error = 0;
 
+    if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||
+        &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
+
+        # Chunk of form to prompt for a scantron file upload.
+
+        $r->print('
+    <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(&get_symb($r,1));
+    my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
+    my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
+    $r->print('
+              <script type="text/javascript" language="javascript">
+    function checkUpload(formname) {
+        if (formname.upfile.value == "") {
+            alert("'.&mt('Please use the browse button to select a file from your local directory.').'");
+            return false;
+        }
+        formname.submit();
+    }
+              </script>
+
+              <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
+                '.$default_form_data.'
+                <input name="courseid" type="hidden" value="'.$cnum.'" />
+                <input name="domainid" type="hidden" value="'.$cdom.'" />
+                <input name="command" value="scantronupload_save" type="hidden" />
+                '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'
+                <br />
+                <input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
+              </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:
 
     $result.= '
@@ -5009,10 +5421,10 @@ sub scantron_selectphase {
             <td> '.&mt('Sequence to grade:').' </td><td> '.$sequence_selector.' </td>
        '.&Apache::loncommon::end_data_table_row().'
        '.&Apache::loncommon::start_data_table_row().'
-            <td> '.&mt('Filename of scoring office file:').' </td><td> '.$file_selector.' </td>
+            <td> '.&mt('Filename of bubblesheet data file:').' </td><td> '.$file_selector.' </td>
        '.&Apache::loncommon::end_data_table_row().'
        '.&Apache::loncommon::start_data_table_row().'
-            <td> '.&mt('Format of data file:').' </td><td> '.$format_selector.' </td>
+            <td> '.&mt('Format of bubblesheet data file:').' </td><td> '.$format_selector.' </td>
        '.&Apache::loncommon::end_data_table_row().'
        '.&Apache::loncommon::start_data_table_row().'
             <td> '.&mt('Saved CODEs to validate against:').' </td><td> '.$CODE_selector.' </td>
@@ -5030,7 +5442,7 @@ sub scantron_selectphase {
        '.&Apache::loncommon::end_data_table_row().'
        '.&Apache::loncommon::start_data_table_row().'
             <td colspan="2">
-              <input type="submit" value="'.&mt('Grading: Validate Scantron Records').'" />
+              <input type="submit" value="'.&mt('Grading: Validate Bubblesheet Records').'" />
             </td>
        '.&Apache::loncommon::end_data_table_row().'
     '.&Apache::loncommon::end_data_table().'
@@ -5039,54 +5451,6 @@ sub scantron_selectphase {
    
     $r->print($result);
 
-    if (&Apache::lonnet::allowed('usc',$env{'request.role.domain'}) ||
-        &Apache::lonnet::allowed('usc',$env{'request.course.id'})) {
-
-	# Chunk of form to prompt for a scantron file upload.
-
-        $r->print('
-    <br />
-    '.&Apache::loncommon::start_data_table('LC_scantron_action').'
-       '.&Apache::loncommon::start_data_table_header_row().'
-            <th>
-              &nbsp;'.&mt('Specify a Scantron data file to upload.').'
-            </th>
-       '.&Apache::loncommon::end_data_table_header_row().'
-       '.&Apache::loncommon::start_data_table_row().'
-            <td>
-');
-    my $default_form_data=&defaultFormData(&get_symb($r,1));
-    my $cdom= $env{'course.'.$env{'request.course.id'}.'.domain'};
-    my $cnum= $env{'course.'.$env{'request.course.id'}.'.num'};
-    $r->print('
-              <script type="text/javascript" language="javascript">
-    function checkUpload(formname) {
-	if (formname.upfile.value == "") {
-	    alert("'.&mt('Please use the browse button to select a file from your local directory.').'");
-	    return false;
-	}
-	formname.submit();
-    }
-              </script>
-
-              <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
-                '.$default_form_data.'
-                <input name="courseid" type="hidden" value="'.$cnum.'" />
-                <input name="domainid" type="hidden" value="'.$cdom.'" />
-                <input name="command" value="scantronupload_save" type="hidden" />
-                '.&mt('File to upload: [_1]','<input type="file" name="upfile" size="50" />').'
-                <br />
-                <input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Scantron Data').'" />
-              </form>
-');
-
-        $r->print('
-            </td>
-       '.&Apache::loncommon::end_data_table_row().'
-       '.&Apache::loncommon::end_data_table().'
-');
-    }
-
     # Chunk of the form that prompts to view a scoring office file,
     # corrected file, skipped records in a file.
 
@@ -5118,7 +5482,7 @@ sub scantron_selectphase {
              &Apache::loncommon::start_data_table('LC_scantron_action')."\n".
              &Apache::loncommon::start_data_table_header_row()."\n".
              '<th colspan="2">
-              &nbsp;'.&mt('Review scantron data and submissions for a previously graded folder/sequence')."\n".
+              &nbsp;'.&mt('Review bubblesheet data and submissions for a previously graded folder/sequence')."\n".
              '</th>'."\n".
               &Apache::loncommon::end_data_table_header_row()."\n".
               &Apache::loncommon::start_data_table_row()."\n".
@@ -5134,9 +5498,13 @@ sub scantron_selectphase {
               '<td> '.$format_selector.' </td>'."\n".
               &Apache::loncommon::end_data_table_row()."\n".
               &Apache::loncommon::start_data_table_row()."\n".
+              '<td> '.&mt('Options').' </td>'."\n".
+              '<td> <label><input type="checkbox" name="scantron_options_hidden" value="ignore_hidden"/> '.&mt('Skip hidden resources').'</label></td>'.
+              &Apache::loncommon::end_data_table_row()."\n".
+              &Apache::loncommon::start_data_table_row()."\n".
               '<td colspan="2">'."\n".
               '<input type="hidden" name="command" value="checksubmissions" />'."\n".
-              '<input type="submit" value="'.&mt('Review Scantron Data and Submission Records').'" />'."\n".
+              '<input type="submit" value="'.&mt('Review Bubblesheet Data and Submission Records').'" />'."\n".
               '</td>'."\n".
               &Apache::loncommon::end_data_table_row()."\n".
               &Apache::loncommon::end_data_table()."\n".
@@ -5177,8 +5545,8 @@ sub scantron_selectphase {
       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 ID number starts
-      IDlength    - length of the student ID info
+      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
@@ -5238,7 +5606,7 @@ sub get_scantron_config {
 
 =item username_to_idmap
 
-    creates a hash keyed by student id with values of the corresponding
+    creates a hash keyed by student/employee ID with values of the corresponding
     student username:domain.
 
   Arguments:
@@ -5277,7 +5645,7 @@ sub username_to_idmap {
     $whichline         - line number of the passed in scanline
     $field             - type of change to process 
                          (either 
-                          'ID'     -> correct the student ID number
+                          'ID'     -> correct the student/employee ID
                           'CODE'   -> correct the CODE
                           'answer' -> fixup the submitted answers)
     
@@ -5451,7 +5819,7 @@ sub digits_to_letters {
        CODE_ignore_dup - 1 if the CODE is a duplicated use when unique
                             CODEs were selected, but the usage has been
                             forced by the operator
-       ID  - student ID
+       ID  - student/employee ID
        PaperID - if used, the ID number printed on the sheet when the 
                  paper was scanned
        FirstName - first name from the sheet
@@ -5487,7 +5855,8 @@ sub scantron_parse_scanline {
     my ($line,$whichline,$scantron_config,$scan_data,$just_header)=@_;
 
     my %record;
-    my $questions=substr($line,$$scantron_config{'Qstart'}-1);  # Answers
+    my $lastpos = $env{'form.scantron_maxbubble'}*$$scantron_config{'Qlength'};
+    my $questions=substr($line,$$scantron_config{'Qstart'}-1,$lastpos);  # Answers
     my $data=substr($line,0,$$scantron_config{'Qstart'}-1);     # earlier stuff
     if (!($$scantron_config{'CODElocation'} eq 0 ||
 	  $$scantron_config{'CODElocation'} eq 'none')) {
@@ -5977,7 +6346,7 @@ sub remember_current_skipped {
 =item check_for_error
 
     Checks if there was an error when attempting to remove a specific
-    scantron_.. bubble sheet data file. Prints out an error if
+    scantron_.. bubblesheet data file. Prints out an error if
     something went wrong.
 
 =cut
@@ -6048,15 +6417,15 @@ sub scantron_do_warning {
     if ( $env{'form.selectpage'} eq '' ||
 	 $env{'form.scantron_selectfile'} eq '' ||
 	 $env{'form.scantron_format'} eq '' ) {
-	$r->print("<p>".&mt('You have forgetten to specify some information. Please go Back and try again.')."</p>");
+	$r->print("<p>".&mt('You have forgotten to specify some information. Please go Back and try again.')."</p>");
 	if ( $env{'form.selectpage'} eq '') {
 	    $r->print('<p><span class="LC_error">'.&mt('You have not selected a Sequence to grade').'</span></p>');
 	} 
 	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 '') {
-	    $r->print('<p><span class="LC_error">'.&mt('You have not selected a 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 {
 	my $warning=&scantron_warning_screen('Grading: Validate Records');
@@ -6113,7 +6482,7 @@ SCANTRONFORM
 
 =item scantron_validate_file
 
-    Dispatch routine for doing validation of a bubble sheet data file.
+    Dispatch routine for doing validation of a bubblesheet data file.
 
     Also processes any necessary information resets that need to
     occur before validation begins (ignore previous corrections,
@@ -6150,7 +6519,12 @@ sub scantron_validate_file {
     $r->print('<p>'.&mt('Gathering necessary information.').'</p>');$r->rflush();
     #get the student pick code ready
     $r->print(&Apache::loncommon::studentbrowser_javascript());
-    my $max_bubble=&scantron_get_maxbubble();
+    my $nav_error;
+    my $max_bubble=&scantron_get_maxbubble(\$nav_error);
+    if ($nav_error) {
+        $r->print(&navmap_errormsg());
+        return '';
+    }
     my $result=&scantron_form_start($max_bubble).$default_form_data;
     $r->print($result);
     
@@ -6177,27 +6551,33 @@ sub scantron_validate_file {
     }
     if (!$stop) {
 	my $warning=&scantron_warning_screen('Start Grading');
-	$r->print(&mt('Validation process complete.').'<br />
-'.$warning.'
-<input type="submit" name="submit" value="'.&mt('Start Grading').'" />
-<input type="hidden" name="command" value="scantron_process" />
-');
-
+	$r->print(&mt('Validation process complete.').'<br />'.
+                  $warning.
+                  &mt('Perform verification for each student after storage of submissions?').
+                  '&nbsp;<span class="LC_nobreak"><label>'.
+                  '<input type="radio" name="verifyrecord" value="1" />'.&mt('Yes').'</label>'.
+                  ('&nbsp;'x3).'<label>'.
+                  '<input type="radio" name="verifyrecord" value="0" checked="checked" />'.&mt('No').
+                  '</label></span><br />'.
+                  &mt('Grading will take longer if you use verification.').'<br />'.
+                  &mt("Alternatively, the 'Review bubblesheet data' utility (see grading menu) can be used for all students after grading is complete.").'<br /><br />'.
+                  '<input type="submit" name="submit" value="'.&mt('Start Grading').'" />'.
+                  '<input type="hidden" name="command" value="scantron_process" />'."\n");
     } else {
 	$r->print('<input type="hidden" name="command" value="scantron_validate" />');
 	$r->print("<input type='hidden' name='validatepass' value='".$currentphase."' />");
     }
     if ($stop) {
 	if ($validate_phases[$currentphase] eq 'sequence') {
-	    $r->print('<input type="submit" name="submit" value="'.&mt('Ignore -&gt;').' " />');
+	    $r->print('<input type="submit" name="submit" value="'.&mt('Ignore').' &rarr; " />');
 	    $r->print(' '.&mt('this error').' <br />');
 
 	    $r->print(" <p>".&mt("Or click the 'Grading Menu' button to start over.")."</p>");
 	} else {
             if ($validate_phases[$currentphase] eq 'doublebubble' || $validate_phases[$currentphase] eq 'missingbubbles') {
-	        $r->print('<input type="button" name="submitbutton" value="'.&mt('Continue -&gt;').'" onclick="javascript:verify_bubble_radio(this.form)" />');
+	        $r->print('<input type="button" name="submitbutton" value="'.&mt('Continue').' &rarr;" onclick="javascript:verify_bubble_radio(this.form)" />');
             } else {
-                $r->print('<input type="submit" name="submit" value="'.&mt('Continue -&gt;').'" />');
+                $r->print('<input type="submit" name="submit" value="'.&mt('Continue').' &rarr;" />');
             }
 	    $r->print(' '.&mt('using corrected info').' <br />');
 	    $r->print("<input type='submit' value='".&mt("Skip")."' name='scantron_skip_record' />");
@@ -6213,7 +6593,7 @@ sub scantron_validate_file {
 
 =item scantron_remove_file
 
-   Removes the requested bubble sheet data file, makes sure that
+   Removes the requested bubblesheet data file, makes sure that
    scantron_original_<filename> is never removed
 
 
@@ -6238,7 +6618,7 @@ sub scantron_remove_file {
 
 =item scantron_remove_scan_data
 
-   Removes all scan_data correction for the requested bubble sheet
+   Removes all scan_data correction for the requested bubblesheet
    data file.  (In the case that both the are doing skipped records we need
    to remember the old skipped lines for the time being so that element
    persists for a while.)
@@ -6275,7 +6655,7 @@ sub scantron_remove_scan_data {
 
 =item scantron_getfile
 
-    Fetches the requested bubble sheet data file (all 3 versions), and
+    Fetches the requested bubblesheet data file (all 3 versions), and
     the scan_data hash
   
   Arguments:
@@ -6375,7 +6755,7 @@ sub lonnet_putfile {
 
 =item scantron_putfile
 
-    Stores the current version of the bubble sheet data files, and the
+    Stores the current version of the bubblesheet data files, and the
     scan_data hash. (Does not modify the original version only the
     corrected and skipped versions.
 
@@ -6469,7 +6849,7 @@ sub get_todo_count {
 
 =item scantron_put_line
 
-    Updates the 'corrected' or 'skipped' versions of the bubble sheet
+    Updates the 'corrected' or 'skipped' versions of the bubblesheet
     data file.
 
  Arguments:
@@ -6556,6 +6936,10 @@ sub scantron_validate_sequence {
     my ($r,$currentphase) = @_;
 
     my $navmap=Apache::lonnavmaps::navmap->new();
+    unless (ref($navmap)) {
+        $r->print(&navmap_errormsg());
+        return (1,$currentphase);
+    }
     my (undef,undef,$sequence)=
 	&Apache::lonnet::decode_symb($env{'form.selectpage'});
 
@@ -6587,8 +6971,13 @@ sub scantron_validate_ID {
     #get scantron line setup
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();
-    
-    &scantron_get_maxbubble();	# parse needs the bubble_lines.. array.
+
+    my $nav_error;
+    &scantron_get_maxbubble(\$nav_error); # parse needs the bubble_lines.. array.
+    if ($nav_error) {
+        $r->print(&navmap_errormsg());
+        return(1,$currentphase);
+    }
 
     my %found=('ids'=>{},'usernames'=>{});
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
@@ -6649,19 +7038,28 @@ sub scantron_get_correction {
 #the previous one or the current one
 
     if ( $$scan_record{'scantron.PaperID'} =~ /\S/) {
-	$r->print("<p>".&mt("<b>An error was detected ($error)</b>".
-			    " for PaperID <tt>[_1]</tt>",
-			    $$scan_record{'scantron.PaperID'})."</p> \n");
+        $r->print(
+            '<p class="LC_warning">'
+           .&mt('An error was detected ([_1]) for PaperID [_2]',
+                "<b>$error</b>",
+                '<tt>'.$$scan_record{'scantron.PaperID'}.'</tt>')
+           ."</p> \n");
     } else {
-	$r->print("<p>".&mt("<b>An error was detected ($error)</b>".
-			    " in scanline [_1] <pre>[_2]</pre>",
-			    $i,$line)."</p> \n");
-    }
-    my $message="<p>".&mt("The ID on the form is  <tt>[_1]</tt><br />".
-			  "The name on the paper is [_2],[_3]",
-			  $$scan_record{'scantron.ID'},
-			  $$scan_record{'scantron.LastName'},
-			  $$scan_record{'scantron.FirstName'})."</p>";
+        $r->print(
+            '<p class="LC_warning">'
+           .&mt('An error was detected ([_1]) in scanline [_2] [_3]',
+                "<b>$error</b>", $i, "<pre>$line</pre>")
+           ."</p> \n");
+    }
+    my $message =
+        '<p>'
+       .&mt('The ID on the form is [_1]',
+            "<tt>$$scan_record{'scantron.ID'}</tt>")
+       .'<br />'
+       .&mt('The name on the paper is [_1], [_2]',
+            $$scan_record{'scantron.LastName'},
+            $$scan_record{'scantron.FirstName'})
+       .'</p>';
 
     $r->print('<input type="hidden" name="scantron_corrections" value="'.$error.'" />'."\n");
     $r->print('<input type="hidden" name="scantron_line" value="'.$i.'" />'."\n");
@@ -6671,10 +7069,10 @@ sub scantron_get_correction {
 
     if ($error =~ /ID$/) {
 	if ($error eq 'incorrectID') {
-	    $r->print("<p>".&mt("The encoded ID is not in the classlist").
+	    $r->print('<p class="LC_warning">'.&mt("The encoded ID is not in the classlist").
 		      "</p>\n");
 	} elsif ($error eq 'duplicateID') {
-	    $r->print("<p>".&mt("The encoded ID has also been used by a previous paper [_1]",$arg)."</p>\n");
+	    $r->print('<p class="LC_warning">'.&mt("The encoded ID has also been used by a previous paper [_1]",$arg)."</p>\n");
 	}
 	$r->print($message);
 	$r->print("<p>".&mt("How should I handle this?")." <br /> \n");
@@ -6690,14 +7088,15 @@ sub scantron_get_correction {
 	$r->print('</li>');
     } elsif ($error =~ /CODE$/) {
 	if ($error eq 'incorrectCODE') {
-	    $r->print("<p>".&mt("The encoded CODE is not in the list of possible CODEs.")."</p>\n");
+	    $r->print('<p class="LC_warning">'.&mt("The encoded CODE is not in the list of possible CODEs.")."</p>\n");
 	} elsif ($error eq 'duplicateCODE') {
-	    $r->print("<p>".&mt("The encoded CODE has also been used by a previous paper [_1], and CODEs are supposed to be unique.",join(', ',@{$arg}))."</p>\n");
+	    $r->print('<p class="LC_warning">'.&mt("The encoded CODE has also been used by a previous paper [_1], and CODEs are supposed to be unique.",join(', ',@{$arg}))."</p>\n");
 	}
-	$r->print("<p>".&mt("The CODE on the form is  <tt>'[_1]'</tt>",
-			    $$scan_record{'scantron.CODE'})."<br />\n");
+        $r->print("<p>".&mt('The CODE on the form is [_1]',
+                            "<tt>'$$scan_record{'scantron.CODE'}'</tt>")
+                 ."</p>\n");
 	$r->print($message);
-	$r->print("<p>".&mt("How should I handle this?")." <br /> \n");
+	$r->print("<p>".&mt("How should I handle this?")."</p>\n");
 	$r->print("\n<br /> ");
 	my $i=0;
 	if ($error eq 'incorrectCODE' 
@@ -6706,10 +7105,10 @@ sub scantron_get_correction {
 	    if ($closest > 0) {
 		foreach my $testcode (@{$closest}) {
 		    my $checked='';
-		    if (!$i) { $checked=' checked="checked" '; }
+		    if (!$i) { $checked=' checked="checked"'; }
 		    $r->print("
    <label>
-       <input type='radio' name='scantron_CODE_resolution' value='use_closest_$i' $checked />
+       <input type='radio' name='scantron_CODE_resolution' value='use_closest_$i'$checked />
        ".&mt("Use the similar CODE [_1] instead.",
 	    "<b><tt>".$testcode."</tt></b>")."
     </label>
@@ -6720,11 +7119,11 @@ sub scantron_get_correction {
 	    }
 	}
 	if ($$scan_record{'scantron.CODE'}=~/\S/ ) {
-	    my $checked; if (!$i) { $checked=' checked="checked" '; }
+	    my $checked; if (!$i) { $checked=' checked="checked"'; }
 	    $r->print("
     <label>
-        <input type='radio' name='scantron_CODE_resolution' value='use_unfound' $checked />
-       ".&mt("Use the CODE [_1] that is was on the paper, ignoring the error.",
+        <input type='radio' name='scantron_CODE_resolution' value='use_unfound'$checked />
+       ".&mt("Use the CODE [_1] that was on the paper, ignoring the error.",
 	     "<b><tt>".$$scan_record{'scantron.CODE'}."</tt></b>")."
     </label>");
 	    $r->print("\n<br />");
@@ -6754,7 +7153,7 @@ ENDSCRIPT
        ".&mt("[_1]Select[_2] a CODE from the list of all CODEs and use it.",
 	     "<a target='_blank' href='$href'>","</a>")."
     </label> 
-    ".&mt("Selected CODE is [_1]","<input readonly='true' type='text' size='8' name='scantron_CODE_selectedvalue' onfocus=\"javascript:change_radio('use_found')\" onchange=\"javascript:change_radio('use_found')\" />"));
+    ".&mt("Selected CODE is [_1]",'<input readonly="readonly" type="text" size="8" name="scantron_CODE_selectedvalue" onfocus="javascript:change_radio(\'use_found\')" onchange="javascript:change_radio(\'use_found\')" />'));
 	    $r->print("\n<br />");
 	}
 	$r->print("
@@ -6764,7 +7163,7 @@ ENDSCRIPT
 	     "</label><input type='text' size='8' name='scantron_CODE_newvalue' onfocus=\"javascript:change_radio('use_typed')\" onkeypress=\"javascript:change_radio('use_typed')\" />"));
 	$r->print("\n<br /><br />");
     } elsif ($error eq 'doublebubble') {
-	$r->print("<p>".&mt("There have been multiple bubbles scanned for some question(s)")."</p>\n");
+	$r->print('<p class="LC_warning">'.&mt("There have been multiple bubbles scanned for some question(s)")."</p>\n");
 
 	# The form field scantron_questions is acutally a list of line numbers.
 	# represented by this form so:
@@ -6782,7 +7181,7 @@ ENDSCRIPT
 	}
         $r->print(&verify_bubbles_checked(@lines_to_correct));
     } elsif ($error eq 'missingbubble') {
-	$r->print("<p>".&mt("There have been <b>no</b> bubbles scanned for some question(s)")."</p>\n");
+	$r->print('<p class="LC_warning">'.&mt("There have been [_1]no[_2] bubbles scanned for some question(s)",'<b>','</b>')."</p>\n");
 	$r->print($message);
 	$r->print("<p>".&mt("Please indicate which bubble should be used for grading.")."</p>");
 	$r->print(&mt("Some questions have no scanned bubbles.")."\n");
@@ -6947,7 +7346,7 @@ sub prompt_for_corrections {
             ($responsetype_per_response{$question-1} eq 'imageresponse') ||
             ($responsetype_per_response{$question-1} eq 'reactionresponse') ||
             ($responsetype_per_response{$question-1} eq 'organicresponse')) {
-            $r->print(&mt("Although this particular question type requires handgrading, the instructions for this question in the exam directed students to leave [quant,_1,line] blank on their scantron sheets.",$lines).'<br /><br />'.&mt('A non-zero score can be assigned to the student during scantron grading by selecting a bubble in at least one line.').'<br />'.&mt('The score for this question will be a sum of the numeric values for the selected bubbles from each line, where A=1 point, B=2 points etc.').'<br />'.&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.").'<br /><br />');
+            $r->print(&mt("Although this particular question type requires handgrading, the instructions for this question in the exam directed students to leave [quant,_1,line] blank on their bubblesheets.",$lines).'<br /><br />'.&mt('A non-zero score can be assigned to the student during bubblesheet grading by selecting a bubble in at least one line.').'<br />'.&mt('The score for this question will be a sum of the numeric values for the selected bubbles from each line, where A=1 point, B=2 points etc.').'<br />'.&mt("To assign a score of zero for this question, mark all lines as 'No bubble'.").'<br /><br />');
         } else {
             $r->print(&mt("Select at most one bubble in a single line and select 'No Bubble' in all the other lines. ")."<br />");
         }
@@ -7141,7 +7540,12 @@ sub scantron_validate_CODE {
 
     my %allcodes=&get_codes();
 
-    &scantron_get_maxbubble();	# parse needs the lines per response array.
+    my $nav_error;
+    &scantron_get_maxbubble(\$nav_error); # parse needs the lines per response array.
+    if ($nav_error) {
+        $r->print(&navmap_errormsg());
+        return(1,$currentphase);
+    }
 
     my ($scanlines,$scan_data)=&scantron_getfile();
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
@@ -7195,7 +7599,12 @@ sub scantron_validate_doublebubble {
     #get scantron line setup
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();
-    &scantron_get_maxbubble();	# parse needs the bubble line array.
+    my $nav_error;
+    &scantron_get_maxbubble(\$nav_error); # parse needs the bubble line array.
+    if ($nav_error) {
+        $r->print(&navmap_errormsg());
+        return(1,$currentphase);
+    }
 
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
 	my $line=&scantron_get_line($scanlines,$scan_data,$i);
@@ -7213,6 +7622,7 @@ sub scantron_validate_doublebubble {
 
 
 sub scantron_get_maxbubble {
+    my ($nav_error) = @_;
     if (defined($env{'form.scantron_maxbubble'}) &&
 	$env{'form.scantron_maxbubble'}) {
 	&restore_bubble_lines();
@@ -7223,129 +7633,85 @@ sub scantron_get_maxbubble {
 	&Apache::lonnet::decode_symb($env{'form.selectpage'});
 
     my $navmap=Apache::lonnavmaps::navmap->new();
+    unless (ref($navmap)) {
+        if (ref($nav_error)) {
+            $$nav_error = 1;
+        }
+        return;
+    }
     my $map=$navmap->getResourceByUrl($sequence);
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
 
     &Apache::lonxml::clear_problem_counter();
 
-    my $uname       = $env{'form.student'};
-    my $udom        = $env{'form.userdom'};
+    my $uname       = $env{'user.name'};
+    my $udom        = $env{'user.domain'};
     my $cid         = $env{'request.course.id'};
     my $total_lines = 0;
     %bubble_lines_per_response = ();
     %first_bubble_line         = ();
     %subdivided_bubble_lines   = ();
     %responsetype_per_response = ();
-  
+
     my $response_number = 0;
     my $bubble_line     = 0;
     foreach my $resource (@resources) {
-        my $symb = $resource->symb();
-
-        my (@parts,@allparts,@possible_parts);
-
-        # Need to retrieve part IDs and response IDs because essayresponse,
-        # reactionresponse and organicresponse items are not included in 
-        # $analysis{'parts'} from lonnet::ssi.  
-        if (ref($resource->parts()) eq 'ARRAY') {
-            foreach my $part (@{$resource->parts()}) {
-                if (!&Apache::loncommon::check_if_partid_hidden($part,$symb,$udom,$uname)) {
-                    my @resp_ids = $resource->responseIds($part);
-                    foreach my $id (@resp_ids) {
-                        my $part_id = $part.'.'.$id;
-                        push(@possible_parts,$part_id);
+        my ($analysis,$parts) = &scantron_partids_tograde($resource,$cid,$uname,$udom);
+        if ((ref($analysis) eq 'HASH') && (ref($parts) eq 'ARRAY')) {
+	    foreach my $part_id (@{$parts}) {
+                my $lines;
+
+	        # TODO - make this a persistent hash not an array.
+
+                # optionresponse, matchresponse and rankresponse type items 
+                # render as separate sub-questions in exam mode.
+                if (($analysis->{$part_id.'.type'} eq 'optionresponse') ||
+                    ($analysis->{$part_id.'.type'} eq 'matchresponse') ||
+                    ($analysis->{$part_id.'.type'} eq 'rankresponse')) {
+                    my ($numbub,$numshown);
+                    if ($analysis->{$part_id.'.type'} eq 'optionresponse') {
+                        if (ref($analysis->{$part_id.'.options'}) eq 'ARRAY') {
+                            $numbub = scalar(@{$analysis->{$part_id.'.options'}});
+                        }
+                    } elsif ($analysis->{$part_id.'.type'} eq 'matchresponse') {
+                        if (ref($analysis->{$part_id.'.items'}) eq 'ARRAY') {
+                            $numbub = scalar(@{$analysis->{$part_id.'.items'}});
+                        }
+                    } elsif ($analysis->{$part_id.'.type'} eq 'rankresponse') {
+                        if (ref($analysis->{$part_id.'.foils'}) eq 'ARRAY') {
+                            $numbub = scalar(@{$analysis->{$part_id.'.foils'}});
+                        }
                     }
-                }
-            }
-        }
-
-        my $result=&ssi_with_retries($resource->src(), $ssi_retries,
-                                        ('symb' => $symb,
-                                         'grade_target' => 'analyze',
-                                         'grade_courseid' => $cid,
-                                         'grade_domain' => $udom,
-                                         'grade_username' => $uname));
-        my (undef, $an) =
-            split(/_HASH_REF__/,$result, 2);
-
-	my %analysis = &Apache::lonnet::str2hash($an);
-
-        if (ref($analysis{'parts'}) eq 'ARRAY') {
-            foreach my $part (@{$analysis{'parts'}}) {
-                my ($id,$respid) = split(/\./,$part);
-                if (!&Apache::loncommon::check_if_partid_hidden($id,$symb,$udom,$uname)) {
-                    push(@parts,$part);
-                }
-            }
-        }
-        # Add part_ids for any essayresponse, reactionresponse or 
-        # organicresponse items. 
-        foreach my $part_id (@possible_parts) {
-            if (grep(/^\Q$part_id\E$/,@parts)) {
-                push(@allparts,$part_id);
-            } else {
-                if (($analysis{$part_id.'.type'} eq 'essayresponse') ||
-                    ($analysis{$part_id.'.type'} eq 'reactionresponse') ||
-                    ($analysis{$part_id.'.type'} eq 'organicresponse')) {
-                    push(@allparts,$part_id);
-                }
-            }
-        }
-
-	foreach my $part_id (@allparts) {
-            my $lines;
-
-	    # TODO - make this a persistent hash not an array.
-
-            # optionresponse, matchresponse and rankresponse type items 
-            # render as separate sub-questions in exam mode.
-            if (($analysis{$part_id.'.type'} eq 'optionresponse') ||
-                ($analysis{$part_id.'.type'} eq 'matchresponse') ||
-                ($analysis{$part_id.'.type'} eq 'rankresponse')) {
-                my ($numbub,$numshown);
-                if ($analysis{$part_id.'.type'} eq 'optionresponse') {
-                    if (ref($analysis{$part_id.'.options'}) eq 'ARRAY') {
-                        $numbub = scalar(@{$analysis{$part_id.'.options'}});
+                    if (ref($analysis->{$part_id.'.shown'}) eq 'ARRAY') {
+                        $numshown = scalar(@{$analysis->{$part_id.'.shown'}});
                     }
-                } elsif ($analysis{$part_id.'.type'} eq 'matchresponse') {
-                    if (ref($analysis{$part_id.'.items'}) eq 'ARRAY') {
-                        $numbub = scalar(@{$analysis{$part_id.'.items'}});
+                    my $bubbles_per_line = 10;
+                    my $inner_bubble_lines = int($numbub/$bubbles_per_line);
+                    if (($numbub % $bubbles_per_line) != 0) {
+                        $inner_bubble_lines++;
                     }
-                } elsif ($analysis{$part_id.'.type'} eq 'rankresponse') {
-                    if (ref($analysis{$part_id.'.foils'}) eq 'ARRAY') {
-                        $numbub = scalar(@{$analysis{$part_id.'.foils'}});
+                    for (my $i=0; $i<$numshown; $i++) {
+                        $subdivided_bubble_lines{$response_number} .= 
+                            $inner_bubble_lines.',';
                     }
-                }
-                if (ref($analysis{$part_id.'.shown'}) eq 'ARRAY') {
-                    $numshown = scalar(@{$analysis{$part_id.'.shown'}});
-                }
-                my $bubbles_per_line = 10;
-                my $inner_bubble_lines = int($numbub/$bubbles_per_line);
-                if (($numbub % $bubbles_per_line) != 0) {
-                    $inner_bubble_lines++;
-                }
-                for (my $i=0; $i<$numshown; $i++) {
-                    $subdivided_bubble_lines{$response_number} .= 
-                        $inner_bubble_lines.',';
-                }
-                $subdivided_bubble_lines{$response_number} =~ s/,$//;
-                $lines = $numshown * $inner_bubble_lines;
-            } else {
-                $lines = $analysis{"$part_id.bubble_lines"};
-            } 
-
-            $first_bubble_line{$response_number} = $bubble_line;
-	    $bubble_lines_per_response{$response_number} = $lines;
-            $responsetype_per_response{$response_number} = 
-                $analysis{$part_id.'.type'};
-	    $response_number++;
+                    $subdivided_bubble_lines{$response_number} =~ s/,$//;
+                    $lines = $numshown * $inner_bubble_lines;
+                } else {
+                    $lines = $analysis->{"$part_id.bubble_lines"};
+                } 
 
-	    $bubble_line +=  $lines;
-	    $total_lines +=  $lines;
-	}
+                $first_bubble_line{$response_number} = $bubble_line;
+	        $bubble_lines_per_response{$response_number} = $lines;
+                $responsetype_per_response{$response_number} = 
+                    $analysis->{$part_id.'.type'};
+	        $response_number++;
 
+	        $bubble_line +=  $lines;
+	        $total_lines +=  $lines;
+	    }
+        }
     }
-    &Apache::lonnet::delenv('scantron\.');
+    &Apache::lonnet::delenv('scantron.');
 
     &save_bubble_lines();
     $env{'form.scantron_maxbubble'} =
@@ -7353,7 +7719,6 @@ sub scantron_get_maxbubble {
     return $env{'form.scantron_maxbubble'};
 }
 
-
 sub scantron_validate_missingbubbles {
     my ($r,$currentphase) = @_;
     #get student info
@@ -7363,7 +7728,11 @@ sub scantron_validate_missingbubbles {
     #get scantron line setup
     my %scantron_config=&get_scantron_config($env{'form.scantron_format'});
     my ($scanlines,$scan_data)=&scantron_getfile();
-    my $max_bubble=&scantron_get_maxbubble();
+    my $nav_error;
+    my $max_bubble=&scantron_get_maxbubble(\$nav_error);
+    if ($nav_error) {
+        return(1,$currentphase);
+    }
     if (!$max_bubble) { $max_bubble=2**31; }
     for (my $i=0;$i<=$scanlines->{'count'};$i++) {
 	my $line=&scantron_get_line($scanlines,$scan_data,$i);
@@ -7423,9 +7792,41 @@ sub scantron_process_students {
     my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&username_to_idmap($classlist);
     my $navmap=Apache::lonnavmaps::navmap->new();
+    unless (ref($navmap)) {
+        $r->print(&navmap_errormsg());
+        return '';
+    }  
     my $map=$navmap->getResourceByUrl($sequence);
     my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
-#    $r->print("geto ".scalar(@resources)."<br />");
+    my (%grader_partids_by_symb,%grader_randomlists_by_symb);
+    &graders_resources_pass(\@resources,\%grader_partids_by_symb,
+                            \%grader_randomlists_by_symb);
+    my $resource_error;
+    foreach my $resource (@resources) {
+        my $ressymb;
+        if (ref($resource)) {
+            $ressymb = $resource->symb();
+        } else {
+            $resource_error = 1;
+            last;
+        }
+        my ($analysis,$parts) =
+            &scantron_partids_tograde($resource,$env{'request.course.id'},
+                                      $env{'user.name'},$env{'user.domain'},1);
+        $grader_partids_by_symb{$ressymb} = $parts;
+        if (ref($analysis) eq 'HASH') {
+            if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {
+                $grader_randomlists_by_symb{$ressymb} = 
+                    $analysis->{'parts_withrandomlist'};
+            }
+        }
+    }
+    if ($resource_error) {
+        $r->print(&navmap_errormsg());
+        return '';
+    }
+
+    my ($uname,$udom);
     my $result= <<SCANTRONFORM;
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="scantronupload">
   <input type="hidden" name="command" value="scantron_configphase" />
@@ -7434,21 +7835,26 @@ SCANTRONFORM
     $r->print($result);
 
     my @delayqueue;
-    my %completedstudents;
+    my (%completedstudents,%scandata);
     
     my $lock=&Apache::lonnet::set_lock(&mt('Grading bubblesheet exam'));
     my $count=&get_todo_count($scanlines,$scan_data);
-    my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron Status',
- 				    'Scantron Progress',$count,
+    my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Bubblesheet Status',
+ 				    'Bubblesheet Progress',$count,
 				    'inline',undef,'scantronupload');
     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
 					  'Processing first student');
+    $r->print('<br />');
     my $start=&Time::HiRes::time();
     my $i=-1;
-    my ($uname,$udom,$started);
+    my $started;
 
-    &scantron_get_maxbubble();	# Need the bubble lines array to parse.
-    
+    my $nav_error;
+    &scantron_get_maxbubble(\$nav_error); # Need the bubble lines array to parse.
+    if ($nav_error) {
+        $r->print(&navmap_errormsg());
+        return '';
+    }
 
     # If an ssi failed in scantron_get_maxbubble, put an error message out to
     # the user and return.
@@ -7461,6 +7867,9 @@ SCANTRONFORM
 	return '';		# Dunno why the other returns return '' rather than just returning.
     }
 
+    my %lettdig = &letter_to_digits();
+    my $numletts = scalar(keys(%lettdig));
+
     while ($i<$scanlines->{'count'}) {
  	($uname,$udom)=('','');
  	$i++;
@@ -7486,6 +7895,31 @@ SCANTRONFORM
  	}
   	($uname,$udom)=split(/:/,$uname);
 
+        my (%partids_by_symb,$res_error);
+        foreach my $resource (@resources) {
+            my $ressymb;
+            if (ref($resource)) {
+                $ressymb = $resource->symb();
+            } else {
+                $res_error = 1;
+                last;
+            }
+            if ((exists($grader_randomlists_by_symb{$ressymb})) ||
+                (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
+                my ($analysis,$parts) =
+                    &scantron_partids_tograde($resource,$env{'request.course.id'},$uname,$udom);
+                $partids_by_symb{$ressymb} = $parts;
+            } else {
+                $partids_by_symb{$ressymb} = $grader_partids_by_symb{$ressymb};
+            }
+        }
+
+        if ($res_error) {
+            &scantron_add_delay(\@delayqueue,$line,
+                                'An error occurred while grading student '.$uname,2);
+            next;
+        }
+
 	&Apache::lonxml::clear_problem_counter();
   	&Apache::lonnet::appenv($scan_record);
 
@@ -7493,39 +7927,96 @@ SCANTRONFORM
 	    &scantron_putfile($scanlines,$scan_data);
 	}
 	
-	my $i=0;
-	foreach my $resource (@resources) {
-	    $i++;
-	    my %form=('submitted'     =>'scantron',
-		      'grade_target'  =>'grade',
-		      'grade_username'=>$uname,
-		      'grade_domain'  =>$udom,
-		      'grade_courseid'=>$env{'request.course.id'},
-		      'grade_symb'    =>$resource->symb());
-	    if (exists($scan_record->{'scantron.CODE'})
-		&& 
-		&Apache::lonnet::validCODE($scan_record->{'scantron.CODE'})) {
-		$form{'CODE'}=$scan_record->{'scantron.CODE'};
-	    } else {
-		$form{'CODE'}='';
-	    } 
-	    my $result=&ssi_with_retries($resource->src(), $ssi_retries, %form);
-	    if ($ssi_error) {
-		$ssi_error = 0;	# So end of handler error message does not trigger.
-		$r->print("</form>");
-		&ssi_print_error($r);
-		$r->print(&show_grading_menu_form($symb));
-                &Apache::lonnet::remove_lock($lock);
-		return '';	# Why return ''?  Beats me.
-	    }
+        my $scancode;
+        if ((exists($scan_record->{'scantron.CODE'})) &&
+            (&Apache::lonnet::validCODE($scan_record->{'scantron.CODE'}))) {
+            $scancode = $scan_record->{'scantron.CODE'};
+        } else {
+            $scancode = '';
+        }
+
+        if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
+                                   \@resources,\%partids_by_symb) eq 'ssi_error') {
+            $ssi_error = 0; # So end of handler error message does not trigger.
+            $r->print("</form>");
+            &ssi_print_error($r);
+            $r->print(&show_grading_menu_form($symb));
+            &Apache::lonnet::remove_lock($lock);
+            return '';      # Why return ''?  Beats me.
+        }
 
-	    if (&Apache::loncommon::connection_aborted($r)) { last; }
-	}
 	$completedstudents{$uname}={'line'=>$line};
-	if (&Apache::loncommon::connection_aborted($r)) { last; }
+        if ($env{'form.verifyrecord'}) {
+            my $lastpos = $env{'form.scantron_maxbubble'}*$scantron_config{'Qlength'};
+            my $studentdata = substr($line,$scantron_config{'Qstart'}-1,$lastpos);
+            chomp($studentdata);
+            $studentdata =~ s/\r$//;
+            my $studentrecord = '';
+            my $counter = -1;
+            foreach my $resource (@resources) {
+                my $ressymb = $resource->symb();
+                ($counter,my $recording) =
+                    &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
+                                             $counter,$studentdata,$partids_by_symb{$ressymb},
+                                             \%scantron_config,\%lettdig,$numletts);
+                $studentrecord .= $recording;
+            }
+            if ($studentrecord ne $studentdata) {
+                &Apache::lonxml::clear_problem_counter();
+                if (&grade_student_bubbles($r,$uname,$udom,$scan_record,$scancode,
+                                           \@resources,\%partids_by_symb) eq 'ssi_error') {
+                    $ssi_error = 0; # So end of handler error message does not trigger.
+                    $r->print("</form>");
+                    &ssi_print_error($r);
+                    $r->print(&show_grading_menu_form($symb));
+                    &Apache::lonnet::remove_lock($lock);
+                    delete($completedstudents{$uname});
+                    return '';
+                }
+                $counter = -1;
+                $studentrecord = '';
+                foreach my $resource (@resources) {
+                    my $ressymb = $resource->symb();
+                    ($counter,my $recording) =
+                        &verify_scantron_grading($resource,$udom,$uname,$env{'request.course.id'},
+                                                 $counter,$studentdata,$partids_by_symb{$ressymb},
+                                                 \%scantron_config,\%lettdig,$numletts);
+                    $studentrecord .= $recording;
+                }
+                if ($studentrecord ne $studentdata) {
+                    $r->print('<p><span class="LC_warning">');
+                    if ($scancode eq '') {
+                        $r->print(&mt('Mismatch grading bubblesheet for user: [_1] with ID: [_2].',
+                                  $uname.':'.$udom,$scan_record->{'scantron.ID'}));
+                    } else {
+                        $r->print(&mt('Mismatch grading bubblesheet for user: [_1] with ID: [_2] and CODE: [_3].',
+                                  $uname.':'.$udom,$scan_record->{'scantron.ID'},$scancode));
+                    }
+                    $r->print('</span><br />'.&Apache::loncommon::start_data_table()."\n".
+                              &Apache::loncommon::start_data_table_header_row()."\n".
+                              '<th>'.&mt('Source').'</th><th>'.&mt('Bubbled responses').'</th>'.
+                              &Apache::loncommon::end_data_table_header_row()."\n".
+                              &Apache::loncommon::start_data_table_row().
+                              '<td>'.&mt('Bubblesheet').'</td>'.
+                              '<td><span class="LC_nobreak">'.$studentdata.'</span></td>'.
+                              &Apache::loncommon::end_data_table_row().
+                              &Apache::loncommon::start_data_table_row().
+                              '<td>'.&mt('Stored submissions').'</td>'.
+                              '<td><span class="LC_nobreak">'.$studentrecord.'</span></td>'."\n".
+                              &Apache::loncommon::end_data_table_row().
+                              &Apache::loncommon::end_data_table().'</p>');
+                } else {
+                    $r->print('<br /><span class="LC_warning">'.
+                             &mt('A second grading pass was needed for user: [_1] with ID: [_2], because a mismatch was seen on the first pass.',$uname.':'.$udom,$scan_record->{'scantron.ID'}).'<br />'.
+                             &mt("As a consequence, this user's submission history records two tries.").
+                                 '</span><br />');
+                }
+            }
+        }
+        if (&Apache::loncommon::connection_aborted($r)) { last; }
     } continue {
 	&Apache::lonxml::clear_problem_counter();
-	&Apache::lonnet::delenv('scantron\.');
+	&Apache::lonnet::delenv('scantron.');
     }
     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
     &Apache::lonnet::remove_lock($lock);
@@ -7537,41 +8028,122 @@ SCANTRONFORM
     return '';
 }
 
+sub graders_resources_pass {
+    my ($resources,$grader_partids_by_symb,$grader_randomlists_by_symb) = @_;
+    if ((ref($resources) eq 'ARRAY') && (ref($grader_partids_by_symb)) && 
+        (ref($grader_randomlists_by_symb) eq 'HASH')) {
+        foreach my $resource (@{$resources}) {
+            my $ressymb = $resource->symb();
+            my ($analysis,$parts) =
+                &scantron_partids_tograde($resource,$env{'request.course.id'},
+                                          $env{'user.name'},$env{'user.domain'},1);
+            $grader_partids_by_symb->{$ressymb} = $parts;
+            if (ref($analysis) eq 'HASH') {
+                if (ref($analysis->{'parts_withrandomlist'}) eq 'ARRAY') {
+                    $grader_randomlists_by_symb->{$ressymb} =
+                        $analysis->{'parts_withrandomlist'};
+                }
+            }
+        }
+    }
+    return;
+}
+
+sub grade_student_bubbles {
+    my ($r,$uname,$udom,$scan_record,$scancode,$resources,$parts) = @_;
+    if (ref($resources) eq 'ARRAY') {
+        my $count = 0;
+        foreach my $resource (@{$resources}) {
+            my $ressymb = $resource->symb();
+            my %form = ('submitted'      => 'scantron',
+                        'grade_target'   => 'grade',
+                        'grade_username' => $uname,
+                        'grade_domain'   => $udom,
+                        'grade_courseid' => $env{'request.course.id'},
+                        'grade_symb'     => $ressymb,
+                        'CODE'           => $scancode
+                       );
+            if (ref($parts) eq 'HASH') {
+                if (ref($parts->{$ressymb}) eq 'ARRAY') {
+                    foreach my $part (@{$parts->{$ressymb}}) {
+                        $form{'scantron_questnum_start.'.$part} =
+                            1+$env{'form.scantron.first_bubble_line.'.$count};
+                        $count++;
+                    }
+                }
+            }
+            my $result=&ssi_with_retries($resource->src(),$ssi_retries,%form);
+            return 'ssi_error' if ($ssi_error);
+            last if (&Apache::loncommon::connection_aborted($r));
+        }
+    }
+    return;
+}
+
 sub scantron_upload_scantron_data {
     my ($r)=@_;
-    $r->print(&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'}));
+    my $dom = $env{'request.role.domain'};
+    my $domdesc = &Apache::lonnet::domain($dom,'description');
+    $r->print(&Apache::loncommon::coursebrowser_javascript($dom));
     my $select_link=&Apache::loncommon::selectcourse_link('rules','courseid',
 							  'domainid',
-							  'coursename');
-    my $domsel=&Apache::loncommon::select_dom_form($env{'request.role.domain'},
-						   'domainid');
+							  'coursename',$dom);
+    my $syllabuslink = '<a href="javascript:ToSyllabus();">'.&mt('Syllabus').'</a>'.
+                       ('&nbsp'x2).&mt('(shows course personnel)'); 
     my $default_form_data=&defaultFormData(&get_symb($r,1));
+    my $nofile_alert = &mt('Please use the browse button to select a file from your local directory.');
+    my $nocourseid_alert = &mt("Please use the 'Select Course' link to open a separate window where you can search for a course to which a file can be uploaded.");
     $r->print('
 <script type="text/javascript" language="javascript">
     function checkUpload(formname) {
 	if (formname.upfile.value == "") {
-	    alert("Please use the browse button to select a file from your local directory.");
+	    alert("'.$nofile_alert.'");
 	    return false;
 	}
+        if (formname.courseid.value == "") {
+            alert("'.$nocourseid_alert.'");
+            return false;
+        }
 	formname.submit();
     }
+
+    function ToSyllabus() {
+        var cdom = '."'$dom'".';
+        var cnum = document.rules.courseid.value;
+        if (cdom == "" || cdom == null) {
+            return;
+        }
+        if (cnum == "" || cnum == null) {
+           return;
+        }
+        syllwin=window.open("/public/"+cdom+"/"+cnum+"/syllabus","LONCAPASyllabus",
+                            "height=350,width=350,scrollbars=yes,menubar=no");
+        return;
+    }
+
 </script>
 
+<h3>'.&mt('Send bubblesheet data to a course').'</h3>
+
 <form enctype="multipart/form-data" action="/adm/grades" name="rules" method="post">
-'.$default_form_data.'
-<table>
-<tr><td>'.$select_link.'                             </td></tr>
-<tr><td>'.&mt('Course ID:').'     </td>
-    <td><input name="courseid"   type="text" />      </td></tr>
-<tr><td>'.&mt('Course Name:').'   </td>
-    <td><input name="coursename" type="text" />      </td></tr>
-<tr><td>'.&mt('Domain:').'        </td>
-    <td>'.$domsel.'                                  </td></tr>
-<tr><td>'.&mt('File to upload:').'</td>
-    <td><input type="file" name="upfile" size="50" /></td></tr>
-</table>
+'.$default_form_data.
+  &Apache::lonhtmlcommon::start_pick_box().
+  &Apache::lonhtmlcommon::row_title(&mt('Course ID')).
+  '<input name="courseid" type="text" size="30" />'.$select_link.
+  &Apache::lonhtmlcommon::row_closure().
+  &Apache::lonhtmlcommon::row_title(&mt('Course Name')).
+  '<input name="coursename" type="text" size="30" />'.$syllabuslink.
+  &Apache::lonhtmlcommon::row_closure().
+  &Apache::lonhtmlcommon::row_title(&mt('Domain')).
+  '<input name="domainid" type="hidden" />'.$domdesc.
+  &Apache::lonhtmlcommon::row_closure().
+  &Apache::lonhtmlcommon::row_title(&mt('File to upload')).
+  '<input type="file" name="upfile" size="50" />'.
+  &Apache::lonhtmlcommon::row_closure(1).
+  &Apache::lonhtmlcommon::end_pick_box().'<br />
+
 <input name="command" value="scantronupload_save" type="hidden" />
-<input type="button" onClick="javascript:checkUpload(this.form);" value="'.&mt('Upload Scantron Data').'" />
+<input type="button" onclick="javascript:checkUpload(this.form);" value="'.&mt('Upload Bubblesheet Data').'" />
 </form>
 ');
     return '';
@@ -7589,7 +8161,7 @@ sub scantron_upload_scantron_data_save {
     if (!&Apache::lonnet::allowed('usc',$env{'form.domainid'}) &&
 	!&Apache::lonnet::allowed('usc',
 			    $env{'form.domainid'}.'_'.$env{'form.courseid'})) {
-	$r->print(&mt("You are not allowed to upload Scantron data to the requested course.")."<br />");
+	$r->print(&mt("You are not allowed to upload bubblesheet data to the requested course.")."<br />");
 	if ($symb) {
 	    $r->print(&show_grading_menu_form($symb));
 	} else {
@@ -7598,36 +8170,25 @@ sub scantron_upload_scantron_data_save {
 	return '';
     }
     my %coursedata=&Apache::lonnet::coursedescription($env{'form.domainid'}.'_'.$env{'form.courseid'});
-    $r->print(&mt("Doing upload to [_1]",$coursedata{'description'})." <br />");
-    my $fname=$env{'form.upfile.filename'};
-    #FIXME
-    #copied from lonnet::userfileupload()
-    #make that function able to target a specified course
-    # Replace Windows backslashes by forward slashes
-    $fname=~s/\\/\//g;
-    # Get rid of everything but the actual filename
-    $fname=~s/^.*\/([^\/]+)$/$1/;
-    # Replace spaces by underscores
-    $fname=~s/\s+/\_/g;
-    # Replace all other weird characters by nothing
-    $fname=~s/[^\w\.\-]//g;
-    # See if there is anything left
-    unless ($fname) { return 'error: no uploaded file'; }
-    my $uploadedfile=$fname;
-    $fname='scantron_orig_'.$fname;
+    my $uploadedfile;
+    $r->print('<h3>'.&mt("Uploading file to [_1]",$coursedata{'description'}).'</h3>');
     if (length($env{'form.upfile'}) < 2) {
-	$r->print(&mt("<span class=\"LC_error\">Error:</span> The file you attempted to upload, [_1]  contained no information. Please check that you entered the correct filename.",'<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</span>"));
+        $r->print(&mt('[_1]Error:[_2] The file you attempted to upload, [_3] contained no information. Please check that you entered the correct filename.','<span class="LC_error">','</span>','<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'));
     } else {
-	my $result=&Apache::lonnet::finishuserfileupload($env{'form.courseid'},$env{'form.domainid'},'upfile',$fname);
-	if ($result =~ m|^/uploaded/|) {
-	    $r->print(&mt("<span class=\"LC_success\">Success:</span> Successfully uploaded [_1] bytes of data into location [_2]",
-			  (length($env{'form.upfile'})-1),
-			  '<span class="LC_filename">'.$result."</span>"));
+        my $result = 
+            &Apache::lonnet::userfileupload('upfile','','scantron','','','',
+                                            $env{'form.courseid'},$env{'form.domainid'});
+	if ($result =~ m{^/uploaded/}) {
+	    $r->print(&mt('[_1]Success:[_2] Successfully uploaded [_3] bytes of data into location: [_4]',
+                          '<span class="LC_success">','</span>',(length($env{'form.upfile'})-1),
+			  '<span class="LC_filename">'.$result.'</span>'));
+            ($uploadedfile) = ($result =~ m{/([^/]+)$});
+            $r->print(&validate_uploaded_scantron_file($env{'form.domainid'},
+                                                       $env{'form.courseid'},$uploadedfile));
 	} else {
-	    $r->print(&mt("<span class=\"LC_error\">Error:</span> An error ([_1]) occurred when attempting to upload the file, [_2]",
-			  $result,
-			  '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"')."</span>"));
-
+	    $r->print(&mt('[_1]Error:[_2] An error ([_3]) occurred when attempting to upload the file, [_4]',
+                          '<span class="LC_error">','</span>',$result,
+			  '<span class="LC_filename">'.&HTML::Entities::encode($env{'form.upfile.filename'},'<>&"').'</span>'));
 	}
     }
     if ($symb) {
@@ -7638,6 +8199,92 @@ sub scantron_upload_scantron_data_save {
     return '';
 }
 
+sub validate_uploaded_scantron_file {
+    my ($cdom,$cname,$fname) = @_;
+    my $scanlines=&Apache::lonnet::getfile('/uploaded/'.$cdom.'/'.$cname.'/'.$fname);
+    my @lines;
+    if ($scanlines ne '-1') {
+        @lines=split("\n",$scanlines,-1);
+    }
+    my $output;
+    if (@lines) {
+        my (%counts,$max_match_format);
+        my ($max_match_count,$max_match_pct) = (0,0);
+        my $classlist = &Apache::loncoursedata::get_classlist($cdom,$cname);
+        my %idmap = &username_to_idmap($classlist);
+        foreach my $key (keys(%idmap)) {
+            my $lckey = lc($key);
+            $idmap{$lckey} = $idmap{$key};
+        }
+        my %unique_formats;
+        my @formatlines = &get_scantronformat_file();
+        foreach my $line (@formatlines) {
+            chomp($line);
+            my @config = split(/:/,$line);
+            my $idstart = $config[5];
+            my $idlength = $config[6];
+            if (($idstart ne '') && ($idlength > 0)) {
+                if (ref($unique_formats{$idstart.':'.$idlength}) eq 'ARRAY') {
+                    push(@{$unique_formats{$idstart.':'.$idlength}},$config[0].':'.$config[1]); 
+                } else {
+                    $unique_formats{$idstart.':'.$idlength} = [$config[0].':'.$config[1]];
+                }
+            }
+        }
+        foreach my $key (keys(%unique_formats)) {
+            my ($idstart,$idlength) = split(':',$key);
+            %{$counts{$key}} = (
+                               'found'   => 0,
+                               'total'   => 0,
+                              );
+            foreach my $line (@lines) {
+                next if ($line =~ /^#/);
+                next if ($line =~ /^[\s\cz]*$/);
+                my $id = substr($line,$idstart-1,$idlength);
+                $id = lc($id);
+                if (exists($idmap{$id})) {
+                    $counts{$key}{'found'} ++;
+                }
+                $counts{$key}{'total'} ++;
+            }
+            if ($counts{$key}{'total'}) {
+                my $percent_match = (100*$counts{$key}{'found'})/($counts{$key}{'total'});
+                if (($max_match_format eq '') || ($percent_match > $max_match_pct)) {
+                    $max_match_pct = $percent_match;
+                    $max_match_format = $key;
+                    $max_match_count = $counts{$key}{'total'};
+                }
+            }
+        }
+        if (ref($unique_formats{$max_match_format}) eq 'ARRAY') {
+            my $format_descs;
+            my $numwithformat = @{$unique_formats{$max_match_format}};
+            for (my $i=0; $i<$numwithformat; $i++) {
+                my ($name,$desc) = split(':',$unique_formats{$max_match_format}[$i]);
+                if ($i<$numwithformat-2) {
+                    $format_descs .= '"<i>'.$desc.'</i>", ';
+                } elsif ($i==$numwithformat-2) {
+                    $format_descs .= '"<i>'.$desc.'</i>" '.&mt('and').' ';
+                } elsif ($i==$numwithformat-1) {
+                    $format_descs .= '"<i>'.$desc.'</i>"';
+                }
+            }
+            my $showpct = sprintf("%.0f",$max_match_pct).'%';
+            $output .= '<br />'.&mt('Comparison of student IDs in the uploaded file with the course roster found matches for [_1] of the [_2] entries in the file (for the format defined for [_3]).','<b>'.$showpct.'</b>','<b>'.$max_match_count.'</b>',$format_descs).
+                       '<br />'.&mt('A low percentage of matches results from one of the following:').'<ul>'.
+                       '<li>'.&mt('The file was uploaded to the wrong course').'</li>'.
+                       '<li>'.&mt('The data are not in the format expected for the domain: [_1]',
+                                  '<i>'.$cdom.'</i>').'</li>'.
+                       '<li>'.&mt('Students did not bubble their IDs, or mis-bubbled them').'</li>'.
+                       '<li>'.&mt('The course roster is not up to date').'</li>'.
+                       '</ul>';
+        }
+    } else {
+        $output = '<span class="LC_warning">'.&mt('Uploaded file contained no data').'</span>';
+    }
+    return $output;
+}
+
 sub valid_file {
     my ($requested_file)=@_;
     foreach my $filename (sort(&scantron_filenames())) {
@@ -7691,18 +8338,7 @@ sub checkscantron_results {
     if (!$symb) {return '';}
     my $grading_menu_button=&show_grading_menu_form($symb);
     my $cid = $env{'request.course.id'};
-    my %lettdig = (
-                    A => 1,
-                    B => 2,
-                    C => 3,
-                    D => 4,
-                    E => 5,
-                    F => 6,
-                    G => 7,
-                    H => 8,
-                    I => 9,
-                    J => 0,
-                  );
+    my %lettdig = &letter_to_digits();
     my $numletts = scalar(keys(%lettdig));
     my $cnum = $env{'course.'.$cid.'.num'};
     my $cdom = $env{'course.'.$cid.'.domain'};
@@ -7714,8 +8350,16 @@ sub checkscantron_results {
     my $classlist=&Apache::loncoursedata::get_classlist();
     my %idmap=&Apache::grades::username_to_idmap($classlist);
     my $navmap=Apache::lonnavmaps::navmap->new();
+    unless (ref($navmap)) {
+        $r->print(&navmap_errormsg());
+        return '';
+    }
     my $map=$navmap->getResourceByUrl($sequence);
-    my @resources=$navmap->retrieveResources($map,undef,1,0);
+    my @resources=$navmap->retrieveResources($map,\&scantron_filter,1,0);
+    my (%grader_partids_by_symb,%grader_randomlists_by_symb);
+    &graders_resources_pass(\@resources,\%grader_partids_by_symb,                             \%grader_randomlists_by_symb);
+
+    my ($uname,$udom);
     my (%scandata,%lastname,%bylast);
     $r->print('
 <form method="post" enctype="multipart/form-data" action="/adm/grades" name="checkscantron">'."\n");
@@ -7724,12 +8368,16 @@ sub checkscantron_results {
     my %completedstudents;
 
     my $count=&Apache::grades::get_todo_count($scanlines,$scan_data);
-    my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Scantron/Submissions Comparison Status',
-                                    'Progress of Scantron Data/Submission Records Comparison',$count,
+    my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin($r,'Bubblesheet/Submissions Comparison Status',
+                                    'Progress of Bubblesheet Data/Submission Records Comparison',$count,
                                     'inline',undef,'checkscantron');
-    my ($username,$domain,$uname,$started);
-
-    &Apache::grades::scantron_get_maxbubble();  # Need the bubble lines array to parse.
+    my ($username,$domain,$started);
+    my $nav_error;
+    &scantron_get_maxbubble(\$nav_error); # Need the bubble lines array to parse.
+    if ($nav_error) {
+        $r->print(&navmap_errormsg());
+        return '';
+    }
 
     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
                                           'Processing first student');
@@ -7769,126 +8417,21 @@ sub checkscantron_results {
         $scandata{$pid} =~ s/\r$//;
         ($username,$domain)=split(/:/,$uname);
         my $counter = -1;
-        my (%expected,%startpos);
         foreach my $resource (@resources) {
-            next if (!$resource->is_problem());
-            my $symb = $resource->symb();
-            my $partsref = $resource->parts();
-            my @parts;
-            my @part_ids = ();
-            if (ref($partsref) eq 'ARRAY') {
-               @parts = @{$partsref};
-               foreach my $part (@parts) {
-                   my @resp_ids = $resource->responseIds($part);
-                   foreach my $resp (@resp_ids) {
-                       $counter ++;
-                       my $part_id = $part.'.'.$resp;
-                       $expected{$part_id} = 0;
-                       push(@part_ids,$part_id);
-                       if ($env{"form.scantron.sub_bubblelines.$counter"}) {
-                           my @sub_lines = split(/,/,$env{"form.scantron.sub_bubblelines.$counter"});
-                           foreach my $item (@sub_lines) {
-                               $expected{$part_id} += $item;
-                           }
-                       } else {
-                           $expected{$part_id} = $env{"form.scantron.bubblelines.$counter"};
-                       }
-                       $startpos{$part_id} = $env{"form.scantron.first_bubble_line.$counter"};
-                   }
-                }
-            }
-            if ($symb) {
-                my %recorded;
-                my (%returnhash) =
-                    &Apache::lonnet::restore($symb,$cid,$domain,$username);
-                if ($returnhash{'version'}) {
-                    my %lasthash=();
-                    my $version;
-                    for ($version=1;$version<=$returnhash{'version'};$version++) {
-                        foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) {
-                            $lasthash{$key}=$returnhash{$version.':'.$key};
-                        }
-                    }
-                    foreach my $key (keys(%lasthash)) {
-                        if ($key =~ /\.scantron$/) {
-                            my $value = &unescape($lasthash{$key});
-                            my ($part_id) = ($key =~ /^resource\.(.+)\.scantron$/);
-                            if ($value eq '') {
-                                for (my $i=0; $i<$expected{$part_id}; $i++) {
-                                    for (my $j=0; $j<$scantron_config{'length'}; $j++) {
-                                        $recorded{$part_id} .= $;
-                                    }
-                                }
-                            } else {
-                                my @tocheck;
-                                my @items = split(//,$value);
-                                if (($scantron_config{'Qon'} eq 'letter') ||
-                                    ($scantron_config{'Qon'} eq 'number')) {
-                                    if (@items < $expected{$part_id}) {
-                                        my $fragment = substr($scandata{$pid},$startpos{$part_id},$expected{$part_id});
-                                        my @singles = split(//,$fragment);
-                                        foreach my $pos (@singles) {
-                                            if ($pos eq ' ') {
-                                                push(@tocheck,$pos);
-                                            } else {
-                                                my $next = shift(@items);
-                                                push(@tocheck,$next);
-                                            }
-                                        }
-                                    } else {
-                                        @tocheck = @items;
-                                    }
-                                    foreach my $letter (@tocheck) {
-                                        if ($scantron_config{'Qon'} eq 'letter') {
-                                            if ($letter !~ /^[A-J]$/) {
-                                                $letter = $scantron_config{'Qoff'};
-                                            }
-                                            $recorded{$part_id} .= $letter;
-                                        } elsif ($scantron_config{'Qon'} eq 'number') {
-                                            my $digit;
-                                            if ($letter !~ /^[A-J]$/) {
-                                                $digit = $scantron_config{'Qoff'};
-                                            } else {
-                                                $digit = $lettdig{$letter};
-                                            }
-                                            $recorded{$part_id} .= $digit;
-                                        }
-                                    }
-                                } else {
-                                    @tocheck = @items;
-                                    for (my $i=0; $i<$expected{$part_id}; $i++) {
-                                        my $curr_sub = shift(@tocheck);
-                                        my $digit;
-                                        if ($curr_sub =~ /^[A-J]$/) {
-                                            $digit = $lettdig{$curr_sub}-1;
-                                        }
-                                        if ($curr_sub eq 'J') {
-                                            $digit += scalar($numletts);
-                                        }
-                                        for (my $j=0; $j<$scantron_config{'Qlength'}; $j++) {
-                                            if ($j == $digit) {
-                                                $recorded{$part_id} .= $scantron_config{'Qon'};
-                                            } else {
-                                                $recorded{$part_id} .= $scantron_config{'Qoff'};
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-                foreach my $part_id (@part_ids) {
-                    if ($recorded{$part_id} eq '') {
-                        for (my $i=0; $i<$expected{$part_id}; $i++) {
-                            for (my $j=0; $j<$scantron_config{'Qlength'}; $j++) {
-                                $recorded{$part_id} .= $scantron_config{'Qoff'};
-                            }
-                        }
-                    }
-                    $record{$pid} .= $recorded{$part_id};
-                }
+            my $parts;
+            my $ressymb = $resource->symb();
+            if ((exists($grader_randomlists_by_symb{$ressymb})) ||
+                (ref($grader_partids_by_symb{$ressymb}) ne 'ARRAY')) {
+                (my $analysis,$parts) =
+                    &scantron_partids_tograde($resource,$env{'request.course.id'},$username,$domain);
+            } else {
+                $parts = $grader_partids_by_symb{$ressymb};
             }
+            ($counter,my $recording) =
+                &verify_scantron_grading($resource,$domain,$username,$cid,$counter,
+                                         $scandata{$pid},$parts,
+                                         \%scantron_config,\%lettdig,$numletts);
+            $record{$pid} .= $recording;
         }
     }
     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
@@ -7907,14 +8450,14 @@ sub checkscantron_results {
                 if ($scandata{$pid} eq $record{$pid}) {
                     my $css_class = ($passed % 2)?'LC_odd_row':'LC_even_row';
                     $okstudents .= '<tr class="'.$css_class.'">'.
-'<td>'.&mt('Scantron').'</td><td>'.$showscandata.'</td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
+'<td>'.&mt('Bubblesheet').'</td><td>'.$showscandata.'</td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
 '</tr>'."\n".
 '<tr class="'.$css_class.'">'."\n".
 '<td>Submissions</td><td>'.$showrecord.'</td></tr>'."\n";
                     $passed ++;
                 } else {
                     my $css_class = ($failed % 2)?'LC_odd_row':'LC_even_row';
-                    $badstudents .= '<tr class="'.$css_class.'"><td>'.&mt('Scantron').'</td><td><span class="LC_nobreak">'.$scandata{$pid}.'</span></td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
+                    $badstudents .= '<tr class="'.$css_class.'"><td>'.&mt('Bubblesheet').'</td><td><span class="LC_nobreak">'.$scandata{$pid}.'</span></td><td rowspan="2">'.$last.'</td><td rowspan="2">'.$pid.'</td>'."\n".
 '</tr>'."\n".
 '<tr class="'.$css_class.'">'."\n".
 '<td>Submissions</td><td><span class="LC_nobreak">'.$record{$pid}.'</span></td>'."\n".
@@ -7925,10 +8468,17 @@ sub checkscantron_results {
             }
         }
     }
-    $r->print('<p>'.&mt('Comparison of scantron data (including corrections) with corresponding submission records (most recent submission) for <b>[quant,_1,student]</b>  ([_2] scantron lines/student).',$numstudents,$env{'form.scantron_maxbubble'}).'</p>');
+    $r->print('<p>'.
+              &mt('Comparison of bubblesheet data (including corrections) with corresponding submission records (most recent submission) for [_1][quant,_2,student][_3] ([quant,_4,bubblesheet line] per student).',
+                  '<b>',
+                  $numstudents,
+                  '</b>',
+                  $env{'form.scantron_maxbubble'}).
+              '</p>'
+    );
     $r->print('<p>'.&mt('Exact matches for <b>[quant,_1,student]</b>.',$passed).'<br />'.&mt('Discrepancies detected for <b>[quant,_1,student]</b>.',$failed).'</p>');
     if ($passed) {
-        $r->print(&mt('Students with exact correspondence between scantron data and submissions are as follows:').'<br /><br />');
+        $r->print(&mt('Students with exact correspondence between bubblesheet data and submissions are as follows:').'<br /><br />');
         $r->print(&Apache::loncommon::start_data_table()."\n".
                  &Apache::loncommon::start_data_table_header_row()."\n".
                  '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'.
@@ -7937,19 +8487,150 @@ sub checkscantron_results {
                  &Apache::loncommon::end_data_table().'<br />');
     }
     if ($failed) {
-        $r->print(&mt('Students with differences between scantron data and submissions are as follows:').'<br /><br />');
+        $r->print(&mt('Students with differences between bubblesheet data and submissions are as follows:').'<br /><br />');
         $r->print(&Apache::loncommon::start_data_table()."\n".
                  &Apache::loncommon::start_data_table_header_row()."\n".
                  '<th>'.&mt('Source').'</th><th>'.&mt('Bubble records').'</th><th>'.&mt('Name').'</th><th>'.&mt('ID').'</th>'.
                  &Apache::loncommon::end_data_table_header_row()."\n".
                  $badstudents."\n".
                  &Apache::loncommon::end_data_table()).'<br />'.
-                 &mt('Differences can occur if submissions were modified using manual grading after a scantron grading pass.').'<br />'.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original scantron sheets.');  
+                 &mt('Differences can occur if submissions were modified using manual grading after a bubblesheet grading pass.').'<br />'.&mt('If unexpected discrepancies were detected, it is recommended that you inspect the original bubblesheets.');  
     }
     $r->print('</form><br />'.$grading_menu_button);
     return;
 }
 
+sub verify_scantron_grading {
+    my ($resource,$domain,$username,$cid,$counter,$scandata,$partids,
+        $scantron_config,$lettdig,$numletts) = @_;
+    my ($record,%expected,%startpos);
+    return ($counter,$record) if (!ref($resource));
+    return ($counter,$record) if (!$resource->is_problem());
+    my $symb = $resource->symb();
+    return ($counter,$record) if (ref($partids) ne 'ARRAY');
+    foreach my $part_id (@{$partids}) {
+        $counter ++;
+        $expected{$part_id} = 0;
+        if ($env{"form.scantron.sub_bubblelines.$counter"}) {
+            my @sub_lines = split(/,/,$env{"form.scantron.sub_bubblelines.$counter"});
+            foreach my $item (@sub_lines) {
+                $expected{$part_id} += $item;
+            }
+        } else {
+            $expected{$part_id} = $env{"form.scantron.bubblelines.$counter"};
+        }
+        $startpos{$part_id} = $env{"form.scantron.first_bubble_line.$counter"};
+    }
+    if ($symb) {
+        my %recorded;
+        my (%returnhash) = &Apache::lonnet::restore($symb,$cid,$domain,$username);
+        if ($returnhash{'version'}) {
+            my %lasthash=();
+            my $version;
+            for ($version=1;$version<=$returnhash{'version'};$version++) {
+                foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) {
+                    $lasthash{$key}=$returnhash{$version.':'.$key};
+                }
+            }
+            foreach my $key (keys(%lasthash)) {
+                if ($key =~ /\.scantron$/) {
+                    my $value = &unescape($lasthash{$key});
+                    my ($part_id) = ($key =~ /^resource\.(.+)\.scantron$/);
+                    if ($value eq '') {
+                        for (my $i=0; $i<$expected{$part_id}; $i++) {
+                            for (my $j=0; $j<$scantron_config->{'length'}; $j++) {
+                                $recorded{$part_id} .= $scantron_config->{'Qoff'};
+                            }
+                        }
+                    } else {
+                        my @tocheck;
+                        my @items = split(//,$value);
+                        if (($scantron_config->{'Qon'} eq 'letter') ||
+                            ($scantron_config->{'Qon'} eq 'number')) {
+                            if (@items < $expected{$part_id}) {
+                                my $fragment = substr($scandata,$startpos{$part_id},$expected{$part_id});
+                                my @singles = split(//,$fragment);
+                                foreach my $pos (@singles) {
+                                    if ($pos eq ' ') {
+                                        push(@tocheck,$pos);
+                                    } else {
+                                        my $next = shift(@items);
+                                        push(@tocheck,$next);
+                                    }
+                                }
+                            } else {
+                                @tocheck = @items;
+                            }
+                            foreach my $letter (@tocheck) {
+                                if ($scantron_config->{'Qon'} eq 'letter') {
+                                    if ($letter !~ /^[A-J]$/) {
+                                        $letter = $scantron_config->{'Qoff'};
+                                    }
+                                    $recorded{$part_id} .= $letter;
+                                } elsif ($scantron_config->{'Qon'} eq 'number') {
+                                    my $digit;
+                                    if ($letter !~ /^[A-J]$/) {
+                                        $digit = $scantron_config->{'Qoff'};
+                                    } else {
+                                        $digit = $lettdig->{$letter};
+                                    }
+                                    $recorded{$part_id} .= $digit;
+                                }
+                            }
+                        } else {
+                            @tocheck = @items;
+                            for (my $i=0; $i<$expected{$part_id}; $i++) {
+                                my $curr_sub = shift(@tocheck);
+                                my $digit;
+                                if ($curr_sub =~ /^[A-J]$/) {
+                                    $digit = $lettdig->{$curr_sub}-1;
+                                }
+                                if ($curr_sub eq 'J') {
+                                    $digit += scalar($numletts);
+                                }
+                                for (my $j=0; $j<$scantron_config->{'Qlength'}; $j++) {
+                                    if ($j == $digit) {
+                                        $recorded{$part_id} .= $scantron_config->{'Qon'};
+                                    } else {
+                                        $recorded{$part_id} .= $scantron_config->{'Qoff'};
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        foreach my $part_id (@{$partids}) {
+            if ($recorded{$part_id} eq '') {
+                for (my $i=0; $i<$expected{$part_id}; $i++) {
+                    for (my $j=0; $j<$scantron_config->{'Qlength'}; $j++) {
+                        $recorded{$part_id} .= $scantron_config->{'Qoff'};
+                    }
+                }
+            }
+            $record .= $recorded{$part_id};
+        }
+    }
+    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 -------
 #
@@ -7996,36 +8677,49 @@ sub grading_menu {
                   'saveState'=>"",
                   'gradingMenu'=>1,
                   'showgrading'=>"yes");
-    my $url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
-    my @menu = ({ url => $url,
-                     name => &mt('Manual Grading/View Submissions'),
-                     short_description => 
-    &mt('Start the process of hand grading submissions.'),
-                 });
+    
+    my $url1 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
+    
     $fields{'command'} = 'csvform';
-    $url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
-    push(@menu, { url => $url,
-                   name => &mt('Upload Scores'),
-                   short_description => 
-            &mt('Specify a file containing the class scores for current resource.')});
+    my $url2 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
+    
     $fields{'command'} = 'processclicker';
-    $url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
-    push(@menu, { url => $url,
-                   name => &mt('Process Clicker'),
-                   short_description => 
-            &mt('Specify a file containing the clicker information for this resource.')});
+    my $url3 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
+    
     $fields{'command'} = 'scantron_selectphase';
-    $url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
-    push(@menu, { url => $url,
-                   name => &mt('Grade/Manage/Review Scantron Forms'),
-                   short_description => 
-            &mt('Grade scantron exams, upload/download scantron data files, and review previously graded scantron exams.')});
-    $fields{'command'} = 'verify';
-    $url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
-    push(@menu, { url => "",
-                   name => &mt('Verify Receipt'),
-                   short_description => 
-            &mt('')});
+    my $url4 = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
+    
+    my @menu = ({	categorytitle=>'Course Grading',
+            items =>[
+                        {	linktext => 'Manual Grading/View Submissions',
+                    		url => $url1,
+                    		permission => 'F',
+                    		icon => 'edit-find-replace.png',
+                    		linktitle => 'Start the process of hand grading submissions.'
+                        },
+                	    {	linktext => 'Upload Scores',
+                    		url => $url2,
+                    		permission => 'F',
+                    		icon => 'uploadscores.png',
+                    		linktitle => 'Specify a file containing the class scores for current resource.'
+                	    },
+                	    {	linktext => 'Process Clicker',
+                    		url => $url3,
+                    		permission => 'F',
+                    		icon => 'addClickerInfoFile.png',
+                    		linktitle => 'Specify a file containing the clicker information for this resource.'
+                	    },
+                	    {	linktext => 'Grade/Manage/Review Bubblesheets',
+                    		url => $url4,
+                    		permission => 'F',
+                    		icon => 'stat.png',
+                    		linktitle => 'Grade bubblesheet exams, upload/download bubblesheet data files, and review previously graded bubblesheet exams.'
+                	    }
+                    ]
+            });
+
+    #$fields{'command'} = 'verify';
+    #$url = &Apache::lonhtmlcommon::build_url('grades/',\%fields);
     #
     # Create the menu
     my $Str;
@@ -8039,25 +8733,16 @@ sub grading_menu {
 	'<input type="hidden" name="gradingMenu" value="1" />'."\n".
 	'<input type="hidden" name="showgrading" value="yes" />'."\n";
 
-    foreach my $menudata (@menu) {
-        if ($menudata->{'name'} ne &mt('Verify Receipt')) {
-            $Str .='    <h3><a '.
-                $menudata->{'jscript'}.
-                ' href="'.
-                $menudata->{'url'}.'" >'.
-                $menudata->{'name'}."</a></h3>\n";
-        } else {
-            $Str .='<hr /><input type="button" value="'.&mt('Verify Receipt').'" '.
-                $menudata->{'jscript'}.
-                ' onClick="javascript:checkChoice(document.forms.gradingMenu,\'5\',\'verify\')" '.
-                ' /> '.
-		&Apache::lonnet::recprefix($env{'request.course.id'}).
-                    '-<input type="text" name="receipt" size="4" onChange="javascript:checkReceiptNo(this.form,\'OK\')" />';
-        }
-        $Str .= '    '.('&nbsp;'x8).$menudata->{'short_description'}.
-            "\n";
-    }
+    $Str .= Apache::lonhtmlcommon::generate_menu(@menu);
+    #$menudata->{'jscript'}
+    $Str .='<hr /><input type="button" value="'.&mt('Verify Receipt No.').'" '.
+        ' onclick="javascript:checkChoice(document.forms.gradingMenu,\'5\',\'verify\')" '.
+        ' /> '.
+        &Apache::lonnet::recprefix($env{'request.course.id'}).
+        '-<input type="text" name="receipt" size="4" onchange="javascript:checkReceiptNo(this.form,\'OK\')" />';
+
     $Str .="</form>\n";
+    my $receiptalert = &mt("Please enter a receipt number given by a student in the receipt box.");
     $request->print(<<GRADINGMENUJS);
 <script type="text/javascript" language="javascript">
     function checkChoice(formname,val,cmdx) {
@@ -8085,7 +8770,7 @@ sub grading_menu {
 	if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;}
 	if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;}
 	if (checkOpt) {
-	    alert("Please enter a receipt number given by a student in the receipt box.");
+	    alert("$receiptalert");
 	    formname.receipt.value = "";
 	    formname.receipt.focus();
 	    return false;
@@ -8106,6 +8791,7 @@ sub submit_options {
     if (!$symb) {return '';}
     my $probTitle = &Apache::lonnet::gettitle($symb);
 
+    my $receiptalert = &mt("Please enter a receipt number given by a student in the receipt box."); 
     $request->print(<<GRADINGMENUJS);
 <script type="text/javascript" language="javascript">
     function checkChoice(formname,val,cmdx) {
@@ -8133,7 +8819,7 @@ sub submit_options {
 	if (nospace == "OK" && isNaN(receiptNo)) {checkOpt = true;}
 	if (nospace == "notOK" && (isNaN(receiptNo) || receiptNo == "")) {checkOpt = true;}
 	if (checkOpt) {
-	    alert("Please enter a receipt number given by a student in the receipt box.");
+	    alert("$receiptalert");
 	    formname.receipt.value = "";
 	    formname.receipt.focus();
 	    return false;
@@ -8236,7 +8922,7 @@ GRADINGMENUJS
               </label>
             </div>
             <div>
-	      <input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next-&gt;').'" />
+	      <input type="button" onclick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next').' &rarr;" />
             </div>
           </div>
 
@@ -8253,7 +8939,7 @@ GRADINGMENUJS
               </label>
             </div>
             <div>
-	      <input type="button" onClick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next-&gt;').'" />
+	      <input type="button" onclick="javascript:checkChoice(this.form,\'2\');" value="'.&mt('Next').' &rarr;" />
             </div>
         </div>
   </form>';
@@ -8283,6 +8969,21 @@ sub init_perm {
     }
 }
 
+sub init_old_essays {
+    my ($symb,$apath,$adom,$aname) = @_;
+    if ($symb ne '') {
+        my %essays = &Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname);
+        if (keys(%essays) > 0) {
+            $old_essays{$symb} = \%essays;
+        }
+    }
+    return;
+}
+
+sub reset_old_essays {
+    undef(%old_essays);
+}
+
 sub gather_clicker_ids {
     my %clicker_ids;
 
@@ -8354,9 +9055,9 @@ sub process_clicker {
     $result.=$table;
     $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n";
     $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n";
-    $result.='&nbsp;<b>'.&mt('Specify a file containing the clicker information for this resource').
-        '.</b></td></tr>'."\n";
-    $result.='<tr bgcolor=#ffffe6><td>'."\n";
+    $result.='&nbsp;<b>'.&mt('Specify a file containing the clicker information for this resource.').
+        '</b></td></tr>'."\n";
+    $result.='<tr bgcolor="#ffffe6"><td>'."\n";
 # Attempt to restore parameters from last session, set defaults if not present
     my %Saveable_Parameters=&clicker_grading_parameters();
     &Apache::loncommon::restore_course_settings('grades_clicker',
@@ -8369,7 +9070,7 @@ sub process_clicker {
     my %checked;
     foreach my $gradingmechanism ('attendance','personnel','specific','given') {
        if ($env{'form.gradingmechanism'} eq $gradingmechanism) {
-          $checked{$gradingmechanism}="checked='checked'";
+          $checked{$gradingmechanism}=' checked="checked"';
        }
     }
 
@@ -8383,8 +9084,8 @@ sub process_clicker {
     my $pcorrect=&mt("Percentage points for correct solution");
     my $pincorrect=&mt("Percentage points for incorrect solution");
     my $selectform=&Apache::loncommon::select_form($env{'form.upfiletype'},'upfiletype',
-						   ('iclicker' => 'i>clicker',
-                                                    'interwrite' => 'interwrite PRS'));
+                                                   {'iclicker' => 'i>clicker',
+                                                    'interwrite' => 'interwrite PRS'});
     $symb = &Apache::lonenc::check_encrypt($symb);
     $result.=<<ENDUPFORM;
 <script type="text/javascript">
@@ -8433,17 +9134,17 @@ function sanitycheck() {
 <input type="hidden" name="saveState"  value="$env{'form.saveState'}" />
 <input type="file" name="upfile" size="50" />
 <br /><label>$type: $selectform</label>
-<br /><label><input type="radio" name="gradingmechanism" value="attendance" $checked{'attendance'} onClick="sanitycheck()" />$attendance </label>
-<br /><label><input type="radio" name="gradingmechanism" value="personnel" $checked{'personnel'} onClick="sanitycheck()" />$personnel</label>
-<br /><label><input type="radio" name="gradingmechanism" value="specific" $checked{'specific'} onClick="sanitycheck()" />$specific </label>
+<br /><label><input type="radio" name="gradingmechanism" value="attendance"$checked{'attendance'} onclick="sanitycheck()" />$attendance </label>
+<br /><label><input type="radio" name="gradingmechanism" value="personnel"$checked{'personnel'} onclick="sanitycheck()" />$personnel</label>
+<br /><label><input type="radio" name="gradingmechanism" value="specific"$checked{'specific'} onclick="sanitycheck()" />$specific </label>
 <input type="text" name="specificid" value="$env{'form.specificid'}" size="20" />
-<br /><label><input type="radio" name="gradingmechanism" value="given" $checked{'given'} onClick="sanitycheck()" />$given </label>
+<br /><label><input type="radio" name="gradingmechanism" value="given"$checked{'given'} onclick="sanitycheck()" />$given </label>
 <br />&nbsp;&nbsp;&nbsp;
 <input type="text" name="givenanswer" size="50" />
 <input type="hidden" name="waschecked" value="$env{'form.gradingmechanism'}" />
-<br /><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 /><input type="button" onClick="javascript:checkUpload(this.form);" value="$upload" />
+<br /><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 /><input type="button" onclick="javascript:checkUpload(this.form);" value="$upload" />
 </form>
 ENDUPFORM
     $result.='</td></tr></table>'."\n".
@@ -8474,7 +9175,7 @@ sub process_clicker_file {
     if ($env{'form.gradingmechanism'} eq 'given') {
         $env{'form.givenanswer'}=~s/^\s*//gs;
         $env{'form.givenanswer'}=~s/\s*$//gs;
-        $env{'form.givenanswer'}=~s/[^a-zA-Z0-9\.\*\-]+/\,/g;
+        $env{'form.givenanswer'}=~s/[^a-zA-Z0-9\.\*\-\+]+/\,/g;
         $env{'form.givenanswer'}=uc($env{'form.givenanswer'});
         my @answers=split(/\,/,$env{'form.givenanswer'});
         $foundgiven=$#answers+1;
@@ -8534,7 +9235,7 @@ sub process_clicker_file {
     $result.=(<<ENDHEADER);
 <br /><table width="100%" border="0"><tr><td bgcolor="#777777">
 <table width="100%" border="0"><tr bgcolor="#e6ffff"><td>
-<b>$heading</b></td></tr><tr bgcolor=#ffffe6><td>
+<b>$heading</b></td></tr><tr bgcolor="#ffffe6"><td>
 <form method="post" action="/adm/grades" name="clickeranalysis">
 <input type="hidden" name="symb" value="$symb" />
 <input type="hidden" name="command" value="assignclickergrades" />
@@ -8603,7 +9304,7 @@ ENDHEADER
                    "\n".&mt("Username").": <input type='text' name='uname".$id."' />&nbsp;".
                    "\n".&mt("Domain").": ".
                    &Apache::loncommon::select_dom_form($env{'course.'.$env{'request.course.id'}.'.domain'},'udom'.$id).'&nbsp;'.
-                   &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id);
+                   &Apache::loncommon::selectstudent_link('clickeranalysis','uname'.$id,'udom'.$id,0,$id);
           $unknown_count++;
        }
     }
@@ -8648,6 +9349,7 @@ sub iclicker_eval {
 	    $id=~s/^[\#0]+//;
 	    for (my $i=0;$i<$number;$i++) {
 		my $idx=3+$i*6;
+                $entries[$idx]=~s/[^a-zA-Z0-9\.\*\-\+]+//g;
 		push(@idresponses,$entries[$idx]);
 	    }
 	    $$responses{$id}=join(',',@idresponses);
@@ -8692,18 +9394,23 @@ sub assign_clicker_grades {
     my ($symb)=&get_symb($r);
     if (!$symb) {return '';}
 # See which part we are saving to
-    my ($partlist,$handgrade,$responseType) = &response_type($symb);
+    my $res_error;
+    my ($partlist,$handgrade,$responseType) = &response_type($symb,\$res_error);
+    if ($res_error) {
+        return &navmap_errormsg();
+    }
 # FIXME: This should probably look for the first handgradeable part
     my $part=$$partlist[0];
 # Start screen output
     my ($result) = &showResourceInfo($symb,$env{'form.probTitle'});
 
-    my $heading=&mt('Assigning grades based on clicker file');
-    $result.=(<<ENDHEADER);
-<br /><table width="100%" border="0"><tr><td bgcolor="#777777">
-<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>
-<b>$heading</b></td></tr><tr bgcolor=#ffffe6><td>
-ENDHEADER
+    $result .= '<br />'.
+               &Apache::loncommon::start_data_table().
+               &Apache::loncommon::start_data_table_header_row().
+               '<th>'.&mt('Assigning grades based on clicker file').'</th>'.
+               &Apache::loncommon::end_data_table_header_row().
+               &Apache::loncommon::start_data_table_row().'<td>';
+
 # Get correct result
 # FIXME: Possibly need delimiter other than ":"
     my @correct=();
@@ -8719,25 +9426,26 @@ ENDHEADER
                     $result.='<br /><span class="LC_warning">'.
                              &mt('More than one correct result given for question "[_1]": [_2] versus [_3].',
                                  $env{'form.question:'.$i},$correct[$i],$input[$i]).'</span>';
-                 } elsif ($input[$i]) {
+                 } elsif (($input[$i]) || ($input[$i] eq '0')) {
                     $correct[$i]=$input[$i];
                  }
              }
           }
        }
        for (my $i=0;$i<$number;$i++) {
-          if (!$correct[$i]) {
+          if ((!$correct[$i]) && ($correct[$i] ne '0')) {
              $result.='<br /><span class="LC_error">'.
                       &mt('No correct result given for question "[_1]"!',
                           $env{'form.question:'.$i}).'</span>';
           }
        }
-       $result.='<br />'.&mt("Correct answer: [_1]",join(', ',map { ($_?$_:'-') } @correct));
+       $result.='<br />'.&mt("Correct answer: [_1]",join(', ',map { ((($_) || ($_ eq '0'))?$_:'-') } @correct));
     }
 # Start grading
     my $pcorrect=$env{'form.pcorrect'};
     my $pincorrect=$env{'form.pincorrect'};
     my $storecount=0;
+    my %users=();
     foreach my $key (keys(%env)) {
        my $user='';
        if ($key=~/^form\.student\:(.*)$/) {
@@ -8751,24 +9459,42 @@ ENDHEADER
              $user=$env{'form.multi'.$id};
           }
        }
-       if ($user) { 
+       if ($user) {
+          if ($users{$user}) {
+             $result.='<br /><span class="LC_warning">'.
+                      &mt("More than one entry found for <tt>[_1]</tt>!",$user).
+                      '</span><br />';
+          }
+          $users{$user}=1;
           my @answer=split(/\,/,$env{$key});
           my $sum=0;
           my $realnumber=$number;
           for (my $i=0;$i<$number;$i++) {
-             if ($answer[$i]) {
+             if  ($correct[$i] eq '-') {
+                $realnumber--;
+             } elsif ($answer[$i]) {
                 if ($gradingmechanism eq 'attendance') {
                    $sum+=$pcorrect;
-                } elsif ($answer[$i] eq '*') {
+                } elsif ($correct[$i] eq '*') {
                    $sum+=$pcorrect;
-                } elsif ($answer[$i] eq '-') {
-                   $realnumber--;
                 } else {
-                   if ($answer[$i] eq $correct[$i]) {
-                      $sum+=$pcorrect;
-                   } else {
-                      $sum+=$pincorrect;
+# We actually grade if correct or not
+                   my $increment=$pincorrect;
+# Special case: numerical answer "0"
+                   if ($correct[$i] eq '0') {
+                      if ($answer[$i]=~/^[0\.]+$/) {
+                         $increment=$pcorrect;
+                      }
+# General numerical answer, both evaluate to something non-zero
+                   } elsif ((1.0*$correct[$i]!=0) && (1.0*$answer[$i]!=0)) {
+                      if (1.0*$correct[$i]==1.0*$answer[$i]) {
+                         $increment=$pcorrect;
+                      }
+# Must be just alphanumeric
+                   } elsif ($answer[$i] eq $correct[$i]) {
+                      $increment=$pcorrect;
                    }
+                   $sum+=$increment;
                 }
              }
           }
@@ -8790,23 +9516,30 @@ ENDHEADER
        }
     }
 # We are done
-    $result.='<br />'.&mt('Successfully stored grades for [_1] student(s).',$storecount).
-             '</td></tr></table>'."\n".
-             '</td></tr></table><br /><br />'."\n";
+    $result.='<br />'.&mt('Successfully stored grades for [quant,_1,student].',$storecount).
+             '</td>'.
+             &Apache::loncommon::end_data_table_row().
+             &Apache::loncommon::end_data_table()."<br /><br />\n";
     return $result.&show_grading_menu_form($symb);
 }
 
+sub navmap_errormsg {
+    return '<div class="LC_error">'.
+           &mt('An error occurred retrieving information about resources in the course.').'<br />'.
+           &mt('It is recommended that you [_1]re-initialize the course[_2] and then return to this grading page.','<a href="/adm/roles?selectrole=1&newrole='.$env{'request.role'}.'">','</a>').
+           '</div>';
+}
+
 sub handler {
     my $request=$_[0];
     &reset_caches();
-    if ($env{'browser.mathml'}) {
-	&Apache::loncommon::content_type($request,'text/xml');
-    } else {
-	&Apache::loncommon::content_type($request,'text/html');
+    if ($request->header_only) {
+        &Apache::loncommon::content_type($request,'text/html');
+        $request->send_http_header;
+        return OK;
     }
-    $request->send_http_header;
-    return '' if $request->header_only;
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
+
     my $symb=&get_symb($request,1);
     my @commands=&Apache::loncommon::get_env_multiple('form.command');
     my $command=$commands[0];
@@ -8817,10 +9550,13 @@ sub handler {
 
     $ssi_error = 0;
     my $brcrum = [{href=>"/adm/grades",text=>"Grading"}];
-    $request->print(&Apache::loncommon::start_page('Grading',undef,
-                                          {'bread_crumbs' => $brcrum}));
+    my $start_page = &Apache::loncommon::start_page('Grading',undef,
+                                          {'bread_crumbs' => $brcrum});
     if ($symb eq '' && $command eq '') {
 	if ($env{'user.adv'}) {
+            &Apache::loncommon::content_type($request,'text/html');
+            $request->send_http_header;
+            $request->print($start_page);
 	    if (($env{'form.codeone'}) && ($env{'form.codetwo'}) &&
 		($env{'form.codethree'})) {
 		my $token=$env{'form.codeone'}.'*'.$env{'form.codetwo'}.'*'.
@@ -8844,9 +9580,31 @@ sub handler {
 	    } else {
 		$request->print(&Apache::lonxml::tokeninputfield());
 	    }
-	}
+        } elsif ($env{'request.course.id'}) {
+            &init_perm(); 
+            if (!%perm) {
+                $request->internal_redirect('/adm/quickgrades');
+            } else {
+                &Apache::loncommon::content_type($request,'text/html');
+                $request->send_http_header;
+                $request->print($start_page);
+            }
+        }
     } else {
-	&init_perm();
+        &init_perm();
+        if (!$env{'request.course.id'}) {
+            unless ((&Apache::lonnet::allowed('usc',$env{'request.role.domain'})) &&
+                    ($command =~ /^scantronupload/)) {
+                # Not in a course.
+                $env{'user.error.msg'}="/adm/grades::vgr:0:0:Cannot display grades page outside course context";
+                return HTTP_NOT_ACCEPTABLE;
+            }
+        } elsif (!%perm) {
+            $request->internal_redirect('/adm/quickgrades');
+        }
+        &Apache::loncommon::content_type($request,'text/html');
+        $request->send_http_header;
+        $request->print($start_page);
 	if ($command eq 'submission' && $perm{'vgr'}) {
 	    ($env{'form.student'} eq '' ? &listStudents($request) : &submission($request,0,0));
 	} elsif ($command eq 'pickStudentPage' && $perm{'vgr'}) {
@@ -8916,7 +9674,7 @@ sub handler {
         } elsif ($command eq 'checksubmissions' && $perm{'vgr'}) {
             $request->print(&checkscantron_results($request));     
 	} elsif ($command) {
-	    $request->print("Access Denied ($command)");
+	    $request->print('<p class="LC_error">'.&mt('Access Denied ([_1])',$command).'</p>');
 	}
     }
     if ($ssi_error) {
@@ -8924,7 +9682,7 @@ sub handler {
     }
     $request->print(&Apache::loncommon::end_page());
     &reset_caches();
-    return '';
+    return OK;
 }
 
 1;
@@ -9021,6 +9779,13 @@ ssi_with_retries()
 
 =item  scantron_get_maxbubble() : 
 
+   Arguments:
+       $nav_error  - Reference to scalar which is a flag to indicate a
+                      failure to retrieve a navmap object.
+       if $nav_error is set to 1 by scantron_get_maxbubble(), the 
+       calling routine should trap the error condition and display the warning
+       found in &navmap_errormsg().
+
    Returns the maximum number of bubble lines that are expected to
    occur. Does this by walking the selected sequence rendering the
    resource and then checking &Apache::lonxml::get_problem_counter()
@@ -9044,7 +9809,7 @@ ssi_with_retries()
 
 =item  scantron_process_students() : 
 
-   Routine that does the actual grading of the bubble sheet information.
+   Routine that does the actual grading of the bubblesheet information.
 
    The parsed scanline hash is added to %env 
 
@@ -9064,7 +9829,7 @@ ssi_with_retries()
 
 =item  scantron_upload_scantron_data() :
 
-    Creates the screen for adding a new bubble sheet data file to a course.
+    Creates the screen for adding a new bubblesheet data file to a course.
 
 =item  scantron_upload_scantron_data_save() : 
 
@@ -9078,13 +9843,18 @@ ssi_with_retries()
 =item  scantron_download_scantron_data() : 
 
    Shows a list of the three internal files (original, corrected,
-   skipped) for a specific bubble sheet data file that exists in the
+   skipped) for a specific bubblesheet data file that exists in the
    course.
 
 =item  scantron_validate_ID() : 
 
    Validates all scanlines in the selected file to not have any
-   invalid or underspecified student IDs
+   invalid or underspecified student/employee IDs
+
+=item navmap_errormsg() :
+
+   Returns HTML mark-up inside a <div></div> with a link to re-initialize the course.
+   Should be called whenever the request to instantiate a navmap object fails.  
 
 =back