[LON-CAPA-cvs] cvs: loncom /homework inputtags.pm /interface lonpdfupload.pm printout.pl

raeburn raeburn at source.lon-capa.org
Wed Mar 25 22:49:58 EDT 2026


raeburn		Thu Mar 26 02:49:58 2026 EDT

  Modified files:              
    /loncom/interface	lonpdfupload.pm printout.pl 
    /loncom/homework	inputtags.pm 
  Log:
  - Bug 6121. PDF form fields
    - Display order for grading feedback matches order in original PDF.
    - Links to problem(s) honor encrypturl state.
    - Grading feedback honors problemstatus currently in effect.
    - Message shown for graded problem parts determined by standard 
      inputtags::decideoutput() instead of custom &parse_grade_answer().
    - Information about ungradeable problems (e.g., closed problems, or
      problems in exam mode) shown in separate data table.
    - Grading PDF forms with radiobuttonresponse items filled in PDF viewers
      that are not Acrobat Reader (e.g., PDFGear, Okular etc.) is supported.
  
  
-------------- next part --------------
Index: loncom/interface/lonpdfupload.pm
diff -u loncom/interface/lonpdfupload.pm:1.36 loncom/interface/lonpdfupload.pm:1.37
--- loncom/interface/lonpdfupload.pm:1.36	Mon Mar 23 00:22:20 2026
+++ loncom/interface/lonpdfupload.pm	Thu Mar 26 02:49:57 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # PDF Form Upload Handler
 #
