From 13b33da51a70300f9080a643b25cf2c474322fbe Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Mon, 09 Jun 2014 09:14:36 -0400
Subject: [PATCH] Managesieve: Improved UI accessibility

---
 plugins/managesieve/skins/larry/templates/vacation.html    |    6 +
 plugins/managesieve/Changelog                              |    1 
 plugins/managesieve/skins/larry/managesieve.css            |    1 
 plugins/managesieve/localization/en_US.inc                 |    5 +
 plugins/managesieve/managesieve.js                         |   49 +++++++++++-----
 plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php |   20 +++---
 plugins/managesieve/skins/larry/templates/managesieve.html |   45 ++++++++------
 7 files changed, 79 insertions(+), 48 deletions(-)

diff --git a/plugins/managesieve/Changelog b/plugins/managesieve/Changelog
index 29b359d..856b8ac 100644
--- a/plugins/managesieve/Changelog
+++ b/plugins/managesieve/Changelog
@@ -1,6 +1,7 @@
 - Added optional separate interface for out-of-office management (#1488266)
 - Fix disabled "create filter" action
 - Fix enotify/notify extension handling
+- Improved UI accessibility
 
 * version 7.2 [2014-02-14]
 -----------------------------------------------------------
diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php
index 636b5fc..2f99874 100644
--- a/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php
+++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php
@@ -216,18 +216,18 @@
             ) + $attrib);
 
         // form elements
-        $subject   = new html_inputfield(array('name' => 'vacation_subject', 'size' => 50));
-        $reason    = new html_textarea(array('name' => 'vacation_reason', 'cols' => 60, 'rows' => 8));
-        $interval  = new html_inputfield(array('name' => 'vacation_interval', 'size' => 5));
-        $addresses = '<textarea name="vacation_addresses" data-type="list" data-size="30" style="display: none">'
+        $subject   = new html_inputfield(array('name' => 'vacation_subject', 'id' => 'vacation_subject', 'size' => 50));
+        $reason    = new html_textarea(array('name' => 'vacation_reason', 'id' => 'vacation_reason', 'cols' => 60, 'rows' => 8));
+        $interval  = new html_inputfield(array('name' => 'vacation_interval', 'id' => 'vacation_interval', 'size' => 5));
+        $addresses = '<textarea name="vacation_addresses" id="vacation_addresses" data-type="list" data-size="30" style="display: none">'
             . rcube::Q(implode("\n", (array) $this->vacation['addresses']), 'strict', false) . '</textarea>';
-        $status    = new html_select(array('name' => 'vacation_status'));
+        $status    = new html_select(array('name' => 'vacation_status', 'id' => 'vacation_status'));
 
         $status->add($this->plugin->gettext('vacation.on'), 'on');
         $status->add($this->plugin->gettext('vacation.off'), 'off');
 
         if ($this->rc->config->get('managesieve_vacation') != 2 && count($this->vacation['list'])) {
-            $after = new html_select(array('name' => 'vacation_after'));
+            $after = new html_select(array('name' => 'vacation_after', 'id' => 'vacation_after'));
 
             $after->add('', '');
             foreach ($this->vacation['list'] as $idx => $rule) {
@@ -247,7 +247,7 @@
         }
 
         if ($date_extension) {
-            $date_from   = new html_inputfield(array('name' => 'vacation_datefrom', 'class' => 'datepicker', 'size' => 12));
+            $date_from   = new html_inputfield(array('name' => 'vacation_datefrom', 'id' => 'vacation_datefrom', 'class' => 'datepicker', 'size' => 12));
             $date_to     = new html_inputfield(array('name' => 'vacation_dateto', 'class' => 'datepicker', 'size' => 12));
             $date_format = $this->rc->config->get('date_format', 'Y-m-d');
 
@@ -283,12 +283,12 @@
         // Advanced tab
         $table = new html_table(array('cols' => 2));
 
-        $table->add('title', $this->plugin->gettext('vacation.addresses'));
+        $table->add('title', html::label('vacation_addresses', $this->plugin->gettext('vacation.addresses')));
         $table->add(null, $addresses);
-        $table->add('title', $this->plugin->gettext('vacation.interval'));
+        $table->add('title', html::label('vacation_interval', $this->plugin->gettext('vacation.interval')));
         $table->add(null, $interval_txt);
         if ($after) {
-            $table->add('title', $this->plugin->gettext('vacation.after'));
+            $table->add('title', html::label('vacation_after', $this->plugin->gettext('vacation.after')));
             $table->add(null, $after->show($this->vacation['idx'] - 1));
         }
 
diff --git a/plugins/managesieve/localization/en_US.inc b/plugins/managesieve/localization/en_US.inc
index 1ea7d96..6b13eff 100644
--- a/plugins/managesieve/localization/en_US.inc
+++ b/plugins/managesieve/localization/en_US.inc
@@ -177,6 +177,11 @@
 $labels['vacation.interval'] = 'Reply interval';
 $labels['vacation.after'] = 'Put vacation rule after';
 $labels['vacation.saving'] = 'Saving data...';
+$labels['arialabelfiltersetactions'] = 'Filter set actions';
+$labels['arialabelfilteractions'] = 'Filter actions';
+$labels['arialabelfilterform'] = 'Filter properties';
+$labels['ariasummaryfilterslist'] = 'List of filters';
+$labels['ariasummaryfiltersetslist'] = 'List of filter sets';
 
 $messages = array();
 $messages['filterunknownerror'] = 'Unknown server error.';
diff --git a/plugins/managesieve/managesieve.js b/plugins/managesieve/managesieve.js
index 27ab38a..e81ab33 100644
--- a/plugins/managesieve/managesieve.js
+++ b/plugins/managesieve/managesieve.js
@@ -73,7 +73,7 @@
             changeMonth: true,
             showOtherMonths: true,
             selectOtherMonths: true,
-            onSelect: function(dateText) { $(this).focus().val(dateText) }
+            onSelect: function(dateText) { $(this).focus().val(dateText); }
           });
           $('input.datepicker').datepicker();
         }
