Merge remote-tracking branch 'upstream/24.05.x' into HEAD

This commit is contained in:
Wainui Witika-Park 2025-01-06 16:23:33 +13:00
commit 17ea7afac8
375 changed files with 7112 additions and 5143 deletions

View File

@ -82,14 +82,9 @@ BEGIN {
get_template_and_user haspermission create_basic_session
);
$ldap = C4::Context->config('useldapserver') || 0;
$cas = C4::Context->preference('casAuthentication');
$caslogout = C4::Context->preference('casLogout');
if ($ldap) {
require C4::Auth_with_ldap;
import C4::Auth_with_ldap qw(checkpw_ldap);
}
if ($cas) {
require C4::Auth_with_cas; # no import
import C4::Auth_with_cas qw(check_api_auth_cas checkpw_cas login_cas logout_cas login_cas_url logout_if_required multipleAuth getMultipleAuth);
@ -554,9 +549,9 @@ sub get_template_and_user {
my $opac_limit_override = $ENV{'OPAC_LIMIT_OVERRIDE'};
my $opac_name = '';
if (
( $opac_limit_override && $opac_search_limit && $opac_search_limit =~ /branch:([\w-]+)/ ) ||
( $in->{'query'}->param('limit') && $in->{'query'}->param('limit') =~ /branch:([\w-]+)/ ) ||
( $in->{'query'}->param('limit') && $in->{'query'}->param('limit') =~ /multibranchlimit:(\w+)/ )
( $opac_limit_override && $opac_search_limit && $opac_search_limit =~ /^branch:([\w-]+)/ ) ||
( $in->{'query'}->param('limit') && $in->{'query'}->param('limit') =~ /^branch:([\w-]+)/ ) ||
( $in->{'query'}->param('limit') && $in->{'query'}->param('limit') =~ /^multibranchlimit:(\w+)/ )
) {
$opac_name = $1; # opac_search_limit is a branch, so we use it.
} elsif ( $in->{'query'}->param('multibranchlimit') ) {
@ -1418,6 +1413,11 @@ sub checkauth {
my $auth_error = $query->param('auth_error');
my $auth_template_name = ( $type eq 'opac' ) ? 'opac-auth.tt' : 'auth.tt';
my $template = C4::Templates::gettemplate( $auth_template_name, $type, $query );
my $borrowernumber = $patron and $patron->borrowernumber;
my $anonymous_patron = C4::Context->preference('AnonymousPatron');
my $is_anonymous_patron = $patron && ( $patron->borrowernumber eq $anonymous_patron );
$template->param(
login => 1,
INPUTS => \@inputs,
@ -1452,6 +1452,7 @@ sub checkauth {
opac_css_override => $ENV{'OPAC_CSS_OVERRIDE'},
too_many_login_attempts => ( $patron and $patron->account_locked ),
password_has_expired => ( $patron and $patron->password_expired ),
is_anonymous_patron => ( $is_anonymous_patron ),
auth_error => $auth_error,
);
@ -1981,20 +1982,24 @@ sub checkpw {
my $shib = C4::Context->config('useshibboleth') && shib_ok();
my $shib_login = $shib ? get_login_shib() : undef;
my $anonymous_patron = C4::Context->preference('AnonymousPatron');
my @return;
my $check_internal_as_fallback = 0;
my $passwd_ok = 0;
my $patron;
# Note: checkpw_* routines returns:
# 1 if auth is ok
# 0 if auth is nok
# -1 if user bind failed (LDAP only)
$ldap = C4::Context->config('useldapserver') || 0;
if ( $ldap && defined($password) ) {
my ( $retval, $retcard, $retuserid );
( $retval, $retcard, $retuserid, $patron ) = checkpw_ldap(@_); # EXTERNAL AUTH
require C4::Auth_with_ldap;
import C4::Auth_with_ldap qw(checkpw_ldap);
( $retval, $retcard, $retuserid, $patron ) = C4::Auth_with_ldap::checkpw_ldap(@_); # EXTERNAL AUTH
if ( $retval == 1 ) {
@return = ( $retval, $retcard, $retuserid, $patron );
$passwd_ok = 1;
@ -2058,7 +2063,9 @@ sub checkpw {
@return = ();
} elsif ($passwd_ok) {
$patron->update( { login_attempts => 0 } );
if ( $patron->password_expired ) {
if ( defined($anonymous_patron) && ($patron->borrowernumber eq $anonymous_patron) ) {
@return = ( -3, $patron );
} elsif ( $patron->password_expired ) {
@return = ( -2, $patron );
}
} else {

View File

@ -227,6 +227,7 @@ sub checkpw_ldap {
map { exists( $borrower{$_} ) ? ( $_ => $borrower{$_} ) : () } @columns
}
)->store;
$patron->discard_changes;
die "Insert of new patron failed" unless $patron;
$borrowernumber = $patron->borrowernumber;
C4::Members::Messaging::SetMessagingPreferencesFromDefaults(

View File

@ -135,6 +135,8 @@ sub _autocreate {
}
my $patron = Koha::Patron->new( \%borrower )->store;
$patron->discard_changes;
C4::Members::Messaging::SetMessagingPreferencesFromDefaults(
{
borrowernumber => $patron->borrowernumber,

View File

@ -323,17 +323,17 @@ sub SearchAuthorities {
if ( C4::Context->preference('ShowHeadingUse') ) {
# checking valid heading use
my $f008 = $authrecord->field('008');
my $pos14to16 = substr( $f008->data, 14, 3 );
my $main = substr( $pos14to16, 0, 1 );
$newline{main} = 1 if $main eq 'a';
my $subject = substr( $pos14to16, 1, 1);
$newline{subject} = 1 if $subject eq 'a';
my $series = substr( $pos14to16, 2, 1 );
$newline{series} = 1 if $series eq 'a';
if ($f008) {
my $pos14to16 = substr( $f008->data, 14, 3 );
my $main = substr( $pos14to16, 0, 1 );
$newline{main} = 1 if $main eq 'a';
my $subject = substr( $pos14to16, 1, 1 );
$newline{subject} = 1 if $subject eq 'a';
my $series = substr( $pos14to16, 2, 1 );
$newline{series} = 1 if $series eq 'a';
}
}
$newline{authtype} = defined($thisauthtype) ?
$thisauthtype->authtypetext : '';
$newline{authtype} = defined($thisauthtype) ? $thisauthtype->authtypetext : '';
$newline{summary} = $summary;
$newline{even} = $counter % 2;
$newline{reported_tag} = $reported_tag;

View File

@ -94,11 +94,12 @@ sub max {
return $self->db_max;
}
sub db_max {
my $self = shift;
my $query = "SELECT max(abs(barcode)) FROM items LIMIT 1"; # Possible problem if multiple barcode types populated
my $sth = C4::Context->dbh->prepare($query);
$sth->execute();
return $sth->fetchrow_array || $self->initial;
my $self = shift;
my $query = "SELECT max(abs(CAST(barcode AS UNSIGNED))) FROM items LIMIT 1"
; # Possible problem if multiple barcode types populated
my $sth = C4::Context->dbh->prepare($query);
$sth->execute();
return $sth->fetchrow_array || $self->initial;
}
sub next_value {
my $self = shift;

View File

@ -555,9 +555,13 @@ sub DelBiblio {
# We delete any existing holds
my $holds = $biblio->holds;
$holds->update( { deleted_biblionumber => $biblionumber }, { no_triggers => 1 } );
my $old_holds = $biblio->old_holds;
$old_holds->update( { deleted_biblionumber => $biblionumber }, { no_triggers => 1 } );
while ( my $hold = $holds->next ) {
# no need to update the holds queue on each step, we'll do it at the end
$hold->cancel({ skip_holds_queue => 1 });
$hold->cancel( { skip_holds_queue => 1 } );
}
# We update any existing orders

View File

@ -1266,9 +1266,8 @@ sub CanBookBeIssued {
## CHECK AGE RESTRICTION
my $agerestriction = $biblioitem->agerestriction;
my ( $restriction_age, $daysToAgeRestriction ) =
GetAgeRestriction( $agerestriction, $patron->unblessed );
if ( $daysToAgeRestriction && $daysToAgeRestriction > 0 ) {
my $restriction_age = GetAgeRestriction( $agerestriction );
if ( $restriction_age && $patron->dateofbirth && $restriction_age > $patron->get_age() ) {
if ( C4::Context->preference('AgeRestrictionOverride') ) {
$needsconfirmation{AGE_RESTRICTION} = "$agerestriction";
}
@ -2461,7 +2460,7 @@ sub AddReturn {
ccode => $item->ccode,
categorycode => $categorycode,
interface => C4::Context->interface,
}) unless ( $skip_localuse && $stat_type eq 'localuse' );
}) unless ( $skip_localuse && !$issue );
# Send a check-in slip. # NOTE: borrower may be undef. Do not try to send messages then.
if ( $patron ) {
@ -4421,21 +4420,15 @@ sub IsItemIssued {
=head2 GetAgeRestriction
my ($ageRestriction, $daysToAgeRestriction) = GetAgeRestriction($record_restrictions, $borrower);
my ($ageRestriction, $daysToAgeRestriction) = GetAgeRestriction($record_restrictions);
if($daysToAgeRestriction <= 0) { #Borrower is allowed to access this material, as they are older or as old as the agerestriction }
if($daysToAgeRestriction > 0) { #Borrower is this many days from meeting the agerestriction }
my $ageRestriction = GetAgeRestriction($record_restrictions);
@PARAM1 the koha.biblioitems.agerestriction value, like K18, PEGI 13, ...
@PARAM2 a borrower-object with koha.borrowers.dateofbirth. (OPTIONAL)
@RETURNS The age restriction age in years and the days to fulfill the age restriction for the given borrower.
Negative days mean the borrower has gone past the age restriction age.
@RETURNS The age restriction age in years.
=cut
sub GetAgeRestriction {
my ($record_restrictions, $borrower) = @_;
my ($record_restrictions) = @_;
my $markers = C4::Context->preference('AgeRestrictionMarker');
return unless $record_restrictions;
@ -4470,26 +4463,7 @@ sub GetAgeRestriction {
last if ( $restriction_year > 0 );
}
#Check if the borrower is age restricted for this material and for how long.
if ($restriction_year && $borrower) {
if ( $borrower->{'dateofbirth'} ) {
my @alloweddate = split /-/, $borrower->{'dateofbirth'};
$alloweddate[0] += $restriction_year;
#Prevent runime eror on leap year (invalid date)
if ( ( $alloweddate[1] == 2 ) && ( $alloweddate[2] == 29 ) ) {
$alloweddate[2] = 28;
}
#Get how many days the borrower has to reach the age restriction
my @Today = split /-/, dt_from_string()->ymd();
my $daysToAgeRestriction = Date_to_Days(@alloweddate) - Date_to_Days(@Today);
#Negative days means the borrower went past the age restriction age
return ($restriction_year, $daysToAgeRestriction);
}
}
return ($restriction_year);
return $restriction_year;
}

View File

@ -218,7 +218,7 @@ sub GetRecords {
my $biblioitem = $biblio->biblioitem->unblessed;
my $record = $biblio->metadata->record({ embed_items => 1 });
my $record = $biblio->metadata->record({ embed_items => 1, opac => 1, });
if ($record) {
$biblioitem->{marcxml} = $record->as_xml_record( C4::Context->preference('marcflavour') );
}
@ -236,7 +236,7 @@ sub GetRecords {
foreach my $checkout (@$checkouts) {
delete $checkout->{'borrowernumber'};
}
my @items = $biblio->items->as_list;
my @items = $biblio->items->filter_by_visible_in_opac->as_list;
$biblioitem->{items}->{item} = [];

View File

@ -291,6 +291,7 @@ sub _export_table_csv
for my $field (@fields) {
my $value = $hashRef->{$field} // q||;
$value =~ s/[\r\n]//g;
$value =~ s/"/""/g;
$$strCSV .= '"' . $value . '",';
}
chop $$strCSV;

View File

@ -45,6 +45,7 @@ BEGIN {
}
use Carp qw( croak );
use C4::Circulation qw( barcodedecode );
use C4::Context;
use C4::Koha;
use C4::Biblio qw( GetMarcStructure TransformMarcToKoha );
@ -459,11 +460,15 @@ sub CheckItemPreSave {
my %errors = ();
# check for duplicate barcode
if (exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'}) {
my $existing_item= Koha::Items->find({barcode => $item_ref->{'barcode'}});
if ( exists $item_ref->{'barcode'} and defined $item_ref->{'barcode'} ) {
my $barcode = C4::Circulation::barcodedecode( $item_ref->{'barcode'} );
my $existing_item = Koha::Items->find( { barcode => $barcode } );
if ($existing_item) {
if (!exists $item_ref->{'itemnumber'} # new item
or $item_ref->{'itemnumber'} != $existing_item->itemnumber) { # existing item
if (
!exists $item_ref->{'itemnumber'} # new item
or $item_ref->{'itemnumber'} != $existing_item->itemnumber
)
{ # existing item
$errors{'duplicate_barcode'} = $item_ref->{'barcode'};
}
}
@ -1178,7 +1183,7 @@ sub SearchItems {
LEFT JOIN biblio ON biblio.biblionumber = items.biblionumber
LEFT JOIN biblioitems ON biblioitems.biblioitemnumber = items.biblioitemnumber
LEFT JOIN biblio_metadata ON biblio_metadata.biblionumber = biblio.biblionumber
LEFT JOIN issues ON issues.itemnumber = items.itemnumber
LEFT JOIN issues USING (itemnumber)
WHERE 1
};
if (defined $where_str and $where_str ne '') {

View File

@ -1569,8 +1569,8 @@ sub _wrap_html {
$css = qq{<link rel="stylesheet" type="text/css" href="$css">} if $css;
return <<EOS;
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
"https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xml:lang="en" xmlns="https://www.w3.org/1999/xhtml">
<head>
<title>$title</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

View File

@ -288,10 +288,12 @@ sub CalcFine {
&& $amount > $issuing_rule->{overduefinescap};
# This must be moved to Koha::Item (see also similar code in C4::Accounts::chargelostitem
$item->{replacementprice} ||= $itemtype->defaultreplacecost
if $itemtype
&& ( ! defined $item->{replacementprice} || $item->{replacementprice} == 0 )
&& C4::Context->preference("useDefaultReplacementCost");
if ( $itemtype
&& ( !defined $item->{replacementprice} || $item->{replacementprice} + .0 == 0 )
&& C4::Context->preference("useDefaultReplacementCost") )
{
$item->{replacementprice} = $itemtype->defaultreplacecost;
}
$amount = $item->{replacementprice} if ( $issuing_rule->{cap_fine_to_replacement_price} && $item->{replacementprice} && $amount > $item->{replacementprice} );

View File

@ -44,7 +44,6 @@ use Koha::Holds;
use Koha::ItemTypes;
use Koha::Items;
use Koha::Libraries;
use Koha::Old::Holds;
use Koha::Patrons;
use Koha::Plugins;
use Koha::Policy::Holds;
@ -485,10 +484,6 @@ sub CanItemBeReserved {
}
}
# we retrieve borrowers and items informations #
# item->{itype} will come for biblioitems if necessery
my $borrower = $patron->unblessed;
# If an item is damaged and we don't allow holds on damaged items, we can stop right here
return _cache { status =>'damaged' }
if ( $item->damaged
@ -497,9 +492,8 @@ sub CanItemBeReserved {
if( GetMarcFromKohaField('biblioitems.agerestriction') ){
my $biblio = $item->biblio;
# Check for the age restriction
my ( $ageRestriction, $daysToAgeRestriction ) =
C4::Circulation::GetAgeRestriction( $biblio->biblioitem->agerestriction, $borrower );
return _cache { status => 'ageRestricted' } if $daysToAgeRestriction && $daysToAgeRestriction > 0;
my $ageRestriction = C4::Circulation::GetAgeRestriction( $biblio->biblioitem->agerestriction );
return _cache { status => 'ageRestricted' } if $ageRestriction && $patron->dateofbirth && $ageRestriction > $patron->get_age();
}
# Check that the patron doesn't have an item level hold on this item already
@ -527,14 +521,14 @@ sub CanItemBeReserved {
}
elsif ( $controlbranch eq "PatronLibrary" ) {
$branchfield = "borrowers.branchcode";
$reserves_control_branch = $borrower->{branchcode};
$reserves_control_branch = $patron->branchcode;
}
# we retrieve rights
if (
my $reservesallowed = Koha::CirculationRules->get_effective_rule({
itemtype => $item->effective_itemtype,
categorycode => $borrower->{categorycode},
categorycode => $patron->categorycode,
branchcode => $reserves_control_branch,
rule_name => 'reservesallowed',
})
@ -547,7 +541,7 @@ sub CanItemBeReserved {
}
my $rights = Koha::CirculationRules->get_effective_rules({
categorycode => $borrower->{'categorycode'},
categorycode => $patron->categorycode,
itemtype => $item->effective_itemtype,
branchcode => $reserves_control_branch,
rules => ['holds_per_record','holds_per_day']
@ -656,7 +650,7 @@ sub CanItemBeReserved {
}
if ( $branchitemrule->{holdallowed} eq 'from_home_library'
&& $borrower->{branchcode} ne $item->homebranch )
&& $patron->branchcode ne $item->homebranch )
{
return _cache { status => 'cannotReserveFromOtherBranches' };
}
@ -685,7 +679,7 @@ sub CanItemBeReserved {
if ($branchitemrule->{hold_fulfillment_policy} eq 'holdgroup' && !$item_library->validate_hold_sibling( {branchcode => $pickup_branchcode} )) {
return _cache { status => 'pickupNotInHoldGroup' };
}
if ($branchitemrule->{hold_fulfillment_policy} eq 'patrongroup' && !Koha::Libraries->find({branchcode => $borrower->{branchcode}})->validate_hold_sibling({branchcode => $pickup_branchcode})) {
if ($branchitemrule->{hold_fulfillment_policy} eq 'patrongroup' && !Koha::Libraries->find({branchcode => $patron->branchcode})->validate_hold_sibling({branchcode => $pickup_branchcode})) {
return _cache { status => 'pickupNotInHoldGroup' };
}
}
@ -1602,6 +1596,7 @@ sub _FixPriority {
$hold = Koha::Holds->find( $reserve_id );
if (!defined $hold){
# may have already been checked out and hold fulfilled
require Koha::Old::Holds;
$hold = Koha::Old::Holds->find( $reserve_id );
}
return unless $hold;

View File

@ -311,7 +311,7 @@ sub end_patron_session {
my ($self, $patron_id) = @_;
# success?, screen_msg, print_line
return (1, 'Thank you !', '');
return (1, 'Thank you!', '');
}
sub pay_fee {

View File

@ -121,6 +121,9 @@ sub new {
}
}
# Get currency 3 chars max
my $currency = substr Koha::Acquisition::Currencies->get_active->currency, 0, 3;
my $circ_blocked =( C4::Context->preference('OverduesBlockCirc') ne "noblock" && defined $flags->{ODUES}->{itemlist} ) ? 1 : 0;
{
no warnings; # any of these $kp->{fields} being concat'd could be undef
@ -164,6 +167,7 @@ sub new {
fine_blocked => $fine_blocked,
fee_limit => $fee_limit,
userid => $kp->{userid},
currency => $currency,
);
}
@ -205,7 +209,7 @@ sub new {
}
}
# FIXME: populate fine_items recall_items
# FIXME: populate recall_items
$ilspatron{unavail_holds} = _get_outstanding_holds($kp->{borrowernumber});
my $pending_checkouts = $patron->pending_checkouts;
@ -390,6 +394,7 @@ sub x_items {
my $item_list = [];
if ($self->{$array_var}) {
if ($start && $start > 1) {
--$start;
}
@ -397,6 +402,7 @@ sub x_items {
$start = 0;
}
if ( $end && $end < @{$self->{$array_var}} ) {
--$end;
}
else {
$end = @{$self->{$array_var}};
@ -430,29 +436,33 @@ sub charged_items {
return $self->x_items('items', @_);
}
sub fine_items {
require Koha::Database;
require Template;
my $self = shift;
my $start = shift;
my $end = shift;
my $self = shift;
my $start = shift;
my $end = shift;
my $server = shift;
my @fees = Koha::Database->new()->schema()->resultset('Accountline')->search(
my @fees =
Koha::Database->new()->schema()->resultset('Accountline')->search(
{
borrowernumber => $self->{borrowernumber},
amountoutstanding => { '>' => '0' },
}
);
);
$start = $start ? $start - 1 : 0;
$end = $end ? $end - 1 : scalar @fees - 1;
my $av_field_template = $server ? $server->{account}->{av_field_template} : undef;
$av_field_template ||= "[% accountline.description %] [% accountline.amountoutstanding | format('%.2f') %]";
my $av_field_template =
$server ? $server->{account}->{av_field_template} : undef;
$av_field_template ||=
"[% accountline.description %] [% accountline.amountoutstanding | format('%.2f') %]";
my @return_values;
for ( my $i = $start; $i <= $end; $i++ ) {
for ( my $i = $start ; $i <= $end ; $i++ ) {
my $fee = $fees[$i];
next unless $fee;
@ -462,7 +472,9 @@ sub fine_items {
}
return \@return_values;
}
sub recall_items {
my $self = shift;
return $self->x_items('recall_items', @_);

View File

@ -8,7 +8,7 @@ use FindBin qw($Bin);
use lib "$Bin";
use Net::Server::PreFork;
use IO::Socket::INET;
use Socket qw(:DEFAULT :crlf);
use Socket qw(:DEFAULT :crlf TCP_KEEPIDLE TCP_KEEPINTVL IPPROTO_TCP);
use Scalar::Util qw(blessed);
require UNIVERSAL::require;
@ -90,6 +90,126 @@ push @parms, 'group=' . $>;
# This is the main event.
__PACKAGE__ ->run(@parms);
#
# Server
#
=head3 options
As per Net::Server documentation, override "options" to provide your own
custom options to the Net::Server* object. This allows us to use the Net::Server
infrastructure for configuration rather than hacking our own configuration into the
object.
=cut
sub options {
my $self = shift;
my $prop = $self->{'server'};
my $template = shift;
# setup options in the parent classes
$self->SUPER::options($template);
$prop->{'custom_tcp_keepalive'} ||= undef;
$template->{'custom_tcp_keepalive'} = \$prop->{'custom_tcp_keepalive'};
$prop->{'custom_tcp_keepalive_time'} ||= undef;
$template->{'custom_tcp_keepalive_time'} = \$prop->{'custom_tcp_keepalive_time'};
$prop->{'custom_tcp_keepalive_intvl'} ||= undef;
$template->{'custom_tcp_keepalive_intvl'} = \$prop->{'custom_tcp_keepalive_intvl'};
}
=head3 post_configure_hook
As per Net::Server documentation, this method validates our custom configuration.
=cut
sub post_configure_hook {
my $self = shift;
my $prop = $self->{'server'};
if ( defined $prop->{'custom_tcp_keepalive'} && $prop->{'custom_tcp_keepalive'} ) {
#NOTE: Any true value defined is forced to 1 just for the sake of predictability
$prop->{'custom_tcp_keepalive'} = 1;
}
foreach my $key ( 'custom_tcp_keepalive_time', 'custom_tcp_keepalive_intvl' ) {
my $value = $prop->{$key};
#NOTE: A regex is used here to detect a positive integer, as int() returns integers but does not validate them
#NOTE: We do not allow zero as it can lead to problematic behaviour
if ( $value && $value =~ /^\d+$/ ) {
#NOTE: Strictly, you must convert into an integer as a string will cause setsockopt to fail
$prop->{$key} = int($value);
}
}
}
=head3 post_accept_hook
This hook occurs after the client connection socket is created, which gives
us an opportunity to enable support for TCP keepalives using the SO_KEEPALIVE
socket option.
By default, the kernel-level defaults (in seconds) are used. You can view these in the output of "sysctl -a":
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_time = 7200
Alternatively, you can use "custom_tcp_keepalive_time" and "custom_tcp_keepalive_intvl" to define
your own custom values for the socket. Note that these parameters are defined at the top server-level
and not on the listener-level.
If you lower "custom_tcp_keepalive_time" below 75, you will also need to set "custom_tcp_keepalive_intvl".
The "tcp_keepalive_time" is the initial time used for the keepalive timer, and the "tcp_keepalive_intvl"
is the time used for subsequent keepalive timers. However, timers only send keepalive ACKs if the idle time
elapsed is greater than "tcp_keepalive_time".
Thus, if "tcp_keepalive_time = 10" and "tcp_keepalive_intvl = 5", a keepalive ACK will be sent every 10 seconds
of idle time. If "tcp_keepalive_intvl = 10" and "tcp_keepalive_time = 5", a keepalive ACK will be sent after 5
seconds of idle time, and the next keepalive ACK will be sent after 10 seconds of idle time. Generally speaking,
it's best to set "tcp_keepalive_time" to be higher than "tcp_keepalive_intvl".
Reminder: once these settings are set on the socket, they are handled by the operating system kernel, and not
by the SIP server application. If you are having trouble with your settings, monitor your TCP traffic using
a tool such as "tcpdump" to review and refine how they work.
=cut
sub post_accept_hook {
my $self = shift;
my $prop = $self->{'server'};
my $client = shift || $prop->{'client'};
my $tcp_keepalive = $prop->{custom_tcp_keepalive};
my $tcp_keepalive_time = $prop->{custom_tcp_keepalive_time};
my $tcp_keepalive_intvl = $prop->{custom_tcp_keepalive_intvl};
if ($tcp_keepalive) {
#NOTE: set the SO_KEEPALIVE option to enable TCP keepalives
setsockopt( $client, SOL_SOCKET, SO_KEEPALIVE, 1 )
or die "Unable to set SO_KEEPALIVE: $!";
if ($tcp_keepalive_time) {
#NOTE: override "net.ipv4.tcp_keepalive_time" kernel parameter for this socket
setsockopt( $client, IPPROTO_TCP, TCP_KEEPIDLE, $tcp_keepalive_time )
or die "Unable to set TCP_KEEPIDLE: $!";
}
if ($tcp_keepalive_intvl) {
#NOTE: override "net.ipv4.tcp_keepalive_intvl" kernel parameter for this socket
setsockopt( $client, IPPROTO_TCP, TCP_KEEPINTVL, $tcp_keepalive_intvl )
or die "Unable to set TCP_KEEPINTVL: $!";
}
}
}
#
# Child
#

View File

@ -187,34 +187,33 @@ sub boolspace {
#
sub write_msg {
my ($self, $msg, $file, $terminator, $encoding) = @_;
my ( $self, $msg, $server ) = @_;
my $terminator = $server->{account}->{terminator};
my $encoding = $server->{account}->{encoding};
$terminator ||= q{};
$terminator = ( $terminator eq 'CR' ) ? $CR : $CRLF;
$msg = encode($encoding, $msg) if ( $encoding );
my $separator = $server->{account}->{convert_nonprinting_characters};
$msg =~ s/[^[:print:]]/$separator/g if defined $separator;
$msg = encode( $encoding, $msg ) if ($encoding);
my $cksum;
# $msg = encode_utf8($msg);
if ($error_detection) {
if (defined($self->{seqno})) {
if ( defined( $self->{seqno} ) ) {
$msg .= 'AY' . $self->{seqno};
}
$msg .= 'AZ';
$cksum = checksum($msg);
$msg .= sprintf('%04.4X', $cksum);
$msg .= sprintf( '%04.4X', $cksum );
}
if ($file) {
$file->autoflush(1);
print $file $msg, $terminator;
} else {
STDOUT->autoflush(1);
print $msg, $terminator;
siplog("LOG_INFO", "OUTPUT MSG: '$msg'");
}
STDOUT->autoflush(1);
print $msg, $terminator;
siplog( "LOG_INFO", "OUTPUT MSG: '$msg'" );
$last_response = $msg;
}

View File

@ -504,12 +504,12 @@ sub handle_patron_status {
$ils->check_inst_id( $fields->{ (FID_INST_ID) }, "handle_patron_status" );
$patron = $ils->find_patron( $fields->{ (FID_PATRON_ID) } );
if ( C4::Context->preference('TrackLastPatronActivityTriggers') ) {
if ( C4::Context->preference('TrackLastPatronActivityTriggers') && $patron ) {
my $koha_patron = Koha::Patrons->find($patron->{borrowernumber});
$koha_patron->update_lastseen('connection');
}
$resp = build_patron_status( $patron, $lang, $fields, $server );
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (PATRON_STATUS_REQ);
}
@ -649,7 +649,7 @@ sub handle_checkout {
}
}
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (CHECKOUT);
}
@ -748,7 +748,7 @@ sub handle_checkin {
$resp .= maybe_add( FID_SCREEN_MSG, $status->screen_msg, $server );
$resp .= maybe_add( FID_PRINT_LINE, $status->print_line, $server );
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (CHECKIN);
}
@ -795,7 +795,7 @@ sub handle_block_patron {
}
$resp = build_patron_status( $patron, $patron->language, $fields, $server );
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (BLOCK_PATRON);
}
@ -836,7 +836,7 @@ sub handle_request_acs_resend {
# We haven't sent anything yet, so respond with a
# REQUEST_SC_RESEND msg (p. 16)
$self->write_msg( REQUEST_SC_RESEND, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( REQUEST_SC_RESEND, $server );
} elsif ( ( length($last_response) < 9 )
|| substr( $last_response, -9, 2 ) ne 'AY' ) {
@ -850,7 +850,7 @@ sub handle_request_acs_resend {
# Cut out the sequence number and checksum, since the old
# checksum is wrong for the resent message.
my $rebuilt = substr( $last_response, 0, -9 );
$self->write_msg( $rebuilt, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $rebuilt, $server );
}
return REQUEST_ACS_RESEND;
@ -932,7 +932,7 @@ sub handle_login {
$status = login_core( $server, $uid, $pwd );
}
$self->write_msg( LOGIN_RESP . $status, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( LOGIN_RESP . $status, $server );
return $status ? LOGIN : '';
}
@ -971,7 +971,7 @@ sub summary_info {
my $fid = $summary_map[$summary_type]->{fid};
my $itemlist = &$func( $patron, $start, $end, $server );
siplog( "LOG_DEBUG", "summary_info: list = (%s)", join( ", ", map{ $_->{barcode} } @{$itemlist} ) );
siplog( "LOG_DEBUG", "summary_info: list = (%s)", join( ", ", map { $_->{barcode} } @{$itemlist} ) );
foreach my $i ( @{$itemlist} ) {
$resp .= add_field( $fid, $i->{barcode}, $server );
}
@ -1101,7 +1101,7 @@ sub handle_patron_info {
$resp .= maybe_add( FID_SCREEN_MSG, INVALID_CARD, $server );
}
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (PATRON_INFO);
}
@ -1128,7 +1128,7 @@ sub handle_end_patron_session {
$resp .= maybe_add( FID_SCREEN_MSG, $screen_msg, $server );
$resp .= maybe_add( FID_PRINT_LINE, $print_line, $server );
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (END_PATRON_SESSION);
}
@ -1210,7 +1210,7 @@ sub handle_fee_paid {
$resp .= maybe_add( FID_SCREEN_MSG, $status->screen_msg, $server );
$resp .= maybe_add( FID_PRINT_LINE, $status->print_line, $server );
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (FEE_PAID);
}
@ -1303,7 +1303,7 @@ sub handle_item_information {
$resp .= $item->build_custom_field_string( $server );
}
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (ITEM_INFORMATION);
}
@ -1353,7 +1353,7 @@ sub handle_item_status_update {
$resp .= maybe_add( FID_SCREEN_MSG, $status->screen_msg, $server );
$resp .= maybe_add( FID_PRINT_LINE, $status->print_line, $server );
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (ITEM_STATUS_UPDATE);
}
@ -1407,7 +1407,7 @@ sub handle_patron_enable {
$resp .= add_field( FID_INST_ID, $ils->institution, $server );
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (PATRON_ENABLE);
}
@ -1472,7 +1472,7 @@ sub handle_hold {
$resp .= maybe_add( FID_SCREEN_MSG, $status->screen_msg, $server );
$resp .= maybe_add( FID_PRINT_LINE, $status->print_line, $server );
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (HOLD);
}
@ -1559,7 +1559,7 @@ sub handle_renew {
$resp .= maybe_add( FID_SCREEN_MSG, $status->screen_msg, $server );
$resp .= maybe_add( FID_PRINT_LINE, $status->print_line, $server );
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (RENEW);
}
@ -1612,7 +1612,7 @@ sub handle_renew_all {
$resp .= maybe_add( FID_SCREEN_MSG, $status->screen_msg, $server );
$resp .= maybe_add( FID_PRINT_LINE, $status->print_line, $server );
$self->write_msg( $resp, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $resp, $server );
return (RENEW_ALL);
}
@ -1718,7 +1718,7 @@ sub send_acs_status {
# Do we want to tell the terminal its location?
$self->write_msg( $msg, undef, $server->{account}->{terminator}, $server->{account}->{encoding} );
$self->write_msg( $msg, $server );
return 1;
}

View File

@ -73,6 +73,26 @@ sub text{
my $line = shift;
my $work = shift; # original text
my $is_cdata = shift;
# If there is a split template toolkit tag
if ( $work =~ m/^(?:(?!\[%).)*?%\]/s ) {
my @strings = ($&);
my $token;
$work = $';
while ( $token = pop @tokens ) {
if ( $token->string =~ m/\[%.*?$/s ) {
push @tokens, C4::TmplToken->new( $`, $token->type, $token->line_number, $token->pathname );
push @strings, $&;
last;
} else {
push @strings, $token->string;
}
}
push @tokens,
C4::TmplToken->new(
join( '', reverse @strings ), C4::TmplTokenType::DIRECTIVE,
$token ? $token->line_number : $line, $self->{filename}
);
}
while($work){
# if there is a template_toolkit tag
if( $work =~ m/\[%.*?%\]/ ){

View File

@ -320,9 +320,9 @@ sub buildKohaItemsNamespace {
$status = 'other';
$substatus = 'In transit';
}
elsif ($item->damaged) {
$status = 'other';
$substatus = "Damaged";
elsif ( $item->damaged && !C4::Context->preference('AllowHoldsOnDamagedItems') ) {
$status = 'other';
$substatus = 'Damaged';
}
elsif ($item->itemlost) {
$status = 'other';

View File

@ -29,7 +29,7 @@ use vars qw{ $VERSION };
# - #4 : the developer version. The 4th number is the database subversion.
# used by developers when the database changes. updatedatabase take care of the changes itself
# and is automatically called by Auth.pm when needed.
$VERSION = "24.05.05.000";
$VERSION = "24.05.05.009";
sub version {
return $VERSION;

View File

@ -27,7 +27,6 @@ use Koha::Account::Offsets;
use Koha::Database;
use Koha::DateUtils qw( dt_from_string );
use Koha::Exceptions::Account;
use Koha::Items;
use Koha::Patron::Debarments;
use base qw(Koha::Object Koha::Object::Mixin::AdditionalFields);
@ -80,6 +79,7 @@ sub item {
my ( $self ) = @_;
my $rs = $self->_result->itemnumber;
return unless $rs;
require Koha::Item;
return Koha::Item->_new_from_dbic( $rs );
}

View File

@ -81,12 +81,13 @@ sub search_for_display {
qq|(SELECT COUNT(*) FROM additional_contents_localizations WHERE lang='$lang' AND additional_content_id=me.additional_content_id)=0|;
my $search_params;
$search_params->{location} = $params->{location};
$search_params->{branchcode} = $params->{library_id} ? [ $params->{library_id}, undef ] : undef;
$search_params->{published_on} = { '<=' => \'CAST(NOW() AS DATE)' };
$search_params->{expirationdate} = [ '-or', { '>=' => \'CAST(NOW() AS DATE)' }, undef ];
$search_params->{'additional_content.id'} = $params->{id} if $params->{id};
$search_params->{location} = $params->{location};
$search_params->{branchcode} = $params->{library_id} ? [ $params->{library_id}, undef ] : undef;
$search_params->{published_on} = { '<=' => \'CAST(NOW() AS DATE)' } unless $params->{id};
$search_params->{expirationdate} = [ '-or', { '>=' => \'CAST(NOW() AS DATE)' }, undef ] unless $params->{id};
$search_params->{category} = $params->{category} if $params->{category};
$search_params->{lang} = 'default' if !$lang || $lang eq 'default';
$search_params->{lang} = 'default' if !$lang || $lang eq 'default';
$search_params->{-or} = [ { 'lang' => $lang }, '-and' => [ 'lang', 'default', \$subquery ] ]
if !$search_params->{lang};

View File

@ -132,6 +132,26 @@ sub is_integer_only {
return $self->_result->category->is_integer_only;
}
=head3 to_api
my $json = $av->to_api;
Overloaded method that returns a JSON representation of the Koha::AuthorisedValue object,
suitable for API output.
=cut
sub to_api {
my ( $self, $params ) = @_;
my $response = $self->SUPER::to_api($params);
my $overrides = {};
$overrides->{description} = $self->lib // q{};
return { %$response, %$overrides };
}
=head2 Internal methods
=head3 _check_is_integer_only

View File

@ -229,7 +229,7 @@ sub read_file {
my $delimiter = $file->{filename} =~ /\.tsv$/ ? "\t" : ",";
my $quote_char = $file->{filename} =~ /\.tsv$/ ? "\"" : "\"";
open my $fh, "<", \$file_content or die;
open my $fh, "<", \$file_content or die "Could not open file $file->{filename}: $!";
my $csv = Text::CSV_XS->new(
{
sep_char => $delimiter,
@ -241,14 +241,16 @@ sub read_file {
my $headers_to_check = $csv->getline($fh);
my $column_headers = rescue_EBSCO_files($headers_to_check);
my $lines = $csv->getline_all( $fh, 0 );
my ( $cde, $str, $pos ) = $csv->error_diag();
my $error = $cde ? "$cde, $str, $pos" : "";
warn $error if $error;
close($fh);
return ( $column_headers, $lines, $error );
unless($csv->eof()) {
my ( $cde, $str, $pos ) = $csv->error_diag();
my $error = $cde ? "$cde, $str, $pos" : "";
warn $error if $error;
return ( $column_headers, $lines, $error );
}
return ( $column_headers, $lines );
}
=head3 create_title_hash_from_line_data

View File

@ -144,7 +144,7 @@ sub process {
$matcher_code = $matcher->code();
$num_with_matches =
BatchFindDuplicates( $batch_id, $matcher, 10, 50,
sub { my $job_progress = shift; $self->progress( $self->progress + $job_progress )->store } );
sub { my $job_progress = shift; $self->progress( $job_progress )->store } );
SetImportBatchMatcher( $batch_id, $matcher_id );
SetImportBatchOverlayAction( $batch_id, $overlay_action );
SetImportBatchNoMatchAction( $batch_id, $nomatch_action );

View File

@ -52,6 +52,7 @@ use Koha::Item::Transfer::Limits;
use Koha::Items;
use Koha::Libraries;
use Koha::Old::Checkouts;
use Koha::Old::Holds;
use Koha::Ratings;
use Koha::Recalls;
use Koha::RecordProcessor;
@ -462,15 +463,14 @@ the I<OpacHiddenItems> system preference.
sub hidden_in_opac {
my ( $self, $params ) = @_;
return 0 unless C4::Context->preference('OpacHiddenItemsHidesRecord');
my $rules = $params->{rules} // {};
my @items = $self->items->as_list;
return 0 unless @items; # Do not hide if there is no item
# Ok, there are items, don't even try the rules unless OpacHiddenItemsHidesRecord
return 0 unless C4::Context->preference('OpacHiddenItemsHidesRecord');
return !(any { !$_->hidden_in_opac({ rules => $rules }) } @items);
}
@ -613,7 +613,8 @@ sub items {
my $items_rs = $self->_result->items;
return Koha::Items->_new_from_dbic( $items_rs ) unless $params->{host_items};
return Koha::Items->_new_from_dbic($items_rs)
unless $params->{host_items} && C4::Context->preference('EasyAnalyticalRecords');
my @itemnumbers = $items_rs->get_column('itemnumber')->all;
my $host_itemnumbers = $self->_host_itemnumbers();
@ -650,7 +651,7 @@ sub host_items {
my $host_itemnumbers = $self->_host_itemnumbers;
return Koha::Items->search( { itemnumber => { -in => $host_itemnumbers } } );
return Koha::Items->search( { "me.itemnumber" => { -in => $host_itemnumbers } } );
}
=head3 _host_itemnumbers
@ -707,6 +708,21 @@ sub holds {
return Koha::Holds->_new_from_dbic($hold_rs);
}
=head3 old_holds
my $old_holds = $biblio->old_holds();
return the historic holds placed on this record
=cut
sub old_holds {
my ( $self, $params, $attributes ) = @_;
$attributes->{order_by} = 'priority' unless exists $attributes->{order_by};
my $old_hold_rs = $self->_result->old_reserves->search( $params, $attributes );
return Koha::Old::Holds->_new_from_dbic($old_hold_rs);
}
=head3 current_holds
my $holds = $biblio->current_holds

View File

@ -27,9 +27,6 @@ use Modern::Perl;
use Koha::Exceptions;
use Koha::Biblio::Metadata::Extractor::MARC::MARC21;
use Koha::Biblio::Metadata::Extractor::MARC::UNIMARC;
=head1 API
=head2 Class methods
@ -58,6 +55,8 @@ sub new {
unless $valid_schemas->{$schema};
my $sub_class = "Koha::Biblio::Metadata::Extractor::MARC::$schema";
require "Koha/Biblio/Metadata/Extractor/MARC/$schema.pm";
return $sub_class->new($params);
}

View File

@ -45,6 +45,7 @@ use Koha::Plugins; # Adds plugin dirs to @INC
use Koha::Plugins::Handler;
use Koha::Acquisition::Baskets;
use Koha::Acquisition::Booksellers;
use Koha::Util::FrameworkPlugin qw( biblio_008 );
our $VERSION = 1.1;
@ -684,6 +685,10 @@ sub quote_item {
if ( !defined $bib ) {
$bib = {};
my $bib_record = _create_bib_from_quote( $item, $quote );
# Check for and add default 008 as this is a mandatory field
$bib_record = _handle_008_field($bib_record);
( $bib->{biblionumber}, $bib->{biblioitemnumber} ) =
AddBiblio( $bib_record, q{} );
$logger->trace("New biblio added $bib->{biblionumber}");
@ -1215,6 +1220,17 @@ sub _create_item_from_quote {
return $item_hash;
}
sub _handle_008_field {
my ($bib_record) = @_;
if ( !$bib_record->field('008') ) {
my $default008 = biblio_008();
$bib_record->insert_fields_ordered( MARC::Field->new( '008', $default008 ) );
}
return $bib_record;
}
1;
__END__
@ -1373,6 +1389,14 @@ Koha::EDI
Returns the Aqbudget object for the active budget given the passed budget_code
or undefined if one does not exist
=head2 _handle_008_field
bib_record = _handle_008_field($bib_record)
Checks whether an 008 field exists on the record and adds a default field it does not
Returns the bib_record
=head1 AUTHOR
Colin Campbell <colin.campbell@ptfs-europe.com>

View File

@ -180,8 +180,11 @@ sub harvest_sushi {
$ua->agent( 'Koha/' . Koha::version() );
my $response = $ua->simple_request($request);
my $result = decode_json( $response->decoded_content );
if ( $response->code >= 400 ) {
my $result = decode_json( $response->decoded_content );
return if $self->_sushi_errors($result);
my $message;
if ( ref($result) eq 'ARRAY' ) {
@ -199,7 +202,6 @@ sub harvest_sushi {
}
}
#TODO: May want to add a job error message here?
warn sprintf "ERROR - SUSHI service %s returned %s - %s\n", $url,
$response->code, $message;
if ( $response->code == 404 ) {
@ -208,7 +210,6 @@ sub harvest_sushi {
Koha::Exceptions::Authorization::Unauthorized->throw($message);
} else {
#TODO: May want to add a job error message here?
die sprintf "ERROR requesting SUSHI service\n%s\ncode %s: %s\n",
$url, $response->code,
$message;
@ -217,12 +218,10 @@ sub harvest_sushi {
return;
}
my $decoded_response = decode_json( $response->decoded_content );
return if $self->_sushi_errors($decoded_response);
return if $self->_sushi_errors($result);
# Parse the SUSHI response
my $sushi_counter = Koha::ERM::EUsage::SushiCounter->new( { response => $decoded_response } );
my $sushi_counter = Koha::ERM::EUsage::SushiCounter->new( { response => $result } );
my $counter_file = $sushi_counter->get_COUNTER_from_SUSHI;
return if $self->_counter_file_size_too_large($counter_file);
@ -399,6 +398,7 @@ sub _build_url_query {
$url .= '&api_key=' . $self->api_key if $self->api_key;
$url .= '&begin_date=' . substr $self->{begin_date}, 0, 7 if $self->{begin_date};
$url .= '&end_date=' . substr $self->{end_date}, 0, 7 if $self->{end_date};
$url .= '&platform=' . $self->service_platform if $self->service_platform;
return $url;
}
@ -459,19 +459,24 @@ Additionally, adds background job report message(s) if that is the case
sub _sushi_errors {
my ( $self, $decoded_response ) = @_;
if ( $decoded_response->{Severity} ) {
my $severity = $decoded_response->{Severity} // $decoded_response->{severity};
my $message = $decoded_response->{Message} // $decoded_response->{message};
my $code = $decoded_response->{Code} // $decoded_response->{code};
if ($severity) {
$self->{job_callbacks}->{add_message_callback}->(
{
type => 'error',
code => $decoded_response->{Code},
message => $decoded_response->{Severity} . ' - ' . $decoded_response->{Message},
code => $code,
message => $severity . ' - ' . $message,
}
) if $self->{job_callbacks};
return 1;
}
if ( $decoded_response->{Report_Header}->{Exceptions} ) {
foreach my $exception ( @{ $decoded_response->{Report_Header}->{Exceptions} } ) {
my $exceptions = $decoded_response->{Report_Header}->{Exceptions} // $decoded_response->{Exceptions};
if ($exceptions) {
foreach my $exception ( @{$exceptions} ) {
$self->{job_callbacks}->{add_message_callback}->(
{
type => 'error',
@ -483,7 +488,7 @@ sub _sushi_errors {
return 1;
}
if ( scalar @{ $decoded_response->{Report_Items} } == 0 ) {
if ( $decoded_response->{Report_Items} && scalar @{ $decoded_response->{Report_Items} } == 0 ) {
$self->{job_callbacks}->{add_message_callback}->(
{
type => 'error',

View File

@ -210,10 +210,10 @@ sub embed {
sub build_query_pagination {
my ( $self, $params ) = @_;
my $per_page = $params->{per_page}
my $per_page = $params->{_per_page}
// C4::Context->preference('RESTdefaultPageSize') // 20;
if ( $per_page == -1 || $per_page > 100 ) { $per_page = 100; }
my $page = $params->{page} || 1;
my $page = $params->{_page} || 1;
return ( $per_page, $page );
}

View File

@ -34,7 +34,6 @@ use Koha::Biblios;
use Koha::Hold::CancellationRequests;
use Koha::Items;
use Koha::Libraries;
use Koha::Old::Holds;
use Koha::Calendar;
use Koha::Plugins;
@ -1014,6 +1013,7 @@ Move a hold to the old_reserve table following the same pattern as Koha::Patron-
sub _move_to_old {
my ($self) = @_;
my $hold_infos = $self->unblessed;
require Koha::Old::Hold;
return Koha::Old::Hold->new( $hold_infos )->store;
}
@ -1026,25 +1026,26 @@ on the API.
sub to_api_mapping {
return {
reserve_id => 'hold_id',
borrowernumber => 'patron_id',
reservedate => 'hold_date',
biblionumber => 'biblio_id',
branchcode => 'pickup_library_id',
notificationdate => undef,
reminderdate => undef,
cancellationdate => 'cancellation_date',
reservenotes => 'notes',
found => 'status',
itemnumber => 'item_id',
waitingdate => 'waiting_date',
expirationdate => 'expiration_date',
reserve_id => 'hold_id',
borrowernumber => 'patron_id',
reservedate => 'hold_date',
biblionumber => 'biblio_id',
deleted_biblionumber => 'deleted_biblio_id',
branchcode => 'pickup_library_id',
notificationdate => undef,
reminderdate => undef,
cancellationdate => 'cancellation_date',
reservenotes => 'notes',
found => 'status',
itemnumber => 'item_id',
waitingdate => 'waiting_date',
expirationdate => 'expiration_date',
patron_expiration_date => undef,
lowestPriority => 'lowest_priority',
suspend => 'suspended',
suspend_until => 'suspended_until',
itemtype => 'item_type',
item_level_hold => 'item_level',
lowestPriority => 'lowest_priority',
suspend => 'suspended',
suspend_until => 'suspended_until',
itemtype => 'item_type',
item_level_hold => 'item_level',
};
}

View File

@ -401,20 +401,23 @@ sub status {
my $backend_plugin = $self->get_backend_plugin($backend_name);
Returns the installed I<Koha::Plugin> corresponding to the given backend_id
Returns the installed I<Koha::Plugin> corresponding to the given backend_id or undef if no plugin is found
=cut
sub get_backend_plugin {
my ( $self, $backend_id ) = @_;
my @backend_plugins = Koha::Plugins->new()->GetPlugins(
my $koha_plugins = Koha::Plugins->new();
my @backend_plugins = $koha_plugins
? Koha::Plugins->new()->GetPlugins(
{
method => 'ill_backend',
metadata => { name => $backend_id },
all => 1,
}
);
)
: ();
return $backend_plugins[0];
}

View File

@ -113,14 +113,17 @@ Returns a list of names for all the installed ILL backend plugins.
=cut
sub get_backend_plugin_names {
my ( $self ) = @_;
my ($self) = @_;
my @backend_plugins = Koha::Plugins->new()->GetPlugins(
my $koha_plugins = Koha::Plugins->new();
my @backend_plugins = $koha_plugins
? $koha_plugins->GetPlugins(
{
method => 'ill_backend',
all => 1
}
);
)
: ();
return map { $_->{metadata}->{name} } @backend_plugins;
}

View File

@ -56,7 +56,7 @@ sub get_marc_record {
$format = 'UNIMARCAUTH';
}
my $record = MARC::Record->new_from_xml($self->marcxml, $self->encoding, $format);
my $record = MARC::Record->new_from_xml( $self->marcxml, 'UTF-8', $format );
return $record;
}

View File

@ -920,7 +920,7 @@ sub hidden_in_opac {
foreach my $field ( keys %{$rules} ) {
if ( any { $self->$field eq $_ } @{ $rules->{$field} } ) {
if ( any { defined $self->$field && $self->$field eq $_ } @{ $rules->{$field} } ) {
$hidden_in_opac = 1;
last;
}

View File

@ -44,10 +44,19 @@ sub call {
my $request_method = $req->method // q{};
my $uri = $req->uri // q{};
my $referer = $req->referer // q{No referer};
my $request_path = $req->path // q{};
my %path_exceptions = (
'/ilsdi.pl' => 1,
);
#NOTE: Ignore ErrorDocument requests for CSRF
if ( $env->{'psgix.errordocument.SCRIPT_NAME'} ) {
return $self->app->($env);
} elsif ( $path_exceptions{$request_path} ) {
#NOTE: Ignore path exceptions for CSRF
return $self->app->($env);
}
my ($error);

View File

@ -19,6 +19,7 @@ package Koha::Number::Price;
use Modern::Perl;
use Math::BigFloat;
use Number::Format;
use C4::Context;
use Koha::Acquisition::Currencies;
@ -83,12 +84,7 @@ sub round {
my $format_params = $self->_format_params;
# To avoid the system to crash, we will not format big number
# We divide per 100 because we want to keep the default DECIMAL_DIGITS (2)
# error - round() overflow. Try smaller precision or use Math::BigFloat
return $self->value if $self->value > Number::Format::MAX_INT/100;
return Number::Format->new(%$format_params)->round($self->value);
return Number::Format->new(%$format_params)->round( Math::BigFloat->new( $self->value ) )->bdstr();
}
sub _format_params {

View File

@ -19,7 +19,6 @@ use Modern::Perl;
use Koha::Database;
use Koha::Exceptions;
use Koha::Libraries;
use Try::Tiny qw( catch try );
@ -87,6 +86,8 @@ sub get_library_limits {
return unless @branchcodes;
require Koha::Libraries;
my $filter = [ map { { branchcode => $_ } } @branchcodes ];
my $libraries = Koha::Libraries->search( $filter );

View File

@ -167,17 +167,19 @@ sub search_related {
=cut
sub delete {
my ($self) = @_;
my ( $self, $params ) = @_;
if ( Class::Inspector->function_exists( $self->object_class, 'delete' ) ) {
my $objects_deleted;
$self->_resultset->result_source->schema->txn_do( sub {
$self->reset; # If we iterated already over the set
while ( my $o = $self->next ) {
$o->delete;
$objects_deleted++;
$self->_resultset->result_source->schema->txn_do(
sub {
$self->reset; # If we iterated already over the set
while ( my $o = $self->next ) {
$o->delete($params);
$objects_deleted++;
}
}
});
);
return $objects_deleted;
}

View File

@ -20,7 +20,7 @@ package Koha::Patron;
use Modern::Perl;
use List::MoreUtils qw( any uniq );
use List::MoreUtils qw( any none uniq );
use JSON qw( to_json );
use Unicode::Normalize qw( NFKD );
use Try::Tiny;
@ -315,7 +315,7 @@ sub store {
}
foreach my $guarantor (@$guarantors) {
if ( $guarantor->is_child or $guarantor->category->can_be_guarantee ) {
if ( $guarantor->is_child ) {
Koha::Exceptions::Patron::Relationship::InvalidRelationship->throw( invalid_guarantor => 1 );
}
}
@ -372,7 +372,7 @@ sub store {
}
foreach my $guarantor (@$guarantors) {
if ( $guarantor->is_child or $guarantor->category->can_be_guarantee ) {
if ( $guarantor->is_child ) {
Koha::Exceptions::Patron::Relationship::InvalidRelationship->throw( invalid_guarantor => 1 );
}
}
@ -443,7 +443,7 @@ sub delete {
Koha::Exceptions::Patron::FailedDeleteAnonymousPatron->throw() if $anonymous_patron && $self->id eq $anonymous_patron;
# Check if patron is protected
Koha::Exceptions::Patron::FailedDeleteProtectedPatron->throw() if $self->protected == 1;
Koha::Exceptions::Patron::FailedDeleteProtectedPatron->throw() if defined $self->protected && $self->protected == 1;
$self->_result->result_source->schema->txn_do(
sub {
@ -499,7 +499,7 @@ Returns a Koha::Library object representing the patron's home library.
sub library {
my ( $self ) = @_;
return Koha::Library->_new_from_dbic($self->_result->branchcode);
return Koha::Library->_new_from_dbic($self->_result->library);
}
=head3 sms_provider
@ -533,6 +533,18 @@ sub guarantor_relationships {
return Koha::Patron::Relationships->search( { guarantee_id => $self->id } );
}
=head3 is_guarantee
Returns true if the patron has a guarantor.
=cut
sub is_guarantee {
my ($self) = @_;
return $self->guarantor_relationships()->count();
}
=head3 guarantee_relationships
Returns Koha::Patron::Relationships object for this patron's guarantors
@ -557,6 +569,18 @@ sub guarantee_relationships {
);
}
=head3 is_guarantor
Returns true if the patron is a guarantor.
=cut
sub is_guarantor {
my ($self) = @_;
return $self->guarantee_relationships()->count();
}
=head3 relationships_debt
Returns the amount owed by the patron's guarantors *and* the other guarantees of those guarantors
@ -1527,6 +1551,7 @@ sub get_age {
my $date_of_birth = dt_from_string( $self->dateofbirth, undef, 'floating' );
my $today = dt_from_string(undef, undef, 'floating')->truncate( to => 'day' );
return 0 unless $date_of_birth;
return $today->subtract_datetime( $date_of_birth )->years;
}
@ -1584,6 +1609,7 @@ Return all the historical holds for this patron
sub old_holds {
my ($self) = @_;
my $old_holds_rs = $self->_result->old_reserves->search( {}, { order_by => 'reservedate' } );
require Koha::Old::Holds;
return Koha::Old::Holds->_new_from_dbic($old_holds_rs);
}
@ -1629,23 +1655,27 @@ sub return_claims {
=head3 notice_email_address
my $email = $patron->notice_email_address;
my $email = $patron->notice_email_address;
Return the email address of patron used for notices.
Returns the empty string if no email address.
=cut
sub notice_email_address{
my ( $self ) = @_;
sub notice_email_address {
my ($self) = @_;
my $which_address = C4::Context->preference("EmailFieldPrimary");
# if syspref is set to 'first valid' (value == OFF), look up email address
if ( $which_address eq 'OFF' ) {
return $self->first_valid_email_address;
if ( $which_address && ( none { $_ eq $which_address } qw{email emailpro B_email cardnumber MULTI} ) ) {
warn "Invalid value for EmailFieldPrimary ($which_address)";
$which_address = undef;
}
# if syspref is set to 'first valid', look up email address
return $self->first_valid_email_address
unless $which_address;
# if syspref is set to 'selected addresses' (value == MULTI), look up email addresses
if ( $which_address eq 'MULTI' ) {
my @addresses;
@ -1654,11 +1684,10 @@ sub notice_email_address{
my $email_address = $self->$email_field;
push @addresses, $email_address if $email_address;
}
return join(",",@addresses);
return join( ",", @addresses );
}
return $self->$which_address || '';
}
=head3 first_valid_email_address
@ -2179,17 +2208,22 @@ sub extended_attributes {
}
# Check globally mandatory types
my @required_attribute_types =
Koha::Patron::Attribute::Types->search(
{
mandatory => 1,
category_code => [ undef, $self->categorycode ],
'borrower_attribute_types_branches.b_branchcode' =>
undef,
},
{ join => 'borrower_attribute_types_branches' }
)->get_column('code');
for my $type ( @required_attribute_types ) {
my $interface = C4::Context->interface;
my $params = {
mandatory => 1,
category_code => [ undef, $self->categorycode ],
'borrower_attribute_types_branches.b_branchcode' => undef,
};
if ( $interface eq 'opac' ) {
$params->{opac_editable} = 1;
}
my @required_attribute_types = Koha::Patron::Attribute::Types->search(
$params,
{ join => 'borrower_attribute_types_branches' }
)->get_column('code');
for my $type (@required_attribute_types) {
Koha::Exceptions::Patron::MissingMandatoryExtendedAttribute->throw(
type => $type,
) if !$new_types->{$type};

View File

@ -59,6 +59,11 @@ sub store {
Koha::Exceptions::Patron::Attribute::InvalidAttributeValue->throw( attribute => $self )
unless $self->value_ok();
C4::Context->dbh->do(
"UPDATE borrowers SET updated_on = NOW() WHERE borrowernumber = ?", undef,
$self->borrowernumber
);
return $self->SUPER::store();
}

View File

@ -512,7 +512,7 @@ sub _validate_operator {
my ($operator) = @_;
my %allowed_operators =
map { $_ => 1 }
qw(= != < > <= >= -in -ident -bool -not_bool -or like -like -not_like -between -not_between -not -and -regexp);
qw(= != < > <= >= -in -ident -bool -not_bool -or like -like -not_like -between -not_between -not -and -regexp -not_in);
Koha::Exceptions::REST::Query::InvalidOperator->throw( operator => $operator )
unless exists $allowed_operators{$operator};
return;

View File

@ -36,8 +36,9 @@ sub list {
my $c = shift->openapi->valid_input or return;
return try {
my $authorised_value_categories_set = Koha::AuthorisedValueCategories->new;
my $authorised_value_categories = $c->objects->search( $authorised_value_categories_set );
my $authorised_value_categories_set =
Koha::AuthorisedValueCategories->search( {}, { order_by => 'category_name' } );
my $authorised_value_categories = $c->objects->search($authorised_value_categories_set);
return $c->render( status => 200, openapi => $authorised_value_categories );
}
catch {

View File

@ -120,7 +120,7 @@ sub _check_availability {
# Upgrade some confirmations to blockers
my @should_block =
qw/TOO_MANY ISSUED_TO_ANOTHER RESERVED RESERVED_WAITING TRANSFERRED PROCESSING AGE_RESTRICTION/;
qw/TOO_MANY ISSUED_TO_ANOTHER RESERVED RESERVE_WAITING TRANSFERRED PROCESSING AGE_RESTRICTION/;
for my $block (@should_block) {
if ( exists( $confirmation->{$block} ) ) {
$impossible->{$block} = $confirmation->{$block};

View File

@ -35,12 +35,7 @@ sub list {
my $result = $ebsco->request( GET => '/packages' . $params );
my $base_total = $result->{totalResults};
my ( $per_page, $page ) = $ebsco->build_query_pagination(
{
per_page => $c->stash('koha.pagination.per_page'),
page => $c->stash('koha.pagination.page'),
}
);
my ( $per_page, $page ) = $ebsco->build_query_pagination( $c->req->params->to_hash );
my $additional_params =
$ebsco->build_additional_params( $c->req->params->to_hash );

View File

@ -73,12 +73,7 @@ sub list {
my $base_total = $result->{totalResults};
my ( $per_page, $page ) = $ebsco->build_query_pagination(
{
per_page => $c->stash('koha.pagination.per_page'),
page => $c->stash('koha.pagination.page'),
}
);
my ( $per_page, $page ) = $ebsco->build_query_pagination( $c->req->params->to_hash );
my $additional_params = $ebsco->build_additional_params( $c->req->params->to_hash );
my $searchfield = 'titlename';

View File

@ -41,12 +41,7 @@ sub list {
my $ebsco = Koha::ERM::Providers::EBSCO->new;
# We cannot get base_total as a search kw is required by the API
my ( $per_page, $page ) = $ebsco->build_query_pagination(
{
per_page => $c->stash('koha.pagination.per_page'),
page => $c->stash('koha.pagination.page'),
}
);
my ( $per_page, $page ) = $ebsco->build_query_pagination( $c->req->params->to_hash );
my $additional_params = $ebsco->build_additional_params( $c->req->params->to_hash );

View File

@ -207,14 +207,14 @@ sub add_debit {
my $c = shift->openapi->valid_input or return;
my $patron = Koha::Patrons->find( $c->param('patron_id') );
my $user = $c->stash('koha.user');
return $c->render_resource_not_found("Patron")
unless $patron;
unless $patron;
return try {
my $data =
Koha::Account::Debit->new_from_api( $c->req->json )
->unblessed;
Koha::Account::Debit->new_from_api( $c->req->json )->unblessed;
$data->{library_id} = delete $data->{branchcode};
$data->{type} = delete $data->{debit_type_code};
@ -223,11 +223,10 @@ sub add_debit {
$data->{transaction_type} = delete $data->{payment_type};
$data->{interface} = 'api'
; # Should this always be API, or should we allow the API consumer to choose?
$data->{user_id} = $patron->borrowernumber
; # Should this be API user OR staff the API may be acting on behalf of?
$data->{user_id} = delete $data->{manager_id} || $user->id;
my $debit = $patron->account->add_debit($data);
$debit = Koha::Account::Debit->_new_from_dbic($debit->{_result});
$debit = Koha::Account::Debit->_new_from_dbic( $debit->{_result} );
$c->res->headers->location(
$c->req->url->to_string . '/' . $debit->id );
@ -248,13 +247,13 @@ sub add_debit {
}
elsif ( $_->isa('Koha::Exceptions::Account::AmountNotPositive') ) {
return $c->render(
status => 400,
status => 400,
openapi => { error => $_->description }
);
}
elsif ( $_->isa('Koha::Exceptions::Account::UnrecognisedType') ) {
return $c->render(
status => 400,
status => 400,
openapi => { error => $_->description }
);
}

View File

@ -55,12 +55,8 @@ sub get {
return try {
my $source = $c->objects->find( Koha::RecordSources->new, $c->param('record_source_id') );
unless ($source) {
return $c->render(
status => 404,
openapi => { error => "Object not found" }
);
}
return $c->render_resource_not_found("Record source")
unless $source;
return $c->render( status => 200, openapi => $source );
} catch {
@ -97,12 +93,8 @@ sub update {
my $source = $c->objects->find_rs( Koha::RecordSources->new, $c->param('record_source_id') );
unless ($source) {
return $c->render(
status => 404,
openapi => { error => "Object not found" }
);
}
return $c->render_resource_not_found("Record source")
unless $source;
return try {
$source->set_from_api( $c->req->json )->store;
@ -122,19 +114,12 @@ sub delete {
my $source = $c->objects->find_rs( Koha::RecordSources->new, $c->param('record_source_id') );
unless ($source) {
return $c->render(
status => 404,
openapi => { error => "Object not found" }
);
}
return $c->render_resource_not_found("Record source")
unless $source;
return try {
$source->delete;
return $c->render(
status => 204,
openapi => q{}
);
return $c->render_resource_deleted;
} catch {
$c->unhandled_exception($_);
};

View File

@ -2191,6 +2191,13 @@ __PACKAGE__->many_to_many("permissions", "user_permissions", "permission");
# Created by DBIx::Class::Schema::Loader v0.07051 @ 2024-05-02 11:36:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:jUOMoSonNmA3KwtZKVxqOA
__PACKAGE__->belongs_to(
"library",
"Koha::Schema::Result::Branch",
{ branchcode => "branchcode" },
{ is_deferrable => 1, on_delete => "RESTRICT", on_update => "RESTRICT" },
);
__PACKAGE__->has_many(
"restrictions",
"Koha::Schema::Result::BorrowerDebarment",

View File

@ -61,7 +61,7 @@ item barcode (MARC21 952$p)
default_value: 0
is_nullable: 0
boolean value defining whether this item is available for bookings or not
boolean value defining whether this this item is available for bookings or not
=head2 dateaccessioned

View File

@ -88,9 +88,8 @@ priority of the renewal
=head2 license_info
data_type: 'varchar'
data_type: 'mediumtext'
is_nullable: 1
size: 80
info about the license
@ -114,7 +113,7 @@ __PACKAGE__->add_columns(
"renewal_priority",
{ data_type => "varchar", is_nullable => 1, size => 80 },
"license_info",
{ data_type => "varchar", is_nullable => 1, size => 80 },
{ data_type => "mediumtext", is_nullable => 1 },
);
=head1 PRIMARY KEY
@ -267,8 +266,8 @@ Composing rels: L</erm_eholdings_packages_agreements> -> package
__PACKAGE__->many_to_many("packages", "erm_eholdings_packages_agreements", "package");
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2022-11-11 11:52:09
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:N93LnvdKirtuV6BSrTGzVg
# Created by DBIx::Class::Schema::Loader v0.07051 @ 2024-12-17 23:37:27
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:lc0DnoQ5ai9sVBjczw6FdQ
__PACKAGE__->has_many(
"user_roles",

View File

@ -142,6 +142,14 @@ requestor email
report types provided by the harvester
=head2 service_platform
data_type: 'varchar'
is_nullable: 1
size: 80
platform if provider requires it
=cut
__PACKAGE__->add_columns(
@ -175,6 +183,8 @@ __PACKAGE__->add_columns(
{ data_type => "varchar", is_nullable => 1, size => 80 },
"report_types",
{ data_type => "varchar", is_nullable => 1, size => 255 },
"service_platform",
{ data_type => "varchar", is_nullable => 1, size => 80 },
);
=head1 PRIMARY KEY
@ -328,8 +338,8 @@ __PACKAGE__->has_many(
);
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2023-10-11 10:09:06
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:MSvyv/Abt3S3fqZF21v/7w
# Created by DBIx::Class::Schema::Loader v0.07051 @ 2024-12-05 19:06:48
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:VzC6luzfNA1QBZ7slRs6OQ
# __PACKAGE__->add_columns(
# '+active' => { is_boolean => 1 }

View File

@ -63,7 +63,7 @@ item barcode (MARC21 952$p)
default_value: 0
is_nullable: 0
boolean value defining whether this item is available for bookings or not
boolean value defining whether this this item is available for bookings or not
=head2 dateaccessioned

View File

@ -54,6 +54,13 @@ the date the hold was places
foreign key from the biblio table defining which bib record this hold is on
=head2 deleted_biblionumber
data_type: 'integer'
is_nullable: 1
links the hold to the deleted bibliographic record (deletedbiblio.biblionumber)
=head2 item_group_id
data_type: 'integer'
@ -235,6 +242,8 @@ __PACKAGE__->add_columns(
{ data_type => "date", datetime_undef_if_invalid => 1, is_nullable => 1 },
"biblionumber",
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
"deleted_biblionumber",
{ data_type => "integer", is_nullable => 1 },
"item_group_id",
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
"branchcode",
@ -428,8 +437,8 @@ __PACKAGE__->belongs_to(
);
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2024-04-05 06:44:57
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:SQp2OEYb3im1x3A+W0h37w
# Created by DBIx::Class::Schema::Loader v0.07051 @ 2024-12-05 19:06:48
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:7mO1W8tdYtEk7pijJuxsMA
__PACKAGE__->belongs_to(
"item",

View File

@ -57,6 +57,13 @@ the date the hold was placed
foreign key from the biblio table defining which bib record this hold is on
=head2 deleted_biblionumber
data_type: 'integer'
is_nullable: 1
links the hold to the deleted bibliographic record (deletedbiblio.biblionumber)
=head2 item_group_id
data_type: 'integer'
@ -243,6 +250,8 @@ __PACKAGE__->add_columns(
is_foreign_key => 1,
is_nullable => 0,
},
"deleted_biblionumber",
{ data_type => "integer", is_nullable => 1 },
"item_group_id",
{ data_type => "integer", is_foreign_key => 1, is_nullable => 1 },
"branchcode",
@ -456,8 +465,8 @@ __PACKAGE__->belongs_to(
);
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2023-11-01 18:21:38
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:6BAjuPSwjtyWJaJN9fcOtg
# Created by DBIx::Class::Schema::Loader v0.07051 @ 2024-12-05 19:06:48
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OU6N7F+5LfbGfkhJMv53GA
__PACKAGE__->belongs_to(
"item",

View File

@ -790,7 +790,20 @@ sub marc_records_to_documents {
local $SIG{__WARN__} = sub {
push @warnings, $_[0];
};
$record_document->{'marc_data'} = encode_base64(encode('UTF-8', $record->as_usmarc()));
my $usmarc_record = $record->as_usmarc();
#NOTE: Try to round-trip the record to prove it will work for retrieval after searching
my $decoded_usmarc_record = MARC::Record->new_from_usmarc($usmarc_record);
if ( $decoded_usmarc_record->warnings() ) {
#NOTE: We override the warnings since they're many and misleading
@warnings = (
"Warnings encountered while roundtripping a MARC record to/from USMARC. Failing over to MARCXML.",
);
}
my $marc_data = encode_base64( encode( 'UTF-8', $usmarc_record ) );
$record_document->{'marc_data'} = $marc_data;
}
if (@warnings) {
# Suppress warnings if record length exceeded

View File

@ -248,6 +248,20 @@ sub search_auth_compat {
$result{authtype} = $authtype ? $authtype->authtypetext : $authtypecode;
$result{reported_tag} = $reported_tag;
if ( C4::Context->preference('ShowHeadingUse') ) {
# checking valid heading use
my $f008 = $marc->field('008');
if ($f008) {
my $pos14to16 = substr( $f008->data, 14, 3 );
my $main = substr( $pos14to16, 0, 1 );
$result{main} = 1 if $main eq 'a';
my $subject = substr( $pos14to16, 1, 1 );
$result{subject} = 1 if $subject eq 'a';
my $series = substr( $pos14to16, 2, 1 );
$result{series} = 1 if $series eq 'a';
}
}
# Reimplementing BuildSummary is out of scope because it'll be hard
$result{summary} =
C4::AuthoritiesMarc::BuildSummary( $marc, $result{authid},
@ -497,7 +511,7 @@ sub _convert_facets {
push @{ $facet->{facets} }, {
facet_count => $c,
facet_link_value => $t,
facet_title_value => $t . " ($c)",
facet_title_value => $t,
facet_label_value => $label, # TODO either truncate this,
# or make the template do it like it should anyway
type_link_value => $type,

View File

@ -51,7 +51,7 @@ sub expand_filter {
my $operands = $query->{operands};
my $indexes = $query->{indexes};
my $query_limit;
my $query_limit = "(";
for( my $i = 0; $i < scalar @$operands; $i++ ){
next unless @$operands[$i];
my $index = @$indexes[$i] ? @$indexes[$i] . "=" : "";
@ -61,6 +61,7 @@ sub expand_filter {
my $limit = $operator . $index . $query;
$query_limit .= $limit;
}
$query_limit .= ")";
return ($limits, $query_limit);
}

View File

@ -150,7 +150,14 @@ sub pseudonymize {
my ($self) = @_;
return unless ( $self->borrowernumber && grep { $_ eq $self->type } qw(renew issue return onsite_checkout) );
Koha::BackgroundJob::PseudonymizeStatistic->new->enqueue( { statistic => $self->unblessed } );
# FIXME When getting the object from svc/renewal we get a DateTime object
# normally we just fetch from DB to clear this, but statistics has no primary key
# so we just force it to string context
my $unblessed = $self->unblessed;
$unblessed->{datetime} = $unblessed->{datetime} . "";
Koha::BackgroundJob::PseudonymizeStatistic->new->enqueue( { statistic => $unblessed } );
}

View File

@ -40,10 +40,11 @@ sub get {
my $content = Koha::AdditionalContents->search_for_display(
{
category => $category,
location => $location,
lang => $lang,
category => $category,
location => $location,
lang => $lang,
( $library ? ( library_id => $library ) : () ),
( $id ? ( id => $id ) : () ),
}
);

View File

@ -0,0 +1,66 @@
package Koha::Template::Plugin::SafeURL;
# 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 URI;
use base 'Template::Plugin::Filter';
sub init {
my $self = shift;
my $name = 'safe_url';
$self->{_DYNAMIC} = 1;
$self->install_filter($name);
return $self;
}
sub filter {
my ( $self, $text, $args, $config ) = @_;
my $uri = URI->new($text);
if ($uri) {
return $uri;
}
#NOTE: If it isn't a real URL, just return a safe fragment
return '#';
}
1;
=head1 NAME
Koha::Template::Plugin::SafeURL - TT Plugin for filtering whole URLs
=head1 SYNOPSIS
[% USE SafeURL %]
[% $url | safe_url %]
This filter parses the text as a URL and returns the object which
is stringified in a safe format.
=head1 METHODS
=head2 init
This method installs the filter name and declares it as a dynamic filter
=head2 filter
Returns a stringified version of a Perl URL object
=cut

View File

@ -361,10 +361,13 @@ sub build_authorized_values_list {
}
}
my $id_subfield = $subfield;
$id_subfield = "00" if $id_subfield eq "@";
return {
type => 'select',
id => "tag_".$tag."_subfield_".$subfield."_".$index_tag."_".$index_subfield,
name => "tag_".$tag."_subfield_".$subfield."_".$index_tag."_".$index_subfield,
id => "tag_".$tag."_subfield_".$id_subfield."_".$index_tag."_".$index_subfield,
name => "tag_".$tag."_subfield_".$id_subfield."_".$index_tag."_".$index_subfield,
default => $value,
values => \@authorised_values,
labels => \%authorised_lib,

View File

@ -82,7 +82,7 @@ sub createMergeHash {
(
!defined($tagslib) || ( defined $tagslib->{$fieldtag}
&& defined $tagslib->{$fieldtag}->{'tab'}
&& $tagslib->{$fieldtag}->{'tab'} >= 0 )
&& ( $tagslib->{$fieldtag}->{'tab'} ? $tagslib->{$fieldtag}->{'tab'} : 0 ) >= 0 )
)
&& @subfield_array
)

View File

@ -404,6 +404,7 @@ my $target_map = {
'cypress.config.ts' => 'NONE',
'tsconfig.json' => 'NONE',
'webpack.config.js' => 'NONE',
'rspack.config.js' => 'NONE',
};
=head1 CONFIGURATION OPTIONS
@ -673,9 +674,7 @@ $file_map->{'koha-tmpl/intranet-tmpl/prog/css/installer.css'} = 'blib/INTRANET_T
$file_map->{'koha-tmpl/intranet-tmpl/prog/css/mainpage.css'} = 'blib/INTRANET_TMPL_DIR/prog/css/mainpage.css';
$file_map->{'koha-tmpl/intranet-tmpl/prog/css/staff-global.css'} = 'blib/INTRANET_TMPL_DIR/prog/css/staff-global.css';
$file_map->{'koha-tmpl/intranet-tmpl/prog/js/vue/dist/erm.js'} = 'blib/INTRANET_TMPL_DIR/prog/js/vue/dist/erm.js';
$file_map->{'koha-tmpl/intranet-tmpl/prog/js/vue/dist/erm.js.LICENSE.txt'} = 'blib/INTRANET_TMPL_DIR/prog/js/vue/dist/erm.js.LICENSE.txt';
$file_map->{'koha-tmpl/intranet-tmpl/prog/js/vue/dist/preservation.js'} = 'blib/INTRANET_TMPL_DIR/prog/js/vue/dist/preservation.js';
$file_map->{'koha-tmpl/intranet-tmpl/prog/js/vue/dist/preservation.js.LICENSE.txt'} = 'blib/INTRANET_TMPL_DIR/prog/js/vue/dist/preservation.js.LICENSE.txt';
#OPAC_TMPL_DIR
$file_map->{'koha-tmpl/opac-tmpl/bootstrap/css/opac.css'} = 'blib/OPAC_TMPL_DIR/bootstrap/css/opac.css';
$file_map->{'koha-tmpl/opac-tmpl/bootstrap/css/opac-rtl.css'} = 'blib/OPAC_TMPL_DIR/bootstrap/css/opac-rtl.css';

View File

@ -532,10 +532,9 @@ sub import_biblios_list {
my $quantity = delete $iteminfos->{quantity} || 1;
# Handle incorrectly named original parameters for MarcItemFieldsToOrder
$iteminfos->{location} = delete $iteminfos->{loc} if $iteminfos->{loc};
$iteminfos->{copynumber} = delete $iteminfos->{copyno} if $iteminfos->{copyno};
$iteminfos->{location} = delete $iteminfos->{loc} if $iteminfos->{loc};
# Price is handled as "itemprice" in the UI form to distinguish from MarcFieldsToOrder
$iteminfos->{itemprice} = delete $iteminfos->{price} if $iteminfos->{itemprice};
$iteminfos->{itemprice} = delete $iteminfos->{price} if $iteminfos->{itemprice};
# Convert budge code to a budget id
my $item_budget_code = delete $iteminfos->{budget_code};

View File

@ -169,15 +169,16 @@ my @gst_values = map {
}, split( '\|', C4::Context->preference("TaxRates") );
$template->param(
freight => $freight,
name => $bookseller->name,
active_currency => $active_currency,
currencies => $currencies->search({ rate => { '!=' => 1 } }),
invoiceincgst => $bookseller->invoiceincgst,
invoiceid => $invoice->{invoiceid},
invoice => $invoice->{invoicenumber},
gst_values => \@gst_values,
vendor => $bookseller,
freight => $freight,
name => $bookseller->name,
active_currency => $active_currency,
currencies => $currencies->search( { rate => { '!=' => 1 } } ),
invoiceincgst => $bookseller->invoiceincgst,
invoiceid => $invoice->{invoiceid},
invoice => $invoice->{invoicenumber},
gst_values => \@gst_values,
vendor => $bookseller,
UniqueItemFields => C4::Context->preference('UniqueItemFields'),
);
my $patron = Koha::Patrons->find( $loggedinuser )->unblessed;

View File

@ -92,7 +92,6 @@ if ( $op eq 'add_form' ) {
#ADD_VALIDATE: called by add_form, used to insert/modify data in DB
elsif ( $op eq 'cud-add_validate' ) {
## Please see file perltidy.ERR
$template->param( add_validate => 1 );
my $is_a_modif = $input->param("is_a_modif");

View File

@ -119,19 +119,8 @@ my $budgets_ref = GetBudgetHierarchy(
);
# build categories list
my $sth = $dbh->prepare("select distinct category from authorised_values where category like 'A%' ");
$sth->execute;
# the list
my @category_list;
# a hash, to check that some hardcoded categories exist.
my %categories;
while ( my ($category) = $sth->fetchrow_array ) {
push( @category_list, $category );
$categories{$category} = 1;
}
# push koha system categories
push( @category_list, 'MONTHS' );
push( @category_list, 'ITEMTYPES' );

View File

@ -411,7 +411,7 @@ modules:
-
columnname: holdings_datelastborrowed
-
columnname: holdings_barcodes
columnname: holdings_barcode
-
columnname: holdings_uri
-
@ -2026,6 +2026,20 @@ modules:
-
columnname: item_coursereserves
subscriptionst:
columns:
-
columnname: serial_serialseq
-
columnname: serial_publisheddate
-
columnname: serial_planneddate
-
columnname: serial_status
-
columnname: serial_notes
course_list:
course_reserves_table:
columns:
-
@ -2043,19 +2057,7 @@ modules:
-
columnname: notes
subscriptionst:
columns:
-
columnname: serial_serialseq
-
columnname: serial_publisheddate
-
columnname: serial_planneddate
-
columnname: serial_status
-
columnname: serial_notes
course_items:
course-items-table:
columns:
-

View File

@ -28,6 +28,7 @@ use C4::Context;
use Koha::Caches;
use Koha::AuthorisedValues;
use Koha::BiblioFrameworks;
use Koha::Database;
# retrieve parameters
my $input = CGI->new;
@ -120,6 +121,7 @@ if ($op eq 'add_form') {
my $authorised_value = $input->param('authorised_value');
my $ind1_defaultvalue = $input->param('ind1_defaultvalue');
my $ind2_defaultvalue = $input->param('ind2_defaultvalue');
my $error;
if ($input->param('modif')) {
$sth = $dbh->prepare(
"UPDATE marc_tag_structure SET liblibrarian=? ,libopac=? ,repeatable=? ,mandatory=? ,important=? ,authorised_value=?, ind1_defaultvalue=?, ind2_defaultvalue=? WHERE frameworkcode=? AND tagfield=?"
@ -136,26 +138,40 @@ if ($op eq 'add_form') {
$tagfield
);
} else {
$sth = $dbh->prepare(
"INSERT INTO marc_tag_structure (tagfield,liblibrarian,libopac,repeatable,mandatory,important,authorised_value,ind1_defaultvalue,ind2_defaultvalue,frameworkcode) values (?,?,?,?,?,?,?,?,?,?)"
);
$sth->execute($tagfield,
$liblibrarian,
$libopac,
$repeatable,
$mandatory,
$important,
$authorised_value,
$ind1_defaultvalue,
$ind2_defaultvalue,
$frameworkcode
);
my $schema = Koha::Database->new()->schema();
my $rs = $schema->resultset('MarcTagStructure');
my $field = $rs->find( { tagfield => $tagfield, frameworkcode => $frameworkcode } );
if ( !$field ) {
$sth = $dbh->prepare(
"INSERT INTO marc_tag_structure (tagfield,liblibrarian,libopac,repeatable,mandatory,important,authorised_value,ind1_defaultvalue,ind2_defaultvalue,frameworkcode) values (?,?,?,?,?,?,?,?,?,?)"
);
$sth->execute(
$tagfield,
$liblibrarian,
$libopac,
$repeatable,
$mandatory,
$important,
$authorised_value,
$ind1_defaultvalue,
$ind2_defaultvalue,
$frameworkcode
);
} else {
$error = 'duplicate_tagfield';
}
}
$cache->clear_from_cache("MarcStructure-0-$frameworkcode");
$cache->clear_from_cache("MarcStructure-1-$frameworkcode");
$cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode");
$cache->clear_from_cache("MarcCodedFields-$frameworkcode");
print $input->redirect("/cgi-bin/koha/admin/marctagstructure.pl?searchfield=$tagfield&frameworkcode=$frameworkcode");
if ( !$error ) {
$cache->clear_from_cache("MarcStructure-0-$frameworkcode");
$cache->clear_from_cache("MarcStructure-1-$frameworkcode");
$cache->clear_from_cache("MarcSubfieldStructure-$frameworkcode");
$cache->clear_from_cache("MarcCodedFields-$frameworkcode");
}
my $redirect_url = "/cgi-bin/koha/admin/marctagstructure.pl?searchfield=$tagfield&frameworkcode=$frameworkcode";
if ($error) {
$redirect_url .= "&error=$error";
}
print $input->redirect($redirect_url);
exit;
# END $OP eq ADD_VALIDATE
################## DELETE_CONFIRM ##################################
@ -200,6 +216,12 @@ if ($op eq 'add_form') {
################## DEFAULT ##################################
} else { # DEFAULT
my $error_code = $input->param('error');
if ($error_code){
if ($error_code eq 'duplicate_tagfield'){
$template->param('blocking_error' => $error_code);
}
}
# here, $op can be unset or set to "cud-framework_create_confirm".
if ($searchfield ne '') {
$template->param(searchfield => $searchfield);

View File

@ -3125,21 +3125,6 @@ biblios:
marc_type: marc21
sort: 1
suggestible: ''
- facet: ''
marc_field: 752ad
marc_type: marc21
sort: 1
suggestible: ''
- facet: ''
marc_field: 210a
marc_type: unimarc
sort: 1
suggestible: ''
- facet: ''
marc_field: 214a
marc_type: unimarc
sort: 1
suggestible: ''
opac: 1
staff_client: 1
type: ''
@ -3194,6 +3179,21 @@ biblios:
marc_type: marc21
sort: 1
suggestible: ''
- facet: ''
marc_field: 752ad
marc_type: marc21
sort: 1
suggestible: ''
- facet: ''
marc_field: 210a
marc_type: unimarc
sort: 1
suggestible: ''
- facet: ''
marc_field: 214a
marc_type: unimarc
sort: 1
suggestible: ''
opac: 1
staff_client: 1
type: ''
@ -3768,7 +3768,7 @@ biblios:
sort: 1
suggestible: 1
- facet: ''
marc_field: '246'
marc_field: 246abnp
marc_type: marc21
sort: 0
suggestible: 1
@ -4143,7 +4143,7 @@ biblios:
sort: 1
suggestible: ''
- facet: ''
marc_field: '246'
marc_field: 246abnp
marc_type: marc21
sort: 1
suggestible: ''
@ -4184,7 +4184,7 @@ biblios:
sort: 1
suggestible: ''
- facet: ''
marc_field: '246'
marc_field: 246abnp
marc_type: marc21
sort: 1
suggestible: ''
@ -4196,7 +4196,7 @@ biblios:
mandatory: ~
mappings:
- facet: ''
marc_field: '246'
marc_field: 246abnp
marc_type: marc21
sort: 1
suggestible: ''

View File

@ -73,6 +73,11 @@ properties:
type:
- string
- "null"
service_platform:
description: service platform of the harvester
type:
- string
- "null"
metric_type:
description: metric type of the harvester when reporting
type:

View File

@ -16,6 +16,11 @@ properties:
biblio_id:
type: integer
description: Internal biblio identifier
deleted_biblio_id:
type:
- integer
- "null"
description: Internal deleted biblio identifier
item_group_id:
type:
- string

View File

@ -119,6 +119,11 @@ properties:
description: List of aliases
items:
$ref: "vendor_alias.yaml"
subscriptions_count:
type:
- integer
- "null"
description: Number of linked subscriptions
additionalProperties: false
required:
- name

View File

@ -23,6 +23,7 @@
type: string
enum:
- extended_attributes
- library
collectionFormat: csv
produces:
- application/json

View File

@ -90,6 +90,7 @@
type: string
enum:
- extended_attributes
- library
collectionFormat: csv
produces:
- application/json
@ -149,6 +150,7 @@
type: string
enum:
- extended_attributes
- library
collectionFormat: csv
produces:
- application/json

View File

@ -35,6 +35,7 @@
type: string
enum:
- aliases
- subscriptions+count
collectionFormat: csv
responses:
"200":

View File

@ -472,6 +472,7 @@
- item_type
- in_bundle
- bundle_host
- bundle_host.biblio
- bundle_items_lost+count
- bundle_items_not_lost+count
- course_item.course_reserves.course

View File

@ -23,6 +23,7 @@
type: string
enum:
- extended_attributes
- library
collectionFormat: csv
produces:
- application/json

View File

@ -104,7 +104,7 @@
- cancellation_requested
- biblio
- pickup_library
collectionFormat: csv
collectionFormat: csv
produces:
- application/json
responses:

View File

@ -269,6 +269,7 @@
type: string
enum:
- extended_attributes
- library
collectionFormat: csv
produces:
- application/json

View File

@ -540,7 +540,6 @@ my $error = $input->param('error');
my $authid = $input->param('authid') =~ s/\D//gr
; # if authid exists, it's a modif, not a new authority. We remove from authid all non-digit characters just in case the CGI parameter contains weird characters like spaces
my $op = $input->param('op');
my $nonav = $input->param('nonav');
my $myindex = $input->param('index');
my $linkid=$input->param('linkid');
my $authtypecode = $input->param('authtypecode');
@ -570,7 +569,7 @@ my ($template, $loggedinuser, $cookie)
type => "intranet",
flagsrequired => {editauthorities => 1},
});
$template->param(nonav => $nonav,index=>$myindex,authtypecode=>$authtypecode,breedingid=>$breedingid, count=>$count);
$template->param( index => $myindex, authtypecode => $authtypecode, breedingid => $breedingid, count => $count );
$tagslib = GetTagsLabels(1,$authtypecode);
$mandatory_z3950 = GetMandatoryFieldZ3950($authtypecode);
@ -632,15 +631,6 @@ if ($op eq "cud-add") {
duplicateauthvalue => $duplicateauthvalue->{'authorized'}->[0]->{'heading'},
);
}
} elsif ($op eq "cud-delete") {
#------------------------------------------------------------------------------------------------------------------------------
DelAuthority({ authid => $authid });
if ($nonav){
print $input->redirect("auth_finder.pl");
}else{
print $input->redirect("authorities-home.pl?authid=0");
}
exit;
} else {
if ( $op eq "duplicate" ) {
$authid = "";

View File

@ -20,7 +20,7 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
my $op = $query->param("op");
my $format = $query->param("format");
my $error = '';
if ( $op eq "cud-export" ) {
if ( $op eq "export" ) {
my $authid = $query->param("authid");
if ($authid) {

View File

@ -148,7 +148,6 @@ use C4::Languages qw( getlanguage getLanguages );
use C4::Koha qw( getitemtypeimagelocation GetAuthorisedValues );
use URI::Escape;
use POSIX qw(ceil floor);
use C4::Search qw( searchResults enabled_staff_search_views z3950_search_args new_record_from_zebra );
use Koha::ItemTypes;
use Koha::Library::Groups;
@ -243,7 +242,7 @@ my @search_groups =
my $branch_limit = '';
my $limit_param = $cgi->param('limit');
if ( $limit_param and $limit_param =~ /branch:([\w-]+)/ ) {
if ( $limit_param and $limit_param =~ /^branch:([\w-]+)/ ) {
$branch_limit = $1;
}
@ -425,9 +424,9 @@ if ($params->{'limit-yr'}) {
# $ %z3950p will be a hash ref if the indexes are present (advacned search), otherwise undef
my $z3950par;
my $indexes2z3950 = {
kw=>'title', au=>'author', 'au,phr'=>'author', nb=>'isbn', ns=>'issn',
'lcn,phr'=>'dewey', su=>'subject', 'su,phr'=>'subject',
ti=>'title', 'ti,phr'=>'title', se=>'title'
kw => 'title', au => 'author', 'au,phr' => 'author', nb => 'isbn', ns => 'issn',
callnum => 'dewey', su => 'subject', 'su,phr' => 'subject',
ti => 'title', 'ti,phr' => 'title', se => 'title'
};
for (my $ii = 0; $ii < @operands; ++$ii)
{

View File

@ -50,12 +50,14 @@ my $builder = sub {
function Click$params->{id}(ev) {
ev.preventDefault();
var code = document.getElementById(ev.data.id);
const csrf_token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
\$.ajax({
url: '/cgi-bin/koha/cataloguing/plugin_launcher.pl',
type: 'POST',
data: {
'plugin_name': 'stocknumberAV.pl',
'code' : code.value,
'csrf_token': csrf_token
},
success: function(data){
var field = document.getElementById(ev.data.id);

View File

@ -97,7 +97,7 @@ if ( $op eq 'cud-edit_message' && $message_id ) {
my $url = $input->referer;
if ($url) {
if ( $url =~ m|circulation\.pl$| ) {
if ( $url =~ m/(circulation\.pl|members\/files\.pl)$/ ) {
# Trick for POST form from batch checkouts
$url .= "?borrowernumber=$borrowernumber";

View File

@ -81,6 +81,7 @@ $template->param(
caller => 'article-request',
plain => 0,
style => $style,
id => 'ar_slip',
);
output_html_with_http_headers $cgi, $cookie, $template->output;

View File

@ -112,7 +112,7 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user (
}
);
my $op = $query->param('op');
my $op = $query->param('op') // '';
my $override_high_holds = $query->param('override_high_holds');
my $override_high_holds_tmp = $query->param('override_high_holds_tmp');
@ -364,19 +364,16 @@ if (@$barcodes && $op eq 'cud-checkout') {
}
if ( $issuingimpossible->{'STATS'} ) {
$template->param( STATS => 1 );
my ( $stats_return, $stats_messages, $stats_iteminformation, $stats_borrower ) =
AddReturn( $item->barcode, C4::Context->userenv->{'branch'}, undef, undef, 1 );
if ( $item->onloan ) {
my ( $stats_return, $stats_messages, $stats_iteminformation, $stats_borrower ) =
AddReturn( $item->barcode, C4::Context->userenv->{'branch'}, undef, undef, 1 );
$template->param(
CHECKEDIN => $stats_return,
MESSAGES => $stats_messages,
ITEM => $stats_iteminformation,
BORROWER => $stats_borrower,
);
}
$template->param(
STATS => 1,
CHECKEDIN => $stats_return,
MESSAGES => $stats_messages,
ITEM => $stats_iteminformation,
BORROWER => $stats_borrower,
);
#increment items.localuse
my $localuse_count = $item->localuse;

View File

@ -54,6 +54,7 @@ $template->param( slip => $slip ) if ($slip);
$template->param( caller => 'hold-transfer' );
$template->param( plain => !$is_html );
$template->param( style => $style );
$template->param( id => 'reserve_slip' );
output_html_with_http_headers $input, $cookie, $template->output;

View File

@ -194,7 +194,7 @@ if ( $holds->count ) {
# patrons count per biblio
my $patrons_count = {
map { $_->{biblionumber} => $_->{patrons_count} } @{ Koha::Holds->search(
{ 'suspend' => 0 },
{ 'suspend' => 0, 'found' => undef },
{
select => [ 'biblionumber', { count => { distinct => 'borrowernumber' } } ],
as => [qw( biblionumber patrons_count )],

View File

@ -143,7 +143,7 @@ foreach ( $query->param ) {
push( @inputloop, \%input );
}
my $op = $query->param('op');
my $op = $query->param('op') // '';
############
# Deal with the requests....

View File

@ -60,6 +60,7 @@ $template->param(
caller => 'cud-transfer',
stylesheet => C4::Context->preference("SlipCSS"),
style => $style,
id => 'transfer_slip',
);
output_html_with_http_headers $input, $cookie, $template->output;

View File

@ -145,23 +145,24 @@ if ($item && $tab eq 'holdsover' && !@cancel_result) {
exit;
sub cancel {
my ($item, $borrowernumber, $fbr, $tbr, $skip_transfers ) = @_;
my ( $itemnumber, $borrowernumber, $fbr, $tbr, $skip_transfers ) = @_;
my $transfer = $fbr ne $tbr; # XXX && !$nextreservinfo;
my $item = Koha::Items->find( $itemnumber );
return if $transfer && $skip_transfers;
my ( $messages, $nextreservinfo ) = ModReserveCancelAll( $item, $borrowernumber );
my ( $messages, $nextreservinfo ) = ModReserveCancelAll( $itemnumber, $borrowernumber );
# if the document is not in his homebranch location and there is not reservation after, we transfer it
if ($transfer && !$nextreservinfo) {
ModItemTransfer( $item, $fbr, $tbr, 'CancelReserve' );
if ( $transfer && !$nextreservinfo && !$item->itemlost ) {
ModItemTransfer( $itemnumber, $fbr, $tbr, 'CancelReserve' );
}
# if we have a result
if ($nextreservinfo) {
my %res;
my $patron = Koha::Patrons->find( $nextreservinfo );
my $title = Koha::Items->find( $item )->biblio->title;
my $title = $item->biblio->title;
if ( $messages->{'transfert'} ) {
$res{messagetransfert} = $messages->{'transfert'};
$res{branchcode} = $messages->{'transfert'};
@ -171,7 +172,7 @@ sub cancel {
$res{nextreservnumber} = $nextreservinfo;
$res{nextreservsurname} = $patron->surname;
$res{nextreservfirstname} = $patron->firstname;
$res{nextreservitem} = $item;
$res{nextreservitem} = $itemnumber;
$res{nextreservtitle} = $title;
$res{waiting} = $messages->{'waiting'} ? 1 : 0;

View File

@ -63,7 +63,8 @@
<term><option>--queue</option></term>
<listitem>
<para>Allows specifying the queue worker (defaults to 'default').</para>
<para>Current queues are: default, long_tasks, elastic_index.</para>
<para>Current queues are: default, long_tasks.</para>
<para>Note: There used to be a queue called elastic_index, but since the introduction of koha-es-indexer this queue should not be active.</para>
</listitem>
</varlistentry>
<varlistentry>

View File

@ -47,7 +47,7 @@ $scriptname [--quiet|-q] [--exclude-indexes] instancename1 [instancename2...]
$scriptname -h|--help
--schema-only Dump only the database schema
--exclude-indexes Include Zebra indexes on the backup
--exclude-indexes Exclude Zebra indexes from the backup
--exclude-logs Exclude /var/log/koha/name from the backup
--uploaded_files Include uploaded files.
--uploaded_temp_files Include temporary uploaded files.
@ -124,30 +124,37 @@ dump_instance()
output="$output to $metadump"
[ "$quiet" = "no" ] && echo "$output"
# tar has exit status 1 if any file has changed while being read,
# which is likely to happen as the logfiles are included.
set +e
if [ "$exclude_logs" = "yes" ]; then
tar -czf "$metadump" -C / $excludes \
"etc/koha/sites/$name" \
"etc/apache2/sites-available/$instancefile" \
"etc/apache2/sites-enabled/$instancefile" \
"var/lib/koha/$name" \
$uploaded_files_dir \
$uploaded_temp_files_dir
chown "root:$name-koha" "$metadump"
chmod g+r "$metadump"
tar -czf "$metadump" -C / $excludes \
"etc/koha/sites/$name" \
"etc/apache2/sites-available/$instancefile" \
"etc/apache2/sites-enabled/$instancefile" \
"var/lib/koha/$name" \
$uploaded_files_dir \
$uploaded_temp_files_dir
else
tar -czf "$metadump" -C / $excludes \
"etc/koha/sites/$name" \
"etc/apache2/sites-available/$instancefile" \
"etc/apache2/sites-enabled/$instancefile" \
"var/lib/koha/$name" \
"var/log/koha/$name" \
$uploaded_files_dir \
$uploaded_temp_files_dir
tar -czf "$metadump" -C / $excludes \
"etc/koha/sites/$name" \
"etc/apache2/sites-available/$instancefile" \
"etc/apache2/sites-enabled/$instancefile" \
"var/lib/koha/$name" \
"var/log/koha/$name" \
$uploaded_files_dir \
$uploaded_temp_files_dir
fi
if [ "$?" != "1" -a "$?" != "0" ]; then
exit $?
fi
set -e
chown "root:$name-koha" "$metadump"
chmod g+r "$metadump"
fi
[ "$quiet" = "no" ] && echo "Done."
fi

View File

@ -90,12 +90,12 @@ run_rebuild_elasticsearch()
KOHA_BINDIR=${KOHA_HOME}/misc
fi
if [ "${clo_commit_size}" > 0 ]; then
if [ "${clo_commit_size}" -gt 0 ] 2>/dev/null; then
commit_size=${clo_commit_size}
fi
rebuild_opts="--commit ${commit_size}"
if [ "${clo_processes}" > 0 ]; then
if [ "${clo_processes}" -gt 0 ] 2>/dev/null; then
processes=${clo_processes}
fi
rebuild_opts="${rebuild_opts} --processes ${processes}"

View File

@ -280,21 +280,21 @@ email turned on.
Usage: $scriptname [--enabled|--disabled] [--email|--noemail] [--sip|--nosip] [-h]
Options:
--enabled Show enabled instances
--disabled Show disabled instances
--elasticsearch Show instances with Elasticsearch enabled
--noelasticsarch Show instances with Elasticsearch disabled
--email Show instances with email enabled
--noemail Show instances with email disabled
--sip Show instances with SIP enabled
--nosip Show instances with SIP disabled
--plack Show instances with Plack enabled
--noplack Show instances with Plack disabled
--letsencrypt Show instances with letsencrypt enabled
--noletsencrypt Show instances with letsencrypt disabled
--z3950 Show instances with Z39.50/SRU enabled
--noz3950 Show instances with Z39.50/SRU disabled
--help | -h Show this help
--enabled Show enabled instances
--disabled Show disabled instances
--elasticsearch Show instances with Elasticsearch enabled
--noelasticsearch Show instances with Elasticsearch disabled
--email Show instances with email enabled
--noemail Show instances with email disabled
--sip Show instances with SIP enabled
--nosip Show instances with SIP disabled
--plack Show instances with Plack enabled
--noplack Show instances with Plack disabled
--letsencrypt Show instances with letsencrypt enabled
--noletsencrypt Show instances with letsencrypt disabled
--z3950 Show instances with Z39.50/SRU enabled
--noz3950 Show instances with Z39.50/SRU disabled
--help | -h Show this help
The filtering options can be combined, and you probably want to do this
(except --email and --noemail, or --enabled and --disabled, that's just silly.)

View File

@ -70,7 +70,7 @@ if ! test $days -gt 0 ; then
exit 1
fi
for name in $(koha-list --enabled | grep -Fxv demo)
for name in $(koha-list --enabled)
do
if koha-dump ${exclude_indexes} ${exclude_logs} "$name" > /dev/null; then
# Only delete old backups if dump script return success.

Some files were not shown because too many files have changed in this diff Show More