-# $Id: lonpdfupload.pm,v 1.36 2026/03/23 00:22:20 raeburn Exp $
+# $Id: lonpdfupload.pm,v 1.37 2026/03/26 02:49:57 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -205,15 +205,21 @@
 sub processPDF {
     my $result; # message for Browser
     my @pdfdata = (); # answers from PDF-Forms
+    my $error;
     
-    @pdfdata = &get_pdf_data(); # get answers from PDF-Form
+    ($error, at pdfdata) = &get_pdf_data(); # get answers from PDF-Form
     
-    if (scalar @pdfdata) {    
-        $result = &grade_pdf(@pdfdata);
+    if (scalar(@pdfdata)) {    
+        $result = &grade_pdf($error, at pdfdata);
     } else {
-        $result = '<p class="LC_error">'
-                 .&mt("Can't find any valid PDF form fields.")
-                 .'</p>';
+        $result = '<h2 class="LC_heading_2">'.&mt('Result of PDF Form upload').'</h2>';
+        if ($error) {
+            $result .= '<p class="LC_error">'.$error.'</p>';
+        } else {
+            $result .= '<p class="LC_error">'
+                      .&mt("Can't find any valid PDF form fields.")
+                      .'</p>';
+        }
     }
     return $result;
 }
@@ -221,36 +227,92 @@
 sub get_pdf_data() {
     my @data = ();
     my $pdf = CAM::PDF->new($env{'form.file'});
-
-    if($pdf) {
-        my @formFields = $pdf->getFormFieldList(); #get names of form fields
+    my $error;
+    if ($pdf) {
+        my @formFields;
+#
+# If the form fields include an optionresponse item in checkbox mode and  
+# another form element, then if Adobe Acrobat Reader is used to save the
+# PDF after filling form field(s), the formatting used will cause the
+# getFormFieldList() routine in CAM::PDF to exit with a die(). An eval {};
+# is used to trap that, and a message is displayed recommending use of a
+# different PDF viewer.
+#
+        eval {
+            @formFields = $pdf->getFormFieldList(); #get names of form fields
+        };
+        if ($@) {
+            $error = &mt('An error occurred when processing the uploaded PDF file.').'<br />'.
+                     &mt("To avoid this, generate a PDF with LON-CAPA's print utility and then open the file with PDFGear or with a web browser to enter answers, instead of opening with Adobe Acrobat Reader.");
+        }
+        my %radiobutton;
         foreach my $field (@formFields) {
-            my $value;
+            my ($value,$unencfield);
+            $unencfield = &check_encoding($field);
             my $dict = $pdf->getFormFieldDict($pdf->getFormField($field)); # get form field dictonary
-
+#
 # Checking for $dict->{'V'}, present in rev. 1.19 to rev. 1.31
 # to handle fragmentary names from form fieldnames containing
 # a dot, eliminated for rev. 1.32 as fieldnames no longer
 # contain dots.
+#
 # This allows checkboxes which are unchecked to be included
 # as form fields to support checkbox mode for optionresponse.
+#
+# Extra effort is needed to identify the value of the checked item
+# in a group of radiobutton form fields when the filled PDF was 
+# saved in a viewer other than Adobe Acrobat (including Reader).
+#
             if ((ref($dict)) && (ref($dict->{'V'}))) {
                 $value = $dict->{'V'}{'value'};
             }
-            $field = &check_encoding($field);
-            if ($value ne '') {
+            if (($unencfield =~ /^uuid_/) && ($unencfield =~ /&radiobutton&/)) {
+                if (($value eq '') || ($value eq 'Off')) {
+                    $radiobutton{$field} = $unencfield;
+                } else {
+                    $value = &check_encoding($value);
+                    if (exists($radiobutton{$field})) {
+                        delete($radiobutton{$field});
+                    }
+                }
+            } else {
                 $value = &check_encoding($value);
             }
-            push(@data, $field."?". $value); #binding fieldname with value
+            push(@data, $unencfield."?". $value); #binding fieldname with value
         }
+        if (keys(%radiobutton)) {
+            foreach my $objnum (sort { $a <=> $b } keys %{ $pdf->{xref} }) {
+                my $object;
+                eval {
+                    $object = $pdf->getObjValue($objnum);
+                };
+                if ($@) {
+                    next;
+                }
+                if ((ref($object) eq 'HASH') && (exists($object->{'T'})) && (exists($object->{'V'}))) {
+                    if ((ref($object->{'T'}) eq 'CAM::PDF::Node') &&
+                        (ref($object->{'V'}) eq 'CAM::PDF::Node')) {
+                        if (($object->{'V'}->{'value'} ne '') && ($object->{'V'}->{'value'} ne 'Off')) {
+                            my $fieldname = $object->{'T'}->{'value'};
+                            if (exists($radiobutton{$fieldname})) {
+                                my $val = &check_encoding($object->{'V'}->{'value'});
+                                push(@data, $radiobutton{$fieldname}."?". $val);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    } else {
+        $error = &mt('Failed to retrieve uploaded PDF file');
     }
-    return @data;
+    return ($error, at data);
 }
 
 sub check_encoding {
     my ($data) = @_;
 
-# ps2pdf13 used by LON-CAPA to output PDFs produces PDF format 1.3.
+# ps2pdf used by LON-CAPA to output PDFs with form fields produces PDF format 1.3.
 # For pre-PDF 2.0, Adobe Acrobat attempts to use "best host encoding" for
 # values being submitted, which will depend on characters entered by the user.
 #
@@ -299,12 +361,12 @@
 }
 
 sub grade_pdf {
-    my @pdfdata = @_;
+    my ($error, at pdfdata) = @_;
     my ($result,$meta,%grades,%problems,%foreigncourse,%mismatchuser,
         %types,%checkboxoff,%checkboxon,%checkboxtotal,%checkboxinfo,
-        %essaydraft,$debug);
+        %essaydraft,%symb_to_res,%possfields,%orderedsymb,$navmap,$count,$debug);
 
-    my $navmap = Apache::lonnavmaps::navmap->new();
+    $navmap = Apache::lonnavmaps::navmap->new();
     if (!defined($navmap)) {
         $result = '<h3>'.&mt('Verification of PDF form items failed').'</h3>'.
                   '<div class="LC_error">'.
@@ -312,16 +374,21 @@
                   &mt('You may need to [_1]re-select[_2] the course.','<a href="/adm/roles">','</a>').
                   '</div>';
         return $result;
+    } elsif ($error) {
+        $result = '<p class="LC_error">'.$error.'</p>';
     }
-    my %restitles;
+    $count = 0;
     foreach my $res ($navmap->retrieveResources()) {
-        my $symb = $res->symb; 
-        $restitles{$symb} = $res->compTitle();
+        my $symb = $res->symb;
+        if ($symb) {
+            $symb_to_res{$symb} = $res;
+            $orderedsymb{$symb} = $count;
+            $count ++;
+        }
     }
-   
+
     $debug  .= "Found: ". scalar @pdfdata." Entries \n";
 
-    my %possfields;
     foreach my $entry (sort(@pdfdata)) {
         if ($entry =~ /^meta.*/) {
             $debug .= 'found: metadata -> '.$entry . "<br />";
@@ -369,7 +436,7 @@
                 push(@{$foreigncourse{$cid}},$symb);
                 next;
             }
-            next unless (exists($restitles{$symb}));
+            next unless (exists($symb_to_res{$symb}));
             my ($map,$id,$resource)=&Apache::lonnet::decode_symb($symb);
             if (ref($possfields{$uuid}) eq 'ARRAY') {
                 foreach my $item (@{$possfields{$uuid}}) {
@@ -377,7 +444,7 @@
                         while (my ($key, $value) = each(%{$item})) {
                             my ($part, $type, $HWVAL, $input_id) = split(/&/,$key);
                             #filter incorrect radiobuttons (Bug in CABAReT Stage)
-                            if (($type eq 'radiobutton') && ($value eq '')) {
+                            if (($type eq 'radiobutton') && (($value eq '') || ($value eq 'Off'))) {
                                 next;
                             }
                             if (($type eq 'option') && ($HWVAL =~ /^HWHDN(ON|OFF|NUM)([^:]+):0$/)) {
@@ -404,7 +471,6 @@
                                 $essaydraft{$symb.$part}{'HWVAL'.$1} = $value;
                                 next;
                             }
-
                             my $submit = $part;
                             $submit =~ s/part_(.*)/submit_$1/;
                             if ($problems{$symb.$part}) {
@@ -484,17 +550,14 @@
             }
         }
     }
-
+    my (%gradeable,%ungradeable,%showparts);
     if (keys(%problems) > 0) {
-        $result .= &Apache::loncommon::start_data_table()
-                  .&Apache::loncommon::start_data_table_header_row()
-                  .'<th>'.&mt('Problem Name').'</th>'
-                  .'<th>'.&mt('Grading').'</th>'
-                  .&Apache::loncommon::start_data_table_header_row()
-                  .&Apache::loncommon::end_data_table_header_row();
-
         foreach my $key (sort(keys(%problems))) {
             my %problem = %{$problems{$key}};
+            my $symb = $problems{$key}{'symb'};
+            my $part = $problems{$key}{'submitted'};
+            $part =~ s/^part_//;
+            my $res = $symb_to_res{$symb};
             if (ref($types{$key}) eq 'HASH') {
                 foreach my $hwval (keys(%{$types{$key}})) {
                     if (ref($problem{$hwval}) eq 'HASH') {
@@ -534,31 +597,40 @@
                     }
                 }
             }
-            my ($problemname, $grade) = &grade_problem(%problem);
-
-            $result .= &Apache::loncommon::start_data_table_row();
-            $result .= '<td><a href="/res/'.$problem{'resource'}.
-                       '?symb='.
-                       &HTML::Entities::encode($problem{'symb'},'"&<>').
-                       '">'.$problemname.'</a></td><td><span class="';
-            if ($grade eq "EXACT_ANS" || $grade eq "APPROX_ANS") {
-                $result .= 'LC_answer_correct';
-            } elsif ($grade eq "DRAFT") {
-                $result .= 'LC_answer_not_charged_try';
+            if ($res->is_exam()) {
+                $ungradeable{$symb}{$part} = &mt('Question type set to "exam".');
             } else {
-                $result .= 'LC_answer_charged_try';
+                my ($status,$datemsg) = &Apache::lonhomework::check_slot_access($part,'problem',$symb);
+                if ($status eq 'CAN_ANSWER') {
+                    %{$gradeable{$symb}{$part}} = %problem;
+                    unless ($showparts{'graded'}) {
+                        unless ($part eq '0') {
+                            $showparts{'graded'} = 1;
+                       }
+                    }
+                } else {
+                    $ungradeable{$symb}{$part} = $datemsg;
+                    unless ($showparts{'ungraded'}) {
+                        unless ($part eq '0') {
+                            $showparts{'ungraded'} = 1;
+                        }
+                    }
+                }
             }
-            $result .= '">';
-            $grade = &parse_grade_answer($grade);
-            $result .= $grade.'</span></td>';
-            $result .= &Apache::loncommon::end_data_table_row();
         }
-        $result .= &Apache::loncommon::end_data_table();
     } else {
         $result .= '<p class="LC_warning">'.
                    &mt('As no gradable form items were found, no submissions have been recorded.').
                    '</p>';
     }
+    if (keys(%gradeable)) {
+        $result .= '<h3 class="LC_heading_3">'.&mt('Saved').'</h3>'.
+                   &show_results('graded',$showparts{'graded'},\%symb_to_res,\%gradeable,\%orderedsymb);
+    }
+    if (keys(%ungradeable)) {
+        $result .= '<h3 class="LC_heading_3">'.&mt('Not Saved').'</h3>'.
+                   &show_results('ungraded',$showparts{'ungraded'},\%symb_to_res,\%ungradeable,\%orderedsymb);
+    }
     if (keys(%foreigncourse)) {
         my ($numother,$othercrsmsg);
         foreach my $cid (sort(keys(%foreigncourse))) {
@@ -584,7 +656,7 @@
                            $othercrsmsg.'</li></ul>';
             } else {
                 $result .= &mt('Your uploaded PDF form contained the following resource(s) from a different course:').' '.$othercrsmsg.
-                           &mt('Did you download the PDF form from another course and upload it to the wrong course?'); 
+                           &mt('Did you download the PDF form from another course and upload it to the wrong course?');
             }
             $result .= '</div>';
         }
@@ -598,24 +670,122 @@
     return $result;
 }
 
-sub grade_problem {
-    my (%problem) = @_;
-    my ($title, $part) = ();
-
-    &Apache::loncommon::ssi_with_retries('/res/'.$problem{'resource'}, 5, %problem);
-
-    $title = &Apache::lonnet::gettitle($problem{'symb'});    
-    $part = $problem{submitted};
-    $part =~ s/part_(.*)/$1/;
-    unless($part eq '0') {
-        #add information about part number
-        $title .= " - Part $part";
-    }
- 
-    my %problemhash = &Apache::lonnet::restore($problem{'symb'});
-    my $grade = $problemhash{"resource.$part.award"};
+sub show_results {
+    my ($category,$showparts,$symb_to_res,$cathashref,$orderref) = @_;
+    return unless ((ref($symb_to_res)) && (ref($cathashref) eq 'HASH') &&
+                   (ref($orderref) eq 'HASH'));
+    my %colheaders;
+    if ($category eq 'graded') {
+        %colheaders =
+            &Apache::lonlocal::texthash(
+                                         name => 'Problem Name',
+                                         info => 'Grading',
+                                       );
+    } else {
+        %colheaders =
+            &Apache::lonlocal::texthash(
+                                         name => 'Problem Name',
+                                         info => 'Reason submission not saved',
+                                       );
+    }
+    my $output = &Apache::loncommon::start_data_table()
+                .&Apache::loncommon::start_data_table_header_row();
+    if ($showparts) {
+        $output .= '<th colspan="2">';
+    } else {
+        $output .= '<th>';
+    }
+    $output .= $colheaders{'name'}.'</th>'
+              .'<th>'.$colheaders{'info'}.'</th>'
+              .&Apache::loncommon::end_data_table_header_row();
+    foreach my $symb (sort { $orderref->{$a} <=> $orderref->{$b} } (keys(%{$cathashref}))) {
+        my $res = $symb_to_res->{$symb};
+        my ($title,$encrypted);
+        if (ref($res) eq 'Apache::lonnavmaps::resource') {
+            $title = $res->compTitle();
+            $encrypted = $res->encrypted();
+        }
+        my ($mapurl, $rid, $resurl) = &Apache::lonnet::decode_symb($symb);
+        my $shownurl = &Apache::lonnet::clutter($resurl);
+        my $shownsymb = $symb;
+        if ($encrypted) {
+            $shownurl = &Apache::lonenc::encrypted($shownurl);
+            $shownsymb = &Apache::lonenc::encrypted($symb);
+        }
+        my $numparts = scalar(keys(%{$cathashref->{$symb}}));
+        my $rowspan;
+        if ($numparts > 1) {
+            $rowspan = ' rowspan="'.$numparts.'"';
+        }
+        $output .= &Apache::loncommon::start_data_table_row().
+                   '<td'.$rowspan.'><a href="'.&HTML::Entities::encode($shownurl,'<>&"').
+                   '?symb='.&HTML::Entities::encode($shownsymb,'<>&"').'">'.
+                   $title.'</a></td>';
+        my $count = 1;
+        foreach my $part (sort(keys(%{$cathashref->{$symb}}))) {
+            my $message;
+            if ($category eq 'graded') {
+                my $problemstatus;
+                if (ref($res) eq 'Apache::lonnavmaps::resource') {
+                    $problemstatus = $res->problemstatus($part);
+                }
+                $message = &grade_problem($symb,$part,$problemstatus,$cathashref->{$symb}{$part});
+            } else {
+                $message = $cathashref->{$symb}{$part};
+            }
+            if (($numparts > 1) && ($count > 1)) {
+                $output .= &Apache::loncommon::end_data_table_row().
+                           &Apache::loncommon::continue_data_table_row();
+            }
+            if ($showparts) {
+                $output .= '<td>';
+                unless ($part eq '0') {
+                    $output .= &mt('(Part: [_1])',$part);
+                }
+                $output .= '</td>';
+            }
+            $output .= '<td>'.$message.'</td>';
+            $count ++;
+        }
+        $output .= &Apache::loncommon::end_data_table_row();
+    }
+    $output .= &Apache::loncommon::end_data_table();
+    return $output;
+}
 
-    return ($title, $grade);
+sub grade_problem {
+    my ($symb,$part,$problemstatus,$to_grade) = @_;
+    return unless (ref($to_grade) eq 'HASH');
+    my %problem = %{$to_grade};
+    &Apache::loncommon::ssi_with_retries(&Apache::lonnet::clutter($problem{'resource'}), 5,%problem);
+    my %record = &Apache::lonnet::restore($symb);
+    my $award = $record{"resource.$part.award"};
+    my $awarded = $record{"resource.$part.awarded"};
+    my $solved = $record{"resource.$part.solved"};
+    my $previous = $record{"resource.$part.previous"};
+    my $awardmsg = $record{"resource.$part.awardmsg"};
+    my $latefrac = $record{"resource.$part.latefrac"};
+    my ($showbutton,$css_class,$message,$previousmsg);
+    if ($award ne '' || $solved ne '') {
+        $Apache::inputtags::part = $part;
+        &Apache::lonhomework::set_show_problem_status($problemstatus);
+        ($showbutton,$css_class,$message,$previousmsg) =
+            &Apache::inputtags::decideoutput($award,$awarded,$awardmsg,$solved,$previous,
+                                             'web',1,'',$latefrac,1,$symb);
+        &Apache::lonhomework::reset_show_problem_status();
+        undef($Apache::inputtags::part);
+        if (($award eq 'INCORRECT') ||
+            (($solved =~ /^correct/) && ($awarded == 0)) ||
+            (($solved =~ /^incorrect/ || $solved eq '') &&
+            ($award eq 'EXACT_ANS' || $award eq 'APPROX_ANS'))) {
+            unless ($problemstatus =~ /^no/) {
+                $message = &mt('You are incorrect.');
+            }
+        }
+    } else {
+        return &mt('No information to report.');
+    }
+    return '<span class="'.$css_class.'" style="padding: 3px;">'.$message.'</span>';
 }
 
 sub pdfforms_blocked {
@@ -635,24 +805,6 @@
     return;
 }
 
-sub parse_grade_answer {
-    my ($shortcut) = @_;
-    my %answerhash = ('EXACT_ANS' => &mt('You are correct.'),
-                      'APPROX_ANS' => &mt('You are correct.'),
-                      'INCORRECT' => &mt('You are incorrect'),
-                      'DRAFT' => &mt('Copy saved but not submitted.'),
-    );
-
-    foreach my $key (keys(%answerhash)) {
-        if($shortcut eq $key) {
-            return $answerhash{$shortcut};
-        }  
-    }
-    return &mt('See course contents for further information.');
-
-}
-
-
 sub dumpenv  {
     my $r = shift;
 
Index: loncom/interface/printout.pl
diff -u loncom/interface/printout.pl:1.181 loncom/interface/printout.pl:1.182
--- loncom/interface/printout.pl:1.181	Thu Mar 12 13:25:47 2026
+++ loncom/interface/printout.pl	Thu Mar 26 02:49:57 2026
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 # CGI-script to run LaTeX, dvips, ps2ps, ps2pdf etc.
 #
-# $Id: printout.pl,v 1.181 2026/03/12 13:25:47 raeburn Exp $
+# $Id: printout.pl,v 1.182 2026/03/26 02:49:57 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -582,9 +582,9 @@
                       }
                   }
                   if ($pdfFormFields eq 'yes') {
-                      $comma = "gs -sDEVICE=pdfwrite -dUNROLLFORMS ";
+                      $comma = "gs -sDEVICE=pdfwrite -dUNROLLFORMS -dCompatibilityLevel=1.3";
                       if (($major > 9) || (($major == 9) && ($minor >= 50))) {
-                          $comma .= '--permit-file-read=* ';
+                          $comma .= ' --permit-file-read=*';
                       }
                       &busy_wait_command("$comma -o $pdf_file $new_name_file 2>/dev/null 1>/dev/null",
                                          "for $status_statement now Converting PS to PDF",
@@ -711,7 +711,7 @@
                   $ps_file=$new_ps_file;
                   $comma = 'ps2pdf13';
                   if ($pdfFormFields eq 'yes') {
-                      $comma = 'ps2pdf -dCompatibilityLevel=1.5';
+                      $comma = 'ps2pdf -dCompatibilityLevel=1.3';
                   }
 		  &busy_wait_command("$comma $ps_file $pdf_file 1>/dev/null 2>/dev/null",
 				     "for $status_statement now Converting PS to PDF",
Index: loncom/homework/inputtags.pm
diff -u loncom/homework/inputtags.pm:1.375 loncom/homework/inputtags.pm:1.376
--- loncom/homework/inputtags.pm:1.375	Tue Mar 17 19:59:25 2026
+++ loncom/homework/inputtags.pm	Thu Mar 26 02:49:58 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # input definitions
 #
-# $Id: inputtags.pm,v 1.375 2026/03/17 19:59:25 raeburn Exp $
+# $Id: inputtags.pm,v 1.376 2026/03/26 02:49:58 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -302,7 +302,8 @@
     } elsif ($target eq 'tex') {
 	my $number_of_lines = &Apache::lonxml::get_param('rows',$parstack,$safeeval);
 	my $width_of_box = &Apache::lonxml::get_param('cols',$parstack,$safeeval);
-	if ($$tagstack[-2] eq 'essayresponse' and $Apache::lonhomework::type eq 'exam') {
+	if ((($$tagstack[-2] eq 'essayresponse') or ($$tagstack[-2] eq 'externalresponse'))
+            and ($Apache::lonhomework::type eq 'exam')) {
 	    $result = '\fbox{\fbox{\parbox{\textwidth-5mm}{';
 	    for (my $i=0;$i<int $number_of_lines*2;$i++) {$result.='\strut \\\\ ';}
 	    $result.='\strut \\\\\strut \\\\\strut \\\\\strut \\\\}}}';
@@ -1100,7 +1101,8 @@
 }
 
 sub decideoutput {
-    my ($award,$awarded,$awardmsg,$solved,$previous,$target,$nocorrect,$tdclass,$latefrac)=@_;
+    my ($award,$awarded,$awardmsg,$solved,$previous,$target,$nocorrect,
+        $tdclass,$latefrac,$plaintext,$symb)=@_;
 
     my $message='';
     my $button=0;
@@ -1148,9 +1150,13 @@
 	    $added_computer_text=1;
 	} else {
 	    if ($target eq 'tex') {
-		$message = '\textbf{'.$message.'}';
+                unless ($plaintext) {
+                    $message = '\textbf{'.$message.'}';
+                }
 	    } else {
-		$message = "<b>".$message."</b>";
+                unless ($plaintext) {
+                    $message = "<b>".$message."</b>";
+                }
                 if ($computer) {
                     $message = "$computer $message";
                 }
@@ -1159,17 +1165,19 @@
 	    if ($awarded > 0) {
                 if ((($latefrac ne '') && ($latefrac >= 0) && ($latefrac < 1)) &&
                     ($target eq 'web')) {
-                    $message.= '<br /><span style="font-style: italic">'.
+                    $message.= ($plaintext?' ':'<br />').'<span style="font-style: italic">'.
                                &mt('Submitted late -- score reduced').
                                '</span>';
                 }
-		my ($symb) = &Apache::lonnet::whichuser();
+                if ($symb eq '') {
+                    ($symb) = &Apache::lonnet::whichuser();
+                }
 		if (($symb ne '') 
 		    &&
 		    ($env{'course.'.$env{'request.course.id'}.
 			      '.disable_receipt_display'} ne 'yes') &&
                     ($Apache::lonhomework::type ne 'practice')) { 
-		    $message.=(($target eq 'web')?'<br />':' ').
+		    $message.=((($target eq 'web') && (!$plaintext))?'<br />':' ').
 			&mt('Your receipt no. is [_1]',
 			    (&Apache::lonnet::receipt($Apache::inputtags::part).
 			     (($target eq 'web')?&Apache::loncommon::help_open_topic('Receipt'):'')));
@@ -1185,11 +1193,16 @@
         }
 	$previousmsg='';
     } elsif ($solved =~ /^excused/) {
-	if ($target eq 'tex') {
-	    $message = ' \textbf{'.&mt('You are excused from the problem.').'} ';
-	} else {
-	    $message = "<b>".&mt('You are excused from the problem.')."</b>";
-	}
+        $message = &mt('You are excused from the problem.');
+        unless ($plaintext) {
+            if ($target eq 'tex') {
+                $message = " \textbf{$message} ";
+            } else {
+                unless ($plaintext) {
+                    $message = "<b>$message</b>";
+                }
+            }
+        }
 	$css_class=$possible_class{'charged_try'};
 	$button=0;
 	$previousmsg='';
@@ -1199,20 +1212,25 @@
 	    $css_class=$possible_class{'charged_try'};
 	    $button=1;
 	} else {
-	    if ($target eq 'tex') {
-		$message = '\textbf{'.&mt('You are correct.').'}';
-	    } else {
-		$message = "<b>".&mt('You are correct.')."</b>";
+            $message = &mt('You are correct.');
+            if ($target eq 'tex') {
+                unless ($plaintext) {
+                    $message = '\textbf{'.$message.'}';
+                }
+            } else {
+                unless ($plaintext) {
+                    $message = "<b>$message</b>";
+                }
                 if ($computer) {
                     $message = "$computer $message";
                 }
-	    }
+            }
 	    $added_computer_text=1;
 	    if  ($awarded > 0 
 		 && $env{'course.'.
 			     $env{'request.course.id'}.
 			     '.disable_receipt_display'} ne 'yes') { 
-		$message.=(($target eq 'web')?'<br />':' ').
+		$message.=((($target eq 'web') && (!$plaintext))?'<br />':' ').
 		    &mt('Your receipt is [_1]',
 			(&Apache::lonnet::receipt($Apache::inputtags::part).
 			 (($target eq 'web')?&Apache::loncommon::help_open_topic('Receipt'):'')));


More information about the LON-CAPA-cvs mailing list