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

raeburn raeburn at source.lon-capa.org
Sat Dec 27 00:59:46 EST 2025


raeburn		Sat Dec 27 05:59:46 2025 EDT

  Modified files:              
    /loncom/interface	lonwishlist.pm lonwishlistdisplay.pm 
  Log:
  - WCAG 2 compliance
    - Support "Skip to main content" when tabbing in page.
    - Include labels for form elements
    - Header for each column in a data table by using <th></th> for screen
      reader.
  
  
-------------- next part --------------
Index: loncom/interface/lonwishlist.pm
diff -u loncom/interface/lonwishlist.pm:1.28 loncom/interface/lonwishlist.pm:1.29
--- loncom/interface/lonwishlist.pm:1.28	Wed Dec 24 18:36:07 2025
+++ loncom/interface/lonwishlist.pm	Sat Dec 27 05:59:46 2025
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Utility-routines for wishlist
 #
-# $Id: lonwishlist.pm,v 1.28 2025/12/24 18:36:07 raeburn Exp $
+# $Id: lonwishlist.pm,v 1.29 2025/12/27 05:59:46 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1114,6 +1114,12 @@
 
     foreach my $n (@$nodes) {
         my $index = $n->value()->nindex();
+        my $labeltext;
+        if ($n->value()->path() eq '') {
+            $labeltext = &mt('Select [_1] as a folder to which action will apply',$n->value()->title());
+        } else {
+            $labeltext = &mt('Select [_1] as a stored link to which action will apply',$n->value()->title());
+        }
 
         # start row, use data_table routines to set class to LC_even or LC_odd automatically. this row contains a checkbox, the title and the note-icon.
         # only display the top level entries on load
@@ -1123,7 +1129,7 @@
  
         # checkboxes
         $wishlistHTMLview .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
-                             'onclick="selectAction('."'row".$index."'".')" /></td>';
+                             'onclick="selectAction('."'row".$index."'".')" aria-label="'.$labeltext.'" /></td>';
 
         # entry is a folder
         if ($n->value()->path() eq '') {
@@ -1149,6 +1155,13 @@
             $noteIMG = 'anot2.png';
         }
 
+        my $notelabel;
+        if ($n->value()->path() eq '') {
+            $notelabel = &mt('Add or modify note for folder [_1]',$n->value()->title());
+        } else {
+            $notelabel = &mt('Add or modify note for stored link [_1]',$n->value()->title());
+        }
+
         $wishlistHTMLview .= '<td style="padding-left:10px;"><a href="javascript:;" onclick="setDisplayNote('."'note".$index."'".')">'.
                              '<img id="noteImg'.$index.'" src="/res/adm/pages/'.$noteIMG.'" alt="'.&mt('Note').'" title="'.&mt('Note').'" '.
                              ' class="LC_icon"/></a></td>';
@@ -1159,7 +1172,7 @@
         $wishlistHTMLview .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
                              '<td></td><td>'.
                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
-                             'name="newnote" >'.
+                             'name="newnote" aria-label="'.$notelabel.'">'.
                              $n->value()->note().'</textarea></td><td></td>';
         $wishlistHTMLview .= &Apache::loncommon::end_data_table_row();
 
@@ -1183,6 +1196,18 @@
 
     foreach my $n (@$nodes) {
         my $index = $n->value()->nindex();
+        my $actionlabel = &mt('select [_1] as item to move or delete',$n->value()->title(),$index);
+        my $titlelabel = &mt('modify title for item currently titled: [_1]',$n->value()->title());
+        my $pathlabel = &mt('modify url for item with current url: [_1]',$n->value()->path());
+        my $notelabel = &mt('modify note for item currently titled: [_1]',$n->value()->title());
+        my $parent;
+        if ($n->parent()->value() eq 'root') {
+            $parent = &mt('Top level');
+        } else {
+            $parent = $n->parent()->value();
+        }
+        my $orderlabel = &mt('change order for [_1], currently item #[_2] in [_3] folder',
+                             $n->value()->title(),$curNode,$parent);
 
         # start row, use data_table routines to set class to LC_even or LC_odd automatically.
         # this rows contains a checkbox, a select-field for sorting entries, the title in an input-field and the note-icon.
@@ -1192,7 +1217,7 @@
 
         # checkboxes
         $wishlistHTMLedit .= '<td><input type="checkbox" name="mark" id="check'.$index.'" value="'.$index.'" '.
-                             'onclick="selectAction('."'row".$index."'".')" /></td>';
+                             'onclick="selectAction('."'row".$index."'".')" aria-label="'.$actionlabel.'" /></td>';
 
         # option-tags for sorting entries. we need the numbers from 1 to n with n being the number of entries on the same level as the current entry.
         # set the number for the current entry into brackets 
@@ -1209,24 +1234,25 @@
 
         # entry is a folder
         if ($n->value()->path() eq '') {
-            $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
+            $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.
+                                 '" onchange="submitSelect();" aria-label="'.$orderlabel.'">'.
                                  $options.'</select></td>'.
                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent_edit-$indentConst)<0?0:($indent_edit-$indentConst)).'px;">'.
                                  '<a href="javascript:;" onclick="folderAction('."'row".$index."'".')" style="vertical-align:top" >'.
                                  '<img src="/adm/lonIcons/arrow.closed.gif" id="img'.$index.'" alt = ""  class="LC_icon"/>'.
                                  '<img src="/adm/lonIcons/navmap.folder.closed.gif" id="imgFolder'.$index.'" alt="folder"/></a>'.
-                                 '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'" />'.
+                                 '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'" aria-label="'.$titlelabel.'" />'.
                                  '</td><td></td>';
 
         }
         # entry is a link
         else {
-            $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();">'.
+            $wishlistHTMLedit .= '<td><select class="LC_hidden" name="sel" id="sel'.$index.'" onchange="submitSelect();" aria-label="'.$orderlabel.'">'.
                                  $options.'</select></td>'.
                                  '<td id="padd'.$index.'" style="padding-left:'.(($indent_edit-$indentConst)<=0?$indentConst:$indent_edit).'px;">'.
                                  '<img src="/res/adm/pages/wishlist-link.png" id="img'.$index.'" alt="link"/>'.
-                                 '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'" /></td>'.
-                                 '<td><input type="text" name="newpath" value="'.$n->value()->path().'" alt = "'.$n->value()->path().'" /></td>';
+                                 '<input type="text" name="newtitle" value="'.$n->value()->title().'" alt = "'.$n->value()->title().'" aria-label="'.$titlelabel.'" /></td>'.
+                                 '<td><input type="text" name="newpath" value="'.$n->value()->path().'" alt = "'.$n->value()->path().'" aria-label="'.$pathlabel.'" /></td>';
         }
         
         # note-icon, different icons for an entries with note and those without
