[LON-CAPA-cvs] cvs: loncom /homework inputtags.pm matchresponse.pm optionresponse.pm radiobuttonresponse.pm rankresponse.pm /interface lonpdfupload.pm /xml lonxml.pm

raeburn raeburn at source.lon-capa.org
Sat Feb 21 11:03:58 EST 2026


raeburn		Sat Feb 21 16:03:58 2026 EDT

  Modified files:              
    /loncom/homework	inputtags.pm optionresponse.pm matchresponse.pm 
                    	rankresponse.pm radiobuttonresponse.pm 
    /loncom/interface	lonpdfupload.pm 
    /loncom/xml	lonxml.pm 
  Log:
  - Bug 6121
    - Support eforms.sty included in 2021-06-19 version of AcroTex bundle, in
      which UTF-16BE text encoding (including initial BOM) is used for form
      field names.
    - Escape underscores and ampersands in form field names.
    - Avoid potentially problematic characters including .) in form field
      names by always using encrypted symb as first item in field name.
    - Include &user_<hashed uname:udom> in each form field name and check
      against hashed uname:dom from %env when processing uploaded PDF form. 
    - Field name stem defined outside foils loop (approriate $temp appended 
      in loop for each foil).
    - Include ID_$input_id as last item in form field name for textline and
      textarea to support multiple ordered submissions for a single response
      item.
  
  
-------------- next part --------------
Index: loncom/homework/inputtags.pm
diff -u loncom/homework/inputtags.pm:1.373 loncom/homework/inputtags.pm:1.374
--- loncom/homework/inputtags.pm:1.373	Fri Feb  6 22:01:31 2026
+++ loncom/homework/inputtags.pm	Sat Feb 21 16:03:57 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # input definitions
 #
-# $Id: inputtags.pm,v 1.373 2026/02/06 22:01:31 raeburn Exp $
+# $Id: inputtags.pm,v 1.374 2026/02/21 16:03:57 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -50,11 +50,12 @@
 =cut
 
 package Apache::inputtags;
-use HTML::Entities();
 use strict;
+use HTML::Entities();
 use Apache::loncommon;
 use Apache::lonhtmlcommon;
 use Apache::lonlocal;
+use Apache::lonxml;
 use Apache::lonnet;
 use LONCAPA;
  