@@ -82,36 +82,36 @@
         rcmail.enable_command('plugin.managesieve-add', 'plugin.managesieve-setadd', !rcmail.env.sieveconnerror);
       }
 
-      var i, p = rcmail, setcnt, set = rcmail.env.currentset;
+      var setcnt, set = rcmail.env.currentset;
 
       if (rcmail.gui_objects.filterslist) {
         rcmail.filters_list = new rcube_list_widget(rcmail.gui_objects.filterslist,
-          {multiselect:false, draggable:true, keyboard:false});
+          {multiselect:false, draggable:true, keyboard:true});
 
         rcmail.filters_list
-          .addEventListener('select', function(e) { p.managesieve_select(e); })
-          .addEventListener('dragstart', function(e) { p.managesieve_dragstart(e); })
-          .addEventListener('dragend', function(e) { p.managesieve_dragend(e); })
+          .addEventListener('select', function(e) { rcmail.managesieve_select(e); })
+          .addEventListener('dragstart', function(e) { rcmail.managesieve_dragstart(e); })
+          .addEventListener('dragend', function(e) { rcmail.managesieve_dragend(e); })
           .addEventListener('initrow', function(row) {
-            row.obj.onmouseover = function() { p.managesieve_focus_filter(row); };
-            row.obj.onmouseout = function() { p.managesieve_unfocus_filter(row); };
+            row.obj.onmouseover = function() { rcmail.managesieve_focus_filter(row); };
+            row.obj.onmouseout = function() { rcmail.managesieve_unfocus_filter(row); };
           })
-          .init().focus();
+          .init();
       }
 
       if (rcmail.gui_objects.filtersetslist) {
         rcmail.filtersets_list = new rcube_list_widget(rcmail.gui_objects.filtersetslist,
-          {multiselect:false, draggable:false, keyboard:false});
+          {multiselect:false, draggable:false, keyboard:true});
 
-        rcmail.filtersets_list
-          .addEventListener('select', function(e) { p.managesieve_setselect(e); })
-          .init().focus();
+        rcmail.filtersets_list.init().focus();
 
         if (set != null) {
           set = rcmail.managesieve_setid(set);
-          rcmail.filtersets_list.shift_start = set;
-          rcmail.filtersets_list.highlight_row(set, false);
+          rcmail.filtersets_list.select(set);
         }
+
+        // attach select event after initial record was selected
+        rcmail.filtersets_list.addEventListener('select', function(e) { rcmail.managesieve_setselect(e); });
 
         setcnt = rcmail.filtersets_list.rowcount;
         rcmail.enable_command('plugin.managesieve-set', true);
@@ -119,9 +119,10 @@
         rcmail.enable_command('plugin.managesieve-setdel', setcnt > 1);
 
         // Fix dragging filters over sets list
-        $('tr', rcmail.gui_objects.filtersetslist).each(function (i, e) { p.managesieve_fixdragend(e); });
+        $('tr', rcmail.gui_objects.filtersetslist).each(function (i, e) { rcmail.managesieve_fixdragend(e); });
       }
     }