@@ -1246,7 +1272,7 @@
         $wishlistHTMLedit .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
                              '<td></td><td></td><td colspan="2">'.
                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
-                             'name="newnote">'.
+                             'name="newnote" aria-label="'.$notelabel.'">'.
                              $n->value()->note().'</textarea></td><td></td>';
         $wishlistHTMLedit .= &Apache::loncommon::end_data_table_row();
 
@@ -1367,6 +1393,13 @@
 
     foreach my $n (@$nodes) {
         my $index = $n->value()->nindex();
+        my ($labeltext,$notelabel);
+        if ($n->value()->path() eq '') {
+            $notelabel = &mt("Read-only note for '[_1]' folder",$n->value()->title());
+        } else {
+            $labeltext = &mt("Select '[_1]' stored link for import",$n->value()->title());
+            $notelabel = &mt("Read-only note for '[_1]' stored link",$n->value()->title());
+        }
 
         #
         # Determine which resources in stored links may be imported into a course/community.
@@ -1413,7 +1446,7 @@
             $image = 'wishlist-link.png';
         }
         $wishlistHTMLimport .= '<input type="checkbox" name="check" id="check'.$index.'" value="'.$index.'" '.
-                               $disabled.$onclick.' />'.
+                               $disabled.$onclick.' aria-label="'.$labeltext.'" />'.
                                '<input type="hidden" name="title'.$index.'" value="'.&escape($n->value()->title()).'" />'.
                                '<input type="hidden" name="filelink'.$index.'" value="'.&escape($n->value()->path()).'" />'.
                                '<input type="hidden" name="id'.$index.'" />';
@@ -1456,7 +1489,7 @@
         $wishlistHTMLimport .= &Apache::loncommon::continue_data_table_row('LC_hidden','note'.$index).
                              '<td></td><td>'.
                              '<textarea id="noteText'.$index.'" cols="25" rows="3" style="width:100%" '.
