Bugzilla – Attachment 194372 Details for
Bug 15792
Double-clicking the 'renew' button on circulation.pl will double-charge account management fee
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 15792: Throttle patron renewal links to prevent double-clicks
Bug-15792-Throttle-patron-renewal-links-to-prevent.patch (text/plain), 9.45 KB, created by
Paul Derscheid
on 2026-03-03 09:49:51 UTC
(
hide
)
Description:
Bug 15792: Throttle patron renewal links to prevent double-clicks
Filename:
MIME Type:
Creator:
Paul Derscheid
Created:
2026-03-03 09:49:51 UTC
Size:
9.45 KB
patch
obsolete
>From e341beb0a83455b7ec8e3bd3633f553dbab36ffe Mon Sep 17 00:00:00 2001 >From: Paul Derscheid <paul.derscheid@lmscloud.de> >Date: Tue, 3 Mar 2026 10:08:05 +0100 >Subject: [PATCH] Bug 15792: Throttle patron renewal links to prevent > double-clicks > >- Replace plain <a> renewal links in patron_messages.inc with the > reusable throttled-button component introduced in Bug 40896 >- Covers all four renewal links: warndeparture and expired states, > in both circulation and patron detail contexts >- Include throttledButton.js in circulation.tt and moremember.tt >- Add persist mode and done icon state to throttled-button component > so the button stays disabled on bfcache restore with a check icon > instead of a misleading spinner > >Test plan: >1) Create or update a patron category with an enrollment fee and an > enrollment period in months (e.g. 24 months) >2) Create a patron of that type and write off any initial enrollment > fee >3) Set the patron's dateexpiry to the past so the account is expired >4) Go to /cgi-bin/koha/circ/circulation.pl and scan the patron >5) Observe the "Renew" link in the expiration warning now shows as a > small button with a rotate icon >6) Click "Renew" and confirm the button becomes disabled with a > spinner, preventing a second click >7) Verify the patron is renewed correctly with only one enrollment > fee charged and the correct expiry date >8) Press the browser back button to return via bfcache >9) Verify the button remains disabled and shows a check icon instead > of the spinner, indicating the action was already completed >10) Repeat from the patron detail page (moremember.pl) to verify the > same throttle and done-state behavior there >11) Test the warndeparture case by setting dateexpiry to a near-future > date and confirming the same behavior >--- > .../prog/en/includes/patron_messages.inc | 12 ++++++++---- > .../prog/en/includes/throttled-button.inc | 6 +++++- > .../prog/en/modules/circ/circulation.tt | 1 + > .../prog/en/modules/members/moremember.tt | 1 + > .../intranet-tmpl/prog/js/throttledButton.js | 17 ++++++++++++++++- > 5 files changed, 31 insertions(+), 6 deletions(-) > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/patron_messages.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/patron_messages.inc >index dbabcef9aa4..00e236bc7e3 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/patron_messages.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/patron_messages.inc >@@ -24,10 +24,12 @@ > <span class="circ-hlt">Expiration:</span> > <span>Patron's card expires on [% patron.dateexpiry | $KohaDates %].</span> > [% IF ( moremember ) %] >- <a href="/cgi-bin/koha/members/setstatus.pl?borrowernumber=[% patron.borrowernumber | uri %]&destination=member&reregistration=y">Renew</a> or >+ [% INCLUDE 'throttled-button.inc' element_type='link' href='/cgi-bin/koha/members/setstatus.pl?borrowernumber=' _ patron.borrowernumber _ '&destination=member&reregistration=y' classes='btn btn-default btn-xs' ready_icon='fa-solid fa-rotate' label=t('Renew') persist=1 done_icon='fa-solid fa-check' %] >+ or > <a href="/cgi-bin/koha/members/memberentry.pl?op=edit_form&destination=member&borrowernumber=[% patron.borrowernumber | uri %]">Edit details</a> > [% ELSE %] >- <a href="/cgi-bin/koha/members/setstatus.pl?borrowernumber=[% patron.borrowernumber | uri %]&destination=circ&reregistration=y">Renew</a> or >+ [% INCLUDE 'throttled-button.inc' element_type='link' href='/cgi-bin/koha/members/setstatus.pl?borrowernumber=' _ patron.borrowernumber _ '&destination=circ&reregistration=y' classes='btn btn-default btn-xs' ready_icon='fa-solid fa-rotate' label=t('Renew') persist=1 done_icon='fa-solid fa-check' %] >+ or > <a href="/cgi-bin/koha/members/memberentry.pl?op=edit_form&destination=circ&borrowernumber=[% patron.borrowernumber | uri %]">Edit details</a> > [% END %] > </li> >@@ -47,10 +49,12 @@ > <span>Patron's card expired on [% expiry | $KohaDates %]</span> > [% END %] > [% IF ( moremember ) %] >- <a href="/cgi-bin/koha/members/setstatus.pl?borrowernumber=[% patron.borrowernumber | uri %]&destination=member&reregistration=y">Renew</a> or >+ [% INCLUDE 'throttled-button.inc' element_type='link' href='/cgi-bin/koha/members/setstatus.pl?borrowernumber=' _ patron.borrowernumber _ '&destination=member&reregistration=y' classes='btn btn-default btn-xs' ready_icon='fa-solid fa-rotate' label=t('Renew') persist=1 done_icon='fa-solid fa-check' %] >+ or > <a href="/cgi-bin/koha/members/memberentry.pl?op=edit_form&destination=member&borrowernumber=[% patron.borrowernumber | uri %]">Edit details</a> > [% ELSE %] >- <a href="/cgi-bin/koha/members/setstatus.pl?borrowernumber=[% patron.borrowernumber | uri %]&destination=circ&reregistration=y">Renew</a> or >+ [% INCLUDE 'throttled-button.inc' element_type='link' href='/cgi-bin/koha/members/setstatus.pl?borrowernumber=' _ patron.borrowernumber _ '&destination=circ&reregistration=y' classes='btn btn-default btn-xs' ready_icon='fa-solid fa-rotate' label=t('Renew') persist=1 done_icon='fa-solid fa-check' %] >+ or > <a href="/cgi-bin/koha/members/memberentry.pl?op=edit_form&destination=circ&borrowernumber=[% patron.borrowernumber | uri %]">Edit details</a> > [% END %] > </li> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/throttled-button.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/throttled-button.inc >index f48e5aa8c6a..8cf32dd8660 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/throttled-button.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/throttled-button.inc >@@ -7,12 +7,16 @@ > [% DEFAULT href = '#' %] > > [%# Common attributes shared by both button and link %] >-[% BLOCK common_attrs %]class="[% classes | html %]"[% IF id %]id="[% id | html %]"[% END %] data-state="ready" data-throttled-button [% IF attrs %][% FOREACH key IN attrs.keys %][% key | html %]="[% attrs.$key | html %]"[% END %][% END %][% END %] >+[% BLOCK common_attrs %] >+ class="[% classes | html %]"[% IF id %]id="[% id | html %]"[% END %] data-state="ready" data-throttled-button [% IF persist %]data-throttle-persist[% END %] >+ [% IF attrs %][% FOREACH key IN attrs.keys %][% key | html %]="[% attrs.$key | html %]"[% END %][% END %] >+[% END %] > > [%# Inner content shared by both button and link %] > [% BLOCK inner_content %] > <span class="button-icon-ready"><i class="[% ready_icon | html %]"></i></span> > <span class="button-icon-running" style="display: none;"><i class="[% active_icon | html %]"></i></span> >+ [% IF done_icon %]<span class="button-icon-done" style="display: none;"><i class="[% done_icon | html %]"></i></span>[% END %] > [% UNLESS icon_only %][% label | html %][% END %] > [% END %] > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt >index 7fe3f813b65..2b3a1fb0f53 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt >@@ -1254,6 +1254,7 @@ > [% Asset.js("js/checkouts.js") | $raw %] > [% Asset.js("js/tables/bookings.js") | $raw %] > [% Asset.js("js/recalls.js") | $raw %] >+ [% Asset.js("js/throttledButton.js") | $raw %] > [% END %] > > [% INCLUDE 'intranet-bottom.inc' %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt >index cc59617307f..f9dfb34df70 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt >@@ -837,6 +837,7 @@ > [% INCLUDE 'str/members-menu.inc' %] > [% Asset.js("js/members-menu.js") | $raw %] > [% Asset.js("js/recalls.js") | $raw %] >+ [% Asset.js("js/throttledButton.js") | $raw %] > [% Asset.js("js/hold-group.js") | $raw %] > <script> > const LoadCheckoutsTableDelay = 0; >diff --git a/koha-tmpl/intranet-tmpl/prog/js/throttledButton.js b/koha-tmpl/intranet-tmpl/prog/js/throttledButton.js >index 9ad563f647d..eede27d7a0d 100644 >--- a/koha-tmpl/intranet-tmpl/prog/js/throttledButton.js >+++ b/koha-tmpl/intranet-tmpl/prog/js/throttledButton.js >@@ -74,7 +74,22 @@ > clearTimeout(timeoutId); > pendingTimeouts.delete(button); > } >- toggleButton(button); >+ if (!button.hasAttribute("data-throttle-persist")) { >+ toggleButton(button); >+ } else { >+ const runningIcon = button.querySelector( >+ ".button-icon-running" >+ ); >+ const doneIcon = >+ button.querySelector(".button-icon-done"); >+ if (runningIcon) { >+ runningIcon.style.display = "none"; >+ } >+ if (doneIcon) { >+ doneIcon.style.display = ""; >+ } >+ button.dataset.state = "done"; >+ } > }); > } > }); >-- >2.39.5
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 15792
:
194372
|
194418