From 62c45161c4efd101b940134b60bb73881746e5f5 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Tue, 10 Jun 2014 08:40:22 -0400
Subject: [PATCH] ACL: Improved UI accessibility

---
 plugins/acl/skins/larry/acl.css              |   17 +++-----
 plugins/acl/skins/larry/templates/table.html |   21 ++++++----
 plugins/acl/localization/en_US.inc           |    5 ++
 plugins/acl/acl.php                          |    4 +-
 plugins/acl/acl.js                           |   52 +++++++++++++++-----------
 5 files changed, 55 insertions(+), 44 deletions(-)

diff --git a/plugins/acl/acl.js b/plugins/acl/acl.js
index acea60a..d82725c 100644
--- a/plugins/acl/acl.js
+++ b/plugins/acl/acl.js
@@ -140,15 +140,16 @@
 rcube_webmail.prototype.acl_list_init = function()
 {
     var method = this.env.acl_advanced ? 'addClass' : 'removeClass';
+
     $('#acl-switch')[method]('selected');
     $(this.gui_objects.acltable)[method]('advanced');
 
     this.acl_list = new rcube_list_widget(this.gui_objects.acltable,
-        {multiselect:true, draggable:false, keyboard:true, toggleselect:true});
-    this.acl_list.addEventListener('select', function(o) { rcmail.acl_list_select(o); });
-    this.acl_list.addEventListener('dblclick', function(o) { rcmail.acl_list_dblclick(o); });
-    this.acl_list.addEventListener('keypress', function(o) { rcmail.acl_list_keypress(o); });
-    this.acl_list.init();
+        {multiselect: true, draggable: false, keyboard: true});
+    this.acl_list.addEventListener('select', function(o) { rcmail.acl_list_select(o); })
+        .addEventListener('dblclick', function(o) { rcmail.acl_list_dblclick(o); })
+        .addEventListener('keypress', function(o) { rcmail.acl_list_keypress(o); })
+        .init();
 }
 
 // ACL table row selection handler
@@ -228,16 +229,23 @@
         row = $('thead > tr', table).clone();
 
     // Update new row
