Bug 14060: Remove readonly attributes on date inputs
authorJonathan Druart <jonathan.druart@biblibre.com>
Fri, 24 Apr 2015 13:36:34 +0000 (15:36 +0200)
committerKyle M Hall <kyle@bywatersolutions.com>
Thu, 8 Sep 2016 12:04:19 +0000 (12:04 +0000)
In order to remove accessibility issues due to the readonly attributes
on date inputs, this patch will remove them and introduce a javascript
validation on them.

This patch is not perfect for some reason:
I didn't manage to force the user to select a valid date. One solution
would be to reopen the datepicker plugin until a valid date is inserted.
But it could be annoying for users (and for me: I did not manage to
implement this solution).
You will note that input is emptied if the date is not valid. This is a
quick and efficient solution to prevent submitting invalid date and make
Koha explodes. A proper solution would be to implement the check server
side send a friendly message to the user.

Test plan:
For all inputs, try an invalid and a valid date.
 1/ Debar a patron
 2/ On the checkout tables (circulation and moremember), add a renewal
due date (at the bottom of the tables)
 3/ On the checkout page, specify a due date
 4/ On the return page, specify a return date
 5/ On the invoice page (acquisition module), enter a shipment and
billing date
 6/ On the invoice search page (invoices.pl) use filters shipment and
billing dates
 7/ On the offline circ page, specify a due date
 8/ On the edit patron page (memberentry), add a debarment
 9/ On the reserve page (reserve/request.pl), use the date inputs to
suspend until a defined date
10/ Edit patrons in a batch (tools/modborrowers.pl) and use the
registration and expiry date inputs

Signed-off-by: Owen Leonard <oleonard@myacpl.org>

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>

13 files changed:
koha-tmpl/intranet-tmpl/prog/en/includes/borrower_debarments.inc
koha-tmpl/intranet-tmpl/prog/en/includes/calendar.inc
koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table-footer.inc
koha-tmpl/intranet-tmpl/prog/en/modules/acqui/invoice.tt
koha-tmpl/intranet-tmpl/prog/en/modules/acqui/invoices.tt
koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
koha-tmpl/intranet-tmpl/prog/en/modules/circ/offline.tt
koha-tmpl/intranet-tmpl/prog/en/modules/circ/returns.tt
koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt
koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt
koha-tmpl/intranet-tmpl/prog/en/modules/reserve/request.tt
koha-tmpl/intranet-tmpl/prog/en/modules/tools/modborrowers.tt
koha-tmpl/intranet-tmpl/prog/js/pages/circulation.js

index 9944ec4..a72a76e 100644 (file)
@@ -85,7 +85,7 @@
                 <legend>Add manual restriction</legend>
                 <ol>
                     <li><label for="rcomment">Comment:</label> <input type="text" id="rcomment" name="comment" /></li>
-                    <li><label for="rexpiration">Expiration:</label> <input name="expiration" id="rexpiration" size="10" readonly="readonly" value="" class="datepicker" />
+                    <li><label for="rexpiration">Expiration:</label> <input name="expiration" id="rexpiration" size="10" value="" class="datepicker" />
                         <a href="#" class="clear-date" id="clear-date-rexpiration">Clear date</a></li>
                 </ol>
             <fieldset class="action"><input type="submit" value="Add restriction" /> <a href="#" class="cancel" id="cancel_manual_restriction">Cancel</a></fieldset>
index 9b6dd52..7439304 100644 (file)
@@ -6,6 +6,24 @@ var debug    = "[% debug %]";
 var dformat  = "[% dateformat %]";
 var sentmsg = 0;
 if (debug > 1) {alert("dateformat: " + dformat + "\ndebug is on (level " + debug + ")");}