-                             'name="newnote" readonly="readonly">'.
+                             'name="newnote" readonly="readonly" aria-label="'.$notelabel.'">'.
                              $n->value()->note().'</textarea></td><td></td>';
         $wishlistHTMLimport .= &Apache::loncommon::end_data_table_row();
 
@@ -1496,6 +1529,7 @@
                                                         'onunload' => 'javascript:window.name = '."'$windowname'"}});
 
     my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Stored Links','Wishlist');
+    my $landmark = '<div class="LC_landmark" role="main" id="LC_main_content">'."\n";
 
     # get javascript-code for wishlist-interactions
     my $js = &JSforWishlist();
@@ -1570,8 +1604,8 @@
     my $subbox = &Apache::loncommon::head_subbox($functions);
 
     # start form 
-    my $inner .= '<form name="list" action ="/adm/wishlist" method="post">'.
-                 '<input type="hidden" id="action" name="action" value="" />';
+    my $inner = '<form name="list" action ="/adm/wishlist" method="post">'.
+                '<input type="hidden" id="action" name="action" value="" />';
  
     # only display subbox in view- or edit-mode
     if ($mode eq 'view' || $mode eq 'edit') {
@@ -1583,6 +1617,13 @@
         &wishlistEdit(\@childrenRt);
         if ($wishlistHTMLedit ne '') {
             $inner .= &Apache::loncommon::start_data_table("LC_tableOfContent");
+            $inner .= &Apache::loncommon::start_data_table_header_row('LC_visually_hidden').
+                      '<th>'.&mt('select for action').'</th>'.
+                      '<th>'.&mt('set item order').'</th>'.
+                      '<th>'.&mt('title of link or folder').'</th>'.
+                      '<th>'.&mt('link url').'</th>'.
+                      '<th>'.&mt('access to notes').'</th>'.
+                      &Apache::loncommon::end_data_table_row()."\n";
             $inner .= $wishlistHTMLedit;
             $inner .= &Apache::loncommon::end_data_table();
         }
@@ -1594,7 +1635,13 @@
     elsif ($mode eq 'view') {
         &wishlistView(\@childrenRt);
         if ($wishlistHTMLview ne '') {
-            $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLview.'</table>';
+            $inner .= '<table class="LC_data_table LC_tableOfContent">'.
+                      &Apache::loncommon::start_data_table_header_row('LC_visually_hidden').
+                      '<th>'.&mt('select for action').'</th>'.
+                      '<th>'.&mt('title of link or folder').'</th>'.
+                      '<th>'.&mt('access to notes').'</th>'.
+                      &Apache::loncommon::end_data_table_row()."\n".
+                      $wishlistHTMLview.'</table>';
         }
         else {
             $inner .= '<span class="LC_info">'.&mt("Your Stored Links list is currently empty.").'</span>';
@@ -1629,10 +1676,10 @@
     $inner .= '</form>';
 
     # end_page 
-    my $endPage =  &Apache::loncommon::end_page();
+    my $endPage = '</div>'.&Apache::loncommon::end_page();
 
     # put all page-elements together
-    my $page = $startPage.$breadcrumbs.$js.$inner.$endPage;
+    my $page = $startPage.$breadcrumbs.$js.$landmark.$inner.$endPage;
 
     return $page;
 }
@@ -1662,9 +1709,11 @@
     my $startPageWishlistlink = 
         &Apache::loncommon::start_page('Save to Stored Links',undef,
                                       {'only_body' => 1,
-                                       'bgcolor'   => '#FFFFFF',});
+                                       'bgcolor'   => '#FFFFFF',})
+       .'<div class="LC_landmark" role="main" id="LC_main_content">'."\n";
 
     my $warningLink = &mt('You must insert a title!');
+    my $labeltext = &mt('Folder where new link will be saved');
     my $warningLinkNotAllowed1 =
         &mt('You can only insert links to LON-CAPA resources from the resource-pool'.
             ' or to external websites.'.
@@ -1673,7 +1722,7 @@
     &js_escape(\$warningLink);
     &js_escape(\$warningLinkNotAllowed1);
 
-    my $inPageWishlistlink1 = '<h1>'.&mt('Save to Stored Links').'</h1>';
+    my $inPageWishlistlink1 = '<h1 class="LC_heading_1">'.&mt('Save to Stored Links').'</h1>';
     # If no title is delivered, 'New Link' is called up from the wishlist-interface, so after
     # submitting the window should close instead of offering a link to wishlist (like it should do
     # if we call 'Set New Link' from within a browsed ressource)
@@ -1686,32 +1735,32 @@
                                 'onsubmit="return newlinksubmit();" >';
     }
     $inPageWishlistlink1 .= &Apache::lonhtmlcommon::start_pick_box().
