[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