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

raeburn raeburn at source.lon-capa.org
Mon May 4 20:43:18 EDT 2026


raeburn		Tue May  5 00:43:18 2026 EDT

  Modified files:              
    /loncom/interface	lonnotify.pm lonhtmlcommon.pm 
  Log:
  - WCAG 2.2 compliance for Domain Coordinator's "Send broadcast e-mail" tool.
    - Include labels for form elements.
    - Satisfy minimum spacing between touch targets.
    - 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.
    - Group form elements in fieldset with legend for screenreaders.
    - Replace use of <table> for layout with <div>.
  
  
-------------- next part --------------
Index: loncom/interface/lonnotify.pm
diff -u loncom/interface/lonnotify.pm:1.46 loncom/interface/lonnotify.pm:1.47
--- loncom/interface/lonnotify.pm:1.46	Sat Dec 13 13:33:33 2025
+++ loncom/interface/lonnotify.pm	Tue May  5 00:43:17 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Sending messages
 #
-# $Id: lonnotify.pm,v 1.46 2025/12/13 13:33:33 raeburn Exp $
+# $Id: lonnotify.pm,v 1.47 2026/05/05 00:43:17 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -215,25 +215,36 @@
 
     $output .= &Apache::lonhtmlcommon::start_pick_box();
     $output .= &Apache::lonhtmlcommon::row_title(&mt('Date range'));
-    $output .= '<table><tr><td>'.&mt('Earliest to display:').' </td><td>'.
-                $startdateform.'</td></tr>';
-    $output .= '<tr><td>'.&mt('Latest to display:').' </td><td>'.$enddateform.
-               '</td></tr></table>';
+    $output .= '<div class="LC_grid" role="grid" style="margin: 0;">'
+              .'<div class="LC_grid_row" role="row">'
+              .'<div class="LC_grid_cell" role="gridcell">'
+              .&mt('Earliest to display:').' </div>'
+              .'<div class="LC_grid_cell" role="gridcell">'.$startdateform.'</div>'
+              .'</div><div class="LC_grid_row" role="row">'
+              .'<div class="LC_grid_cell" role="gridcell">'
+              .&mt('Latest to display:').' </div>'
+              .'<div class="LC_grid_cell" role="gridcell">'.$enddateform.'</div>'
+              .'</div></div>';
     $output .= &Apache::lonhtmlcommon::row_closure();
     $output .= &Apache::lonhtmlcommon::row_title(&mt('Choose sender(s)'));
     my %personnel = &Apache::lonnet::get_domain_roles($cdom,\@roles);
     my @domcc = ();
+    $output .= '<fieldset class="LC_borderless" style="line-height: 185%;">'
+              .'<legend class="LC_visually_hidden">'.&mt('Filter by sender').'</legend>';
     foreach my $server (keys(%personnel)) {
         foreach my $user (sort(keys(%{$personnel{$server}}))) {
             my ($trole,$uname,$udom,$runame,$rudom,$rsec) = split(/:/,$user);
             unless (grep/^$uname:$udom$/, at domcc) {
                 my %userinfo = &Apache::lonnet::get('environment',['lastname','firstname'],$udom,$uname);
-                $output .= '<input type="checkbox" name="sender" value="'.$uname.':'.$udom.'" /> '.$userinfo{'firstname'}.' '.$userinfo{'lastname'}.'  ('.$uname.':'.$udom.')';
-                push (@domcc,$uname.':'.$udom);
+                $output .= '<span class="LC_nobreak"><label>'
+                          .'<input type="checkbox" name="sender" value="'.$uname.':'.$udom.'" />'
+                          .' '.$userinfo{'firstname'}.' '.$userinfo{'lastname'}.'</label></span>'
+                          .'  ('.$uname.':'.$udom.') ';
+                push(@domcc,$uname.':'.$udom);
             }
         }
     }
-    $output .= &Apache::lonhtmlcommon::row_closure();
+    $output .= '</fieldset>'.&Apache::lonhtmlcommon::row_closure();
     $output .= &Apache::lonhtmlcommon::submit_row(&mt('Submit'),$cmd,$submit_text);
     $output .= &Apache::lonhtmlcommon::end_pick_box();
     $output .= qq(<input type="hidden" name="sortby" value="date" />\n).