+var MSG_PLEASE_ENTER_A_VALID_DATE = ("Please enter a valid date.");
+
+function validate_date (dateText, inst) {
+    var dateformat = '[% Koha.Preference('dateformat') %]';
+    if ( dateformat == 'us' ) {
+        dateformat = 'mm/dd/yy';
+    } else if ( dateformat == 'metric' ) {
+        dateformat = 'dd/mm/yy';
+    } else if (dateformat == 'iso' ) {
+        dateformat = 'yy-mm-dd';
+    }
+    try {
+        $.datepicker.parseDate(dateformat, dateText);
+    } catch (e) {
+        alert(MSG_PLEASE_ENTER_A_VALID_DATE);
+        $('#'+inst.id).val('');
+    };
+}
 
 function Date_from_syspref(dstring) {
         var dateX = dstring.split(/[-/.]/);
@@ -89,7 +107,11 @@ $.datepicker.setDefaults({
         selectOtherMonths: true
     });
 
-    $( ".datepicker" ).datepicker();
+    $( ".datepicker" ).datepicker({
+        onClose: function(dateText, inst) {
+            validate_date(dateText, inst);
+        },
+    });
     // http://jqueryui.com/demos/datepicker/#date-range
     var dates = $( ".datepickerfrom, .datepickerto" ).datepicker({
         changeMonth: true,
@@ -102,7 +124,10 @@ $.datepicker.setDefaults({
                     $.datepicker._defaults.dateFormat,
                     selectedDate, instance.settings );
             dates.not( this ).datepicker( "option", option, date );
-        }
+        },
+        onClose: function(dateText, inst) {
+            validate_date(dateText, inst);
+        },
     });
 });
 //]]>
index 0039c8e..a8b906b 100644 (file)
@@ -5,7 +5,7 @@
         <td id="totalfine" style="text-align: right;">[% finetotal %]</td>
         <td id="totalprice" style="text-align: right;">[% totalprice %]</td>
                 <td colspan="3"><div class="date-select">
-            <p><label for="newduedate">Renewal due date:</label> <input type="text" size="12" id="newduedate" name="newduedate" value="[% newduedate %]" readonly="readonly" />
+            <p><label for="newduedate">Renewal due date:</label> <input type="text" size="12" id="newduedate" name="newduedate" value="[% newduedate %]" />
 </p>
             <p><label for="exemptfine">Forgive fines on return: <input type="checkbox" id="exemptfine" name="exemptfine" value="1" /></label></p></div>
                </td>
index 8f8a317..5b8ee46 100644 (file)
         <fieldset class="rows">
             <ol>
             <li><label for="shipmentdate">Shipment date:</label>
-                    <input type="text" size="10" id="shipmentdate" name="shipmentdate" value="[% shipmentdate | $KohaDates %]" readonly="readonly" class="datepicker" /></li>
+                    <input type="text" size="10" id="shipmentdate" name="shipmentdate" value="[% shipmentdate | $KohaDates %]" class="datepicker" /></li>
 
             <li><label for="billingdate">Billing date:</label>
-                    <input type="text" size="10" id="billingdate" name="billingdate" value="[% billingdate | $KohaDates %]" readonly="readonly" class="datepicker" /></li>
+                    <input type="text" size="10" id="billingdate" name="billingdate" value="[% billingdate | $KohaDates %]" class="datepicker" /></li>
 
             <li><label for="shipmentcost">Shipping cost:</label>
                     <input type="text" size="10" id="shipmentcost" name="shipmentcost" value="[% shipmentcost %]" /></li>
