[LON-CAPA-cvs] cvs: loncom /interface courseprefs.pm

raeburn raeburn at source.lon-capa.org
Mon May 25 22:24:24 EDT 2026


raeburn		Tue May 26 02:24:24 2026 EDT

  Modified files:              
    /loncom/interface	courseprefs.pm 
  Log:
  - WCAG 2.2 compliance for Domain Coordinator's "External Tools (LTI)" config.
    - Replace use of <table> for layout with <div>.
    - Include labels for form elements.
    - Use <th> tags for column headings and row headings in data table
      with scope="row" attribute for latter.
    - For form elements in data table cells use aria-labelledby to reference 
      appropriate column and row headers.
    - Satisfy minimum spacing between touch targets.
  
  
-------------- next part --------------
Index: loncom/interface/courseprefs.pm
diff -u loncom/interface/courseprefs.pm:1.146 loncom/interface/courseprefs.pm:1.147
--- loncom/interface/courseprefs.pm:1.146	Thu May  7 23:08:11 2026
+++ loncom/interface/courseprefs.pm	Tue May 26 02:24:24 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Handler to set configuration settings for a course
 #
-# $Id: courseprefs.pm,v 1.146 2026/05/07 23:08:11 raeburn Exp $
+# $Id: courseprefs.pm,v 1.147 2026/05/26 02:24:24 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -6791,8 +6791,9 @@
                 }
             }
             my $chgstr = ' onchange="javascript:reorderLTITools(this.form,'."'ltitools_".$item."'".');"';
+            my $poslabel = &mt('Set list order for [_1]',$title);
             $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'
