[LON-CAPA-cvs] cvs: loncom /interface loncommon.pm lonfeedback.pm
raeburn
raeburn at source.lon-capa.org
Sun Jun 21 13:18:12 EDT 2026
raeburn Sun Jun 21 17:18:12 2026 EDT
Modified files:
/loncom/interface lonfeedback.pm loncommon.pm
Log:
- WCAG 2 compliance
- Replace use of <table> for visual representation of threaded discussion
with semantic <ul> <li> etc.
- Include page region and heading structure in modal "Display Options"
window used to set "Other Views" of discussion posts.
- Replace use of <table> for layout with <div>.
- Group form elements in fieldset with legend for screenreaders.
- Satisfy minimum spacing between touch targets.
- Contrast ratio of at least 4.5:1 for normal text.
-------------- next part --------------
Index: loncom/interface/lonfeedback.pm
diff -u loncom/interface/lonfeedback.pm:1.398 loncom/interface/lonfeedback.pm:1.399
--- loncom/interface/lonfeedback.pm:1.398 Sat Jan 31 02:43:24 2026
+++ loncom/interface/lonfeedback.pm Sun Jun 21 17:18:12 2026
@@ -1,7 +1,7 @@
# The LearningOnline Network
# Feedback
#
-# $Id: lonfeedback.pm,v 1.398 2026/01/31 02:43:24 raeburn Exp $
+# $Id: lonfeedback.pm,v 1.399 2026/06/21 17:18:12 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -457,7 +457,6 @@
$discussion .= $lt{'aner'}.'<br />';
}
} else {
- my $colspan=$maxdepth+1;
$discussion.= &Apache::lonhtmlcommon::scripttag(qq|
function verifydelete (caller,symb,idx,newflag,previous,groupparm) {
var symbparm = symb+':::'+idx
@@ -479,15 +478,14 @@
}
|);
$discussion.='<form name="readchoices" method="post" action="/adm/feedback?chgreads='.$ressymb.'" >'.
- "\n".'<table width="100%" class="LC_discussion">';
- $discussion .= &action_links_bar($colspan,$ressymb,$visible,
- $newpostsflag,$group,
+ "\n".'<div class="LC_grid LC_discussion" role="grid" style="width: 100%; margin: 8px 0 0 0;">';
+ $discussion .= &action_links_bar($ressymb,$visible,$newpostsflag,$group,
$prevread,$markondisp,$seehidden);
my $escsymb=&escape($ressymb);
my $numhidden = keys(%notshown);
if ($numhidden > 0) {
- my $colspan = $maxdepth+1;
- $discussion.="\n".'<tr><td bgcolor="#CCCCCC" colspan="'.$colspan.'">';
+ $discussion.="\n".'<div class="LC_grid_row" role="row" style="width: 100%;">'.
+ '<div class="LC_grid_cell" role=gridcell" style="background-color: #CCCCCC; margin: 0;">';
my $href = '/adm/feedback?allposts=1&symb='.$escsymb;
if ($newpostsflag) {
$href .= '&previous='.$prevread;
@@ -500,7 +498,7 @@
$discussion .= &mt('[_1]Show all posts[_2] to display [quant,_3,post] previously viewed',
'<a href="'.$href.'">','</a>',$numhidden);
}
- $discussion .= '<br/></td></tr>';
+ $discussion .= '<br/></div></div>';
}
}
@@ -531,6 +529,20 @@
}
my $currdepth = 0;
my $firstidx = $alldiscussion{$showposts[0]};
+ if (@showposts) {
+ $discussion.= '<div class="LC_grid_row" role="row" style="margin: 0; width: 100%;">'.
+ '<div class="LC_grid_cell" role="gridcell" style="margin: 0; width: 100%;">';
+ }
+ my ($doindent,$prevdepth);
+ unless ($outputtarget eq 'tex') {
+ if (($sortposts eq 'thread') ||
+ (($sortposts eq '') && (!$env{'environment.unthreadeddiscussion'}))) {
+ $doindent = 1;
+ }
+ unless ($doindent) {
+ $discussion.= '<ul style="margin: 0; padding: 0; list-style-type: none;">';
+ }
+ }
foreach my $post (@showposts) {
unless (($sortposts eq 'thread') ||
(($sortposts eq '') && (!$env{'environment.unthreadeddiscussion'})) ||
@@ -538,16 +550,31 @@
$alldiscussion{$post} = $post;
}
unless ( ($notshown{$alldiscussion{$post}} eq '1') || ($shown{$alldiscussion{$post}} == 0) ) {
- if ($outputtarget ne 'tex' && $outputtarget ne 'export') {
- $discussion.="\n<tr>";
- }
my $thisdepth=$depth[$alldiscussion{$post}];
if ($outputtarget ne 'tex' && $outputtarget ne 'export') {
- for (1..$thisdepth) {
- $discussion.='<td> </td>';
- }
- }
- my $colspan=$maxdepth-$thisdepth+1;
+ if ($doindent) {
+ my $diffdepth = $thisdepth - $prevdepth;
+ if ($prevdepth eq '') {
+ $discussion.= '<ul style="margin: 0; padding: 0; list-style-type: none;">';
+ } else {
+ my $diffdepth = $thisdepth - $prevdepth;
+ if ($diffdepth > 0) {
+ $discussion.="\n".'<ul style="margin: 0; padding-left: 2em; list-style-type: none;">';
+ } else {
+ $discussion.='</li>'."\n";
+ if ($diffdepth < 0) {
+ for (my $i=0; $i>$diffdepth; $i--) {
+ $discussion.="\n</ul></li>";
+ }
+ }
+ }
+ }
+ $discussion.="\n<li>";
+ $prevdepth = $thisdepth;
+ } else {
+ $discussion.="\n<li>";
+ }
+ }
if ($outputtarget eq 'tex') {
#cleanup block
$discussionitems[$alldiscussion{$post}]=~s/<table([^>]*)>/<table TeXwidth="90 mm">/;
@@ -593,21 +620,32 @@
}
$copyresult.=&replicate_attachments($imsitems{$alldiscussion{$post}}{'allattachments'},$tempexport);
} else {
- $discussion.='<td class="'.$bgcols[$newitem{$alldiscussion{$post}}].
- '" colspan="'.$colspan.'">'. $discussionitems[$alldiscussion{$post}].
- '</td></tr>';
+ $discussion.= '<div class="'.$bgcols[$newitem{$alldiscussion{$post}}].'">'
+ .$discussionitems[$alldiscussion{$post}].'</div>';
+ unless ($doindent) {
+ $discussion.= '</li>'."\n";
+ }
}
}
}
+ if (@showposts) {
+ unless ($outputtarget eq 'tex') {
+ if ($doindent) {
+ for (0..$prevdepth) {
+ $discussion.= '</li></ul>';
+ }
+ } else {
+ $discussion.= '</ul>';
+ }
+ }
+ $discussion.= '</div></div>';
+ }
unless ($outputtarget eq 'tex' || $outputtarget eq 'export') {
- my $colspan=$maxdepth+1;
$discussion .= <<END;
- <tr>
- <td colspan="$colspan">
- <table width="100%">
- <tr>
- <td class="LC_disc_action_left">
- <font size="-1"><b>$lt{'cuse'}</b>:
+ <div class="LC_grid_row" role="row">
+ <div class="LC_grid_cell" role="gridcell">
+ <div class="LC_split_text" style="width: 100%; margin: 0; line-height: 170%;">
+ <span style="font-size: smaller;"><b>$lt{'cuse'}</b>:
END
if ($newpostsflag) {
$discussion .=
@@ -624,8 +662,7 @@
}
}
$discussion .= <<END;
- <b><a href="$chglink">$lt{'chgt'}</a></b></font>
- </td>
+ <b><a href="$chglink">$lt{'chgt'}</a></b></span>
END
if ($sortposts) {
my %sort_types = ();
@@ -633,7 +670,7 @@
my %status_types = ();
&sort_filter_names(\%sort_types,\%role_types,\%status_types,$crstype);
- $discussion .= '<td class="LC_disc_action_right"><font size="-1"><b>'.&mt('Sorted by').'</b>: '.$sort_types{$sortposts}.'<br />';
+ $discussion .= '<span style="font-size: smaller;"><b>'.&mt('Sorted by').'</b>: '.$sort_types{$sortposts}.'<br />';
if (defined($env{'form.totposters'})) {
$discussion .= &mt('Posts by').':';
if ($totposters > 0) {
@@ -669,27 +706,20 @@
$discussion .= '<b>'.&mt('Filters').'</b>: '.$filterchoice;
}
}
- $discussion .= '</font></td>';
-
+ $discussion .= '</span>';
}
if ($dischash{$toggkey}) {
my $storebutton = &mt('Save read/unread changes');
- $discussion.='<td align="right">'.
+ $discussion.= '<span>'.
'<input type="hidden" name="discsymb" value="'.$ressymb.'" />'."\n".
'<input type="button" name="readoptions" value="'.$storebutton.'"'.
' onclick="this.form.submit();" />'."\n".
- '</td>';
+ '</span>';
}
- $discussion .= (<<END);
- </tr>
- </table>
- </td>
- </tr>
-END
- $discussion .= &action_links_bar($colspan,$ressymb,$visible,
- $newpostsflag,$group,
+ $discussion .= '</div></div></div>';
+ $discussion .= &action_links_bar($ressymb,$visible,$newpostsflag,$group,
$prevread,$markondisp,$seehidden);
- $discussion .= "</table></form>\n";
+ $discussion .= "</div></form>\n";
}
if ($outputtarget eq 'export') {
if ($manifestok) {
@@ -916,11 +946,12 @@
}
sub action_links_bar {
- my ($colspan,$ressymb,$visible,$newpostsflag,$group,$prevread,$markondisp,
+ my ($ressymb,$visible,$newpostsflag,$group,$prevread,$markondisp,
$seehidden) = @_;
- my $discussion = '<tr><td colspan="'.$colspan.'">'.
- '<table width="100%"><tr>'.
- '<td class="LC_disc_action_left">';
+ my $discussion = '<div class="LC_grid_row" role="row" style="margin: 0;">'.
+ '<div class="LC_grid_cell" role="gridcell" style="margin: 0;">'.
+ '<div class="LC_split_text" style="width: 100%; margin: 0; line-height: 180%;">'.
+ '<span>';
my $escsymb=&escape($ressymb);
if ($visible) {
$discussion .= '<a href="/adm/feedback?cmd=threadedon&symb='.$escsymb;
@@ -959,23 +990,19 @@
$discussion .= &group_args($group);
$discussion .= '">'.&mt('Undelete all deleted entries').'</a>';
}
- $discussion.='</td>';
+ $discussion.='</span>';
if ($newpostsflag) {
if (!$markondisp) {
- $discussion .='<td class="LC_disc_action_right"><a href="/adm/preferences?action=changediscussions';
+ $discussion .='<span><a href="/adm/preferences?action=changediscussions';
$discussion .= &group_args($group);
$discussion .= '">'.
&mt('My general preferences on what is marked as NEW').
'</a><br /><a href="/adm/feedback?markread=1&symb='.$escsymb;
$discussion .= &group_args($group);
- $discussion .= '">'.&mt('Mark NEW posts no longer new').'</a></td>';
- } else {
- $discussion .= '<td> </td>';
+ $discussion .= '">'.&mt('Mark NEW posts no longer new').'</a></span>';
}
- } else {
- $discussion .= '<td> </td>';
}
- $discussion .= '</tr></table></td></tr>';
+ $discussion .= '</div></div></div>';
return $discussion;
}
@@ -1300,7 +1327,7 @@
}
$sender = '<b>'.$sender.'</b>';
if ($contrib{$idx.':anonymous'}) {
- $sender.=' <font color="red"><b>['.$$anonhash{$key}.']</b></font> '.
+ $sender.=' <span class="LC_info"><b>['.$$anonhash{$key}.']</b></span> '.
$screenname;
}
if ($see_anonymous) {
@@ -1515,7 +1542,7 @@
unless ($$notshown{$idx} == 1) {
if ($prevread > 0 && $prevread <= $posttime) {
$$newitem{$idx} = 1;
- $$discussionitems[$idx] .= '<font color="#FF0000"><b>'.&mt('NEW').' </b></font>';
+ $$discussionitems[$idx] .= '<span class="LC_info"><b>'.&mt('NEW').' </b></span>';
} else {
$$newitem{$idx} = 0;
}
@@ -1593,7 +1620,7 @@
if ($contrib{$idx.':history'}) {
my @postversions = ();
$$discussionitems[$idx] .= ' '.&mt('This post has been edited by the author.');
- if ($seehidden) {
+ if (($seehidden) && ($outputtarget ne 'tex')) {
$$discussionitems[$idx] .= ' '.
&discussion_link($ressymb,&mt('Display all versions'),'allversions',$idx,$$newpostsflag,$prevread,&group_args($group));
}
@@ -1607,6 +1634,7 @@
my $version = $i+1;
$$discussionitems[$idx] .= '<b>'.$version.'.</b> - '.&Apache::lonlocal::locallocaltime($postversions[$i]).' ';
}
+ $$discussionitems[$idx].='<br/>';
}
# end of unless ($$notshown ...)
}
@@ -2176,6 +2204,7 @@
'curr' => 'Current setting ',
'actn' => 'Action',
'deff' => 'Default for all discussions',
+ 'sdpf' => 'Set display preferences for this discussion',
'prca' => 'Preferences can be set for this discussion that determine ....',
'whpo' => 'Which posts are displayed when you display this discussion board or resource, and',
'unwh' => 'Under what circumstances posts are identified as "NEW", and',
@@ -2307,13 +2336,15 @@
my $start_page =
- &Apache::loncommon::start_page('Discussion display options',$js);
+ &Apache::loncommon::start_page('Discussion display options',$js)."\n".
+ '<div class="LC_landmark" role="main" id="LC_main_content">'."\n";
my $end_page =
- &Apache::loncommon::end_page();
+ '</div>'.&Apache::loncommon::end_page();
$r->print(<<END);
$start_page
<form name="modifydisp" method="post" action="/adm/feedback">
-$lt{'sdpf'}<br/> $lt{'prca'} <ol><li>$lt{'whpo'}</li><li>$lt{'unwh'}</li><li>$lt{'wipa'}</li></ol>
+<h1 class="LC_heading_3">$lt{'sdpf'}</h1>
+$lt{'prca'} <ol><li>$lt{'whpo'}</li><li>$lt{'unwh'}</li><li>$lt{'wipa'}</li></ol>
<br />
END
$r->print(&Apache::loncommon::start_data_table());
@@ -2328,9 +2359,13 @@
$r->print(<<END);
<td>$lt{'disa'}</td>
<td>$lt{$discdisp}</td>
- <td><label><input type="checkbox" name="discdisp" onclick="discdispChk('0')" /> $lt{'chgt'} "$dispchangeA"</label>
+ <td>
+ <fieldset class="LC_borderless" style="line-height: 185%;">
+ <legend class="LC_visually_hidden">$lt{'disa'}</legend>
+ <label><input type="checkbox" name="discdisp" onclick="discdispChk('0')" /> $lt{'chgt'} "$dispchangeA"</label>
<br />
<label><input type="checkbox" name="discdisp" onclick="discdispChk('1')" /> $lt{'chgt'} "$dispchangeB"</label>
+ </fieldset>
</td>
END
$r->print(&Apache::loncommon::end_data_table_row());
@@ -2448,11 +2483,11 @@
'actn' => 'Action',
'prca' => 'Set options that control the sort order of posts, and/or which posts are displayed.',
'soor' => 'Sort order',
- 'spur' => 'Specific user roles',
+ 'spur' => 'Specific roles',
'sprs' => 'Specific role status',
'spse' => 'Specific sections',
'spgr' => 'Specific groups',
- 'psub' => 'Pick specific users (by name)',
+ 'psub' => 'Specific users',
'shal' => 'Show a list of current posters',
'stor' => 'Save changes',
);
@@ -2501,32 +2536,28 @@
END
my $start_page=
- &Apache::loncommon::start_page('Discussion options',$js);
+ &Apache::loncommon::start_page('Discussion options',$js,{no_main_skip => 1});
my $end_page=
&Apache::loncommon::end_page();
$r->print(<<END);
$start_page
+<div class="LC_landmark" role="contentinfo">
<form name="modifyshown" method="post" action="/adm/feedback">
-<b>$lt{'diso'}</b><br/> $lt{'prca'}
+<h1 class="LC_heading_3">$lt{'diop'}</h1>$lt{'prca'}
<br />
-<table border="0">
+<table border="0" style="border-collapse: separate; border-spacing: 10px; 0;">
<tr>
- <th>$lt{'soor'}</th>
- <th> </th>
- <th>$lt{'sprs'}</th>
- <th> </th>
- <th>$lt{'spur'}</th>
- <th> </th>
- <th>$lt{'spse'}</th>
- <th> </th>
- <th>$lt{'spgr'}</th>
- <th> </th>
+ <th><label for="sortposts">$lt{'soor'}</th>
+ <th><label for="statusfilter">$lt{'sprs'}</th>
+ <th><label for="rolefilter">$lt{'spur'}</th>
+ <th><label for="sectionpick">$lt{'spse'}</th>
+ <th><label for="grouppick">$lt{'spgr'}</label></th>
<th>$lt{'psub'}</th>
</tr>
<tr>
<td align="center" valign="top">
- <select name="sortposts">
+ <select name="sortposts" id="sortposts">
<option value="ascdate" selected="selected">$sort_types{'ascdate'}</option>
<option value="descdate">$sort_types{'descdate'}</option>
<option value="thread">$sort_types{'thread'}</option>
@@ -2535,18 +2566,16 @@
<option value="lastfirst">$sort_types{'lastfirst'}</option>
</select>
</td>
- <td> </td>
<td align="center" valign="top">
- <select name="statusfilter">
+ <select name="statusfilter" id="statusfilter">
<option value="all" selected="selected">$status_types{'all'}</option>
<option value="Active">$status_types{'Active'}</option>
<option value="Expired">$status_types{'Expired'}</option>
<option value="Future">$status_types{'Future'}</option>
</select>
</td>
- <td> </td>
<td align="center" valign="top">
- <select name="rolefilter" multiple="multiple" size="5">
+ <select name="rolefilter" id="rolefilter" multiple="multiple" size="5">
<option value="all">$role_types{'all'}</option>
<option value="st">$role_types{'st'}</option>
<option value="$ccrole">$role_types{$ccrole}</option>
@@ -2557,19 +2586,16 @@
<option value="cr">$role_types{'cr'}</option>
</select>
</td>
- <td> </td>
<td align="center" valign="top">
- <select name="sectionpick" multiple="multiple" size="$numvisible">
+ <select name="sectionpick" id="sectionpick" multiple="multiple" size="$numvisible">
$section_sel
</select>
</td>
- <td> </td>
<td align="center" valign="top">
- <select name="grouppick" multiple="multiple" size="$numvisible">
+ <select name="grouppick" id="grouppick" multiple="multiple" size="$numvisible">
$group_sel
</select>
</td>
- <td> </td>
<td valign="top"><label><input type="checkbox" name="posterlist" value="$symb" />$lt{'shal'}</label></td>
</tr>
</table>
@@ -2590,6 +2616,7 @@
<br />
<br />
</form>
+</div>
$end_page
");
}
@@ -3688,6 +3715,8 @@
my %lt = &Apache::lonlocal::texthash(
'subj' => 'Subject',
+ 'atta' => 'Attachments',
+ 'rema' => 'Remove attachments',
'chth' => 'Check the checkboxes for any you wish to remove.',
'thef' => 'The following attachments have been uploaded for inclusion with this posting.',
'adda' => 'Add a new attachment to this post',
@@ -3742,7 +3771,7 @@
<h1 class="LC_heading_1">$lt{'clic'}</h1>
END
$r->print(&Apache::lonhtmlcommon::start_pick_box());
- $r->print(&Apache::lonhtmlcommon::row_title(&mt('Subject')));
+ $r->print(&Apache::lonhtmlcommon::row_title($lt{'subj'}));
$r->print('<b>'.$subject.'</b>');
$r->print(&Apache::lonhtmlcommon::row_closure());
$r->print(&Apache::lonhtmlcommon::row_title('<label for="addnewattach">'.$lt{'adda'}.'</label>'));
@@ -3752,12 +3781,14 @@
.'onclick="this.form.submit()" /> '.$attachmaxtext);
if(($idx)||(ref($currnewattach) eq 'ARRAY') && (@{$currnewattach} > 0)){
$r->print(&Apache::lonhtmlcommon::row_closure());
- $r->print(&Apache::lonhtmlcommon::row_title(&mt('Attachments')));
+ $r->print(&Apache::lonhtmlcommon::row_title($lt{'atta'}).
+ '<fieldset class="LC_borderless"><legend class="LC_visually_hidden">'.
+ $lt{'rema'}.'</legend>');
if ($idx) {
if ($attachmenturls) {
my @currold = keys(%currattach);
if (@currold > 0) {
- $r->print($lt{'thfo'}.'<br />'.$lt{'chth'}.'<br />'."\n");
+ $r->print($lt{'thef'}.'<br />'.$lt{'chth'}.'<br />'."\n");
foreach my $id (@currold) {
my $attachurl = &HTML::Entities::decode($attachments{$id}{'filename'});
$attachurl =~ m#/([^/]+)$#;
@@ -3768,12 +3799,15 @@
}
}
if ((ref($currnewattach) eq 'ARRAY') && (@{$currnewattach} > 0)) {
- $r->print($lt{'chth'}.'<br />'."\n");
+ $r->print($lt{'chth'}."\n");
foreach my $attach (@{$currnewattach}) {
$attach =~ m#/([^/]+)$#;
- $r->print('<label><input type="checkbox" name="delnewattach" value="'.$attach.'" /> '.$1.'</label><br />'."\n");
+ $r->print('<div class="LC_touch_target">'.
+ '<label><input type="checkbox" name="delnewattach" value="'.$attach.'" />'.
+ ' '.$1.'</label></div>'."\n");
}
}
+ $r->print('</fieldset>');
}
$r->print(&Apache::lonhtmlcommon::row_closure(1));
$r->print(&Apache::lonhtmlcommon::end_pick_box());
@@ -3913,7 +3947,7 @@
&Apache::lonnet::allowuploaded('/adm/feedback',
$attachurl);
} elsif (@attached > 1) {
- $$message.='<ol>';
+ $$message.='<ol style="line-height: 170%;">';
foreach my $attach (@attached) {
my $id = $attach;
my $attachurl = &HTML::Entities::decode($$attachments{$id}{'filename'});
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.1529 loncom/interface/loncommon.pm:1.1530
--- loncom/interface/loncommon.pm:1.1529 Fri Jun 19 13:19:46 2026
+++ loncom/interface/loncommon.pm Sun Jun 21 17:18:12 2026
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# a pile of common routines
#
-# $Id: loncommon.pm,v 1.1529 2026/06/19 13:19:46 raeburn Exp $
+# $Id: loncommon.pm,v 1.1530 2026/06/21 17:18:12 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -7750,6 +7750,12 @@
color: #525252;
}
+.LC_split_text {
+ background: $sidebg;
+ display: flex;
+ justify-content: space-between;
+}
+
.LC_discussion {
background: $data_table_dark;
border: 1px solid black;
More information about the LON-CAPA-cvs
mailing list