+
     if (rcmail.gui_objects.sieveform && rcmail.env.rule_disabled)
       $('#disabled').attr('checked', true);
   });
@@ -778,6 +779,7 @@
 
   input = $('input', elem).attr(attrs).keydown(function(e) {
     var input = $(this);
+
     // element creation event (on Enter)
     if (e.which == 13) {
       var name = input.attr('name').replace(/\[\]$/, ''),
@@ -787,6 +789,21 @@
       input.parent().after(elem);
       $('input', elem).focus();
     }
+    // backspace or delete: remove input, focus previous one
+    else if ((e.which == 8 || e.which == 46) && input.val() == '') {
+
+      var parent = input.parent(), siblings = parent.parent().children();
+
+      if (siblings.length > 1) {
+        if (parent.prev().length)
+          parent.prev().children('input').focus();
+        else
+          parent.next().children('input').focus();
+
+        parent.remove();
+        return false;
+      }
+    }
   });
 
   // element deletion event
diff --git a/plugins/managesieve/skins/larry/managesieve.css b/plugins/managesieve/skins/larry/managesieve.css
index 2172c60..a0526eb 100644
--- a/plugins/managesieve/skins/larry/managesieve.css
+++ b/plugins/managesieve/skins/larry/managesieve.css
@@ -306,6 +306,7 @@
   font-size: 11px;
   padding: 1px;
   vertical-align: middle;
+  max-width: 280px;
 }
 
 html.mozilla #filter-form select
diff --git a/plugins/managesieve/skins/larry/templates/managesieve.html b/plugins/managesieve/skins/larry/templates/managesieve.html
index 471bbf4..8d648db 100644
--- a/plugins/managesieve/skins/larry/templates/managesieve.html
+++ b/plugins/managesieve/skins/larry/templates/managesieve.html
@@ -8,35 +8,38 @@
 
 <roundcube:include file="/includes/header.html" />
 
+<h1 class="voice"><roundcube:label name="settings" /> : <roundcube:label name="managesieve.filters" /></h1>
+
 <div id="mainscreen" class="offset">
 
 <roundcube:include file="/includes/settingstabs.html" />
 
-<div id="settings-right">
-<div id="filtersetslistbox" class="uibox listbox">
-<h2 class="boxtitle"><roundcube:label name="managesieve.filtersets" /></h2>
+<div id="settings-right" role="main">
+<div id="filtersetslistbox" class="uibox listbox" aria-labelledby="aria-label-filtersets">
+<h2 class="boxtitle" id="aria-label-filtersets"><roundcube:label name="managesieve.filtersets" /></h2>
 <div class="scroller withfooter">
-  <roundcube:object name="filtersetslist" id="filtersetslist" class="listing" cellspacing="0" summary="Filters list" type="list" noheader="true" />
+  <roundcube:object name="filtersetslist" id="filtersetslist" class="listing" summary="managesieve.ariasummaryfiltersetslist" type="list" noheader="true" role="listbox" />
 </div>
 <div class="boxfooter">
-  <roundcube:button command="plugin.managesieve-setadd" type="link" title="managesieve.filtersetadd" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="filtersetmenulink" id="filtersetmenulink" type="link" title="moreactions" class="listbutton groupactions" onclick="UI.show_popup('filtersetmenu');return false" innerClass="inner" content="&#9881;" />
+  <roundcube:button command="plugin.managesieve-setadd" type="link" title="managesieve.filtersetadd" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="filtersetmenulink" id="filtersetmenulink" type="link" title="moreactions" class="listbutton groupactions" onclick="return UI.toggle_popup('filtersetmenu', event)" innerClass="inner" content="&#9881;" aria-haspopup="true" aria-expanded="false" aria-owns="filtersetmenu-menu" />
 </div>
 </div>
 
 <div id="filtersscreen">
-<div id="filterslistbox" class="uibox listbox">
-<h2 class="boxtitle"><roundcube:label name="managesieve.filters" /></h2>
+<div id="filterslistbox" class="uibox listbox" aria-labelledby="aria-label-filters">
+<h2 class="boxtitle" id="aria-label-filters"><roundcube:label name="managesieve.filters" /></h2>
 <div class="scroller withfooter">
-  <roundcube:object name="filterslist" id="filterslist" class="listing" cellspacing="0" summary="Filters list" noheader="true" />
+  <roundcube:object name="filterslist" id="filterslist" class="listing" summary="managesieve.ariasummaryfilterslist" noheader="true" role="listbox" />
 </div>
 <div class="boxfooter">