-                         .'<select name="ltitools_'.$item.'"'.$chgstr.'>';
+                         .'<select name="ltitools_'.$item.'"'.$chgstr.' aria-label="'.$poslabel.'">';
             for (my $k=0; $k<=$maxnum; $k++) {
                 my $vpos = $k+1;
                 my $selstr;
@@ -6818,8 +6819,8 @@
                 '<option value="HMAC-SHA1"'.$sigsel{'HMAC-SHA1'}.'>HMAC-SHA1</option>'.
                 '<option value="HMAC-SHA256"'.$sigsel{'HMAC-SHA256'}.'>HMAC-SHA256</option></select></span>'.
                 '<br /><br />'.
-                '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="60" name="ltitools_url_'.$i.'"'.
-                ' value="'.$url.'" /></span>'.
+                '<span class="LC_nobreak"><label>'.$lt{'url'}.':<input type="text" size="60" name="ltitools_url_'.$i.'"'.
+                ' value="'.$url.'" /></label></span>'.
                 (' 'x2).
                 '<span class="LC_nobreak"><label>'.$lt{'lifetime'}.':'.
                 '<input type="text" size="5" name="ltitools_lifetime_'.$i.'" value="'.$lifetime.'" /></label></span><br /><br />';
@@ -6840,13 +6841,13 @@
             }
             if ($switchserver) {
                 if ($usable ne '') {
-                    $datatable .= '<div id="ltitools_divcurrsecret_'.$i.'" style="display:inline-block" /><span class="LC_nobreak">'.
+                    $datatable .= '<div id="ltitools_divcurrsecret_'.$i.'" style="display: inline-block;"><span class="LC_nobreak">'.
                                   $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'</span></div>'.
                                   '<span class="LC_nobreak">'.&mt('Change secret?').
                                   '<label><input type="radio" value="0" name="ltitools_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','ltitools'".');" checked="checked"'.$disabled.' />'.&mt('No').'</label>'.
                                   (' 'x2).
                                   '<label><input type="radio" value="1" name="ltitools_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','ltitools'".');" '.$disabled.' />'.&mt('Yes').'</label>'.(' 'x2).
-                                  '</span><div id="ltitools_divchgsecret_'.$i.'" style="display:none" />'.
+                                  '</span><div id="ltitools_divchgsecret_'.$i.'" style="display: none;">'.
                                   '<span class="LC_nobreak"> - '.$switchmessage.'</span>'.
                                   '</div>';
                 } elsif ($key eq '') {
@@ -6857,21 +6858,21 @@
                 $datatable .= '<input type="hidden" name="ltitools_id_'.$i.'" value="'.$item.'" />';
             } else {
                 if ($usable ne '') {
-                    $datatable .= '<div id="ltitools_divcurrsecret_'.$i.'" style="display:inline-block" /><span class="LC_nobreak">'.
+                    $datatable .= '<div id="ltitools_divcurrsecret_'.$i.'" style="display: inline-block;"><span class="LC_nobreak">'.
                                   $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'</span></div>'.
                                   '<span class="LC_nobreak">'.&mt('Change?').
                                   '<label><input type="radio" value="0" name="ltitools_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','ltitools'".');" checked="checked"'.$disabled.' />'.&mt('No').'</label>'.
                                   (' 'x2).
                                   '<label><input type="radio" value="1" name="ltitools_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','ltitools'".');"'.$disabled.' />'.&mt('Yes').
-                                  '</label>  </span><div id="ltitools_divchgsecret_'.$i.'" style="display:none" />'.
-                                  '<span class="LC_nobreak">'.&mt('New Secret').':'.
-                                  '<input type="password" size="20" name="ltitools_secret_'.$i.'" value="" autocomplete="new-password"'.$disabled.' />'.
+                                  '</label>  </span><div id="ltitools_divchgsecret_'.$i.'" style="display: none;">'.
+                                  '<span class="LC_nobreak"><label>'.&mt('New Secret').':'.
+                                  '<input type="password" size="20" name="ltitools_secret_'.$i.'" value="" autocomplete="new-password"'.$disabled.' /></label>'.
                                   ' <label><input type="checkbox" name="ltitools_visible_'.$i.'" id="ltitools_visible_'.$i.'" onclick="if (this.checked) { this.form.ltitools_secret_'.$i.'.type='."'text'".' } else { this.form.ltitools_secret_'.$i.'.type='."'password'".' }"'.$disabled.' />'.&mt('Visible input').'</label>'.
                                   '<input type="hidden" name="ltitools_id_'.$i.'" value="'.$item.'" /></span></div>';
                 } else {
                     $datatable .=
-                        '<span class="LC_nobreak">'.$lt{'secret'}.':'.
-                        '<input type="password" size="20" name="ltitools_secret_'.$i.'" value="" autocomplete="new-password"'.$disabled.' />'.
+                        '<span class="LC_nobreak"><label>'.$lt{'secret'}.':'.
+                        '<input type="password" size="20" name="ltitools_secret_'.$i.'" value="" autocomplete="new-password"'.$disabled.' /></label>'.
                         ' <label><input type="checkbox" name="ltitools_visible_'.$i.'" id="ltitools_visible_'.$i.'" onclick="if (this.checked) { this.form.ltitools_secret_'.$i.'.type='."'text'".' } else { this.form.ltitools_secret_'.$i.'.type='."'password'".' }"'.$disabled.' />'.&mt('Visible input').'</label>'.
                         '<input type="hidden" name="ltitools_id_'.$i.'" value="'.$item.'" /></span>';
                 }
@@ -6910,10 +6911,10 @@
                               (' 'x2);
             }
             $datatable .= '</span><br />'.
-                          '<div class="LC_left_float">'.$lt{'linktext'}.'<br />'.
-                          '<input type="text" name="ltitools_linktext_'.$i.'" size="25" value="'.$currdisp{'linktext'}.'" /></div>'.
-                          '<div class="LC_left_float">'.$lt{'explanation'}.'<br />'.
-                          '<textarea name="ltitools_explanation_'.$i.'" rows="5" cols="30">'.$currdisp{'explanation'}.
+                          '<div class="LC_left_float"><label for="ltitools_linktext_'.$i.'">'.$lt{'linktext'}.'</label><br />'.
+                          '<input type="text" name="ltitools_linktext_'.$i.'" id="ltitools_linktext_'.$i.'" size="25" value="'.$currdisp{'linktext'}.'" /></div>'.
+                          '<div class="LC_left_float"><label for="ltitools_explanation_'.$i.'">'.$lt{'explanation'}.'</label><br />'.
+                          '<textarea name="ltitools_explanation_'.$i.'" id="ltitools_explanation_'.$i.'" rows="5" cols="30">'.$currdisp{'explanation'}.
                           '</textarea></div><div style=""></div><br />';
             my %units = (
                           'passback' => 'days',
@@ -6957,30 +6958,30 @@
                               '<label><input type="radio" name="ltitools_'.$extra.'_'.$i.'" value="1"'.$checkedon.$onclick.' />'.
                               &mt('Yes').'</label></span></div>';
                 if (($extra eq 'returnurl') || ($extra eq 'desturl')) {
-                    $datatable .= '<div class="LC_floatleft" style="display:'.$validsty.';" id="ltitools_course'.$extra.'_'.$i.'">'.
+                    $datatable .= '<div class="LC_floatleft" style="display: '.$validsty.';" id="ltitools_course'.$extra.'_'.$i.'">'.
                                   '<span class="LC_nobreak"> -- '.&mt('configurable in course').': '.
                                   '<label><input type="radio" name="ltitools_crs'.$extra.'_'.$i.'" value="0"'.$crscheckedoff.' />'.
                                   &mt('No').'</label>'.(' 'x2).
                                   '<label><input type="radio" name="ltitools_crs'.$extra.'_'.$i.'" value="1"'.$crscheckedon.' />'.
                                   &mt('Yes').'</label>';
                 } else {
-                    $datatable .= '<div class="LC_floatleft" style="display:'.$validsty.';" id="ltitools_'.$extra.'time_'.$i.'">'.
-                                  '<span class="LC_nobreak">'.
+                    $datatable .= '<div class="LC_floatleft" style="display: '.$validsty.';" id="ltitools_'.$extra.'time_'.$i.'">'.
+                                  '<span class="LC_nobreak"><label>'.
                                   &mt("until at least [_1] $units{$extra} after launch",
-                                      '<input type="text" name="ltitools_'.$extra.'valid_'.$i.'" value="'.$currvalid.'" />');
+                                      '<input type="text" name="ltitools_'.$extra.'valid_'.$i.'" value="'.$currvalid.'" />').'</label>';
                 }
                 $datatable .= '</span></div><div style="padding:0;clear:both;margin:0;border:0"></div>';
 		if ($extra eq 'desturl') {
-                    $datatable .= '<div style="display:'.$validsty.';" id="ltitools_default'.$extra.'_'.$i.'">'.
+                    $datatable .= '<div style="display:'.$validsty.'; line-height: 180%;" id="ltitools_default'.$extra.'_'.$i.'">'.
                                   '<span class="LC_nobreak"><label>'.&mt('Default destination URL').':'.
                                   '<input type="text" size="60" name="ltitools_defdest_'.$i.'" value="'.$defdest.'" />'.
-                                  '</label><span><br /><span class="LC_nobreak"><label>'.
+                                  '</label></span><br /><span class="LC_nobreak"><label>'.
                                   &mt('Default delay between login and redirect').':'.
                                   '<input type="text" size="3" name="ltitools_defdelay_'.$i.'" value="'.$defdelay.'" />'.
                                   '(s)</label></span></div><br />';
                 }
             }
