From 848e5c76e705628ced099564b0ee9cd5ecd8497f Mon Sep 17 00:00:00 2001 From: Paul Derscheid Date: Fri, 20 Sep 2024 19:08:26 +0000 Subject: [PATCH] Bug 33738: Add bookings to the OPAC This is a rough draft of an OPAC integration. - The display of bookings already works more or less fine - In the patron's summary table - In the new bookings tab that doubles as the base for the placement modal - The placement of new bookings is still more of a demo, because we still need to integrate all of the logic in modals/place_booking.js for flatpickr => To try it out anyway: 1) Make an item bookable 2) Open the OPAC 3) Login 4) Go to opac-detail.pl for that item's biblio 5) Note the 'Place Booking' entry in opac-detail-sidebar.inc and click 6) Hit '+ New booking' (this is going to change of course) 7) Fill in some details and pick a start date (for demo purposes it just adds a week) I'd be very interested in opinions, feedback, or follow-up patches (on vacation next week). --- .../bootstrap/en/includes/bookings-table.inc | 75 +++++++++ .../bootstrap/en/includes/modals/bookings.inc | 93 +++++++++++ .../en/includes/opac-detail-sidebar.inc | 4 + .../bootstrap/en/includes/usermenu.inc | 9 ++ .../bootstrap/en/modules/opac-bookings.tt | 147 ++++++++++++++++++ .../bootstrap/en/modules/opac-user.tt | 13 +- opac/opac-bookings.pl | 130 ++++++++++++++++ opac/opac-detail.pl | 3 + opac/opac-user.pl | 6 + 9 files changed, 479 insertions(+), 1 deletion(-) create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/includes/bookings-table.inc create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/includes/modals/bookings.inc create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-bookings.tt create mode 100755 opac/opac-bookings.pl diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/bookings-table.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/bookings-table.inc new file mode 100644 index 0000000000..6afe5c67cf --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/bookings-table.inc @@ -0,0 +1,75 @@ +[% USE Branches %] +[% USE ItemTypes %] +[% USE KohaDates %] +[% PROCESS 'i18n.inc' %] + + + + + + + + + + + + + + + + + + + [% FOREACH BOOKING IN BOOKINGS %] + + + + + + + [% IF BOOKING.item %] + + + [% ELSE %] + + + [% END %] + + + + + [% END # /FOREACH HOLDS %] + +
Bookings ([% BOOKINGS.count | html %] total)
TitlePlaced onPickup locationStart dateEnd dateItem typeBarcodeProvided byModify
+ [% INCLUDE 'biblio-title.inc' biblio=BOOKING.biblio link => 1 %] + [% BOOKING.item.enumchron | html %] + [% BOOKING.biblio.author | html %] + + [% BOOKING.creation_date | $KohaDates %] + + [% BOOKING.pickup_library.branchname | html %] + + [% PROCESS 'modals/bookings.inc' action = 'change-pickup-location' %] + + [% BOOKING.start_date | $KohaDates %] + + [% BOOKING.end_date | $KohaDates %] + + [% BOOKING.item.itemtype.description %] + + [% BOOKING.item.barcode %] + + [% Branches.GetName(BOOKING.item.homebranch) | html %] + + + [% PROCESS 'modals/bookings.inc' action = 'cancel' %] +
diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/modals/bookings.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/modals/bookings.inc new file mode 100644 index 0000000000..a146d7a408 --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/modals/bookings.inc @@ -0,0 +1,93 @@ +[% USE Branches %] +[% SET booking_id = BOOKING.booking_id | html %] + + \ No newline at end of file diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-detail-sidebar.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-detail-sidebar.inc index 211f6d6169..410320ffd4 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-detail-sidebar.inc +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/opac-detail-sidebar.inc @@ -7,6 +7,10 @@
  • Place hold
  • [% END %] [% END %] + + [% IF ( BookableItems ) %] +
  • Place booking
  • + [% END %] [% END %] [% IF Koha.Preference('UseRecalls') %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc index d088f48ab7..9fa70b3d57 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc @@ -122,6 +122,15 @@ Lists [% END %] + [% IF logged_in_user.bookings.count %] + [% IF ( bookingsview ) %] +
  • + [% ELSE %] +
  • + [% END %] + Bookings ([% logged_in_user.bookings.count | html %])
  • + [% END %] + [% IF Koha.Preference( 'RoutingSerials' ) && logged_in_user && logged_in_user.get_routing_lists.count %] [% IF ( routinglistsview ) %]
  • diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-bookings.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-bookings.tt new file mode 100644 index 0000000000..c498bf4c4a --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-bookings.tt @@ -0,0 +1,147 @@ +[% USE raw %] +[% USE Asset %] +[% USE Koha %] +[% USE KohaDates %] +[% USE Branches %] +[% USE ItemTypes %] +[% USE Price %] +[% USE AuthorisedValues %] +[% USE AdditionalContents %] +[% SET OpacNav = AdditionalContents.get( location => "OpacNav", lang => lang, library => logged_in_user.branchcode || default_branch, blocktitle => 0 ) %] +[% SET OpacNavBottom = AdditionalContents.get( location => "OpacNavBottom", lang => lang, library => logged_in_user.branchcode || default_branch, blocktitle => 0 ) %] +[% SET OpacMySummaryNote = AdditionalContents.get( location => "OpacMySummaryNote", lang => lang, library => branchcode ) %] + +[% SET borrower_club_enrollments = logged_in_user.get_club_enrollments %] +[% SET borrower_enrollable_clubs = logged_in_user.get_enrollable_clubs(1) %] + +[% INCLUDE 'doc-head-open.inc' %] +Your library home › [% IF ( LibraryNameTitle ) %][% LibraryNameTitle | html %][% ELSE %]Koha online[% END %] catalog +[% INCLUDE 'doc-head-close.inc' %] +[% Asset.css("css/overdrive.css") | $raw %] +[% BLOCK cssinclude %][% END %] + +[% INCLUDE 'bodytag.inc' bodyid='opac-user' bodyclass='scrollto' %] +[% INCLUDE 'masthead.inc' %] + +
    + [% WRAPPER breadcrumbs %] + [% WRAPPER breadcrumb_item %] + [% INCLUDE 'patron-title.inc' patron = logged_in_user %] + [% END %] + [% WRAPPER breadcrumb_item bc_active= 1 %] + Your bookings + [% END %] + [% END #/ WRAPPER breadcrumbs %] + +
    +
    +
    + +
    +
    +
    +

    Your bookings

    + + [% IF op == 'list' %] +
    + +
    +
    +
    +
    + + [% PROCESS 'modals/bookings.inc' action = 'add' %] +
    +
    + [% PROCESS 'bookings-table.inc' %] +
    + [% END %] +
    +
    +
    +
    +
    + +[% INCLUDE 'opac-bottom.inc' %] +[% BLOCK jsinclude %] + [% Asset.js("js/form-submit.js") | $raw %] + [% Asset.js("lib/vis-timeline/vis-timeline-graph2d.min.js") | $raw %] + [% INCLUDE 'calendar.inc' %] + [% INCLUDE 'datatables.inc' %] + + + +[% END %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt index 89e2b1dd0a..6229ddd340 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt @@ -298,6 +298,11 @@ Holds ([% RESERVES.count | html %]) [% END %] [% END %] + [% IF ( BOOKINGS.count ) %] + [% WRAPPER tab_item tabname= "opac-user-bookings" %] + Bookings ([% BOOKINGS.count | html %]) + [% END %] + [% END %] [% IF Koha.Preference('UseRecalls') && RECALLS.count %] [% WRAPPER tab_item tabname= "opac-user-recalls" %] Recalls ([% RECALLS.count | html %]) @@ -843,6 +848,12 @@ [% END # /tab_panel#opac-user-holds %] [% END # / #RESERVES.count %] + [% IF ( BOOKINGS.count ) %] + [% WRAPPER tab_panel tabname="opac-user-bookings" %] + [% PROCESS 'bookings-table.inc' %] + [% END # /tab_panel#opac-user-bookings %] + [% END # / #BOOKINGS.count %] + [% IF Koha.Preference('UseRecalls') && RECALLS.count %] [% WRAPPER tab_panel tabname="opac-user-recalls" %] @@ -1217,7 +1228,7 @@ ); }); - var dTables = $("#checkoutst,#holdst,#overduest,#opac-user-relative-issues-table"); + var dTables = $("#checkoutst,#holdst,#overduest,#opac-user-relative-issues-table,#opac-user-bookings-table"); dTables.each(function(){ var thIndex = $(this).find("th.psort").index(); $(this).on("init.dt", function() { diff --git a/opac/opac-bookings.pl b/opac/opac-bookings.pl new file mode 100755 index 0000000000..7b19823854 --- /dev/null +++ b/opac/opac-bookings.pl @@ -0,0 +1,130 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use C4::Members (); +use C4::Auth qw( get_template_and_user ); +use C4::Output qw( output_html_with_http_headers ); + +use Koha::Biblios (); +use Koha::Booking (); +use Koha::Bookings (); +use Koha::DateUtils qw(dt_from_string); +use Koha::Patrons (); + +use CGI qw( -utf8 ); +use Carp qw( croak ); +use Digest::SHA qw( sha1_base64 ); +use Time::HiRes qw( time ); + +my $query = CGI->new; + +my ( $template, $borrowernumber, $cookie ) = get_template_and_user( + { + template_name => 'opac-bookings.tt', + query => $query, + type => 'opac', + } +); + +my $patron = Koha::Patrons->find($borrowernumber); + +my $op = $query->param('op') // 'list'; +if ( $op eq 'list' ) { + my $bookings = Koha::Bookings->search( { patron_id => $patron->borrowernumber } )->filter_by_active; + my $hash = sha1_base64( join q{}, time, rand ); + + my $biblio; + my $biblio_id = $query->param('biblio_id'); + if ($biblio_id) { + $biblio = Koha::Biblios->find($biblio_id); + } + + $template->param( + op => 'list', BOOKINGS => $bookings, + BOOKING => { booking_id => $hash }, + biblio => $biblio, + ); +} + +if ( $op eq 'cud-add' ) { + my $booking = Koha::Booking->new( + { + patron_id => $patron->borrowernumber, + biblio_id => $query->param('biblio_id'), + item_id => $query->param('item_id'), + pickup_library_id => $query->param('pickup_library_id'), + start_date => dt_from_string( $query->param('period') ), + end_date => dt_from_string( $query->param('period') )->add( days => 7 )->truncate( to => 'day' ), + } + )->store; + + print $query->redirect('/cgi-bin/koha/opac-bookings.pl?op=list') or croak; +} + +if ( $op eq 'cud-cancel' ) { + my $booking_id = $query->param('booking_id'); + my $booking = Koha::Bookings->find($booking_id); + if ( !$booking ) { + print $query->redirect('/cgi-bin/koha/errors/404.pl') or croak; + exit; + } + + if ( $booking->patron_id ne $patron->borrowernumber ) { + print $query->redirect('/cgi-bin/koha/errors/403.pl') or croak; + exit; + } + + my $is_deleted = $booking->delete; + if ( !$is_deleted ) { + print $query->redirect('/cgi-bin/koha/errors/500.pl') or croak; + exit; + } + + print $query->redirect('/cgi-bin/koha/opac-user.pl?tab=opac-user-bookings') or croak; +} + +if ( $op eq 'cud-change_pickup_location' ) { + my $booking_id = $query->param('booking_id'); + my $new_pickup_location = $query->param('new_pickup_location'); + my $booking = Koha::Bookings->find($booking_id); + + if ( !$booking ) { + print $query->redirect('/cgi-bin/koha/errors/404.pl') or croak; + exit; + } + + if ( $booking->patron_id ne $patron->borrowernumber ) { + print $query->redirect('/cgi-bin/koha/errors/403.pl') or croak; + exit; + } + + my $is_updated = $booking->update( { pickup_library_id => $new_pickup_location } ); + if ( !$is_updated ) { + print $query->redirect('/cgi-bin/koha/errors/500.pl') or croak; + exit; + } + + print $query->redirect('/cgi-bin/koha/opac-user.pl?tab=opac-user-bookings') or croak; +} + +$template->param( + bookingsview => 1, +); + +output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 }; diff --git a/opac/opac-detail.pl b/opac/opac-detail.pl index e9f188da2b..dd22d0aa27 100755 --- a/opac/opac-detail.pl +++ b/opac/opac-detail.pl @@ -670,6 +670,8 @@ my $holdable_items = $biblio->items->filter_by_for_hold->count; # If we don't have a patron, then holdable items determines holdability my $can_holds_be_placed = $patron ? 0 : $holdable_items; +my $can_bookings_be_placed = $patron ? $biblio->bookable_items->count : 0; + my ( $itemloop_has_images, $otheritemloop_has_images ); my $item_level_holds; my $item_checkouts; @@ -780,6 +782,7 @@ $template->param( item_checkouts => $item_checkouts, item_level_holds => $item_level_holds, ReservableItems => $can_holds_be_placed, + BookableItems => $can_bookings_be_placed, itemloop_has_images => $itemloop_has_images, otheritemloop_has_images => $otheritemloop_has_images, ); diff --git a/opac/opac-user.pl b/opac/opac-user.pl index a1b8b9f910..3a523c6745 100755 --- a/opac/opac-user.pl +++ b/opac/opac-user.pl @@ -334,6 +334,12 @@ $template->param( showpriority => $show_priority, ); +my $bookings = $patron->bookings->filter_by_active; + +$template->param( + BOOKINGS => $bookings, +); + if ( C4::Context->preference('UseRecalls') ) { my $recalls = Koha::Recalls->search( { patron_id => $borrowernumber, completed => 0 } ); $template->param( RECALLS => $recalls ); -- 2.46.1