-                            &Apache::lonhtmlcommon::row_title(&mt('Link Title'));
+                            &Apache::lonhtmlcommon::row_title('<label for="title">'.&mt('Link Title').'</label>');
 
     my $inPageWishlistlink2 = &Apache::lonhtmlcommon::row_closure().
-                              &Apache::lonhtmlcommon::row_title(&mt('Path'));
+                              &Apache::lonhtmlcommon::row_title('<label for="path">'.&mt('Path').'</label>');
 
     my $inPageWishlistlink3 = &Apache::lonhtmlcommon::row_closure().
-                              &Apache::lonhtmlcommon::row_title(&mt('Note')).
-                              '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea>'.
+                              &Apache::lonhtmlcommon::row_title('<label for="note">'.&mt('Note').'</label>').
+                              '<textarea name="note" id="note" rows="3" cols="35" style="width:100%"></textarea>'.
                               &Apache::lonhtmlcommon::row_closure(1).
                               &Apache::lonhtmlcommon::end_pick_box().
                               '<br/><br/>'.
                               '<input type="submit" value="'.&mt('Save in').'" />'.
-                              '<select name="folders">'.
+                              '<select name="folders" aria-label="'.$labeltext.'">'.
                               $options.
                               '</select>'.
                               '<input type="button" value="'.&mt('Cancel').'" onclick="javascript:window.close();" />'.
                               '</form>';
     $options = '';
 
-    my $endPageWishlistlink = &Apache::loncommon::end_page();
+    my $endPageWishlistlink = '</div>'.&Apache::loncommon::end_page();
 
     my $popUp = $startPageWishlistlink.
     $inPageWishlistlink1.
-    '<input type="text" name="title" size="45" value="" />'.
+    '<input type="text" name="title" id="title" size="45" value="" />'.
     $inPageWishlistlink2.
-    '<input type="text" name="path" size="45" value="" />'.
+    '<input type="text" name="path" id="path" size="45" value="" />'.
     $inPageWishlistlink3;
 
     # JavaScript-function to set title and path of ressource automatically
@@ -1772,30 +1821,32 @@
     my $startPageWishlistfolder = 
         &Apache::loncommon::start_page('New Folder',undef,
                                       {'only_body' => 1,
-                                       'bgcolor'   => '#FFFFFF',});
+                                       'bgcolor'   => '#FFFFFF',})
+       .'<div class="LC_landmark" role="main" id="LC_main_content">'."\n";
 
     my $warningFolder = &mt('You must insert a title!');
+    my $labeltext = &mt('Folder in which new (sub)folder will be saved');
     &js_escape(\$warningFolder);
 
-    my $inPageNewFolder = '<h1>'.&mt('New Folder').'</h1>'.
+    my $inPageNewFolder = '<h1 class="LC_heading_1">'.&mt('New Folder').'</h1>'.
                           '<form method="post" name="newfolder" action="/adm/wishlist" target="wishlist" '.
                           'onsubmit="return newfoldersubmit();" >'.
                           &Apache::lonhtmlcommon::start_pick_box().
-                          &Apache::lonhtmlcommon::row_title(&mt('Folder title')).
-                          '<input type="text" name="title" size="45" value="" /><br />'.
+                          &Apache::lonhtmlcommon::row_title('<label for="foldertitle">'.&mt('Folder title').'</label>').
+                          '<input type="text" name="title" id="foldertitle" size="45" value="" /><br />'.
                           &Apache::lonhtmlcommon::row_closure().
-                          &Apache::lonhtmlcommon::row_title(&mt('Note')).
-                          '<textarea name="note" rows="3" cols="35" style="width:100%"></textarea><br />'.
+                          &Apache::lonhtmlcommon::row_title('<label for="foldernote">'.&mt('Note').'</label>').
+                          '<textarea name="note" id="foldernote" rows="3" cols="35" style="width:100%"></textarea><br />'.
                           &Apache::lonhtmlcommon::row_closure(1).
                           &Apache::lonhtmlcommon::end_pick_box().
                           '<br/><br/>'.
                           '<input type="submit" value="'.&mt('Save in').'" />'.