-            $datatable .= '<span class="LC_nobreak">'.$lt{'icon'}.': ';
+            $datatable .= '<span class="LC_nobreak"><label for="ltitools_image_'.$i.'">'.$lt{'icon'}.'</label>: ';
             if ($imgsrc) {
                 $datatable .= $imgsrc.
                               '<label><input type="checkbox" name="ltitools_image_del"'.
@@ -6992,7 +6993,7 @@
             if ($switchserver) {
                 $datatable .= &mt('Upload to library server: [_1]',$switchserver);
             } else {
-                $datatable .= '<input type="file" name="ltitools_image_'.$i.'" value="" />';
+                $datatable .= '<input type="file" name="ltitools_image_'.$i.'" id="ltitools_image_'.$i.'" value="" />';
             }
             $datatable .= '</span></fieldset>';
             my (%checkedfields,%rolemaps,$userincdom);
@@ -7036,21 +7037,24 @@
             $datatable .= '</span>';
             $datatable .= '<div style="'.$userfieldstyle.'" id="ltitools_user_div_'.$i.'">'.
                           '<span class="LC_nobreak"> : '.
-                          '<select name="ltitools_userincdom_'.$i.'">'.
+                          '<select name="ltitools_userincdom_'.$i.'" aria-label="'.&mt('Format for user ID in LTI payload').'">'.
                           '<option value="">'.&mt('Select').'</option>'.
                           '<option value="0"'.$unseluserdom.'>'.&mt('username').'</option>'.
                           '<option value="1"'.$seluserdom.'>'.&mt('username:domain').'</option>'.
-                          '</select></span></div>';
-            $datatable .= '</fieldset>'.
-                          '<fieldset><legend>'.&mt('Role mapping').'</legend><table><tr>';
+                          '</select></span></div>'.
+                          '</fieldset>'.
+                          '<fieldset><legend>'.&mt('Role mapping').'</legend>'.
+                          '<div class="LC_grid" role="grid" style="margin: 0;">'.
+                          '<div class="LC_grid_row" role="row">';
             foreach my $role (@courseroles) {
                 my ($selected,$selectnone);
                 if (!$rolemaps{$role}) {
                     $selectnone = ' selected="selected"';
                 }
-                $datatable .= '<td style="text-align: center">'.
-                              &Apache::lonnet::plaintext($role,'Course').'<br />'.
-                              '<select name="ltitools_roles_'.$role.'_'.$i.'">'.
+                $datatable .= '<div class="LC_grid_cell" role="gridcell" style="text-align: center">'.
+                              '<label for="ltitools_roles_'.$role.'_'.$i.'">'.
+                              &Apache::lonnet::plaintext($role,'Course').'</label><br />'.
+                              '<select name="ltitools_roles_'.$role.'_'.$i.'" id="ltitools_roles_'.$role.'_'.$i.'">'.
                               '<option value=""'.$selectnone.'>'.&mt('Select').'</option>';
                 foreach my $ltirole (@ltiroles) {
                     unless ($selectnone) {
@@ -7062,9 +7066,9 @@
                     }
                     $datatable .= '<option value="'.$ltirole.'"'.$selected.'>'.$ltirole.'</option>';
                 }
-                $datatable .= '</select></td>';
+                $datatable .= '</select></div>';
             }
-            $datatable .= '</tr></table></fieldset>'."\n".
+            $datatable .= '</div></div></fieldset>'."\n".
                           '<fieldset><legend>';
             if ($context eq 'domain') {
                 $datatable .= &mt('Configurable in course');
@@ -7083,24 +7087,34 @@
             }
             $datatable .= '</span></fieldset>'.
                           '<fieldset><legend>'.&mt('Custom items sent on launch').'</legend>'.
-                          '<table><tr><th>'.&mt('Action').'</th><th>'.&mt('Name').'</th><th>'.&mt('Value').'</th></tr>';
+                          '<table><tr><th id="ltitools_'.$i.'_col1">'.&mt('Action').'</th>'.
+                          '<th id="ltitools_'.$i.'_col2">'.&mt('Name').'</th>'.
+                          '<th id="ltitools_'.$i.'_col3">'.&mt('Value').'</th>'.
+                          '</tr>';
             if (ref($settings->{$item}->{'custom'}) eq 'HASH') {
                 my %custom = %{$settings->{$item}->{'custom'}};
                 if (keys(%custom) > 0) {
+                    my $num = 1;
                     foreach my $key (sort(keys(%custom))) {
                         $datatable .= '<tr><td><span class="LC_nobreak">'.
                                       '<label><input type="checkbox" name="ltitools_customdel_'.$i.'" value="'.
-                                      $key.'" />'.&mt('Delete').'</label></span></td><td>'.$key.'</td>'.
+                                      $key.'" />'.&mt('Delete').'</label></span></td>'.
+                                      '<th class="LC_rowheader" id="ltitools_'.$i.'_custom_'.$num.'">'.$key.'</th>'.
                                       '<td><input type="text" name="ltitools_customval_'.$key.'_'.$i.'"'.
-                                      ' value="'.$custom{$key}.'" size="35" /></td></tr>';
+                                      ' value="'.$custom{$key}.'" size="35" '.
+                                      'aria-labelledby="ltitools_'.$i.'_custom_'.$num.' ltitools_'.$i.'_col3" />'.
+                                      '</td></tr>';
+                        $num ++;
                     }
                 }
             }
             $datatable .= '<tr><td><span class="LC_nobreak">'.
                           '<label><input type="checkbox" name="ltitools_customadd" value="'.$i.'" />'.
-                          &mt('Add').'</label></span></td><td><input type="text" name="ltitools_custom_name_'.$i.'" />'.
-                          '</td><td><input type="text" name="ltitools_custom_value_'.$i.'" size="35" /></td></tr>';
-            $datatable .= '</table></fieldset></td></tr>'."\n";
+                          &mt('Add').'</label></span></td>'.
+                          '<td><input type="text" name="ltitools_custom_name_'.$i.'" aria-label="'.&mt('Custom launch item name').'" />'.
+                          '</td><td>'.
+                          '<input type="text" name="ltitools_custom_value_'.$i.'" size="35" aria-label="'.&mt('Custom launch item value').'" />'.
+                          '</td></tr></table></fieldset></td></tr>'."\n";
             $itemcount ++;
         }
     }
@@ -7191,7 +7205,7 @@
             $datatable .= '<div class="LC_floatleft" style="display:none;" id="ltitools_'.$extra.'time_add">'.
                           '<span class="LC_nobreak"><label>'.
                           &mt("until at least [_1] $units{$extra} after launch",
-                              '<input type="text" name="ltitools_'.$extra.'valid_add" value="'.$defaulttimes{$extra}.'" /></label>');
+                              '<input type="text" name="ltitools_'.$extra.'valid_add" value="'.$defaulttimes{$extra}.'" />').'</label>';
         }
         $datatable .= '</span></div><div style="padding:0;clear:both;margin:0;border:0"></div>';
         if ($extra eq 'desturl') {


More information about the LON-CAPA-cvs mailing list