@@ -513,6 +524,8 @@
 
     my %lt=&Apache::lonlocal::texthash(
                       'nore' => 'No recipients identified',
+                      'sele' => 'Select',
+                      'unam' => 'username:domain',
                       'emad' => 'e-mail address',
                    );
     my %elements = (
@@ -620,34 +633,42 @@
   
     if ($totalrecip > 0) {
         $output .= &Apache::lonhtmlcommon::start_pick_box();
-        $output .= &Apache::lonhtmlcommon::row_title(&mt('Subject'));
-        $output .= '<input type="text" name="subject" size="30" />';
+        $output .= &Apache::lonhtmlcommon::row_title('<label for="subject">'.&mt('Subject').'</label>');
+        $output .= '<input type="text" name="subject" id="subject" size="30" />';
         $output .= &Apache::lonhtmlcommon::row_closure();
-        $output .= &Apache::lonhtmlcommon::row_title(&mt('Message'));
+        $output .= &Apache::lonhtmlcommon::row_title('<label for="message">'.&mt('Message').'</label>');
         $output .= '  <textarea name="message" id="message"
                       cols="60" rows="10" wrap="hard"></textarea>';
         $output .= &Apache::lonhtmlcommon::row_closure();
         $output .= &Apache::lonhtmlcommon::row_title(&mt('Recipients'));
-        $output .= '<input type="button" value="check all" 
-                    onclick="javascript:checkAll(document.compose.recipient)" />
-                      <input type="button" value="uncheck all"
-                    onclick="javascript:uncheckAll(document.compose.recipient)" />
-                    <br />';
-	$output .= &Apache::loncommon::start_data_table();
         if (keys(%recipients) > 0) {
-	    $output .= &Apache::loncommon::start_data_table_header_row();
-            $output .= '<th> <th>username:domain</th><th>'.$lt{'emad'}.'</th>';
-	    $output .= &Apache::loncommon::end_data_table_header_row();
-        }
-        foreach my $username (sort(keys(%recipients))) {
-	    $output .= &Apache::loncommon::start_data_table_row();
-            if ($recipients{$username} =~ /\@/) {
-                my $value=&escape($username).':'.&escape($recipients{$username});
-                $output .= '<td><input type="checkbox" name="recipient" value="'.$value.'" /></td><td>'.$username.'</td><td>'.$recipients{$username}.'</td>';
+            $output .= '<input type="button" value="check all" 
+                        onclick="javascript:checkAll(document.compose.recipient)" />
+                          <input type="button" value="uncheck all"
+                        onclick="javascript:uncheckAll(document.compose.recipient)" />
+                        <br />'
+                      .'<fieldset class="LC_borderless" style="line-height: 185%;">'
+                      .'<legend class="LC_visually_hidden">'.&mt('Select Recipients').'</legend>'
+                      .&Apache::loncommon::start_data_table('LC_paramDefault')
+                      .&Apache::loncommon::start_data_table_header_row()
+                      .'<th id="colselect">'.$lt{'sele'}.'</th><th>'.$lt{'unam'}.'</th><th>'.$lt{'emad'}.'</th>'
+                      .&Apache::loncommon::end_data_table_header_row();
+            my $count = 0;
+            foreach my $username (sort(keys(%recipients))) {
+	        $output .= &Apache::loncommon::start_data_table_row();
+                if ($recipients{$username} =~ /\@/) {
+                    my $value=&escape($username).':'.&escape($recipients{$username});
+                    $output .= '<td><input type="checkbox" name="recipient" value="'.$value.'" aria-labelledby="colselect username'.$count.'"/></td>'.
+                               '<th scope="row" class="LC_rowheader" id="username'.$count.'">'.$username.'</th>'.
+                               '<td>'.$recipients{$username}.'</td>';
+                }
+	        $output .= &Apache::loncommon::end_data_table_row();
+                $count ++;
             }
-	    $output .= &Apache::loncommon::end_data_table_row();
+            $output .= &Apache::loncommon::end_data_table();
+        } else {
+            $output .= &mt('No possible recipients (with email addresses)');
         }
-        $output .= &Apache::loncommon::end_data_table();
         if (@unmatched) {
             $output .= '<br /><br />'.&mt('Could not determine e-mail addresses for the following users:').'<ul>';
             foreach my $username (sort(@unmatched)) {
@@ -656,11 +677,12 @@
             $output .= '</ul>';
         }
         $output .= &Apache::lonhtmlcommon::row_closure();
-        $output .= &Apache::lonhtmlcommon::row_title(&mt('Sender e-mail address'));
-        $output .= '<input type="text" name="sender" value="'.$sender.'" />';
+        $output .= &Apache::lonhtmlcommon::row_title('<label for="sender">'.&mt('Sender e-mail address').'</label>');
+        $output .= '<input type="text" name="sender" id="sender" value="'.$sender.'" size="25" />';
         $output .= &Apache::lonhtmlcommon::row_closure();
         $output .= &Apache::lonhtmlcommon::submit_row(&mt('Submit'),'process',&mt('Send'));
-        $output .= &Apache::lonhtmlcommon::end_pick_box();
+        $output .= &Apache::lonhtmlcommon::end_pick_box()
+                  .'</fieldset>';
     } else {
         $output .= $lt{'nore'}."\n".
                    '<input type="hidden" name="command" value="" />'."\n";
Index: loncom/interface/lonhtmlcommon.pm
diff -u loncom/interface/lonhtmlcommon.pm:1.431 loncom/interface/lonhtmlcommon.pm:1.432
--- loncom/interface/lonhtmlcommon.pm:1.431	Thu Apr 30 00:27:07 2026
+++ loncom/interface/lonhtmlcommon.pm	Tue May  5 00:43:17 2026
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common html routines
 #
-# $Id: lonhtmlcommon.pm,v 1.431 2026/04/30 00:27:07 raeburn Exp $
+# $Id: lonhtmlcommon.pm,v 1.432 2026/05/05 00:43:17 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -2586,7 +2586,7 @@
     }
     my $output;
     if (defined($title)) {
-        $output = &row_title($title,$css_class);
+        $output = &row_title('<label for="roles">'.$title.'</label>',$css_class);
     }
     $output .= qq|
                                   <select name="roles" id="roles" multiple="multiple">\n|;
@@ -2661,26 +2661,34 @@
 </script>
     |;
 
-    my ($allcrs,$pickspec);
+    my ($allcrs,$pickspec,$filter);
     if ($crstype eq 'Community') {
         $allcrs = &mt('All communities');
         $pickspec = &mt('Pick specific communities:');
+        $filter = &mt('Community filter');
     } else {
         $allcrs = &mt('All courses');
         $pickspec = &mt('Pick specific course(s):');
+        $filter = &mt('Course filter');
     }
 
     my $courseform='<b>'.&Apache::loncommon::selectcourse_link
                      ($formname,'pickcourse','pickdomain','coursedesc','',1,$crstype).'</b>';
-        $output .= '<label><input type="radio" name="coursepick" value="all" onclick="coursePick(this.form)" />'.$allcrs.'</label><br />';
+        $output .= '<fieldset class="LC_borderless" style="line-height: 185%;">'.
+                   '<legend class="LC_visually_hidden">'.$filter.'</legend>'.
+                   '<label><input type="radio" name="coursepick" value="all" onclick="coursePick(this.form)" />'.
+                   $allcrs.'</label><br />';
     if ($totcodes > 0) {
         my $numtitles = @$codetitles;
         if ($numtitles > 0) {
             $output .= '<label><input type="radio" name="coursepick" value="category" onclick="coursePick(this.form);alert('."'".&html_escape(&mt('Choose categories, from left to right'))."'".')" />'.&mt('Pick courses by category:').'</label><br />';
-            $output .= '<table><tr><td>'.$$codetitles[0].'<br />'."\n".
-               '<select name="'.$standardnames->[0].
-               '" onchange="setPick(this.form);courseSet('."'$$codetitles[0]'".')">'."\n".
-               ' <option value="-1" />Select'."\n";
+            $output .= '<div class="LC_grid" role="grid">'.
+                       '<div class="LC_grid_row" role="row">'.
+                       '<div class="LC_grid_cell" role="grid_cell">'.
+                       $codetitles->[0].'<br />'."\n".
+                       '<select name="'.$standardnames->[0].
+                       '" onchange="setPick(this.form);courseSet('."'$codetitles->[0]'".')">'."\n".
+                       ' <option value="-1" />Select'."\n";
             my @items = ();
             my @longitems = ();
             if ($$idlist{$$codetitles[0]} =~ /","/) {
@@ -2705,16 +2713,17 @@
             for (my $i=0; $i<@items; $i++) {
                 $output .= ' <option value="'.$items[$i].'">'.$longitems[$i].'</option>';
             }
-            $output .= '</select></td>';
+            $output .= '</select></div>';
             for (my $i=1; $i<$numtitles; $i++) {
-                $output .= '<td>'.$$codetitles[$i].'<br />'."\n".
-                          '<select name="'.$standardnames->[$i].
-                          '" onchange="courseSet('."'$$codetitles[$i]'".')">'."\n".
-                          '<option value="-1"><-Pick '.$$codetitles[$i-1].'</option>'."\n".
-                          '</select>'."\n".
-                          '</td>';
+                $output .= '<div class="LC_grid_cell" role="grid_cell">'.
+                           $codetitles->[$i].'<br />'."\n".
+                           '<select name="'.$standardnames->[$i].
+                           '" onchange="courseSet('."'$$codetitles[$i]'".')">'."\n".
+                           '<option value="-1"><-Pick '.$$codetitles[$i-1].'</option>'."\n".
+                           '</select>'."\n".
+                           '</div>';
             }
-            $output .= '</tr></table><br />';
+            $output .= '</div></div><br />';
         }
     }
     $output .=
@@ -2733,7 +2742,7 @@
     my ($types,$title,$css_class) = @_;
     my $output; 
     if (defined($title)) {
-        $output = &row_title($title,$css_class,'LC_pick_box_select');
+        $output = &row_title('<label for="types">'.$title.'</label>',$css_class,'LC_pick_box_select');
     }
     $output .= qq|
                                     <select name="types" id="types" multiple="multiple">\n|;
@@ -2767,9 +2776,9 @@
             $size = 15;
         }
         $output .= &Apache::loncommon::start_data_table_row().
-	    '<td>  '.$$authtypes{$auth}.'</td>'.
+	    '<td> <label for="'.$auth.'">'.$$authtypes{$auth}.'</label></td>'.
 	    '<td align="right">'.$userentry.
-	    '<input type="text" name="'.$auth.'" size="'.$size.'" /></td>'.
+	    '<input type="text" name="'.$auth.'" id="'.$auth.'" size="'.$size.'" /></td>'.
 	    &Apache::loncommon::end_data_table_row();
     }
     $output .= &Apache::loncommon::end_data_table();


More information about the LON-CAPA-cvs mailing list