@@ -201,7 +202,7 @@
 sub start_textfield {
     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
     my $result = "";
-    my $id = &start_input($parstack,$safeeval);
+    my $input_id = &start_input($parstack,$safeeval);
     my $resid=$Apache::inputtags::response[-1];
     if ($target eq 'web') {
 	$Apache::lonxml::evaluate--;
@@ -307,19 +308,18 @@
 	    $result.='\strut \\\\\strut \\\\\strut \\\\\strut \\\\}}}';
 	} else {
             if ($env{'form.pdfFormFields'} eq 'yes') {
-                my $prefix = &Apache::lonenc::check_encrypt($env{'request.symb'});
-                $prefix =~ s{^/}{};
-                unless ($prefix =~ /^(uploaded|enc)/) {
-                    $prefix = 'res/'.$prefix;
-                }
+                my $fieldname = &Apache::lonenc::encrypted($env{'request.symb'},1);
                 unless ($env{'request.symb'} =~ /^uploaded/) {
-                    $prefix .= '&'.$env{'request.course.id'};
-                }
-                my $fieldname = $prefix.
-                                '&part_'. $Apache::inputtags::part.
-                                '&textresponse'.
-                                '&HWVAL_' . $Apache::inputtags::response['-1'];
-                $result.='\TextField[name='.$fieldname.',multiline=true,height=6\baselineskip,width=270,borderwidth=0,backgroundcolor={.85 .85 .85}]\\';
+                    my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+                    my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+                    $fieldname .= '&'.$cdom.'&'.$cnum;
+                }
+                $fieldname .= '&user_'. &Apache::lonxml::get_user_digest().
+                              '&part_'. $Apache::inputtags::part.
+                              '&textresponse'.
+                              '&HWVAL_' . $resid.
+                              '&ID_'. $input_id;
+                $result.='\TextField[name='.$fieldname.',multiline=true,height=6\baselineskip,width=270pt,borderwidth=0,backgroundcolor={.85 .85 .85}]\\';
             } else {
                 my $TeXwidth=$width_of_box/80;
                 $result = '\vskip 1 mm \fbox{\fbox{\parbox{'.$TeXwidth.'\textwidth-5mm}{';
@@ -477,7 +477,7 @@
 		}
 		my $name = 'HWVAL_'.$id;
                 my $itemid = 'HWVAL_'.$partid.'_'.$id;
-                # NOTE: the input id should match the one given by defaut_homework input_id().
+                # NOTE: the input id should match the one given by default_homework input_id().
                 my $input_tag_id = $itemid.'_'.$input_id;
 		if ($Apache::inputtags::status[-1] eq 'CANNOT_ANSWER') {
 		    $name = "none";
@@ -536,18 +536,20 @@
 	if ($size != 0) {$size=$size*2; $size.=' mm';} else {$size='40 mm';}
 	if ($env{'form.pdfFormFields'} eq 'yes'
             && $Apache::inputtags::status[-1] eq 'CAN_ANSWER') {
-            my $prefix = &Apache::lonenc::check_encrypt($env{'request.symb'});
-            $prefix =~ s{^/}{};
-            unless ($prefix =~ /^(uploaded|enc)/) {
-                $prefix = 'res/'.$prefix;
-            }
+            my $fieldname = &Apache::lonenc::encrypted($env{'request.symb'},1);
             unless ($env{'request.symb'} =~ /^uploaded/) {
-                $prefix .= '&'.$env{'request.course.id'};
-            }
-            my $fieldname = $prefix.
-                                 '&part_'. $Apache::inputtags::part.
-                                 '&textresponse'.
-                                 '&HWVAL_' . $Apache::inputtags::response['-1'];
+                my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+                my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+                $fieldname .= '\&'.$cdom.'\&'.$cnum;
+            }
+            $fieldname .= '\&user\_'.&Apache::lonxml::get_user_digest();
+            my $escpart = $Apache::inputtags::part;
+            $escpart =~ s{_}{\\_}g;
+            my $escres = $Apache::inputtags::response[-1];
+            $escres =~ s{_}{\\_}g;
+            my $escid = $input_id;
+            $escid =~ s{_}{\\_}g;
+            $fieldname .= '\&part\_'.$escpart.'\&textresponse\&HWVAL\_'.$escres.'\&ID\_'.$escid;
             $result='\textField{'.$fieldname.'}{'.$size.'}{12 bp}';
         } else {
             $result='\framebox['.$size.'][s]{\tiny\strut}';
Index: loncom/homework/optionresponse.pm
diff -u loncom/homework/optionresponse.pm:1.205 loncom/homework/optionresponse.pm:1.206
--- loncom/homework/optionresponse.pm:1.205	Fri Feb  6 22:01:31 2026
+++ loncom/homework/optionresponse.pm	Sat Feb 21 16:03:57 2026
@@ -1,7 +1,7 @@
 # LearningOnline Network with CAPA
 # option list style responses
 #
-# $Id: optionresponse.pm,v 1.205 2026/02/06 22:01:31 raeburn Exp $
+# $Id: optionresponse.pm,v 1.206 2026/02/21 16:03:57 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -735,17 +735,21 @@
                 ($checkboxchoices?&mt('Choices: ').'<b>'.$opt[0].','.$opt[1].'</b>. ':'').
                  &mt('Select all that are [_1].','<b>'.$checkboxopt.'</b>');
     }
-    my $prefix;
+    my $fieldname;
     if ($target eq 'tex' and $env{'form.pdfFormFields'} eq 'yes'
          && $Apache::inputtags::status[-1] eq 'CAN_ANSWER') {
-        $prefix = &Apache::lonenc::check_encrypt($env{'request.symb'});
-        $prefix =~ s{^/}{};
-        unless ($prefix =~ /^(uploaded|enc)/) {
-            $prefix = 'res/'.$prefix;
-        }
+        $fieldname = &Apache::lonenc::encrypted($env{'request.symb'},1);
         unless ($env{'request.symb'} =~ /^uploaded/) {
-            $prefix .= '&'.$env{'request.course.id'};
+            my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+            my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+            $fieldname .= '\&'.$cdom.'\&'.$cnum;
         }
+        $fieldname .= '\&user\_'.&Apache::lonxml::get_user_digest();
+        my $escpart = $Apache::inputtags::part;
+        $escpart =~ s{_}{\\_}g;
+        my $escres = $Apache::inputtags::response['-1'];
+        $escres =~ s{_}{\\_}g;
+        $fieldname .= '\&part\_'.$escpart.'\&optionresponse\&HWVAL\_'.$escres.':';
     }
     foreach $name (@whichopt) {
       if ($target eq 'web') {
@@ -762,8 +766,7 @@
 
       if($target eq 'tex' and $env{'form.pdfFormFields'} eq 'yes'
          && $Apache::inputtags::status[-1] eq 'CAN_ANSWER') {
-          my $fieldname = $prefix.'&part_'.$Apache::inputtags::part.'&optionresponse'.'&HWVAL_'.$Apache::inputtags::response['-1'].':'.$temp;
-          $optionlist =  &Apache::lonxml::print_pdf_start_combobox($fieldname);
+          $optionlist =  &Apache::lonxml::print_pdf_start_combobox($fieldname.$temp);
       }
 
       foreach my $option (@opt) {
Index: loncom/homework/matchresponse.pm
diff -u loncom/homework/matchresponse.pm:1.98 loncom/homework/matchresponse.pm:1.99
--- loncom/homework/matchresponse.pm:1.98	Fri Feb  6 22:01:31 2026
+++ loncom/homework/matchresponse.pm	Sat Feb 21 16:03:57 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Full matching style response
 #
-# $Id: matchresponse.pm,v 1.98 2026/02/06 22:01:31 raeburn Exp $
+# $Id: matchresponse.pm,v 1.99 2026/02/21 16:03:57 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -679,18 +679,22 @@
         } else {
             $numrows = 1;
         }
-        my $prefix;
+        my ($fieldname,$escpart);
         if (($Apache::lonhomework::type ne 'exam') &&
             ($target eq 'tex' and $env{'form.pdfFormFields'} eq 'yes'
              && $Apache::inputtags::status[-1] eq 'CAN_ANSWER')) {
-            $prefix = &Apache::lonenc::check_encrypt($env{'request.symb'});
-            $prefix =~ s{^/}{};
-            unless ($prefix =~ /^(uploaded|enc)/) {
-                $prefix = 'res/'.$prefix;
-            }
+            $fieldname = &Apache::lonenc::encrypted($env{'request.symb'},1);
             unless ($env{'request.symb'} =~ /^uploaded/) {
-                $prefix .= '&'.$env{'request.course.id'};
-            }
+                my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+                my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+                $fieldname .= '\&'.$cdom.'\&'.$cnum;
+            }
+            $fieldname .= '\&user\_'.&Apache::lonxml::get_user_digest();
+            my $escres = $Apache::inputtags::response['-1'];
+            $escres =~ s{_}{\\_}g;
+            $escpart = $Apache::inputtags::part;
+            $escpart =~ s{_}{\\_}g;
+            $fieldname .= '\&part\_'.$escpart.'\&matchresponse\&HWVAL\_'.$escres.':';
         }
 	foreach my $name (@whichfoils) {
 	    my $lastopt=$lastresponse{$name};
@@ -702,8 +706,7 @@
 		if ($Apache::lonhomework::type ne 'exam') {
                     if($env{'form.pdfFormFields'} eq 'yes'
                        && $Apache::inputtags::status['-1'] eq 'CAN_ANSWER') {
-                        my $fieldname = $prefix . '&part_'. $Apache::inputtags::part .'&matchresponse'. '&HWVAL_' . $Apache::inputtags::response['-1'] . ':' . $temp . '&submit_' . $Apache::inputtags::part . '&';
-                        $optionlist = &Apache::lonxml::print_pdf_start_combobox($fieldname);
+                        $optionlist = &Apache::lonxml::print_pdf_start_combobox($fieldname.$temp.'\&submit\_'.$escpart.'\&');
                     } else {
 
                         $optionlist='\framebox[10 mm][s]{\tiny\strut}';
Index: loncom/homework/rankresponse.pm
diff -u loncom/homework/rankresponse.pm:1.77 loncom/homework/rankresponse.pm:1.78
--- loncom/homework/rankresponse.pm:1.77	Fri Feb  6 22:01:31 2026
+++ loncom/homework/rankresponse.pm	Sat Feb 21 16:03:57 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # rank style response
 #
-# $Id: rankresponse.pm,v 1.77 2026/02/06 22:01:31 raeburn Exp $
+# $Id: rankresponse.pm,v 1.78 2026/02/21 16:03:57 raeburn Exp $
 # Copyright Michigan State University Board of Trustees
 #
 # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
@@ -372,7 +372,7 @@
 	my $temp=1;
 	my $id=$Apache::inputtags::response[-1];
 	my $part=$Apache::inputtags::part;
-        my ($lastresponse,$newvariation,$prefix);
+        my ($lastresponse,$newvariation);
         if ((($Apache::lonhomework::history{"resource.$part.type"} eq 'randomizetry') ||
              ($Apache::lonhomework::type eq 'randomizetry')) &&
         ($Apache::inputtags::status[-1] eq 'CAN_ANSWER')) {
@@ -403,26 +403,30 @@
         } else {
             $numrows = 1;
         }
+        my $fieldname;
         if($target eq 'tex' && $env{'form.pdfFormFields'} eq 'yes') {
             $result .= '\strut \\\\ \strut \\\\' ;
-            $prefix = &Apache::lonenc::check_encrypt($env{'request.symb'});
-            $prefix =~ s{^/}{};
-            unless ($prefix =~ /^(uploaded|enc)/) {
-                $prefix= 'res/'.$prefix;
-            }
+            $fieldname = &Apache::lonenc::encrypted($env{'request.symb'},1);
             unless ($env{'request.symb'} =~ /^uploaded/) {
-                $prefix .= '&'.$env{'request.course.id'};
+                my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+                my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+                $fieldname .= '\&'.$cdom.'\&'.$cnum;
             }
+            $fieldname .= '\&user\_'.&Apache::lonxml::get_user_digest(); 
+            my $escres = $id;
+            $escres =~ s{_}{\\_}g;
+            my $escpart = $part;
+            $escpart =~ s{_}{\\_}g;
+            $fieldname .= '\&part\_'.$escpart.'\&rankresponse\&HWVAL\_'.$escres.':';
         }
         foreach my $name (@whichfoils) {
 	    my $lastopt=$lastresponse{$name};
 	    my $optionlist='';
 	    if ($target ne 'tex') {
                 $optionlist = "<option value=\"\"></option>\n";
-            } 
+            }
             if ($target eq 'tex' && $env{'form.pdfFormFields'} eq 'yes') {
-                my $fieldname = $prefix.'&part_'.$Apache::inputtags::part.'&rankresponse'.'&HWVAL_'.$Apache::inputtags::response['-1'].':'.$temp;
-                $optionlist =  &Apache::lonxml::print_pdf_start_combobox($fieldname);
+                $optionlist =  &Apache::lonxml::print_pdf_start_combobox($fieldname.$temp);
             }
 	    my $option;
 	    foreach $option (@whichopt) {
Index: loncom/homework/radiobuttonresponse.pm
diff -u loncom/homework/radiobuttonresponse.pm:1.164 loncom/homework/radiobuttonresponse.pm:1.165
--- loncom/homework/radiobuttonresponse.pm:1.164	Fri Feb  6 22:01:31 2026
+++ loncom/homework/radiobuttonresponse.pm	Sat Feb 21 16:03:57 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
-# mutliple choice style responses
+# multiple choice style responses
 #
-# $Id: radiobuttonresponse.pm,v 1.164 2026/02/06 22:01:31 raeburn Exp $
+# $Id: radiobuttonresponse.pm,v 1.165 2026/02/21 16:03:57 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1129,24 +1129,20 @@
     my $temp = 0;
     my $result;
 
-    my $prefix = &Apache::lonenc::check_encrypt($env{'request.symb'});
-    $prefix =~ s{^/}{};
-    unless ($prefix =~ /^(uploaded|enc)/) {
-        $prefix = 'res/'.$prefix;
-    }
+    my $fieldname = &Apache::lonenc::encrypted($env{'request.symb'},1);
     unless ($env{'request.symb'} =~ /^uploaded/) {
-        $prefix .= '&'.$env{'request.course.id'};
-    }
-    $result .= "\\begin{$venv}";
+        my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+        my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+        $fieldname .= '\&'.$cdom.'\&'.$cnum;
+    }
+    $fieldname .= '\&user\_'.&Apache::lonxml::get_user_digest();
+    my $escpart = $Apache::inputtags::part;
+    $escpart =~ s{_}{\\_}g;
+    my $escres = $Apache::inputtags::response['-1'];
+    $escres =~ s{_}{\\_}g;
+    $fieldname .= '\&part\_'.$escpart.'\&radiobuttonresponse\&HWVAL\_'.$escres;
+    $result = "\\begin{$venv}";
     foreach my $name ( @{$whichfoils} ) {
-	
-	my $fieldname =
-	    $prefix
-	. '&part_'
-	    . $Apache::inputtags::part
-	    . '&radiobuttonresponse'
-	    . '&HWVAL_'
-	    . $Apache::inputtags::response['-1'];
 	$result .= '\item[{'
 	    . &Apache::lonxml::print_pdf_radiobutton( $fieldname,
 						      $temp )
Index: loncom/interface/lonpdfupload.pm
diff -u loncom/interface/lonpdfupload.pm:1.30 loncom/interface/lonpdfupload.pm:1.31
--- loncom/interface/lonpdfupload.pm:1.30	Sat Feb  7 00:00:21 2026
+++ loncom/interface/lonpdfupload.pm	Sat Feb 21 16:03:57 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # PDF Form Upload Handler
 #
-# $Id: lonpdfupload.pm,v 1.30 2026/02/07 00:00:21 raeburn Exp $
+# $Id: lonpdfupload.pm,v 1.31 2026/02/21 16:03:57 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -37,6 +37,8 @@
 use Apache::lonlocal;
 use File::MMagic;
 use CAM::PDF;
+use Encode::Guess;
+use Digest::SHA();
 use LONCAPA qw(:DEFAULT :match);
 
 use strict;
@@ -158,7 +160,9 @@
        '<h2 class="LC_heading_2">'.&mt('Submit answers via PDF Form upload').'</h2>'
        .'<p>'.&mt('This course allows you to submit your answers to assignments by uploading a PDF containing completed form fields.').'</p>'
        .'<ul><li>'.&mt('Your PDF must have been originally downloaded via the print utility (with PDF form fields option selected in the Layout options)').'</li>'
-       .'<li>'.&mt('Submissions for past-due problems will be ignored, unless a grace period is in effect').'</li></ul>'
+       .'<li>'.&mt('Submissions for past-due problems will be ignored, unless a grace period is in effect').'</li>'
+       .'<li>'.&mt('Submissions for problems which are currently inaccessible will be ignored.').'</li>'
+       .'</ul>'
        .'<br />'
        .'<form method="post" enctype="multipart/form-data" onsubmit="return checkFilename(this);" action="">'
        .&Apache::lonhtmlcommon::start_pick_box()
@@ -180,18 +184,19 @@
 }
 
 sub processPDF {
-    my $result = ();  # message for Browser
+    my $result; # message for Browser
     my @pdfdata = (); # answers from PDF-Forms
     
     @pdfdata = &get_pdf_data(); # get answers from PDF-Form
     
     if (scalar @pdfdata) {    
-        &grade_pdf(@pdfdata);
+        $result = &grade_pdf(@pdfdata);
     } else {
-        $result .= '<p class="LC_error">'
-                  .&mt("Can't find any valid PDF form fields.")
-                  .'</p>';
+        $result = '<p class="LC_error">'
+                 .&mt("Can't find any valid PDF form fields.")
+                 .'</p>';
     }
+    return $result;
 }
 
 sub get_pdf_data() {
@@ -208,6 +213,10 @@
             # dot in fieldnames. So a fieldname like "i.am.aFormfield" will offer three fieldnames
             # "i", "i.am" and "i.am.aFormfield". The fragmentary names keep no values and will be ignored.
             if($dict->{'V'}) {
+                my $decoder = Encode::Guess->guess($field);
+                if (ref($decoder)) {
+                    $field = $decoder->decode($field);
+                }
                 push(@data, $field."?". $dict->{'V'}{'value'}); #binding fieldname with value
             }
         }
@@ -217,7 +226,7 @@
 
 sub grade_pdf {
     my @pdfdata = @_;
-    my ($result,$meta,%grades,%problems,%foreigncourse,$debug);
+    my ($result,$meta,%grades,%problems,%foreigncourse,%mismatchuser,%hwvals,$debug);
 
     my $navmap = Apache::lonnavmaps::navmap->new();
     if (!defined($navmap)) {
@@ -249,32 +258,36 @@
                           ,$env{'user.domain'}.':'.$env{'user.name'})
                       .'</p>';
             }
-
-        } elsif (($entry =~ m{^(uploaded|res)/}) || ($entry =~ m{^enc/\d+/}))  {
+        } elsif ($entry =~ m{^/enc/\d+/}) {
             $debug .= 'found: a problem -> '.$entry;
-            my ($cid, $part, $type, $HWVAL);
+            my ($cid, $cdom, $cnum, $digest_user, $part, $type, $HWVAL, $input_id);
             my ($label, $value) = ($entry =~ /^([^?]*)\?(.*)/);
-            my ($symb, $rest) = split('&', $label, 2);
-            if ($symb =~ m{^enc/}) {
-                $symb = &Apache::lonenc::check_decrypt("/$symb");
-            } elsif ($symb =~ m{^res/}) {
-                $symb =~ s{^res/}{};
+            my ($encsymb, $rest) = split('&', $label, 2);
+            my $symb = &Apache::lonenc::unencrypted($encsymb);
+            if (($env{'request.role.adv'}) ||
+                (&Apache::lonnet::EXT('resource.0.encrypturl',$symb) !~ /^yes$/i)) {
+                &Apache::lonenc::reset_enc();
             }
             if ($symb =~ /^uploaded/) {
-                ($part, $type, $HWVAL) = split('&', $rest);
+                ($digest_user, $part, $type, $HWVAL, $input_id) = split('&', $rest);
             } else {
-                ($cid, $part, $type, $HWVAL) = split('&', $rest);
+                ($cdom, $cnum, $digest_user, $part, $type, $HWVAL, $input_id) = split('&', $rest);
+                $cid = $cdom.'_'.$cnum;
             }
             my ($map,$id,$resource)=&Apache::lonnet::decode_symb($symb);
             if ($map =~ m{^uploaded/($match_domain)/($match_courseid)/default(_?\d*)\.(page|sequence)}) {
-                my $mapcid = $1.'_'.$2;
-                if ($mapcid ne $env{'request.course.id'}) {
-                    push(@{$foreigncourse{$mapcid}},$symb);
-                }
-            } elsif ($map =~ m{^($match_domain)/($match_username)/}) {
-                if ($cid ne $env{'request.course.id'}) {
-                    push(@{$foreigncourse{$cid}},$symb);
-                }
+                ($cdom,$cnum) = ($1,$2);
+                $cid = $cdom.'_'.$cnum;
+            }
+            if ($cid ne $env{'request.course.id'}) {
+                push(@{$foreigncourse{$cid}},$symb);
+                next;
+            }
+            $digest_user =~ s/^user_//;
+            if ($digest_user ne
+                &Digest::SHA::sha1_hex(&Encode::decode('UTF-8',$env{'user.name'}.':'.$env{'user.domain'}))) {
+                $mismatchuser{$symb} = 1;
+                next;
             }
             next unless (exists($restitles{$symb}));
             $value =~ s/(.*)\n/$1/; 
@@ -283,18 +296,36 @@
             if ($type eq 'radiobuttonresponse' && $value eq 'Off' ) {
                 next;
             }
- 
+
             my $submit = $part;
             $submit =~ s/part_(.*)/submit_$1/;
             if ($problems{$symb.$part}) {
-                 $problems{$symb.$part}{$HWVAL} = $value;
+                if ($type eq 'textresponse') {
+                    if (ref($problems{$symb.$part}{$HWVAL}) eq 'HASH') {
+                        $problems{$symb.$part}{$HWVAL}{$input_id} = $value;
+                    } else {
+                        my $prevval = $problems{$symb.$part}{$HWVAL};
+                        my $previd = $problems{$symb.$part}{'inputid'};
+                        delete($problems{$symb.$part}{'inputid'});
+                        $problems{$symb.$part}{$HWVAL} = {
+                                                           $previd => $prevval,
+                                                           $input_id => $value,
+                                                         };
+                    }
+                } else {
+                    $problems{$symb.$part}{$HWVAL} = $value;
+                }
             } else {
-                 $problems{$symb.$part} =  { 'resource' => $resource,
+                $problems{$symb.$part} = { 'resource' => $resource,
                                         'symb' => $symb,
                                         'submitted' => $part,
                                         $submit => 'Answer',
                                         $HWVAL => $value};
+                if ($type eq 'textresponse') {
+                    $problems{$symb.$part}{'inputid'} = $input_id;
+                }
             }
+            $hwvals{$symb.$part}{$HWVAL} = 1;
         } else {
             $debug .= 'found: -> '.$entry;
             next;
@@ -314,6 +345,24 @@
 
         foreach my $key (sort(keys(%problems))) {
             my %problem = %{$problems{$key}};
+            if (ref($hwvals{$key}) eq 'HASH') {
+                foreach my $hwval (keys(%{$hwvals{$key}})) {
+                    if (ref($problem{$hwval}) eq 'HASH') {
+                        my %valhash = %{$problem{$hwval}};
+                        my %ordered;
+                        foreach my $innerkey (keys(%valhash)) {
+                            my $tail = (split(/_/,$innerkey))[-1];
+                            $ordered{$tail} = $problem{$hwval}{$innerkey};
+                        }
+                        if (keys(%ordered)) {
+                            $problem{$hwval} = [];
+                            foreach my $digit (sort( { $a <=> $b } keys(%ordered))) {
+                                push(@{$problem{$hwval}},$ordered{$digit});
+                            }
+                        }
+                    }
+                }
+            }
             my ($problemname, $grade) = &grade_problem(%problem);
 
             $result .= &Apache::loncommon::start_data_table_row();
@@ -370,11 +419,22 @@
         }
     }
 
+    if (keys(%mismatchuser)) {
+        $result .= '<div class="LC_warning">'
+                  .&mt('Your uploaded PDF form contained the following resource(s) for a user who is not you:')
+                  .'<ul>'."\n";
+        foreach my $symb (keys(%mismatchuser)) {
+            my ($map,$id,$resource)=&Apache::lonnet::decode_symb($symb);
+            $result .= '<li>'.$resource.'</li>';
+        }
+        $result .= '</ul>';
+    }
+
     return $result;
 }
 
 sub grade_problem {
-    my %problem = @_;
+    my (%problem) = @_;
     my ($title, $part) = ();
 
     &Apache::loncommon::ssi_with_retries('/res/'.$problem{'resource'}, 5, %problem);
@@ -390,7 +450,7 @@
     my %problemhash = &Apache::lonnet::restore($problem{'symb'});
     my $grade = $problemhash{"resource.$part.award"};
 
-    return ($title, $grade);    
+    return ($title, $grade);
 }
 
 sub parse_grade_answer {
Index: loncom/xml/lonxml.pm
diff -u loncom/xml/lonxml.pm:1.578 loncom/xml/lonxml.pm:1.579
--- loncom/xml/lonxml.pm:1.578	Mon Dec 22 20:50:26 2025
+++ loncom/xml/lonxml.pm	Sat Feb 21 16:03:58 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # XML Parser Module
 #
-# $Id: lonxml.pm,v 1.578 2025/12/22 20:50:26 raeburn Exp $
+# $Id: lonxml.pm,v 1.579 2026/02/21 16:03:58 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -75,6 +75,8 @@
 use POSIX qw(strftime);
 use Time::HiRes qw( gettimeofday tv_interval );
 use Symbol();
+use Encode();
+use Digest::SHA();
 
 sub register {
   my ($space, at taglist) = @_;
@@ -2602,6 +2604,19 @@
     return $result;
 }
 
+sub get_user_digest {
+    my ($uname,$udom,$digest_user);
+    if (($env{'form.grade_username'} ne '') && ($env{'form.grade_domain'} ne '')) {
+        $uname = $env{'form.grade_username'};
+        $udom = $env{'form.grade_domain'};
+    } else {
+        $uname = $env{'user.name'};
+        $udom = $env{'user.domain'};
+    }
+    my $digest = &Digest::SHA::sha1_hex(&Encode::decode('UTF-8',$uname.':'.$udom));
+    return $digest;
+}
+
 1;
 __END__
 


More information about the LON-CAPA-cvs mailing list