-  <roundcube:button command="plugin.managesieve-add" type="link" title="managesieve.filteradd" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="filtermenulink" id="filtermenulink" type="link" title="moreactions" class="listbutton groupactions" onclick="UI.show_popup('filtermenu');return false" innerClass="inner" content="&#9881;" />
+  <roundcube:button command="plugin.managesieve-add" type="link" title="managesieve.filteradd" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="filtermenulink" id="filtermenulink" type="link" title="moreactions" class="listbutton groupactions" onclick="return UI.toggle_popup('filtermenu', event)" innerClass="inner" content="&#9881;" aria-haspopup="true" aria-expanded="false" aria-owns="filtermenu-menu" />
 </div>
 </div>
 
 <div id="filter-box" class="uibox contentbox">
-  <div class="iframebox">
-    <roundcube:object name="filterframe" id="filter-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
+  <div class="iframebox" role="complementary" aria-labelledby="aria-label-filterform">
+    <h2 id="aria-label-filterframe" class="voice"><roundcube:label name="managesieve.arialabelfilterform" /></h2>
+    <roundcube:object name="filterframe" id="filter-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" title="managesieve.arialabelfilterform" />
   </div>
 </div>
 
@@ -44,19 +47,21 @@
 </div>
 </div>
 
-<div id="filtersetmenu" class="popupmenu">
-  <ul class="toolbarmenu">
-    <li><roundcube:button command="plugin.managesieve-setact" label="managesieve.enable" classAct="active" /></li>
-    <li><roundcube:button command="plugin.managesieve-setdel" label="delete" classAct="active" /></li>
-    <li class="separator_above"><roundcube:button command="plugin.managesieve-setget" label="download" classAct="active" /></li>
+<div id="filtersetmenu" class="popupmenu" aria-hidden="true">
+  <h3 id="aria-label-setactions" class="voice"><roundcube:label name="managesieve.arialabelfiltersetactions" /></h3>
+  <ul class="toolbarmenu" id="filtersetmenu-menu" role="menu" aria-labelledby="aria-label-setactions">
+    <li role="menuitem"><roundcube:button command="plugin.managesieve-setact" label="managesieve.enable" classAct="active" /></li>
+    <li role="menuitem"><roundcube:button command="plugin.managesieve-setdel" label="delete" classAct="active" /></li>
+    <li role="menuitem" class="separator_above"><roundcube:button command="plugin.managesieve-setget" label="download" classAct="active" /></li>
     <roundcube:container name="filtersetoptions" id="filtersetmenu" />
   </ul>
 </div>
 
-<div id="filtermenu" class="popupmenu">
-  <ul class="toolbarmenu">
-    <li><roundcube:button command="plugin.managesieve-act" label="managesieve.enable" classAct="active" /></li>
-    <li><roundcube:button command="plugin.managesieve-del" label="delete" classAct="active" /></li>
+<div id="filtermenu" class="popupmenu" aria-hidden="true">
+  <h3 id="aria-label-filteractions" class="voice"><roundcube:label name="managesieve.arialabelfilteractions" /></h3>
+  <ul class="toolbarmenu" id="filtermenu-menu" role="menu" aria-labelledby="aria-label-filteractions">
+    <li role="menuitem"><roundcube:button command="plugin.managesieve-act" label="managesieve.enable" classAct="active" /></li>
+    <li role="menuitem"><roundcube:button command="plugin.managesieve-del" label="delete" classAct="active" /></li>
     <roundcube:container name="filteroptions" id="filtermenu" />
   </ul>
 </div>
diff --git a/plugins/managesieve/skins/larry/templates/vacation.html b/plugins/managesieve/skins/larry/templates/vacation.html
index c91eb87..26dd202 100644
--- a/plugins/managesieve/skins/larry/templates/vacation.html
+++ b/plugins/managesieve/skins/larry/templates/vacation.html
@@ -10,11 +10,13 @@
 
 <div id="mainscreen" class="offset">
 
+<h1 class="voice"><roundcube:label name="settings" /> : <roundcube:label name="managesieve.vacation" /></h1>
+
 <roundcube:include file="/includes/settingstabs.html" />
 
-<div id="managesieve-vacation" class="uibox contentbox">
+<div id="managesieve-vacation" class="uibox contentbox" role="main" aria-labelledby="aria-label-vacationform">
   <div>
-    <h2 class="boxtitle"><roundcube:label name="managesieve.vacation" /></h2>
+    <h2 class="boxtitle" id="aria-label-vacationform"><roundcube:label name="managesieve.vacation" /></h2>
     <roundcube:object name="vacationform" id="vacationform" class="propform boxcontent tabbed" />
   </div>
   <div class="footerleft formbuttons">

--
Gitblit v1.9.1