-    $('td', row).map(function() {
-        var r, cl = this.className.replace(/^acl/, '');
+    $('th', row).map(function() {
+        var td = $('<td>'),
+            title = $(this).attr('title'),
+            cl = this.className.replace(/^acl/, '');
+
+        if (title)
+            td.attr('title', title);
 
         if (items && items[cl])
             cl = items[cl];
 
         if (cl == 'user')
-            $(this).text(o.username);
+            td.addClass(cl).append($('<a>').text(o.username));
         else
-            $(this).addClass(rcmail.acl_class(o.acl, cl)).text('');
+            td.addClass(this.className + ' ' + rcmail.acl_class(o.acl, cl)).text('');
+
+        $(this).replaceWith(td);
     });
 
     row.attr('id', 'rcmrow'+id);
@@ -281,10 +289,10 @@
 {
     var ul, row, td, val = '', type = 'user', li_elements, body = $('body'),
         adv_ul = $('#advancedrights'), sim_ul = $('#simplerights'),
-        name_input = $('#acluser');
+        name_input = $('#acluser'), type_list = $('#usertype');
 
     if (!this.acl_form) {
-        var fn = function () { $('input[value=user]').prop('checked', true); };
+        var fn = function () { $('input[value="user"]').prop('checked', true); };
         name_input.click(fn).keypress(fn);
     }
 
@@ -329,23 +337,24 @@
 
     this.acl_id = id;
 
-    var me = this, inst = window.rcmail, body = document.body;
-    var buttons = {};
-    buttons[rcmail.gettext('save')] = function(e) { inst.command('acl-save'); };
-    buttons[rcmail.gettext('cancel')] = function(e) { inst.command('acl-cancel'); };
+    var buttons = {}, me = this, body = document.body;
+
+    buttons[this.gettext('save')] = function(e) { me.command('acl-save'); };
+    buttons[this.gettext('cancel')] = function(e) { me.command('acl-cancel'); };
 
     // display it as popup
-    this.acl_popup = rcmail.show_popup_dialog(
+    this.acl_popup = this.show_popup_dialog(
         '<div style="width:480px;height:280px">&nbsp;</div>',
-        id ? rcmail.gettext('acl.editperms') : rcmail.gettext('acl.newuser'),
+        id ? this.gettext('acl.editperms') : this.gettext('acl.newuser'),
         buttons,
         {
             modal: true,
-            closeOnEscape: false,
+            closeOnEscape: true,
             close: function(e, ui) {
-                (rcmail.is_framed() ? parent.rcmail : rcmail).ksearch_hide();
+                (me.is_framed() ? parent.rcmail : me).ksearch_hide();
                 me.acl_form.appendTo(body).hide();
                 $(this).remove();
+                window.focus(); // focus iframe
             }
         }
     );
@@ -354,9 +363,8 @@
 
     if (type == 'user')
         name_input.focus();
-
-    // unfocus the list, make backspace key in name input field working
-    this.acl_list.blur();
+    else
+        $('input:checked', type_list).focus();
 }
 
 // Returns class name according to ACL comparision result
diff --git a/plugins/acl/acl.php b/plugins/acl/acl.php
index d7e50f9..95d4eda 100644
--- a/plugins/acl/acl.php
+++ b/plugins/acl/acl.php
@@ -286,7 +286,7 @@
 
         $textfield = new html_inputfield($attrib);
 
-        $fields['user'] = html::label(array('for' => 'iduser'), $this->gettext('username'))
+        $fields['user'] = html::label(array('for' => $attrib['id']), $this->gettext('username'))
             . ' ' . $textfield->show();
 
         // Add special entries
@@ -401,7 +401,7 @@
             }
 
             $table->add_row(array('id' => 'rcmrow'.$userid));
-            $table->add('user', rcube::Q($user));
+            $table->add('user', html::a(array('id' => 'rcmlinkrow'.$userid), rcube::Q($user)));
 
             foreach ($items as $key => $right) {
                 $in = $this->acl_compare($userrights, $right);
diff --git a/plugins/acl/localization/en_US.inc b/plugins/acl/localization/en_US.inc
index 23501da..ff8dde7 100644
--- a/plugins/acl/localization/en_US.inc
+++ b/plugins/acl/localization/en_US.inc
@@ -87,6 +87,11 @@
 $labels['longaclread'] = 'The folder can be opened for reading';
 $labels['longaclwrite'] = 'Messages can be marked, written or copied to the folder';
 $labels['longacldelete'] = 'Messages can be deleted';
+$labels['longaclother'] = 'Other access rights';
+
+$labels['ariasummaryacltable'] = 'List of access rights';
+$labels['arialabelaclactions'] = 'List actions';
+$labels['arialabelaclform'] = 'Access rights form';
 
 $messages['deleting'] = 'Deleting access rights...';
 $messages['saving'] = 'Saving access rights...';
diff --git a/plugins/acl/skins/larry/acl.css b/plugins/acl/skins/larry/acl.css
index 96c1092..bd72b3c 100644
--- a/plugins/acl/skins/larry/acl.css
+++ b/plugins/acl/skins/larry/acl.css
@@ -26,44 +26,39 @@
   border: none;
 }
 
+#acltable th,
 #acltable td
 {
   white-space: nowrap;
   text-align: center;
 }
 
-#acltable thead tr td
+#acltable thead tr th
 {
-  border-left: #BBD3DA dotted 1px;
   font-size: 11px;
   font-weight: bold;
-  width: auto;
 }
 
 #acltable tbody td
 {
-  border-bottom: #DDDDDD 1px solid;
   text-align: center;
   height: 16px;
   cursor: default;
 }
 
-#acltable thead td.user
+#acltable thead tr > .user
 {
   width: 30%;
+  border-left: none;
 }
 
-#acltable.advanced thead td.user {
-	width: 25%;
+#acltable.advanced thead tr > .user {
+  width: 25%;
 }
 
 #acltable tbody td.user
 {
   text-align: left;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  -o-text-overflow: ellipsis;
-  border-left: none;
 }
 
 #acltable tbody td.partial
diff --git a/plugins/acl/skins/larry/templates/table.html b/plugins/acl/skins/larry/templates/table.html
index c0b8329..3f203e4 100644
--- a/plugins/acl/skins/larry/templates/table.html
+++ b/plugins/acl/skins/larry/templates/table.html
@@ -1,23 +1,26 @@
 <div id="aclcontainer" class="uibox listbox">
-<div id="acllist-content" class="scroller withfooter">
-    <roundcube:object name="acltable" id="acltable" class="records-table" />
+<div id="acllist-content" class="scroller withfooter" aria-labelledby="aria-label-acltable">
+    <h2 class="boxtitle" id="aria-label-acltable"><roundcube:label name="acl.ariasummaryacltable" /></h2>
+    <roundcube:object name="acltable" id="acltable" class="records-table" summary="acl.ariasummaryacltable" role="listbox" />
 </div>
 <div id="acllist-footer" class="boxfooter">
-    <roundcube:button command="acl-create" id="aclcreatelink" type="link" title="acl.newuser" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="aclmenulink" id="aclmenulink" type="link" title="acl.actions" class="listbutton groupactions"onclick="UI.show_popup('aclmenu', undefined, {above:1});return false" innerClass="inner" content="&#9881;" />
+    <roundcube:button command="acl-create" id="aclcreatelink" type="link" title="acl.newuser" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="aclmenulink" id="aclmenulink" type="link" title="acl.actions" class="listbutton groupactions" onclick="return UI.toggle_popup('aclmenu', event)" innerClass="inner" content="&#9881;" aria-haspopup="true" aria-expanded="false" aria-owns="aclmenu-menu" />
 </div>
 </div>
 
-<div id="aclmenu" class="popupmenu">
-    <ul class="toolbarmenu selectable iconized">
-        <li><roundcube:button command="acl-edit" label="edit" class="icon" classAct="icon active" innerclass="icon edit" /></li>
-        <li><roundcube:button command="acl-delete" label="delete" class="icon" classAct="icon active" innerclass="icon delete" /></li>
+<div id="aclmenu" class="popupmenu" aria-hidden="true" data-align="bottom">
+    <h3 id="aria-label-aclactions" class="voice"><roundcube:label name="acl.arialabelaclactions" /></h3>
+    <ul id="aclmenu-menu" class="toolbarmenu selectable iconized" role="menu" aria-labelledby="aria-label-aclactions">
+        <li role="menuitem"><roundcube:button command="acl-edit" label="edit" class="icon" classAct="icon active" innerclass="icon edit" /></li>
+        <li role="menuitem"><roundcube:button command="acl-delete" label="delete" class="icon" classAct="icon active" innerclass="icon delete" /></li>
         <roundcube:if condition="!in_array('acl_advanced_mode', (array)config:dont_override)" />
-            <li><roundcube:button name="acl-switch" id="acl-switch" label="acl.advanced" onclick="rcmail.command('acl-mode-switch');return false" class="active" /></li>
+            <li role="menuitem"><roundcube:button name="acl-switch" id="acl-switch" label="acl.advanced" onclick="rcmail.command('acl-mode-switch');return false" class="active" /></li>
         <roundcube:endif />
     </ul>
 </div>
 
-<div id="aclform" class="propform" style="position:absolute; width:480px; top:0; left:0; padding:8px">
+<div id="aclform" class="propform" style="position:absolute; width:480px; top:0; left:0; padding:8px" aria-labelledby="aria-label-aclform" aria-hidden="true" role="dialog">
+    <h3 id="aria-label-aclform" class="voice"><roundcube:label name="acl.arialabelaclform" /></h3>
     <fieldset class="thinbordered"><legend><roundcube:label name="acl.identifier" /></legend>
         <roundcube:object name="acluser" id="acluser" size="35" class="proplist" />
     </fieldset>

--
Gitblit v1.9.1