index e94f2c0..4b46915 100644 (file)
@@ -180,10 +180,10 @@ $(document).ready(function() {
                     </table></li>
                     <li><label for="merge_invoicenumber">Invoice number:</label><span id="merge_invoicenumber"></span></li>
                     <li><label for="merge_shipmentdate">Shipment date:</label>
-                            <input type="text" size="10" id="merge_shipmentdate" name="shipmentdate" value="" readonly="readonly" class="datepicker" /></li>
+                            <input type="text" size="10" id="merge_shipmentdate" name="shipmentdate" value="" class="datepicker" /></li>
 
                     <li><label for="merge_billingdate">Billing date:</label>
-                            <input type="text" size="10" id="merge_billingdate" name="billingdate" value="" readonly="readonly" class="datepicker" /></li>
+                            <input type="text" size="10" id="merge_billingdate" name="billingdate" value="" class="datepicker" /></li>
 
                     <li><label for="merge_shipmentcost">Shipment cost:</label>
                             <input type="text" size="10" id="merge_shipmentcost" name="shipmentcost" value="" /></li>
index 545d0e8..84ddc48 100644 (file)
@@ -71,7 +71,11 @@ function toggle_onsite_checkout(){
         [% END %]
     } else {
         $("#duedatespec").datetimepicker({
-            onClose: function(dateText, inst) { $("#barcode").focus(); },
+            onClose: function(dateText, inst) {
+                if (validate_date(dateText, inst) ) {
+                    $("#barcode").focus();
+                }
+            },
             hour: 23,
             minute: 59
         });
@@ -335,7 +339,7 @@ $(document).ready(function() {
     [% IF ( DEBT ) %]<input type="hidden" name="debt_confirmed" value="1" />[% END %]
     [% IF ( INVALID_DATE ) %]
     <p>
-    <input type="text" size="13" id="duedatespec" name="duedatespec" readonly="readonly" value="[% duedatespec %]" />
+    <input type="text" size="13" id="duedatespec" name="duedatespec" value="[% duedatespec %]" />
     <label for="duedatespec">Due date</label>
     </p>
     [% ELSE %]
@@ -597,9 +601,9 @@ No patron matched <span class="ex">[% message | html %]</span>
                 <div id="specify-due-date" class="checkout-setting">
                     <div class="hint">Specify due date [% INCLUDE 'date-format.inc' %]: </div>
                     [% IF ( duedatespec ) %]
-                        <input type="text" size="13" id="duedatespec" name="duedatespec" value="[% duedatespec %]" readonly="readonly" />
+                        <input type="text" size="13" id="duedatespec" name="duedatespec" value="[% duedatespec %]" />
                     [% ELSE %]
-                        <input type="text" size="13" id="duedatespec" name="duedatespec" value="" readonly="readonly" />
+                        <input type="text" size="13" id="duedatespec" name="duedatespec" value="" />
                     [% END %]
                     <label for="stickyduedate"> Remember for session:</label>
                     [% IF ( stickyduedate ) %]
@@ -647,7 +651,7 @@ No patron matched <span class="ex">[% message | html %]</span>
                 [% IF noissues %]
                     <div class="onsite-checkout-only">
                         <input type="checkbox" id="onsite_checkout" name="onsite_checkout_forced" checked="checked" disabled="disabled" /> <label for="onsite_checkout">On-site checkouts only. Automatic due date: </label>
-                        <input type="text" name="duedatespec" id="duedatespec" readonly="readonly" />
+                        <input type="text" name="duedatespec" id="duedatespec" />
                         <input type="hidden" name="onsite_checkout" checked="checked" value="1" />
                     </div>
                 [% ELSE %]
index b50f038..2bf0868 100644 (file)
@@ -553,7 +553,11 @@ $(document).ready(function () {
         minute: 59
     });
     $("#duedatespec").datetimepicker({
-        onClose: function(dateText, inst) { setTimeout(function() { $('#checkout-barcode').trigger('focus'), 1 }); },
+        onClose: function(dateText, inst) {
+            if (validate_date(dateText, inst) ) {
+                setTimeout(function() { $('#checkout-barcode').trigger('focus'), 1 });
+            }
+        },
         hour: 23,
         minute: 59
     });
@@ -690,7 +694,7 @@ $(document).ready(function () {
 
                             <div class="date-select">
                                 <div class="hint">Specify due date [% INCLUDE 'date-format.inc' %]: </div>
-                                <input type="text" size="13" id="duedatespec" name="duedatespec" value="[% duedatespec %]" readonly="readonly" />
+                                <input type="text" size="13" id="duedatespec" name="duedatespec" value="[% duedatespec %]" />
                                 <label for="stickyduedate"> Remember for session:</label>
                                 <input type="checkbox" id="stickyduedate" onclick="this.form.barcode.focus();" name="stickyduedate" checked="checked" />
                                 <input type="button" class="action" id="cleardate" value="Clear" name="cleardate" onclick="this.checked = false; this.form.duedatespec.value = ''; this.form.stickyduedate.checked = false; this.form.barcode.focus(); return false;" />
index bca3da0..c59f067 100644 (file)
@@ -58,7 +58,11 @@ $(document).ready(function () {
             }, columns_settings);
 
     $("#return_date_override").datetimepicker({
-        onClose: function(dateText, inst) { $("#barcode").focus(); },
+        onClose: function(dateText, inst) {
+            if (validate_date(dateText, inst) ) {
+                $("#barcode").focus();
+            }
+        },
         defaultDate: -1,
         hour: 23,
         minute: 59,
index 5469517..66d2835 100644 (file)
@@ -1003,7 +1003,7 @@ $(document).ready(function() {
                         <legend id="manual_restriction_lgd">Add manual restriction</legend>
                         <ol>
                             <li><label for="debarred_comment">Comment: </label><input type="text" id="debarred_comment" name="debarred_comment" onchange="$('#add_debarment').val(1);" /></li>
-                            <li><label for="debarred_expiration">Expiration: </label><input name="debarred_expiration" id="debarred_expiration" size="10" readonly="readonly" value="" class="datepicker" onchange="$('#add_debarment').val(1);" />
+                            <li><label for="debarred_expiration">Expiration: </label><input name="debarred_expiration" id="debarred_expiration" size="10" value="" class="datepicker" onchange="$('#add_debarment').val(1);" />
                                     <a href='javascript:void(0)' onclick="$('#debarred_expiration').val('');">Clear date</a></li>
 
                         </ol>
index 65bdb7a..72c1548 100644 (file)
@@ -84,11 +84,6 @@ $(document).ready(function() {
        });[% END %]
 
     $("#suspend_until").datepicker({ minDate: 1 }); // require that hold suspended until date is after today
-    $("#newduedate").datetimepicker({
-        minDate: 1, // require that renewal date is after today
-        hour: 23,
-        minute: 59
-    });
     $("#view_restrictions").on("click",function(){
         $('#debarments-tab-link').click();
     });
index 38f9db7..061bbb2 100644 (file)
@@ -401,14 +401,14 @@ function checkMultiHold() {
        [% IF ( reserve_in_future ) %]
        <li>
         <label for="from">Hold starts on date:</label>
-        <input name="reserve_date" id="from" size="10" readonly="readonly" class="datepickerfrom">
+        <input name="reserve_date" id="from" size="10" class="datepickerfrom">
         <a href="#" id="clear-date-from" class="clear-date">Clear date</a>
        </li>
        [% END %]
 
        <li>
         <label for="to">Hold expires on date:</label>
-        <input name="expiration_date" id="to" size="10" readonly="readonly" class="datepickerto" />
+        <input name="expiration_date" id="to" size="10" class="datepickerto" />
         <a href="#" id="clear-date-to" class="clear-date">Clear date</a>
        </li>
 
@@ -881,7 +881,7 @@ function checkMultiHold() {
 
             [% IF AutoResumeSuspendedHolds %]
                 <label for="suspend_until_[% reserveloo.reserve_id %]">[% IF ( reserveloo.suspend ) %] on [% ELSE %] until [% END %]</label>
-                <input name="suspend_until" id="suspend_until_[% reserveloo.reserve_id %]" size="10" readonly="readonly" value="[% reserveloo.suspend_until | $KohaDates %]" class="datepicker suspend_until_datepicker" />
+                <input name="suspend_until" id="suspend_until_[% reserveloo.reserve_id %]" size="10" value="[% reserveloo.suspend_until | $KohaDates %]" class="datepicker suspend_until_datepicker" />
                 <a href='#' onclick="document.getElementById('suspend_until_[% reserveloo.reserve_id %]').value='';">Clear date</a>
             [% ELSE %]
                 <input type="hidden" name="suspend_until" id="suspend_until_[% reserveloo.reserve_id %]" value=""/>
index 54227e6..1b7f9fa 100644 (file)
@@ -1,3 +1,4 @@
+[% USE Koha %]
 [% USE KohaDates %]
 [% INCLUDE 'doc-head-open.inc'%]
 <title>Koha &rsaquo; Tools &rsaquo; Batch patron modification</title>
                                             [% END %]
                                             </label>
                                             [% IF field.mandatory %]
-                                                <input type="checkbox" title="This field is mandatory" name="disable_input" value="[% field.name %]" readonly="readonly" onclick="return false;" />
+                                                <input type="checkbox" title="This field is mandatory" name="disable_input" value="[% field.name %]" onclick="return false;" />
                                             [% ELSE %]
                                                 <input type="checkbox" title="Check to delete this field" name="disable_input" value="[% field.name %]" />
                                             [% END %]
                                                 [% END %]
                                             [% END %]
                                             [% IF ( field.type == 'date' ) %]
-                                                <input type="text" name="[% field.name %]" id="[% field.name %]" value="" size="10" maxlength="10" readonly="readonly" class="datepicker" />
+                                                <input type="text" name="[% field.name %]" id="[% field.name %]" value="" size="10" maxlength="10" class="datepicker" />
                                                 <a href="#" onclick="clearDate('[% field.name %]');return false;"><i class="fa fa-fw fa-trash"></i> Clear</a>
                                             [% END %]
                                             [% IF field.mandatory %]
index d1481ee..cb0cddd 100644 (file)
@@ -29,12 +29,19 @@ $(document).ready(function() {
     });
 
     $("#newduedate").datetimepicker({
+        onClose: function(dateText, inst) {
+            validate_date(dateText, inst);
+        },
         minDate: 1, // require that renewal date is after today
         hour: 23,
         minute: 59
     });
     $("#duedatespec").datetimepicker({
-        onClose: function(dateText, inst) { $("#barcode").focus(); },
+        onClose: function(dateText, inst) {
+            if ( validate_date(dateText, inst) ) {
+                $("#barcode").focus();
+            }
+        },
         hour: 23,
         minute: 59
     });