1 package Koha::REST::V1::Biblios;
3 # This file is part of Koha.
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20 use Mojo::Base 'Mojolicious::Controller';
24 use Koha::RecordProcessor;
25 use C4::Biblio qw( DelBiblio AddBiblio ModBiblio );
26 use C4::Search qw( FindDuplicate );
28 use List::MoreUtils qw( any );
29 use MARC::Record::MiJ;
31 use Try::Tiny qw( catch try );
39 Controller function that handles retrieving a single biblio object
44 my $c = shift->openapi->valid_input or return;
47 $attributes = { prefetch => [ 'metadata' ] } # don't prefetch metadata if not needed
48 unless $c->req->headers->accept =~ m/application\/json/;
50 my $biblio = Koha::Biblios->find( { biblionumber => $c->validation->param('biblio_id') }, $attributes );
56 error => "Object not found."
63 if ( $c->req->headers->accept =~ m/application\/json/ ) {
66 json => $biblio->to_api
70 my $metadata = $biblio->metadata;
71 my $record = $metadata->record;
72 my $schema = $metadata->schema // C4::Context->preference("marcflavour");
78 text => $record->as_xml_record($schema),
83 data => $record->to_mij
88 text => $record->as_usmarc
92 format => 'text/plain',
93 text => $record->as_formatted
99 "application/marcxml+xml",
100 "application/marc-in-json",
109 $c->unhandled_exception($_);
115 Controller function that handles deleting a biblio object
120 my $c = shift->openapi->valid_input or return;
122 my $biblio = Koha::Biblios->find( $c->validation->param('biblio_id') );
124 if ( not defined $biblio ) {
127 openapi => { error => "Object not found" }
132 my $error = DelBiblio( $biblio->id );
137 openapi => { error => $error }
141 return $c->render( status => 204, openapi => "" );
145 $c->unhandled_exception($_);
151 Controller function that handles retrieving a single biblio object
156 my $c = shift->openapi->valid_input or return;
158 my $biblio = Koha::Biblios->find(
159 { biblionumber => $c->validation->param('biblio_id') },
160 { prefetch => ['metadata'] } );
166 error => "Object not found."
173 my $metadata = $biblio->metadata;
174 my $record = $metadata->record;
176 my $opachiddenitems_rules = C4::Context->yaml_preference('OpacHiddenItems');
177 my $patron = $c->stash('koha.user');
179 # Check if the biblio should be hidden for unprivileged access
180 # unless there's a logged in user, and there's an exception for it's
182 unless ( $patron and $patron->category->override_hidden_items ) {
183 if ( $biblio->hidden_in_opac({ rules => $opachiddenitems_rules }) )
188 error => "Object not found."
194 my $schema = $metadata->schema // C4::Context->preference("marcflavour");
196 my $record_processor = Koha::RecordProcessor->new({
197 filters => 'ViewPolicy',
200 frameworkcode => $biblio->frameworkcode
203 # Apply framework's filtering to MARC::Record object
204 $record_processor->process($record);
210 text => $record->as_xml_record($schema),
215 data => $record->to_mij
220 text => $record->as_usmarc
224 format => 'text/plain',
225 text => $record->as_formatted
230 "application/marcxml+xml",
231 "application/marc-in-json",
239 $c->unhandled_exception($_);
245 Controller function that handles retrieving biblio's items
250 my $c = shift->openapi->valid_input or return;
252 my $biblio = Koha::Biblios->find( { biblionumber => $c->validation->param('biblio_id') }, { prefetch => ['items'] } );
258 error => "Object not found."
265 my $items_rs = $biblio->items;
266 my $items = $c->objects->search( $items_rs );
273 $c->unhandled_exception($_);
279 List Koha::Checkout objects
284 my $c = shift->openapi->valid_input or return;
286 my $checked_in = delete $c->validation->output->{checked_in};
289 my $biblio = Koha::Biblios->find( $c->validation->param('biblio_id') );
294 openapi => { error => 'Object not found' }
300 ? $c->objects->search( $biblio->old_checkouts )
301 : $c->objects->search( $biblio->current_checkouts );
305 openapi => $checkouts
309 $c->unhandled_exception($_);
313 =head3 pickup_locations
315 Method that returns the possible pickup_locations for a given biblio
316 used for building the dropdown selector
320 sub pickup_locations {
321 my $c = shift->openapi->valid_input or return;
323 my $biblio_id = $c->validation->param('biblio_id');
324 my $biblio = Koha::Biblios->find( $biblio_id );
329 openapi => { error => "Biblio not found" }
333 my $patron_id = delete $c->validation->output->{patron_id};
334 my $patron = Koha::Patrons->find( $patron_id );
339 openapi => { error => "Patron not found" }
345 my $pl_set = $biblio->pickup_locations( { patron => $patron } );
348 if ( C4::Context->preference('AllowHoldPolicyOverride') ) {
350 my $libraries_rs = Koha::Libraries->search( { pickup_location => 1 } );
351 my $libraries = $c->objects->search($libraries_rs);
355 $library->{needs_override} = (
356 any { $_->branchcode eq $library->{library_id} }
366 my $pickup_locations = $c->objects->search($pl_set);
367 @response = map { $_->{needs_override} = Mojo::JSON->false; $_; } @{$pickup_locations};
372 openapi => \@response
376 $c->unhandled_exception($_);
380 =head3 get_items_public
382 Controller function that handles retrieving biblio's items, for unprivileged
387 sub get_items_public {
388 my $c = shift->openapi->valid_input or return;
390 my $biblio = Koha::Biblios->find( { biblionumber => $c->validation->param('biblio_id') }, { prefetch => ['items'] } );
396 error => "Object not found."
403 my $patron = $c->stash('koha.user');
405 my $items_rs = $biblio->items->filter_by_visible_in_opac({ patron => $patron });
406 my $items = $c->objects->search( $items_rs );
413 $c->unhandled_exception($_);
419 Set rating for the logged in user
425 my $c = shift->openapi->valid_input or return;
427 my $biblio = Koha::Biblios->find( $c->validation->param('biblio_id') );
433 error => "Object not found."
438 my $patron = $c->stash('koha.user');
443 { error => "Cannot rate. Reason: must be logged-in" }
447 my $body = $c->validation->param('body');
448 my $rating_value = $body->{rating};
452 my $rating = Koha::Ratings->find(
454 biblionumber => $biblio->biblionumber,
455 borrowernumber => $patron->borrowernumber,
458 $rating->delete if $rating;
460 if ( $rating_value ) { # Cannot set to 0 from the UI
461 $rating = Koha::Rating->new(
463 biblionumber => $biblio->biblionumber,
464 borrowernumber => $patron->borrowernumber,
465 rating_value => $rating_value,
470 Koha::Ratings->search( { biblionumber => $biblio->biblionumber } );
471 my $average = $ratings->get_avg_rating;
476 rating => $rating && $rating->in_storage ? $rating->rating_value : undef,
478 count => $ratings->count
483 $c->unhandled_exception($_);
489 Controller function that handles creating a biblio object
494 my $c = shift->openapi->valid_input or return;
497 my $headers = $c->req->headers;
499 my $flavour = $headers->header('x-marc-schema');
500 $flavour //= C4::Context->preference('marcflavour');
504 my $frameworkcode = $headers->header('x-framework-id');
505 my $content_type = $headers->content_type;
507 if ( $content_type =~ m/application\/marcxml\+xml/ ) {
508 $record = MARC::Record->new_from_xml( $c->req->body, 'UTF-8', $flavour );
510 elsif ( $content_type =~ m/application\/marc-in-json/ ) {
511 $record = MARC::Record->new_from_mij_structure( $c->req->json );
513 elsif ( $content_type =~ m/application\/marc/ ) {
514 $record = MARC::Record->new_from_usmarc( $c->req->body );
520 "application/marcxml+xml",
521 "application/marc-in-json",
527 my ( $duplicatebiblionumber, $duplicatetitle );
528 ( $duplicatebiblionumber, $duplicatetitle ) = FindDuplicate($record);
530 my $confirm_not_duplicate = $headers->header('x-confirm-not-duplicate');
535 error => "Duplicate biblio $duplicatebiblionumber",
537 ) unless !$duplicatebiblionumber || $confirm_not_duplicate;
539 my ( $biblionumber, $oldbibitemnum );
540 ( $biblionumber, $oldbibitemnum ) = AddBiblio( $record, $frameworkcode );
544 openapi => { id => $biblionumber }
548 $c->unhandled_exception($_);
554 Controller function that handles modifying an biblio object
559 my $c = shift->openapi->valid_input or return;
561 my $biblio_id = $c->param('biblio_id');
562 my $biblio = Koha::Biblios->find($biblio_id);
564 if ( ! defined $biblio ) {
567 openapi => { error => "Object not found" }
572 my $headers = $c->req->headers;
574 my $flavour = $headers->header('x-marc-schema');
575 $flavour //= C4::Context->preference('marcflavour');
577 my $frameworkcode = $headers->header('x-framework-id') || $biblio->frameworkcode;
579 my $content_type = $headers->content_type;
583 if ( $content_type =~ m/application\/marcxml\+xml/ ) {
584 $record = MARC::Record->new_from_xml( $c->req->body, 'UTF-8', $flavour );
586 elsif ( $content_type =~ m/application\/marc-in-json/ ) {
587 $record = MARC::Record->new_from_mij_structure( $c->req->json );
589 elsif ( $content_type =~ m/application\/marc/ ) {
590 $record = MARC::Record->new_from_usmarc( $c->req->body );
597 "application/marcxml+xml",
598 "application/marc-in-json",
604 ModBiblio( $record, $biblio_id, $frameworkcode );
608 openapi => { id => $biblio_id }
612 $c->unhandled_exception($_);
618 Controller function that handles retrieving a single biblio object
623 my $c = shift->openapi->valid_input or return;
627 { prefetch => ['metadata'] } # don't prefetch metadata if not needed
628 unless $c->req->headers->accept =~ m/application\/json/;
630 my $biblios = $c->objects->search_rs( Koha::Biblios->new );
634 if ( $c->req->headers->accept =~ m/application\/json(;.*)?$/ ) {
637 json => $c->objects->to_api( $biblios ),
641 $c->req->headers->accept =~ m/application\/marcxml\+xml(;.*)?$/ )
643 $c->res->headers->add( 'Content-Type', 'application/marcxml+xml' );
646 text => $biblios->print_collection('marcxml')
650 $c->req->headers->accept =~ m/application\/marc-in-json(;.*)?$/ )
652 $c->res->headers->add( 'Content-Type', 'application/marc-in-json' );
655 data => $biblios->print_collection('mij')
658 elsif ( $c->req->headers->accept =~ m/application\/marc(;.*)?$/ ) {
659 $c->res->headers->add( 'Content-Type', 'application/marc' );
662 text => $biblios->print_collection('marc')
665 elsif ( $c->req->headers->accept =~ m/text\/plain(;.*)?$/ ) {
668 text => $biblios->print_collection('txt')
675 "application/json", "application/marcxml+xml",
676 "application/marc-in-json", "application/marc",
683 $c->unhandled_exception($_);