-                          '<select name="folders">'.
+                          '<select name="folders" aria-label="'.$labeltext.'">'.
                           $options.
                           '</select>'.
                           '<input type="button" value="'.&mt('Cancel').'" onclick="javascript:window.close();" />'.
                           '</form>';
-    my $endPageWishlistfolder = &Apache::loncommon::end_page();
+    my $endPageWishlistfolder = '</div>'.&Apache::loncommon::end_page();
 
     my $popUp = $startPageWishlistfolder.
     $inPageNewFolder;
@@ -1826,7 +1877,8 @@
     my $output =
         &Apache::loncommon::start_page($title,undef,
                                        {'only_body' => 1})
-       .'<h1>'.&mt($title).'</h1>';
+       .'<div class="LC_landmark" role="main" id="LC_main_content">'."\n"
+       .'<h1 class="LC_heading_1">'.&mt($title).'</h1>';
     
     # confirm success and offer link to wishlist
     $output .=
@@ -1839,7 +1891,7 @@
             ]);
 
     # end_page 
-    $output .= &Apache::loncommon::end_page();
+    $output .= '</div>'.&Apache::loncommon::end_page();
 
     return $output;
 }
@@ -1860,7 +1912,8 @@
     my $js = &JSforWishlist();
     $js .= &JSforImport($rat);
 
-    my $inner = '<h1>'.&mt('Import Resources from Stored Links').'</h1>';
+    my $landmark = '<div class="LC_landmark" role="main" id="LC_main_content">'."\n";
+    my $inner = '<h1 class="LC_heading_1">'.&mt('Import Resources from Stored Links').'</h1>';
     if (!$rat) {
         $inner .=
             '<ul>'.
@@ -1904,7 +1957,13 @@
     my $numskipped = 0;
     &wishlistImport(\@childrenRt,\$numskipped);
     if ($wishlistHTMLimport ne '') {
-        $inner .= '<table class="LC_data_table LC_tableOfContent">'.$wishlistHTMLimport.'</table>';
+        $inner .= '<table class="LC_data_table LC_tableOfContent">'.
+                  &Apache::loncommon::start_data_table_header_row('LC_visually_hidden').
+                  '<th>'.&mt('select link for import').'</th>'.
+                  '<th>'.&mt('title of link or folder').'</th>'.
+                  '<th>'.&mt('access to notes').'</th>'.
+                  &Apache::loncommon::end_data_table_row()."\n".
+                  $wishlistHTMLimport.'</table>';
     }
     else {
         $inner .= '<span class="LC_info">'.&mt("Your Stored Links list is currently empty.").'</span>';
@@ -1917,10 +1976,10 @@
     $inner .= '</form>';
 
     # end_page 
-    my $endPage =  &Apache::loncommon::end_page();
+    my $endPage = '</div>'.&Apache::loncommon::end_page();
 
     # put all page-elements together
-    my $page = $startPage.$js.$inner.$endPage;
+    my $page = $startPage.$js.$landmark.$inner.$endPage;
 
     return $page;
 }
Index: loncom/interface/lonwishlistdisplay.pm
diff -u loncom/interface/lonwishlistdisplay.pm:1.7 loncom/interface/lonwishlistdisplay.pm:1.8
--- loncom/interface/lonwishlistdisplay.pm:1.7	Fri Dec 24 00:48:30 2021
+++ loncom/interface/lonwishlistdisplay.pm	Sat Dec 27 05:59:46 2025
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Routines to display the wishlist (handler)
 #
-# $Id: lonwishlistdisplay.pm,v 1.7 2021/12/24 00:48:30 raeburn Exp $
+# $Id: lonwishlistdisplay.pm,v 1.8 2025/12/27 05:59:46 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -74,8 +74,9 @@
                  text => 'Stored Links'});
         my $startPage = &Apache::loncommon::start_page('Stored Links');
         my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Stored Links','Wishlist');
-        my $endpage = &Apache::loncommon::end_page();
-        $r->print($startPage.$breadcrumbs.$blocktext);
+        my $landmark = '<div class="LC_landmark" role="main" id="LC_main_content">'."\n";
+        my $endpage = '</div>'.&Apache::loncommon::end_page();
+        $r->print($startPage.$breadcrumbs.$landmark.$blocktext.$endpage);
         return OK;
     }
 


More information about the LON-CAPA-cvs mailing list