Bugzilla – Attachment 100117 Details for
Bug 20936
Holds history for patrons in OPAC
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 20936: Add patron's hold history menu in OPAC
Bug-20936-Add-patrons-hold-history-menu-in-OPAC.patch (text/plain), 34.91 KB, created by
Kyle M Hall (khall)
on 2020-03-04 13:42:16 UTC
(
hide
)
Description:
Bug 20936: Add patron's hold history menu in OPAC
Filename:
MIME Type:
Creator:
Kyle M Hall (khall)
Created:
2020-03-04 13:42:16 UTC
Size:
34.91 KB
patch
obsolete
>From 5aa22da651816ea218a6694614fa43dc6419357a Mon Sep 17 00:00:00 2001 >From: Agustin Moyano <agustinmoyano@theke.io> >Date: Sun, 9 Feb 2020 17:53:54 -0300 >Subject: [PATCH] Bug 20936: Add patron's hold history menu in OPAC > >This patch adds patron's hold history in OPAC. Right now, it only shows records from old_reserves table, but I'll wait till bug 20271 is pushed to show full history (old and new holds) > >To test: >1. apply this patch >2. Find a patron, place several holds and cancel or fulfill them >3. Go to patron's opac >CHECK => There is no 'your holds history' option in menu >4. In admin preferences enable OPACHoldsHistory >5. Go back to patron's opac >SUCCESS => There is a 'your holds history' menu option > => Holds history displays all holds canceled or fulfilled >6. Filter, order and change page >SUCCESS => All controls work as expected >7. Sign off. > >Table content is fetched from the api. If you see data, and you can order and filter then please sign off bug 24561. > >Date columns use $date function to transform dates strings from api (for example '2020-02-20') to 'dateformat' prefernce format ('02/20/20202'). If you change dateformat prefernce and see the changes reflected in date columns, please sign off bug 24455. > >Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> >--- > Koha/REST/V1/Patrons/Holds.pm | 174 ++++++++++++ > Koha/Schema/Result/Biblio.pm | 20 ++ > Koha/Schema/Result/Item.pm | 27 ++ > Koha/Schema/Result/OldReserve.pm | 43 +++ > api/v1/swagger/paths.json | 3 + > api/v1/swagger/paths/public_patrons.json | 188 +++++++++++++ > .../bootstrap/en/includes/usermenu.inc | 24 +- > .../bootstrap/en/modules/opac-holdsrecord.tt | 247 ++++++++++++++++++ > opac/opac-holdsrecord.pl | 161 ++++++++++++ > 9 files changed, 880 insertions(+), 7 deletions(-) > create mode 100644 Koha/REST/V1/Patrons/Holds.pm > create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-holdsrecord.tt > create mode 100755 opac/opac-holdsrecord.pl > >diff --git a/Koha/REST/V1/Patrons/Holds.pm b/Koha/REST/V1/Patrons/Holds.pm >new file mode 100644 >index 0000000000..65be2c4b4d >--- /dev/null >+++ b/Koha/REST/V1/Patrons/Holds.pm >@@ -0,0 +1,174 @@ >+package Koha::REST::V1::Patrons::Holds; >+ >+# 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, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+ >+use Koha::Patrons; >+ >+use Scalar::Util qw(blessed); >+use Koha::DateUtils; >+use Try::Tiny; >+ >+=head1 NAME >+ >+Koha::REST::V1::Patrons::Holds >+ >+=head1 API >+ >+=head2 Methods >+ >+=head3 list >+ >+Mehtod that handles listing patron's holds >+ >+=cut >+ >+sub list { >+ my $c = shift->openapi->valid_input or return; >+ my $patron_id = $c->validation->param('patron_id'); >+ my $old = $c->validation->param('old'); >+ my $patron = Koha::Patrons->find($patron_id); >+ >+ unless ($patron) { >+ return $c->render( status => 404, openapi => { error => "Patron not found." } ); >+ } >+ >+ return try { >+ my $holds_set; >+ if($old) { >+ $holds_set = Koha::Old::Holds->new; >+ } else { >+ $holds_set = Koha::Holds->new; >+ } >+ >+ delete $c->validation->output->{old}; >+ #$c->stash_embed( {spec => $c->match->endpoint->pattern->defaults->{'openapi.op_spec'}} ); >+ my $holds = $c->objects->search( $holds_set ); >+ return $c->render( status => 200, openapi => $holds ); >+ } >+ catch { >+ warn $_; >+use Data::Printer colored=>1; >+p($_); >+ warn $_->{trace}; >+ if ( blessed $_ && $_->isa('Koha::Exceptions') ) { >+ return $c->render( >+ status => 500, >+ openapi => { error => "$_" } >+ ); >+ } >+ else { >+ return $c->render( >+ status => 500, >+ openapi => { error => "Something went wrong, check Koha logs for details." } >+ ); >+ } >+ }; >+} >+ >+=head2 Internal methods >+ >+=head3 _to_model >+ >+Helper function that maps REST api objects into Koha::Hold >+attribute names. >+ >+=cut >+ >+sub _to_model { >+ my $hold = shift; >+use Data::Printer colored=>1; >+p($hold); >+ foreach my $attribute ( keys %{ $Koha::REST::V1::Patrons::Holds::to_model_mapping } ) { >+ my $mapped_attribute = $Koha::REST::V1::Patrons::Holds::to_model_mapping->{$attribute}; >+ if ( exists $hold->{ $attribute } >+ && defined $mapped_attribute ) >+ { >+ # key => !undef >+ $hold->{ $mapped_attribute } = delete $hold->{ $attribute }; >+ } >+ elsif ( exists $hold->{ $attribute } >+ && !defined $mapped_attribute ) >+ { >+ # key => undef / to be deleted >+ delete $hold->{ $attribute }; >+ } >+ } >+ >+ if ( exists $hold->{lowestPriority} ) { >+ $hold->{lowestPriority} = ($hold->{lowestPriority}) ? 1 : 0; >+ } >+ >+ if ( exists $hold->{suspend} ) { >+ $hold->{suspend} = ($hold->{suspend}) ? 1 : 0; >+ } >+ >+ if ( exists $hold->{reservedate} && $hold->{reservedate} != 1 ) { >+ $hold->{reservedate} = output_pref({ str => $hold->{reservedate}, dateformat => 'sql' }); >+ } >+ >+ if ( exists $hold->{cancellationdate} && $hold->{cancellationdate} != 1 ) { >+ $hold->{cancellationdate} = output_pref({ str => $hold->{cancellationdate}, dateformat => 'sql' }); >+ } >+ >+ if ( exists $hold->{timestamp} && $hold->{timestamp} != 1 ) { >+ $hold->{timestamp} = output_pref({ str => $hold->{timestamp}, dateformat => 'sql' }); >+ } >+ >+ if ( exists $hold->{waitingdate} && $hold->{waitingdate} != 1 ) { >+ $hold->{waitingdate} = output_pref({ str => $hold->{waitingdate}, dateformat => 'sql' }); >+ } >+ >+ if ( exists $hold->{expirationdate} && $hold->{expirationdate} != 1 ) { >+ $hold->{expirationdate} = output_pref({ str => $hold->{expirationdate}, dateformat => 'sql' }); >+ } >+ >+ if ( exists $hold->{suspend_until} && $hold->{suspend_until} != 1 ) { >+ $hold->{suspend_until} = output_pref({ str => $hold->{suspend_until}, dateformat => 'sql' }); >+ } >+ >+ return $hold; >+} >+ >+=head2 Global variables >+ >+=head3 $to_model_mapping >+ >+=cut >+ >+our $to_model_mapping = { >+ hold_id => 'reserve_id', >+ patron_id => 'borrowernumber', >+ hold_date => 'reservedate', >+ biblio_id => 'biblionumber', >+ pickup_library_id => 'branchcode', >+ cancelation_date => 'cancellationdate', >+ notes => 'reservenotes', >+ status => 'found', >+ item_id => 'itemnumber', >+ waiting_date => 'waitingdate', >+ expiration_date => 'expirationdate', >+ lowest_priority => 'lowestPriority', >+ suspended => 'suspend', >+ suspended_until => 'suspend_until', >+ item_type => 'itemtype', >+ item_level => 'item_level_hold', >+}; >+ >+1; >\ No newline at end of file >diff --git a/Koha/Schema/Result/Biblio.pm b/Koha/Schema/Result/Biblio.pm >index 7c66b1dc03..b6ef833bec 100644 >--- a/Koha/Schema/Result/Biblio.pm >+++ b/Koha/Schema/Result/Biblio.pm >@@ -428,4 +428,24 @@ __PACKAGE__->add_columns( > "+serial" => { is_boolean => 1 } > ); > >+=head2 koha_objects_class >+ >+Returns related Koha::Objects class name >+ >+=cut >+ >+sub koha_objects_class { >+ 'Koha::Biblios'; >+} >+ >+=head2 koha_object_class >+ >+Returns related Koha::Object class name >+ >+=cut >+ >+sub koha_object_class { >+ 'Koha::Biblio'; >+} >+ > 1; >diff --git a/Koha/Schema/Result/Item.pm b/Koha/Schema/Result/Item.pm >index 58cddf4502..0afa3e5472 100644 >--- a/Koha/Schema/Result/Item.pm >+++ b/Koha/Schema/Result/Item.pm >@@ -759,6 +759,13 @@ __PACKAGE__->belongs_to( > ); > > use C4::Context; >+ >+=head2 koha_objects_class >+ >+Returns item's effective itemtype >+ >+=cut >+ > sub effective_itemtype { > my ( $self ) = @_; > >@@ -772,4 +779,24 @@ sub effective_itemtype { > } > } > >+=head2 koha_objects_class >+ >+Returns related Koha::Objects class name >+ >+=cut >+ >+sub koha_objects_class { >+ 'Koha::Items'; >+} >+ >+=head2 koha_object_class >+ >+Returns related Koha::Object class name >+ >+=cut >+ >+sub koha_object_class { >+ 'Koha::Item'; >+} >+ > 1; >diff --git a/Koha/Schema/Result/OldReserve.pm b/Koha/Schema/Result/OldReserve.pm >index de9fc6cb27..87c96b1056 100644 >--- a/Koha/Schema/Result/OldReserve.pm >+++ b/Koha/Schema/Result/OldReserve.pm >@@ -300,6 +300,49 @@ __PACKAGE__->belongs_to( > # Created by DBIx::Class::Schema::Loader v0.07046 @ 2019-06-17 07:24:39 > # DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZgGAW7ODBby3hGNJ41eeMA > >+__PACKAGE__->belongs_to( >+ "item", >+ "Koha::Schema::Result::Item", >+ { itemnumber => "itemnumber" }, >+ { >+ is_deferrable => 1, >+ join_type => "LEFT", >+ on_delete => "CASCADE", >+ on_update => "CASCADE", >+ }, >+); >+ >+__PACKAGE__->belongs_to( >+ "branch", >+ "Koha::Schema::Result::Branch", >+ { branchcode => "branchcode" }, >+ { >+ is_deferrable => 1, >+ join_type => "LEFT", >+ on_delete => "CASCADE", >+ on_update => "CASCADE", >+ }, >+); >+ >+ >+__PACKAGE__->belongs_to( >+ "biblio", >+ "Koha::Schema::Result::Biblio", >+ { biblionumber => "biblionumber" }, >+ { >+ is_deferrable => 1, >+ join_type => "LEFT", >+ on_delete => "CASCADE", >+ on_update => "CASCADE", >+ }, >+); >+ >+__PACKAGE__->add_columns( >+ '+item_level_hold' => { is_boolean => 1 }, >+ '+lowestPriority' => { is_boolean => 1 }, >+ '+suspend' => { is_boolean => 1 } >+); >+ > sub koha_object_class { > 'Koha::Old::Hold'; > } >diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json >index ed9ed0627c..be15293d0a 100644 >--- a/api/v1/swagger/paths.json >+++ b/api/v1/swagger/paths.json >@@ -98,6 +98,9 @@ > "/public/patrons/{patron_id}/guarantors/can_see_checkouts": { > "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1guarantors~1can_see_checkouts" > }, >+ "/public/patrons/{patron_id}/holds": { >+ "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1holds" >+ }, > "/return_claims": { > "$ref": "paths/return_claims.json#/~1return_claims" > }, >diff --git a/api/v1/swagger/paths/public_patrons.json b/api/v1/swagger/paths/public_patrons.json >index 5db073c4eb..a62b8e6ede 100644 >--- a/api/v1/swagger/paths/public_patrons.json >+++ b/api/v1/swagger/paths/public_patrons.json >@@ -236,5 +236,193 @@ > "allow-owner": true > } > } >+ }, >+ "/public/patrons/{patron_id}/holds": { >+ "get": { >+ "x-mojo-to": "Patrons::Holds#list", >+ "operationId": "listPatronsOldHolds", >+ "tags": [ >+ "patron" >+ ], >+ "parameters": [ >+ { >+ "$ref": "../parameters.json#/patron_id_pp" >+ }, >+ { >+ "name": "hold_id", >+ "in": "query", >+ "description": "Internal reserve identifier", >+ "type": "integer" >+ }, >+ { >+ "name": "hold_date", >+ "in": "query", >+ "description": "Hold", >+ "type": "string", >+ "format": "date" >+ }, >+ { >+ "name": "biblio_id", >+ "in": "query", >+ "description": "Internal biblio identifier", >+ "type": "integer" >+ }, >+ { >+ "name": "pickup_library_id", >+ "in": "query", >+ "description": "Internal library identifier for the pickup library", >+ "type": "string" >+ }, >+ { >+ "name": "cancelation_date", >+ "in": "query", >+ "description": "The date the hold was cancelled", >+ "type": "string", >+ "format": "date" >+ }, >+ { >+ "name": "notes", >+ "in": "query", >+ "description": "Notes related to this hold", >+ "type": "string" >+ }, >+ { >+ "name": "priority", >+ "in": "query", >+ "description": "Where in the queue the patron sits", >+ "type": "integer" >+ }, >+ { >+ "name": "status", >+ "in": "query", >+ "description": "Found status", >+ "type": "string" >+ }, >+ { >+ "name": "timestamp", >+ "in": "query", >+ "description": "Time of latest update", >+ "type": "string" >+ }, >+ { >+ "name": "item_id", >+ "in": "query", >+ "description": "Internal item identifier", >+ "type": "integer" >+ }, >+ { >+ "name": "waiting_date", >+ "in": "query", >+ "description": "Date the item was marked as waiting for the patron", >+ "type": "string" >+ }, >+ { >+ "name": "expiration_date", >+ "in": "query", >+ "description": "Date the hold expires", >+ "type": "string" >+ }, >+ { >+ "name": "lowest_priority", >+ "in": "query", >+ "description": "Lowest priority", >+ "type": "boolean" >+ }, >+ { >+ "name": "suspended", >+ "in": "query", >+ "description": "Suspended", >+ "type": "boolean" >+ }, >+ { >+ "name": "suspended_until", >+ "in": "query", >+ "description": "Suspended until", >+ "type": "string" >+ }, >+ { >+ "name": "old", >+ "in": "query", >+ "description": "Fetch holds history", >+ "type": "boolean" >+ }, >+ { >+ "$ref": "../parameters.json#/match" >+ }, >+ { >+ "$ref": "../parameters.json#/order_by" >+ }, >+ { >+ "$ref": "../parameters.json#/page" >+ }, >+ { >+ "$ref": "../parameters.json#/per_page" >+ }, >+ { >+ "$ref": "../parameters.json#/q_param" >+ }, >+ { >+ "$ref": "../parameters.json#/q_body" >+ }, >+ { >+ "$ref": "../parameters.json#/q_header" >+ } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "List of holds history", >+ "schema": { >+ "$ref": "../definitions.json#/holds" >+ } >+ }, >+ "400": { >+ "description": "Bad request", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "401": { >+ "description": "Authentication required", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "404": { >+ "description": "Patron not found", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "500": { >+ "description": "Internal server error", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "503": { >+ "description": "Under maintenance", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ } >+ }, >+ "x-koha-authorization": { >+ "allow-owner": true >+ }, >+ "x-koha-embed": [ >+ "item", >+ "biblio", >+ "branch" >+ ] >+ } > } > } >\ No newline at end of file >diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc >index f1cdfa5ab2..7f06967756 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc >@@ -58,14 +58,24 @@ > <a href="/cgi-bin/koha/opac-search-history.pl">your search history</a></li> > [% END %] > >- [% IF ( opacreadinghistory ) %] >- [% IF ( readingrecview ) %] >- <li class="active"> >- [% ELSE %] >- <li> >+ [% IF opacreadinghistory || Koha.Preference('OPACHoldsHistory') == 1 %] >+ [% IF opacreadinghistory %] >+ [% IF ( readingrecview ) %] >+ <li class="active"> >+ [% ELSE %] >+ <li> >+ [% END %] >+ <a href="/cgi-bin/koha/opac-readingrecord.pl">your reading history</a></li> >+ [% END %] >+ [% IF Koha.Preference('OPACHoldsHistory') == 1 %] >+ [% IF ( holdsrecview ) %] >+ <li class="active"> >+ [% ELSE %] >+ <li> >+ [% END %] >+ <a href="/cgi-bin/koha/opac-holdsrecord.pl">your holds history</a></li> > [% END %] >- <a href="/cgi-bin/koha/opac-readingrecord.pl">your reading history</a></li> >- [% IF ( OPACPrivacy ) %] >+ [% IF ( OPACPrivacy || Koha.Preference('OPACHoldsPrivacy') == 1 ) %] > [% IF ( privacyview ) %] > <li class="active"> > [% ELSE %] >diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-holdsrecord.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-holdsrecord.tt >new file mode 100644 >index 0000000000..7ea4348e89 >--- /dev/null >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-holdsrecord.tt >@@ -0,0 +1,247 @@ >+[% USE raw %] >+[% USE Koha %] >+[% USE KohaDates %] >+[% INCLUDE 'doc-head-open.inc' %] >+<title>[% IF ( LibraryNameTitle ) %][% LibraryNameTitle | html %][% ELSE %]Koha online[% END %] catalog › Your holds history</title> >+[% INCLUDE 'doc-head-close.inc' %] >+[% BLOCK cssinclude %] >+ <style> >+ .controls { >+ font-size: 75%; >+ } >+ >+ .controls .paginate_button { >+ font-family: 'FontAwesome'; >+ text-decoration: none; >+ } >+ >+ .controls .paginate_button:not(.disabled) { >+ cursor: pointer; >+ } >+ >+ .controls .paginate_button.disabled { >+ color: grey; >+ } >+ >+ .controls .previous:before { >+ content: "\f104"; >+ padding-right: .5em; >+ } >+ >+ .controls .next:after { >+ content: "\f105"; >+ padding-left: .5em; >+ } >+ </style> >+[% END %] >+</head> >+[% INCLUDE 'bodytag.inc' bodyid='opac-holdsrecord' %] >+[% INCLUDE 'masthead.inc' %] >+[% #SET AdlibrisEnabled = Koha.Preference('AdlibrisCoversEnabled') %] >+[% #SET AdlibrisURL = Koha.Preference('AdlibrisCoversURL') %] >+ >+[% #IF Koha.Preference('AmazonAssocTag') %] >+ [% #AmazonAssocTag = '?tag=' _ Koha.Preference('AmazonAssocTag') %] >+[% #ELSE %] >+ [% #AmazonAssocTag = '' %] >+[% #END %] >+ >+<div class="main"> >+ <ul class="breadcrumb"> >+ <li><a href="/cgi-bin/koha/opac-main.pl">Home</a> <span class="divider">›</span></li> >+ <li><a href="/cgi-bin/koha/opac-user.pl">[% INCLUDE 'patron-title.inc' patron = logged_in_user %]</a> <span class="divider">›</span></li> >+ <li><a href="#">Your holds history</a></li> >+ </ul> >+ >+ <div class="container-fluid"> >+ <div class="row-fluid"> >+ <div class="span2"> >+ <div id="navigation"> >+ [% INCLUDE 'navigation.inc' IsPatronPage=1 %] >+ </div> >+ </div> >+ <div class="span10"> >+ <div id="userholdsrecord"> >+ <h3>Holds history</h3> >+ >+ [% IF READING_RECORD.size == 0 %] >+ You have never placed a hold from this library. >+ [% ELSE %] >+ <div id="opac-user-holdsrec"> >+ <div id="tabs-container" style="overflow:auto"> >+ <div class="controls"> >+ <div class="span3 info"> >+ >+ </div> >+ <div class="span2"> >+ <input type="text" class="search" placeholder="search" autofocus/> >+ </div> >+ <div class="span1"> >+ <a class="paginate_button previous disabled" aria-controls="holdsrec" data-dt-idx="1">Previous</a> >+ </div> >+ <div class="span1"> >+ <a class="paginate_button next disabled" aria-controls="holdsrec" data-dt-idx="1">Next</a> >+ </div> >+ </div> >+ <table id="holdsrec" class="table table-bordered table-striped"> >+ <thead> >+ <tr> >+ <th class="anti-the">Title</th> >+ <th class="">Author</th> >+ <th class="">Barcode</th> >+ <th class="">Library</th> >+ <th class="title-string">Hold date</th> >+ <th class="title-string">Expiration date</th> >+ <th class="title-string">Waiting date</th> >+ <th class="title-string">Cancellation date</th> >+ <th>Status</th> >+ </tr> >+ </thead> >+ <tbody> >+ >+ </tbody> >+ </table> >+ <div class="controls"> >+ <div class="span3 info"> >+ >+ </div> >+ <div class="span1"> >+ <a class="paginate_button previous disabled" aria-controls="holdsrec" data-dt-idx="1">Previous</a> >+ </div> >+ <div class="span1"> >+ <a class="paginate_button next disabled" aria-controls="holdsrec" data-dt-idx="1">Next</a> >+ </div> >+ </div> >+ </div> <!-- / .tabs-container --> >+ </div> <!-- / .opac-user-readingrec --> >+ [% END # / IF READING_RECORD.size %] >+ </div> <!-- / .userreadingrecord --> >+ </div> <!-- / .span10 --> >+ </div> <!-- / .row-fluid --> >+ </div> <!-- / .container-fluid --> >+</div> <!-- / .main --> >+ >+[% INCLUDE 'opac-bottom.inc' %] >+[% BLOCK jsinclude %] >+[% INCLUDE 'datatables.inc' %] >+[% INCLUDE 'js-date-format.inc' %] >+<script> >+ $(document).ready(function(){ >+ [% IF ( GoogleJackets ) %]KOHA.Google.GetCoverFromIsbn();[% END %] >+ $('#order').change(function() { >+ $('#sortform').submit(); >+ }); >+ >+ var table = $("#holdsrec").api({ >+ "ajax": { >+ "url": "/api/v1/public/patrons/[% borrowernumber | html %]/holds?old=true" >+ }, >+ "embed": [ >+ "item", >+ "biblio", >+ "branch" >+ ], >+ 'order': [[ 4, "desc" ]], >+ "columnDefs": [ >+ { "targets": [ "nosort" ],"sortable": false,"searchable": false }, >+ { "type": "anti-the", "targets" : [ "anti-the" ] }, >+ { "type": "title-string", "targets" : [ "title-string" ] } >+ ], >+ "columns": [ >+ { >+ "data": "biblio.title", >+ "render": function(data, type, row, meta) { >+ if (type != 'display') return data; >+ return '<a href="opac-detail.pl?biblionumber='+row.biblio_id+'">'+data+'</a>'; >+ } >+ }, >+ {"data": "biblio.author"}, >+ { >+ "data": "item.external_id", >+ "render": function(data, type, row, meta) { >+ if(data) return data; >+ return ''; >+ } >+ }, >+ {"data": "branch.name"}, >+ { >+ "data": "hold_date", >+ "render": function(data, type, row, meta) { >+ return (data&&$date(data))||''; >+ } >+ }, >+ { >+ "data": "expiration_date", >+ "render": function(data, type, row, meta) { >+ return (data&&$date(data))||''; >+ } >+ }, >+ { >+ "data": "waiting_date", >+ "render": function(data, type, row, meta) { >+ return (data&&$date(data))||''; >+ } >+ }, >+ { >+ "data": "cancelation_date", >+ "render": function(data, type, row, meta) { >+ return (data&&$date(data))||''; >+ } >+ }, >+ { >+ "data": "status", >+ "render": function(data, type, row, meta) { >+ if (type != 'display') return data; >+ if(row.cancelation_date) return 'Canceled'; >+ if (data == 'F') return 'Fulfilled'; >+ if (data == 'W') return 'Waiting'; >+ if (data == 'T') return 'In Transit'; >+ return 'Pending'; >+ } >+ } >+ ] >+ }).DataTable(); >+ >+ table.on('draw.dt', function(event, settings) { >+ var page_info = table.page.info(); >+ $(".controls .info").html(_("Showing page %s of %s").format(page_info.page+1, page_info.pages)); >+ if(!page_info.page) { >+ $('.controls .previous').addClass('disabled'); >+ } else { >+ $('.controls .previous').removeClass('disabled'); >+ } >+ if(page_info.pages-1===page_info.page) { >+ $('.controls .next').addClass('disabled'); >+ } else { >+ $('.controls .next').removeClass('disabled'); >+ } >+ }) >+ >+ $('.controls .previous').click(function() { >+ table.page('previous').draw('page'); >+ }); >+ >+ $('.controls .next').click(function() { >+ table.page('next').draw('page'); >+ }); >+ function debounce(func, wait, immediate) { >+ var timeout; >+ return function() { >+ var context = this, args = arguments; >+ var later = function() { >+ timeout = null; >+ if (!immediate) func.apply(context, args); >+ }; >+ var callNow = immediate && !timeout; >+ clearTimeout(timeout); >+ timeout = setTimeout(later, wait); >+ if (callNow) func.apply(context, args); >+ }; >+ }; >+ >+ $(".controls .search").keypress(debounce(function(event) { >+ table.search(this.value).draw(); >+ }, 300)); >+ }); >+</script> >+[% END %] >diff --git a/opac/opac-holdsrecord.pl b/opac/opac-holdsrecord.pl >new file mode 100755 >index 0000000000..f52e5720e7 >--- /dev/null >+++ b/opac/opac-holdsrecord.pl >@@ -0,0 +1,161 @@ >+#!/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 <http://www.gnu.org/licenses>. >+ >+ >+use Modern::Perl; >+ >+use CGI qw ( -utf8 ); >+ >+use C4::Auth; >+use C4::Koha; >+use C4::Biblio; >+use C4::Circulation; >+use C4::Members; >+use Koha::DateUtils; >+use MARC::Record; >+ >+use C4::Output; >+use C4::Charset qw(StripNonXmlChars); >+use Koha::Patrons; >+ >+use Koha::ItemTypes; >+use Koha::Ratings; >+ >+my $query = new CGI; >+ >+# if opacreadinghistory is disabled, leave immediately >+if ( ! C4::Context->preference('OPACHoldsHistory') ) { >+ print $query->redirect("/cgi-bin/koha/errors/404.pl"); >+ exit; >+} >+ >+my ( $template, $borrowernumber, $cookie ) = get_template_and_user( >+ { >+ template_name => "opac-holdsrecord.tt", >+ query => $query, >+ type => "opac", >+ authnotrequired => 0, >+ debug => 1, >+ } >+); >+ >+my $borr = Koha::Patrons->find( $borrowernumber )->unblessed; >+ >+$template->param(%{$borr}); >+ >+# my $itemtypes = { map { $_->{itemtype} => $_ } @{ Koha::ItemTypes->search_with_localization->unblessed } }; >+ >+# # get the record >+# my $order = $query->param('order') || ''; >+# if ( $order eq 'title' ) { >+# $template->param( orderbytitle => 1 ); >+# } >+# elsif ( $order eq 'author' ) { >+# $template->param( orderbyauthor => 1 ); >+# } >+# else { >+# $order = "date_due desc"; >+# $template->param( orderbydate => 1 ); >+# } >+ >+ >+# my $limit = $query->param('limit'); >+# $limit //= ''; >+# $limit = ( $limit eq 'full' ) ? 0 : 50; >+ >+# # my $issues = GetAllIssues( $borrowernumber, $order, $limit ); >+ >+# # my $itype_attribute = >+# # ( C4::Context->preference('item-level_itypes') ) ? 'itype' : 'itemtype'; >+ >+# # my $opac_summary_html = C4::Context->preference('OPACMySummaryHTML'); >+# foreach my $issue ( @{$issues} ) { >+# $issue->{normalized_isbn} = GetNormalizedISBN( $issue->{isbn} ); >+# if ( $issue->{$itype_attribute} ) { >+# $issue->{translated_description} = >+# $itemtypes->{ $issue->{$itype_attribute} }->{translated_description}; >+# $issue->{imageurl} = >+# getitemtypeimagelocation( 'opac', >+# $itemtypes->{ $issue->{$itype_attribute} }->{imageurl} ); >+# } >+# my $marcxml = C4::Biblio::GetXmlBiblio( $issue->{biblionumber} ); >+# if ( $marcxml ) { >+# $marcxml = StripNonXmlChars( $marcxml ); >+# my $marc_rec = >+# MARC::Record::new_from_xml( $marcxml, 'utf8', >+# C4::Context->preference('marcflavour') ); >+# $issue->{normalized_upc} = GetNormalizedUPC( $marc_rec, C4::Context->preference('marcflavour') ); >+# } >+# # My Summary HTML >+# if ($opac_summary_html) { >+# my $my_summary_html = $opac_summary_html; >+# $issue->{author} >+# ? $my_summary_html =~ s/{AUTHOR}/$issue->{author}/g >+# : $my_summary_html =~ s/{AUTHOR}//g; >+# my $title = $issue->{title}; >+# $title =~ s/\/+$//; # remove trailing slash >+# $title =~ s/\s+$//; # remove trailing space >+# $title >+# ? $my_summary_html =~ s/{TITLE}/$title/g >+# : $my_summary_html =~ s/{TITLE}//g; >+# $issue->{normalized_isbn} >+# ? $my_summary_html =~ s/{ISBN}/$issue->{normalized_isbn}/g >+# : $my_summary_html =~ s/{ISBN}//g; >+# $issue->{biblionumber} >+# ? $my_summary_html =~ s/{BIBLIONUMBER}/$issue->{biblionumber}/g >+# : $my_summary_html =~ s/{BIBLIONUMBER}//g; >+# $issue->{MySummaryHTML} = $my_summary_html; >+# } >+# # Star ratings >+# if ( C4::Context->preference('OpacStarRatings') eq 'all' ) { >+# my $ratings = Koha::Ratings->search({ biblionumber => $issue->{biblionumber} }); >+# $issue->{ratings} = $ratings; >+# $issue->{my_rating} = $borrowernumber ? $ratings->search({ borrowernumber => $borrowernumber })->next : undef; >+# } >+# } >+ >+# if (C4::Context->preference('BakerTaylorEnabled')) { >+# $template->param( >+# JacketImages=>1, >+# BakerTaylorEnabled => 1, >+# BakerTaylorImageURL => &image_url(), >+# BakerTaylorLinkURL => &link_url(), >+# BakerTaylorBookstoreURL => C4::Context->preference('BakerTaylorBookstoreURL'), >+# ); >+# } >+ >+# BEGIN { >+# if (C4::Context->preference('BakerTaylorEnabled')) { >+# require C4::External::BakerTaylor; >+# import C4::External::BakerTaylor qw(&image_url &link_url); >+# } >+# } >+ >+# for(qw(AmazonCoverImages GoogleJackets)) { # BakerTaylorEnabled handled above >+# C4::Context->preference($_) or next; >+# $template->param($_=>1); >+# $template->param(JacketImages=>1); >+# } >+ >+$template->param( >+# READING_RECORD => $issues, >+# limit => $limit, >+ holdsrecview => 1, >+# OPACMySummaryHTML => $opac_summary_html ? 1 : 0, >+); >+ >+output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 }; >-- >2.21.1 (Apple Git-122.3)
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 20936
:
98629
|
98630
|
100115
|
100116
|
100117
|
100205
|
100206
|
100207
|
100471
|
100472
|
100473
|
100474
|
101724
|
101725
|
101726
|
101727
|
101772
|
101915
|
102166
|
103948
|
103949
|
103950
|
103951
|
104040
|
104041
|
104466
|
104467
|
105131
|
112667
|
112668
|
112730
|
112731
|
112732
|
112733
|
113446
|
113447
|
113448
|
113449