--- loncom/lti/ltiutils.pm	2024/02/27 03:55:55	1.20
+++ loncom/lti/ltiutils.pm	2024/02/27 04:04:06	1.21
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
-# Utility functions for managing LON-CAPA LTI interactions 
+# Utility functions for managing LON-CAPA LTI interactions
 #
-# $Id: ltiutils.pm,v 1.20 2024/02/27 03:55:55 raeburn Exp $
+# $Id: ltiutils.pm,v 1.21 2024/02/27 04:04:06 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -54,13 +54,13 @@ use LONCAPA qw(:DEFAULT :match);
 # When LON-CAPA is operating as a Consumer, nonce checking
 # occurs when a Tool Provider launched from an instance of
 # an external tool in a LON-CAPA course makes a request to
-# (a) /adm/service/roster or (b) /adm/service/passback to, 
-# respectively, retrieve a roster or store the grade for 
+# (a) /adm/service/roster or (b) /adm/service/passback to,
+# respectively, retrieve a roster or store the grade for
 # the original launch by a specific user.
 #
-# When LON-CAPA is operating as a Provider, nonce checking 
-# occurs when a user in course context in another LMS (the 
-# Consumer) launches an external tool to access a LON-CAPA URL: 
+# When LON-CAPA is operating as a Provider, nonce checking
+# occurs when a user in course context in another LMS (the
+# Consumer) launches an external tool to access a LON-CAPA URL:
 # /adm/lti/ with LON-CAPA symb, map, or deep-link ID appended.
 #
 
@@ -98,8 +98,8 @@ sub check_nonce {
 # LON-CAPA as LTI Consumer
 #
 # Determine the domain and the courseID of the LON-CAPA course
-# for which access is needed by a Tool Provider -- either to 
-# retrieve a roster or store the grade for an instance of an 
+# for which access is needed by a Tool Provider -- either to
+# retrieve a roster or store the grade for an instance of an
 # external tool in the course.
 #
 
@@ -144,8 +144,8 @@ sub get_loncapa_course {
 #
 # LON-CAPA as LTI Consumer
 #
-# Determine the symb and (optionally) LON-CAPA user for an 
-# instance of an external tool in a course -- either to 
+# Determine the symb and (optionally) LON-CAPA user for an
+# instance of an external tool in a course -- either to
 # to retrieve a roster or store a grade.
 #
 # Use the digested symb to lookup the real symb in exttools.db
@@ -157,7 +157,7 @@ sub get_tool_instance {
     my ($cdom,$cnum,$digsymb,$diguser,$errors) = @_;
     return unless (ref($errors) eq 'HASH');
     my ($marker,$symb,$uname,$udom);
-    my @keys = ($digsymb); 
+    my @keys = ($digsymb);
     if ($diguser) {
         push(@keys,$diguser);
     }
@@ -188,15 +188,15 @@ sub get_tool_instance {
 # LON-CAPA as LTI Consumer
 #
 # Retrieve data needed to validate a request from a Tool Provider
-# for a roster or to store a grade for an instance of an external 
+# for a roster or to store a grade for an instance of an external
 # tool in a LON-CAPA course.
 #
-# Retrieve the Consumer key and Consumer secret from the domain 
+# Retrieve the Consumer key and Consumer secret from the domain
 # configuration or the Tool Provider ID stored in the
 # exttool_$marker db file and compare the Consumer key with the
 # one in the POSTed data.
 #
-# Side effect is to populate the $toolsettings hashref with the 
+# Side effect is to populate the $toolsettings hashref with the
 # contents of the .db file (instance of tool in course) and the
 # $ltitools hashref with the configuration for the tool (at
 # domain level).
@@ -309,7 +309,7 @@ sub verify_request {
 
 sub verify_lis_item {
     my ($sigrec,$context,$digsymb,$diguser,$cdom,$cnum,$toolsettings,$ltitools,$errors) = @_;
-    return unless ((ref($toolsettings) eq 'HASH') && (ref($ltitools) eq 'HASH') && 
+    return unless ((ref($toolsettings) eq 'HASH') && (ref($ltitools) eq 'HASH') &&
                    (ref($errors) eq 'HASH'));
     my ($has_action, $valid_for);
     if ($context eq 'grade') {
@@ -330,7 +330,7 @@ sub verify_lis_item {
             my $expected_sig;
             if ($context eq 'grade') {
                 my $uniqid = $digsymb.':::'.$diguser.':::'.$cdom.'_'.$cnum;
-                $expected_sig = (split(/:::/,&get_service_id($secret,$uniqid)))[0]; 
+                $expected_sig = (split(/:::/,&get_service_id($secret,$uniqid)))[0];
                 if ($expected_sig eq $sigrec) {
                     return 1;
                 } else {
@@ -338,7 +338,7 @@ sub verify_lis_item {
                 }
             } elsif ($context eq 'roster') {
                 my $uniqid = $digsymb.':::'.$cdom.'_'.$cnum;
-                $expected_sig = (split(/:::/,&get_service_id($secret,$uniqid)))[0]; 
+                $expected_sig = (split(/:::/,&get_service_id($secret,$uniqid)))[0];
                 if ($expected_sig eq $sigrec) {
                     return 1;
                 } else {
@@ -358,9 +358,9 @@ sub verify_lis_item {
 # LON-CAPA as LTI Consumer
 #
 # Sign a request used to launch an instance of an external
-# tool in a LON-CAPA course, using the key and secret supplied 
+# tool in a LON-CAPA course, using the key and secret supplied
 # by the Tool Provider.
-# 
+#
 
 sub sign_params {
     my ($url,$key,$secret,$paramsref,$sigmethod,$type,$callback,$post) = @_;
@@ -417,12 +417,12 @@ sub get_service_id {
 # grade store). An existing secret past its expiration date
 # will be stored as old<service name>secret, and a new secret
 # <service name>secret will be stored.
-# 
-# Secrets are specific to service name and to the tool instance 
+#
+# Secrets are specific to service name and to the tool instance
 # (and are stored in the exttool_$marker db file).
-# The time period a secret remains valid is determined by the 
+# The time period a secret remains valid is determined by the
 # domain configuration for the specific tool and the service.
-# 
+#
 
 sub set_service_secret {
     my ($cdom,$cnum,$marker,$name,$now,$toolsettings,$ltitools) = @_;
@@ -472,7 +472,7 @@ sub set_service_secret {
 #
 # LON-CAPA as LTI Consumer
 #
-# Add a lock key to exttools.db for the instance of an external tool 
+# Add a lock key to exttools.db for the instance of an external tool
 # when generating and storing a service secret.
 #
 
@@ -539,7 +539,7 @@ sub parse_grade_xml {
                 my ($text) = @_;
                 if ("@state" eq "imsx_POXEnvelopeRequest imsx_POXBody replaceResultRequest resultRecord sourcedGUID sourcedId") {
                     $data{$count}{sourcedid} = $text;
-                } elsif ("@state" eq "imsx_POXEnvelopeRequest imsx_POXBody replaceResultRequest resultRecord result resultScore textString") {                               
+                } elsif ("@state" eq "imsx_POXEnvelopeRequest imsx_POXBody replaceResultRequest resultRecord result resultScore textString") {
                     $data{$count}{score} = $text;
                 }
             }, "dtext"],
@@ -773,10 +773,10 @@ sub send_grade {
         );
         my %info = (
                         method => $sigmethod,
-                   ); 
+                   );
         my ($status,$hashref) =
             &Apache::lonnet::sign_lti($cdom,$cnum,$crsdef,$type,'grade',$url,$ltinum,$keynum,
-                                      \%ltiparams,\%info);   
+                                      \%ltiparams,\%info);
         if (($status eq 'ok') && (ref($hashref) eq 'HASH')) {
             $request=new HTTP::Request('POST',$url);
             $request->content(join('&',map {
@@ -802,15 +802,15 @@ sub send_grade {
   <imsx_POXBody>
     <replaceResultRequest>
       <resultRecord>
-	<sourcedGUID>
-	  <sourcedId>$id</sourcedId>
-	</sourcedGUID>
-	<result>
-	  <resultScore>
-	    <language>en</language>
-	    <textString>$score</textString>
-	  </resultScore>
-	</result>
+        <sourcedGUID>
+          <sourcedId>$id</sourcedId>
+        </sourcedGUID>
+        <result>
+          <resultScore>
+            <language>en</language>
+            <textString>$score</textString>
+          </resultScore>
+        </result>
       </resultRecord>
     </replaceResultRequest>
   </imsx_POXBody>
@@ -834,13 +834,13 @@ END
             &Apache::lonnet::sign_lti($cdom,$cnum,$crsdef,$type,'grade',$url,$ltinum,$keynum,\%params,\%info);
         if (($status eq 'ok') && ($authheader ne '')) {
             $request = HTTP::Request->new(
-	                   $reqmethod,
-	                   $url,
-	                   [
-		              'Authorization' => $authheader,
-		              'Content-Type'  => 'application/xml',
-	                   ],
-	                   $gradexml,
+                           $reqmethod,
+                           $url,
+                           [
+                              'Authorization' => $authheader,
+                              'Content-Type'  => 'application/xml',
+                           ],
+                           $gradexml,
             );
             my $response = &LONCAPA::LWPReq::makerequest('',$request,'','',10);
             my $message=$response->status_line;
@@ -864,7 +864,7 @@ sub setup_logout_callback {
             my %info = (
                 respfmt => 'to_post_body',
             );
-            my ($status,$post) = 
+            my ($status,$post) =
                 &Apache::lonnet::sign_lti($cdom,$cnum,$crstool,'lti','logout',$service_url,$idx,
                                           $keynum,\%ltiparams,\%info);
             if (($status eq 'ok') && ($post ne '')) {
@@ -880,7 +880,7 @@ sub setup_logout_callback {
 #
 # LON-CAPA as LTI Provider
 #
-# Create a new user in LON-CAPA. If the domain's configuration 
+# Create a new user in LON-CAPA. If the domain's configuration
 # includes rules for format of "official" usernames, those rules
 # will apply when determining if a user is to be created.  In
 # additional if institutional user information is available that
@@ -1021,7 +1021,7 @@ sub create_passwd {
 # in the Consumer, user privs will be added to the user's environment for
 # the new role.
 #
-# If this is a self-enroll case, a Course Coordinator role will only be assigned 
+# If this is a self-enroll case, a Course Coordinator role will only be assigned
 # if the current user is also the course owner.
 #
 
@@ -1065,8 +1065,8 @@ sub enrolluser {
 # with LTI Instructor status.
 #
 # A list of users is obtained by a call to get_roster()
-# if the calling Consumer support the LTI extension: 
-# Context Memberships Service. 
+# if the calling Consumer support the LTI extension:
+# Context Memberships Service.
 #
 # If a user included in the retrieved list does not currently
 # have a user account in LON-CAPA, an account will be created.
@@ -1313,7 +1313,7 @@ sub batchaddroster {
 #
 # Which LON-CAPA roles are assignable by the current user
 # and how LTI roles map to LON-CAPA roles (as defined in
-# the domain configuration for the specific Consumer) are 
+# the domain configuration for the specific Consumer) are
 # factored in when compiling the list of available roles.
 #
 # Inputs: 3
@@ -1370,10 +1370,10 @@ sub get_lc_roles {
 # LON-CAPA as LTI Provider
 #
 # Compares current start and dates for a user's role
-# with dates to apply for the same user/role to 
+# with dates to apply for the same user/role to
 # determine if there is a change between the current
 # ones and the updated ones.
-# 
+#
 
 sub datechange_check {
     my ($oldstart,$oldend,$startdate,$enddate) = @_;