From srdjan at catalyst.net.nz Mon Sep 3 07:06:14 2012 From: srdjan at catalyst.net.nz (Srdjan) Date: Mon, 3 Sep 2012 17:06:14 +1200 Subject: [Koha-patches] [PATCH] bug_7243: Be selective when summing up charges for blocking issues In-Reply-To: References: Message-ID: <1346648774-10206-1-git-send-email-srdjan@catalyst.net.nz> Added RentalsInNoissueCharges and ManInvlsInNoissueCharges sys prefs Created C4::Members::cwGetMemberAccountBallance() * A wrapper for GetMemberAccountRecords that gives info on non-issue and other charges * Other charges are: 'Res' 'Rent' if RentalsInNoissueCharges is Mo authorised_values MANUAL_INV if ManInvlsInNoissueCharges is No C4::Members::GetMemberAccountRecords() changes: * Dropped input param $date, it is not used Use split charges in C4::Circulation::CanBookBeIssued() and C4::Members::patronflags(). That way only fines decide whether an item can be issued, and not other non-fine charges --- C4/Circulation.pm | 31 +++++----- C4/Members.pm | 63 +++++++++++++++----- circ/circulation.pl | 17 ++---- installer/data/mysql/sysprefs.sql | 2 + installer/data/mysql/updatedatabase.pl | 10 ++++ .../en/modules/admin/preferences/circulation.pref | 12 ++++ .../prog/en/modules/circ/circulation.tt | 4 ++ opac/opac-user.pl | 1 - t/db_dependent/lib/KohaTest/Members.pm | 1 + 9 files changed, 99 insertions(+), 42 deletions(-) diff --git a/C4/Circulation.pm b/C4/Circulation.pm index cf7e927..f0f86ef 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -763,29 +763,32 @@ sub CanBookBeIssued { # # DEBTS - my ($amount) = - C4::Members::GetMemberAccountRecords( $borrower->{'borrowernumber'}, '' && $duedate->ymd() ); + my ($ballance, $non_issue_ballance, $other_charges) = + C4::Members::GetMemberAccountBallance( $borrower->{'borrowernumber'} ); my $amountlimit = C4::Context->preference("noissuescharge"); my $allowfineoverride = C4::Context->preference("AllowFineOverride"); my $allfinesneedoverride = C4::Context->preference("AllFinesNeedOverride"); if ( C4::Context->preference("IssuingInProcess") ) { - if ( $amount > $amountlimit && !$inprocess && !$allowfineoverride) { - $issuingimpossible{DEBT} = sprintf( "%.2f", $amount ); - } elsif ( $amount > $amountlimit && !$inprocess && $allowfineoverride) { - $needsconfirmation{DEBT} = sprintf( "%.2f", $amount ); - } elsif ( $allfinesneedoverride && $amount > 0 && $amount <= $amountlimit && !$inprocess ) { - $needsconfirmation{DEBT} = sprintf( "%.2f", $amount ); + if ( $non_issue_ballance > $amountlimit && !$inprocess && !$allowfineoverride) { + $issuingimpossible{DEBT} = sprintf( "%.2f", $non_issue_ballance ); + } elsif ( $non_issue_ballance > $amountlimit && !$inprocess && $allowfineoverride) { + $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_ballance ); + } elsif ( $allfinesneedoverride && $non_issue_ballance > 0 && $non_issue_ballance <= $amountlimit && !$inprocess ) { + $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_ballance ); } } else { - if ( $amount > $amountlimit && $allowfineoverride ) { - $needsconfirmation{DEBT} = sprintf( "%.2f", $amount ); - } elsif ( $amount > $amountlimit && !$allowfineoverride) { - $issuingimpossible{DEBT} = sprintf( "%.2f", $amount ); - } elsif ( $amount > 0 && $allfinesneedoverride ) { - $needsconfirmation{DEBT} = sprintf( "%.2f", $amount ); + if ( $non_issue_ballance > $amountlimit && $allowfineoverride ) { + $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_ballance ); + } elsif ( $non_issue_ballance > $amountlimit && !$allowfineoverride) { + $issuingimpossible{DEBT} = sprintf( "%.2f", $non_issue_ballance ); + } elsif ( $non_issue_ballance > 0 && $allfinesneedoverride ) { + $needsconfirmation{DEBT} = sprintf( "%.2f", $non_issue_ballance ); } } + if ($ballance > 0 && $other_charges > 0) { + $alerts{OTHER_CHARGES} = sprintf( "%.2f", $other_charges ); + } my ($blocktype, $count) = C4::Members::IsMemberBlocked($borrower->{'borrowernumber'}); if ($blocktype == -1) { diff --git a/C4/Members.pm b/C4/Members.pm index 2e315aa..adf0987 100644 --- a/C4/Members.pm +++ b/C4/Members.pm @@ -430,21 +430,21 @@ sub patronflags { my %flags; my ( $patroninformation) = @_; my $dbh=C4::Context->dbh; - my ($amount) = GetMemberAccountRecords( $patroninformation->{'borrowernumber'}); - if ( $amount > 0 ) { + my ($ballance, $owing) = GetMemberAccountBallance( $patroninformation->{'borrowernumber'}); + if ( $owing > 0 ) { my %flaginfo; my $noissuescharge = C4::Context->preference("noissuescharge") || 5; - $flaginfo{'message'} = sprintf "Patron owes \$%.02f", $amount; - $flaginfo{'amount'} = sprintf "%.02f", $amount; - if ( $amount > $noissuescharge && !C4::Context->preference("AllowFineOverride") ) { + $flaginfo{'message'} = sprintf "Patron owes \$%.02f", $owing; + $flaginfo{'amount'} = sprintf "%.02f", $owing; + if ( $owing > $noissuescharge && !C4::Context->preference("AllowFineOverride") ) { $flaginfo{'noissues'} = 1; } $flags{'CHARGES'} = \%flaginfo; } - elsif ( $amount < 0 ) { + elsif ( $ballance < 0 ) { my %flaginfo; - $flaginfo{'message'} = sprintf "Patron has credit of \$%.02f", -$amount; - $flaginfo{'amount'} = sprintf "%.02f", $amount; + $flaginfo{'message'} = sprintf "Patron has credit of \$%.02f", -$ballance; + $flaginfo{'amount'} = sprintf "%.02f", $ballance; $flags{'CREDITS'} = \%flaginfo; } if ( $patroninformation->{'gonenoaddress'} @@ -1119,9 +1119,8 @@ total amount outstanding for all of the account lines. =cut -#' sub GetMemberAccountRecords { - my ($borrowernumber,$date) = @_; + my ($borrowernumber) = @_; my $dbh = C4::Context->dbh; my @acctlines; my $numlines = 0; @@ -1129,14 +1128,10 @@ sub GetMemberAccountRecords { SELECT * FROM accountlines WHERE borrowernumber=?); - my @bind = ($borrowernumber); - if ($date && $date ne ''){ - $strsth.=" AND date < ? "; - push(@bind,$date); - } $strsth.=" ORDER BY date desc,timestamp DESC"; my $sth= $dbh->prepare( $strsth ); - $sth->execute( @bind ); + $sth->execute( $borrowernumber ); + my $total = 0; while ( my $data = $sth->fetchrow_hashref ) { if ( $data->{itemnumber} ) { @@ -1152,6 +1147,42 @@ sub GetMemberAccountRecords { return ( $total, \@acctlines,$numlines); } +=head2 GetMemberAccountBallance + + ($total_ballance, $non_issue_ballance, $other_charges) = &GetMemberAccountBallance($borrowernumber); + +Calculates amount immediately owing by the patron - non-issue charges. +Based on GetMemberAccountRecords. +Charges exempt from non-issue are: +* Res (reserves) +* Rent (rental) if RentalsInNoissueCharges syspref is set to false +* Manual invoices if ManInvInNoissueCharges syspref is set to false + +=cut + +my $ACCOUNT_TYPE_LENGTH = 5; # this is plain ridiculous... + +my @not_fines = ('Res'); +push @not_fines, 'Rent' unless C4::Context->preference('RentalsInNoissueCharges'); +unless ( C4::Context->preference('ManInvInNoissueCharges') ) { + my $dbh = C4::Context->dbh; + my $man_inv_types = $dbh->selectcol_arrayref(qq{SELECT authorised_value FROM authorised_values WHERE category = 'MANUAL_INV'}); + push @not_fines, map substr($_, 0, $ACCOUNT_TYPE_LENGTH), @$man_inv_types; +} +my %not_fine = map {$_ => 1} @not_fines; + +sub GetMemberAccountBallance { + my ($borrowernumber) = @_; + + my ($total, $acctlines) = GetMemberAccountRecords($borrowernumber); + my $other_charges = 0; + foreach (@$acctlines) { + $other_charges += $_->{amountoutstanding} if $not_fine{ substr($_->{accounttype}, 0, $ACCOUNT_TYPE_LENGTH) }; + } + + return ( $total, $total - $other_charges, $other_charges); +} + =head2 GetBorNotifyAcctRecord ($total, $acctlines, $count) = &GetBorNotifyAcctRecord($params,$notifyid); diff --git a/circ/circulation.pl b/circ/circulation.pl index 78ac1a4..61346d9 100755 --- a/circ/circulation.pl +++ b/circ/circulation.pl @@ -186,8 +186,7 @@ if ( $print eq 'yes' && $borrowernumber ne '' ) { my $borrowerslist; my $message; if ($findborrower) { - my $borrowers = Search($findborrower, 'cardnumber'); - my @borrowers = @$borrowers; + my $borrowers = Search($findborrower, 'cardnumber') || []; if (C4::Context->preference("AddPatronLists")) { $template->param( "AddPatronLists_".C4::Context->preference("AddPatronLists")=> "1", @@ -198,17 +197,17 @@ if ($findborrower) { $template->param(categories=>$categories); } } - if ( $#borrowers == -1 ) { + if ( @$borrowers == 0 ) { $query->param( 'findborrower', '' ); $message = "'$findborrower'"; } - elsif ( $#borrowers == 0 ) { - $query->param( 'borrowernumber', $borrowers[0]->{'borrowernumber'} ); + elsif ( @$borrowers == 1 ) { + $borrowernumber = $borrowers->[0]->{'borrowernumber'}; + $query->param( 'borrowernumber', $borrowernumber ); $query->param( 'barcode', '' ); - $borrowernumber = $borrowers[0]->{'borrowernumber'}; } else { - $borrowerslist = \@borrowers; + $borrowerslist = $borrowers; } } @@ -536,7 +535,6 @@ foreach my $flag ( sort keys %$flags ) { $flags->{$flag}->{'message'} =~ s#\n#
#g; if ( $flags->{$flag}->{'noissues'} ) { $template->param( - flagged => 1, noissues => 'true', ); if ( $flag eq 'GNA' ) { @@ -568,7 +566,6 @@ foreach my $flag ( sort keys %$flags ) { if ( $flag eq 'CHARGES' ) { $template->param( charges => 'true', - flagged => 1, chargesmsg => $flags->{'CHARGES'}->{'message'}, chargesamount => $flags->{'CHARGES'}->{'amount'}, ); @@ -583,7 +580,6 @@ foreach my $flag ( sort keys %$flags ) { elsif ( $flag eq 'ODUES' ) { $template->param( odues => 'true', - flagged => 1, oduesmsg => $flags->{'ODUES'}->{'message'} ); @@ -595,7 +591,6 @@ foreach my $flag ( sort keys %$flags ) { elsif ( $flag eq 'NOTES' ) { $template->param( notes => 'true', - flagged => 1, notesmsg => $flags->{'NOTES'}->{'message'} ); } diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index 7219e37..f25874a 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -325,6 +325,8 @@ INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('EasyAnalyticalRecords','0','If on, display in the catalogue screens tools to easily setup analytical record relationships','','YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpacShowRecentComments',0,'If ON a link to recent comments will appear in the OPAC masthead',NULL,'YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('CircAutoPrintQuickSlip', '1', 'Choose what should happen when an empty barcode field is submitted in circulation: Display a print quick slip window or Clear the screen.',NULL,'YesNo'); +INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('RentalsInNoissueCharges', '1', 'Include rental charges when summing up charges for NoissueCharge.',NULL,'YesNo'); +INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('ManInvInNoissueCharges', '1', 'Include MAN_INV charges when summing up charges for NoissueCharge.',NULL,'YesNo'); INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('NoticeCSS','','Notices CSS url.',NULL,'free'); INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('SlipCSS','','Slips CSS url.',NULL,'free'); INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('TransferWhenCancelAllWaitingHolds','0','Transfer items when cancelling all waiting holds',NULL,'YesNo'); diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index e58c4c9..3f7e3c0 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -5657,6 +5657,16 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) { SetVersion ($DBversion); } + + +$DBversion = "3.09.00.XXX"; +if (C4::Context->preference("Version") < TransformToNum($DBversion)) { + $dbh->do("INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('RentalsInNoissueCharges', '1', 'Include rental charges when summing up charges for NoissueCharge.',NULL,'YesNo');"); + $dbh->do("INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('ManInvInNoissueCharges', '1', 'Include MAN_INV charges when summing up charges for NoissueCharge.',NULL,'YesNo');"); + print "Upgrade to $DBversion done (Add sysprefs RentalsInNoissueCharges and ManInvInNoissueCharges.)\n"; + SetVersion($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref index a758b19..9d9e5af 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -220,6 +220,18 @@ Circulation: class: integer - '[% local_currency %] in fines.' - + - pref: RentalsInNoissueCharges + choices: + yes: Include + no: "Don't include" + - rental charges when summing up charges for NoissueCharge. + - + - pref: ManInvInNoissueCharges + choices: + yes: Include + no: "Don't include" + - MAN_INV charges when summing up charges for NoissueCharge. + - - pref: ReturnBeforeExpiry choices: yes: Require diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt index 729defc..8edb8c3 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -243,6 +243,10 @@ function validate1(date) {
This item has been lost with a status of "[% alert.ITEM_LOST %]".
[% END %] +[% IF ( alert.OTHER_CHARGES ) %] +
The patron has unpaid charges for reserves, rentals etc of [% alert.OTHER_CHARGES %]
+[% END %] + [% IF ( NEEDSCONFIRMATION ) %]
diff --git a/opac/opac-user.pl b/opac/opac-user.pl index 50a167e..d353185 100755 --- a/opac/opac-user.pl +++ b/opac/opac-user.pl @@ -141,7 +141,6 @@ $template->param( BORROWER_INFO => \@bordat, OPACMySummaryHTML => (C4::Context->preference("OPACMySummaryHTML")) ? 1 : 0, surname => $borr->{surname}, showname => $borr->{showname}, - ); #get issued items .... diff --git a/t/db_dependent/lib/KohaTest/Members.pm b/t/db_dependent/lib/KohaTest/Members.pm index dfde7da..3a2d4e8 100644 --- a/t/db_dependent/lib/KohaTest/Members.pm +++ b/t/db_dependent/lib/KohaTest/Members.pm @@ -27,6 +27,7 @@ sub methods : Test( 1 ) { GetPendingIssues GetAllIssues GetMemberAccountRecords + GetMemberAccountBallance GetBorNotifyAcctRecord checkuniquemember checkcardnumber -- 1.7.9.5 From fridolyn.somers at biblibre.com Mon Sep 3 13:43:24 2012 From: fridolyn.somers at biblibre.com (Fridolyn SOMERS) Date: Mon, 3 Sep 2012 13:43:24 +0200 Subject: [Koha-patches] [PATCH] [SIGNED-OFF] Bug 8293 : Fixing POD to match actuality. Message-ID: <1346672604-5816-1-git-send-email-fridolyn.somers@biblibre.com> From: Chris Cormack Signed-off-by: Fridolyn SOMERS --- C4/Search.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/C4/Search.pm b/C4/Search.pm index b0085c4..3edf118 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -1415,7 +1415,7 @@ sub buildQuery { my @search_results = searchResults($search_context, $searchdesc, $hits, $results_per_page, $offset, $scan, - @marcresults, $hidelostitems); + @marcresults); Format results in a form suitable for passing to the template -- 1.7.9.5 From nengard at bywatersolutions.com Tue Sep 4 17:25:33 2012 From: nengard at bywatersolutions.com (Nicole C. Engard) Date: Tue, 4 Sep 2012 11:25:33 -0400 Subject: [Koha-patches] [PATCH] Bug 8721: Fixes minor typo in AllowItemsOnHoldCheckout Message-ID: <1346772333-2404-1-git-send-email-nengard@bywatersolutions.com> Small string fix to remove repeated word. --- .../en/modules/admin/preferences/circulation.pref | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref index a758b19..308a30f 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -128,7 +128,7 @@ Circulation: choices: yes: Allow no: "Don't allow" - - checkouts of items items reserved to someone else. If allowed do not generate RESERVE_WAITING and RESERVED warning. This allows self checkouts for those items. + - checkouts of items reserved to someone else. If allowed do not generate RESERVE_WAITING and RESERVED warning. This allows self checkouts for those items. - - pref: AllFinesNeedOverride choices: -- 1.7.2.3 From srdjan at catalyst.net.nz Wed Sep 5 02:52:30 2012 From: srdjan at catalyst.net.nz (Srdjan) Date: Wed, 5 Sep 2012 12:52:30 +1200 Subject: [Koha-patches] [PATCH] bug_5911: Transport Cost Matrix In-Reply-To: References: Message-ID: <1346806350-18899-1-git-send-email-srdjan@catalyst.net.nz> Create transport_cost table, added UseTransportCostMatrix syspref. transport_cost table contains branch to branch transfer costs. These are used for filling inter-branch hold transfers. Moved GetHoldsQueueItems() from .pl to HoldsQueue.pm Signed-off-by: Kyle M Hall --- C4/Circulation.pm | 3 +- C4/HoldsQueue.pm | 622 ++++++++++++++++++++ admin/systempreferences.pl | 1 + admin/transport-cost-matrix.pl | 125 ++++ circ/view_holdsqueue.pl | 34 +- installer/data/mysql/kohastructure.sql | 16 + installer/data/mysql/sysprefs.sql | 1 + installer/data/mysql/updatedatabase.pl | 22 + .../prog/en/modules/admin/admin-home.tt | 2 + .../en/modules/admin/preferences/circulation.pref | 6 + .../prog/en/modules/admin/transport-cost-matrix.tt | 131 +++++ misc/cronjobs/holds/build_holds_queue.pl | 385 +----------- t/db_dependent/HoldsQueue.t | 175 ++++++ 13 files changed, 1107 insertions(+), 416 deletions(-) create mode 100755 C4/HoldsQueue.pm create mode 100755 admin/transport-cost-matrix.pl create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/transport-cost-matrix.tt create mode 100755 t/db_dependent/HoldsQueue.t diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 8647943..f4b9cd1 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -1413,7 +1413,8 @@ sub GetBranchItemRule { foreach my $attempt (@attempts) { my ($query, @bind_params) = @{$attempt}; - my $search_result = $dbh->selectrow_hashref ( $query , {}, @bind_params ); + my $search_result = $dbh->selectrow_hashref ( $query , {}, @bind_params ) + or next; # Since branch/category and branch/itemtype use the same per-branch # defaults tables, we have to check that the key we want is set, not diff --git a/C4/HoldsQueue.pm b/C4/HoldsQueue.pm new file mode 100755 index 0000000..ffc6f89 --- /dev/null +++ b/C4/HoldsQueue.pm @@ -0,0 +1,622 @@ +package C4::HoldsQueue; + +# Copyright 2011 Catalyst IT +# +# 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 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# FIXME: expand perldoc, explain intended logic + +use strict; +use warnings; + +use C4::Context; +use C4::Search; +use C4::Items; +use C4::Branch; +use C4::Circulation; +use C4::Members; +use C4::Biblio; +use C4::Dates qw/format_date/; + +use List::Util qw(shuffle); +use Data::Dumper; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); +BEGIN { + $VERSION = 3.03; + require Exporter; + @ISA = qw(Exporter); + @EXPORT_OK = qw( + &CreateQueue + &GetHoldsQueueItems + + &TransportCostMatrix + &UpdateTransportCostMatrix + ); +} + +# XXX This is not safe in a persistant environment +my $dbh = C4::Context->dbh; + +=head1 FUNCTIONS + +=head2 TransportCostMatrix + + TransportCostMatrix(); + +Returns Transport Cost Matrix as a hashref => => cost + +=cut + +sub TransportCostMatrix { + my $transport_costs = $dbh->selectall_arrayref("SELECT * FROM transport_cost",{ Slice => {} }); + + my %transport_cost_matrix; + foreach (@$transport_costs) { + my $from = $_->{frombranch}; + my $to = $_->{tobranch}; + my $cost = $_->{cost}; + my $disabled = $_->{disable_transfer}; + $transport_cost_matrix{$to}{$from} = { cost => $cost, disable_transfer => $disabled }; + } + return \%transport_cost_matrix; +} + +=head2 UpdateTransportCostMatrix + + UpdateTransportCostMatrix($records); + +Updates full Transport Cost Matrix table. $records is an arrayref of records. +Records: { frombranch => , tobranch => , cost =>
, disable_transfer => <0,1> } + +=cut + +sub UpdateTransportCostMatrix { + my ($records) = @_; + + my $sth = $dbh->prepare("INSERT INTO transport_cost (frombranch, tobranch, cost, disable_transfer) VALUES (?, ?, ?, ?)"); + + $dbh->do("TRUNCATE TABLE transport_cost"); + foreach (@$records) { + my $cost = $_->{cost}; + my $from = $_->{frombranch}; + my $to = $_->{tobranch}; + if ($_->{disable_transfer}) { + $cost ||= 0; + } + elsif ( !defined ($cost) || ($cost !~ m/(0|[1-9][0-9]*)(\.[0-9]*)?/o) ) { + warn "Invalid $from -> $to cost $cost - must be a number >= 0, disablig"; + $cost = 0; + $_->{disable_transfer} = 1; + } + $sth->execute( $from, $to, $cost, $_->{disable_transfer} ? 1 : 0 ); + } +} + +=head2 GetHoldsQueueItems + + GetHoldsQueueItems($branch); + +Returns hold queue for a holding branch. If branch is omitted, then whole queue is returned + +=cut + +sub GetHoldsQueueItems { + my ($branchlimit) = @_; + + my @bind_params = (); + my $query = q/SELECT tmp_holdsqueue.*, biblio.author, items.ccode, items.location, items.enumchron, items.cn_sort, biblioitems.publishercode,biblio.copyrightdate,biblioitems.publicationyear,biblioitems.pages,biblioitems.size,biblioitems.publicationyear,biblioitems.isbn,items.copynumber + FROM tmp_holdsqueue + JOIN biblio USING (biblionumber) + LEFT JOIN biblioitems USING (biblionumber) + LEFT JOIN items USING ( itemnumber) + /; + if ($branchlimit) { + $query .=" WHERE tmp_holdsqueue.holdingbranch = ?"; + push @bind_params, $branchlimit; + } + $query .= " ORDER BY ccode, location, cn_sort, author, title, pickbranch, reservedate"; + my $sth = $dbh->prepare($query); + $sth->execute(@bind_params); + my $items = []; + while ( my $row = $sth->fetchrow_hashref ){ + $row->{reservedate} = format_date($row->{reservedate}); + my $record = GetMarcBiblio($row->{biblionumber}); + if ($record){ + $row->{subtitle} = GetRecordValue('subtitle',$record,'')->[0]->{subfield}; + $row->{parts} = GetRecordValue('parts',$record,'')->[0]->{subfield}; + $row->{numbers} = GetRecordValue('numbers',$record,'')->[0]->{subfield}; + } + push @$items, $row; + } + return $items; +} + +=head2 CreateQueue + + CreateQueue(); + +Top level function that turns reserves into tmp_holdsqueue and hold_fill_targets. + +=cut + +sub CreateQueue { + + $dbh->do("DELETE FROM tmp_holdsqueue"); # clear the old table for new info + $dbh->do("DELETE FROM hold_fill_targets"); + + my $total_bibs = 0; + my $total_requests = 0; + my $total_available_items = 0; + my $num_items_mapped = 0; + + my $branches_to_use; + my $transport_cost_matrix; + my $use_transport_cost_matrix = C4::Context->preference("UseTransportCostMatrix"); + if ($use_transport_cost_matrix) { + $transport_cost_matrix = TransportCostMatrix(); + unless (keys %$transport_cost_matrix) { + warn "UseTransportCostMatrix set to yes, but matrix not populated"; + undef $transport_cost_matrix; + } + } + unless ($transport_cost_matrix) { + $branches_to_use = load_branches_to_pull_from(); + } + + my $bibs_with_pending_requests = GetBibsWithPendingHoldRequests(); + + foreach my $biblionumber (@$bibs_with_pending_requests) { + $total_bibs++; + my $hold_requests = GetPendingHoldRequestsForBib($biblionumber); + my $available_items = GetItemsAvailableToFillHoldRequestsForBib($biblionumber, $branches_to_use); + $total_requests += scalar(@$hold_requests); + $total_available_items += scalar(@$available_items); + + my $item_map = MapItemsToHoldRequests($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix); + $item_map or next; + my $item_map_size = scalar(keys %$item_map) + or next; + + $num_items_mapped += $item_map_size; + CreatePicklistFromItemMap($item_map); + AddToHoldTargetMap($item_map); + if (($item_map_size < scalar(@$hold_requests )) and + ($item_map_size < scalar(@$available_items))) { + # DOUBLE CHECK, but this is probably OK - unfilled item-level requests + # FIXME + #warn "unfilled requests for $biblionumber"; + #warn Dumper($hold_requests), Dumper($available_items), Dumper($item_map); + } + } +} + +=head2 GetBibsWithPendingHoldRequests + + my $biblionumber_aref = GetBibsWithPendingHoldRequests(); + +Return an arrayref of the biblionumbers of all bibs +that have one or more unfilled hold requests. + +=cut + +sub GetBibsWithPendingHoldRequests { + my $dbh = C4::Context->dbh; + + my $bib_query = "SELECT DISTINCT biblionumber + FROM reserves + WHERE found IS NULL + AND priority > 0 + AND reservedate <= CURRENT_DATE()"; + my $sth = $dbh->prepare($bib_query); + + $sth->execute(); + my $biblionumbers = $sth->fetchall_arrayref(); + + return [ map { $_->[0] } @$biblionumbers ]; +} + +=head2 GetPendingHoldRequestsForBib + + my $requests = GetPendingHoldRequestsForBib($biblionumber); + +Returns an arrayref of hashrefs to pending, unfilled hold requests +on the bib identified by $biblionumber. The following keys +are present in each hashref: + + biblionumber + borrowernumber + itemnumber + priority + branchcode + reservedate + reservenotes + borrowerbranch + +The arrayref is sorted in order of increasing priority. + +=cut + +sub GetPendingHoldRequestsForBib { + my $biblionumber = shift; + + my $dbh = C4::Context->dbh; + + my $request_query = "SELECT biblionumber, borrowernumber, itemnumber, priority, reserves.branchcode, + reservedate, reservenotes, borrowers.branchcode AS borrowerbranch + FROM reserves + JOIN borrowers USING (borrowernumber) + WHERE biblionumber = ? + AND found IS NULL + AND priority > 0 + AND reservedate <= CURRENT_DATE() + ORDER BY priority"; + my $sth = $dbh->prepare($request_query); + $sth->execute($biblionumber); + + my $requests = $sth->fetchall_arrayref({}); + return $requests; + +} + +=head2 GetItemsAvailableToFillHoldRequestsForBib + + my $available_items = GetItemsAvailableToFillHoldRequestsForBib($biblionumber, $branches_ar); + +Returns an arrayref of items available to fill hold requests +for the bib identified by C<$biblionumber>. An item is available +to fill a hold request if and only if: + + * it is not on loan + * it is not withdrawn + * it is not marked notforloan + * it is not currently in transit + * it is not lost + * it is not sitting on the hold shelf + +=cut + +sub GetItemsAvailableToFillHoldRequestsForBib { + my ($biblionumber, $branches_to_use) = @_; + + my $dbh = C4::Context->dbh; + my $items_query = "SELECT itemnumber, homebranch, holdingbranch, itemtypes.itemtype AS itype + FROM items "; + + if (C4::Context->preference('item-level_itypes')) { + $items_query .= "LEFT JOIN itemtypes ON (itemtypes.itemtype = items.itype) "; + } else { + $items_query .= "JOIN biblioitems USING (biblioitemnumber) + LEFT JOIN itemtypes USING (itemtype) "; + } + $items_query .= "WHERE items.notforloan = 0 + AND holdingbranch IS NOT NULL + AND itemlost = 0 + AND wthdrawn = 0"; + $items_query .= " AND damaged = 0" unless C4::Context->preference('AllowHoldsOnDamagedItems'); + $items_query .= " AND items.onloan IS NULL + AND (itemtypes.notforloan IS NULL OR itemtypes.notforloan = 0) + AND itemnumber NOT IN ( + SELECT itemnumber + FROM reserves + WHERE biblionumber = ? + AND itemnumber IS NOT NULL + AND (found IS NOT NULL OR priority = 0) + ) + AND items.biblionumber = ?"; + $items_query .= " AND damaged = 0 " + unless C4::Context->preference('AllowHoldsOnDamagedItems'); + + my @params = ($biblionumber, $biblionumber); + if ($branches_to_use && @$branches_to_use) { + $items_query .= " AND holdingbranch IN (" . join (",", map { "?" } @$branches_to_use) . ")"; + push @params, @$branches_to_use; + } + my $sth = $dbh->prepare($items_query); + $sth->execute(@params); + + my $itm = $sth->fetchall_arrayref({}); + my @items = grep { ! scalar GetTransfers($_->{itemnumber}) } @$itm; + return [ grep { + my $rule = GetBranchItemRule($_->{homebranch}, $_->{itype}); + $_->{holdallowed} = $rule->{holdallowed} != 0 + } @items ]; +} + +=head2 MapItemsToHoldRequests + + MapItemsToHoldRequests($hold_requests, $available_items, $branches, $transport_cost_matrix) + +=cut + +sub MapItemsToHoldRequests { + my ($hold_requests, $available_items, $branches_to_use, $transport_cost_matrix) = @_; + + # handle trival cases + return unless scalar(@$hold_requests) > 0; + return unless scalar(@$available_items) > 0; + + my $automatic_return = C4::Context->preference("AutomaticItemReturn"); + + # identify item-level requests + my %specific_items_requested = map { $_->{itemnumber} => 1 } + grep { defined($_->{itemnumber}) } + @$hold_requests; + + # group available items by itemnumber + my %items_by_itemnumber = map { $_->{itemnumber} => $_ } @$available_items; + + # items already allocated + my %allocated_items = (); + + # map of items to hold requests + my %item_map = (); + + # figure out which item-level requests can be filled + my $num_items_remaining = scalar(@$available_items); + foreach my $request (@$hold_requests) { + last if $num_items_remaining == 0; + + # is this an item-level request? + if (defined($request->{itemnumber})) { + # fill it if possible; if not skip it + if (exists $items_by_itemnumber{$request->{itemnumber}} and + not exists $allocated_items{$request->{itemnumber}}) { + $item_map{$request->{itemnumber}} = { + borrowernumber => $request->{borrowernumber}, + biblionumber => $request->{biblionumber}, + holdingbranch => $items_by_itemnumber{$request->{itemnumber}}->{holdingbranch}, + pickup_branch => $request->{branchcode} || $request->{borrowerbranch}, + item_level => 1, + reservedate => $request->{reservedate}, + reservenotes => $request->{reservenotes}, + }; + $allocated_items{$request->{itemnumber}}++; + $num_items_remaining--; + } + } else { + # it's title-level request that will take up one item + $num_items_remaining--; + } + } + + # group available items by branch + my %items_by_branch = (); + foreach my $item (@$available_items) { + next unless $item->{holdallowed}; + + push @{ $items_by_branch{ $automatic_return ? $item->{homebranch} + : $item->{holdingbranch} } }, $item + unless exists $allocated_items{ $item->{itemnumber} }; + } + return unless keys %items_by_branch; + + # now handle the title-level requests + $num_items_remaining = scalar(@$available_items) - scalar(keys %allocated_items); + my $pull_branches; + foreach my $request (@$hold_requests) { + last if $num_items_remaining == 0; + next if defined($request->{itemnumber}); # already handled these + + # look for local match first + my $pickup_branch = $request->{branchcode} || $request->{borrowerbranch}; + my ($itemnumber, $holdingbranch); + + my $holding_branch_items = $automatic_return ? undef : $items_by_branch{$pickup_branch}; + if ( $holding_branch_items ) { + foreach my $item (@$holding_branch_items) { + if ( $request->{borrowerbranch} eq $item->{homebranch} ) { + $itemnumber = $item->{itemnumber}; + last; + } + } + $holdingbranch = $pickup_branch; + $itemnumber ||= $holding_branch_items->[0]->{itemnumber}; + } + elsif ($transport_cost_matrix) { + $pull_branches = [keys %items_by_branch]; + $holdingbranch = least_cost_branch( $pickup_branch, $pull_branches, $transport_cost_matrix ); + if ( $holdingbranch ) { + + my $holding_branch_items = $items_by_branch{$holdingbranch}; + foreach my $item (@$holding_branch_items) { + next if $request->{borrowerbranch} ne $item->{homebranch}; + + $itemnumber = $item->{itemnumber}; + last; + } + $itemnumber ||= $holding_branch_items->[0]->{itemnumber}; + } + else { + warn "No transport costs for $pickup_branch"; + } + } + + unless ($itemnumber) { + # not found yet, fall back to basics + if ($branches_to_use) { + $pull_branches = $branches_to_use; + } else { + $pull_branches = [keys %items_by_branch]; + } + PULL_BRANCHES: + foreach my $branch (@$pull_branches) { + my $holding_branch_items = $items_by_branch{$branch} + or next; + + $holdingbranch ||= $branch; + foreach my $item (@$holding_branch_items) { + next if $pickup_branch ne $item->{homebranch}; + + $itemnumber = $item->{itemnumber}; + $holdingbranch = $branch; + last PULL_BRANCHES; + } + } + $itemnumber ||= $items_by_branch{$holdingbranch}->[0]->{itemnumber} + if $holdingbranch; + } + + if ($itemnumber) { + my $holding_branch_items = $items_by_branch{$holdingbranch} + or die "Have $itemnumber, $holdingbranch, but no items!"; + @$holding_branch_items = grep { $_->{itemnumber} != $itemnumber } @$holding_branch_items; + delete $items_by_branch{$holdingbranch} unless @$holding_branch_items; + + $item_map{$itemnumber} = { + borrowernumber => $request->{borrowernumber}, + biblionumber => $request->{biblionumber}, + holdingbranch => $holdingbranch, + pickup_branch => $pickup_branch, + item_level => 0, + reservedate => $request->{reservedate}, + reservenotes => $request->{reservenotes}, + }; + $num_items_remaining--; + } + } + return \%item_map; +} + +=head2 CreatePickListFromItemMap + +=cut + +sub CreatePicklistFromItemMap { + my $item_map = shift; + + my $dbh = C4::Context->dbh; + + my $sth_load=$dbh->prepare(" + INSERT INTO tmp_holdsqueue (biblionumber,itemnumber,barcode,surname,firstname,phone,borrowernumber, + cardnumber,reservedate,title, itemcallnumber, + holdingbranch,pickbranch,notes, item_level_request) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) + "); + + foreach my $itemnumber (sort keys %$item_map) { + my $mapped_item = $item_map->{$itemnumber}; + my $biblionumber = $mapped_item->{biblionumber}; + my $borrowernumber = $mapped_item->{borrowernumber}; + my $pickbranch = $mapped_item->{pickup_branch}; + my $holdingbranch = $mapped_item->{holdingbranch}; + my $reservedate = $mapped_item->{reservedate}; + my $reservenotes = $mapped_item->{reservenotes}; + my $item_level = $mapped_item->{item_level}; + + my $item = GetItem($itemnumber); + my $barcode = $item->{barcode}; + my $itemcallnumber = $item->{itemcallnumber}; + + my $borrower = GetMember('borrowernumber'=>$borrowernumber); + my $cardnumber = $borrower->{'cardnumber'}; + my $surname = $borrower->{'surname'}; + my $firstname = $borrower->{'firstname'}; + my $phone = $borrower->{'phone'}; + + my $bib = GetBiblioData($biblionumber); + my $title = $bib->{title}; + + $sth_load->execute($biblionumber, $itemnumber, $barcode, $surname, $firstname, $phone, $borrowernumber, + $cardnumber, $reservedate, $title, $itemcallnumber, + $holdingbranch, $pickbranch, $reservenotes, $item_level); + } +} + +=head2 AddToHoldTargetMap + +=cut + +sub AddToHoldTargetMap { + my $item_map = shift; + + my $dbh = C4::Context->dbh; + + my $insert_sql = q( + INSERT INTO hold_fill_targets (borrowernumber, biblionumber, itemnumber, source_branchcode, item_level_request) + VALUES (?, ?, ?, ?, ?) + ); + my $sth_insert = $dbh->prepare($insert_sql); + + foreach my $itemnumber (keys %$item_map) { + my $mapped_item = $item_map->{$itemnumber}; + $sth_insert->execute($mapped_item->{borrowernumber}, $mapped_item->{biblionumber}, $itemnumber, + $mapped_item->{holdingbranch}, $mapped_item->{item_level}); + } +} + +# Helper functions, not part of any interface + +sub _trim { + return $_[0] unless $_[0]; + $_[0] =~ s/^\s+//; + $_[0] =~ s/\s+$//; + $_[0]; +} + +sub load_branches_to_pull_from { + my $static_branch_list = C4::Context->preference("StaticHoldsQueueWeight") + or return; + + my @branches_to_use = map _trim($_), split /,/, $static_branch_list; + + @branches_to_use = shuffle(@branches_to_use) if C4::Context->preference("RandomizeHoldsQueueWeight"); + + return \@branches_to_use; +} + +sub least_cost_branch { + + #$from - arrayref + my ($to, $from, $transport_cost_matrix) = @_; + +# Nothing really spectacular: supply to branch, a list of potential from branches +# and find the minimum from - to value from the transport_cost_matrix + return $from->[0] if @$from == 1; + + my ($least_cost, @branch); + foreach (@$from) { + my $cell = $transport_cost_matrix->{$to}{$_}; + next if $cell->{disable_transfer}; + + my $cost = $cell->{cost}; + next unless defined $cost; # XXX should this be reported? + + unless (defined $least_cost) { + $least_cost = $cost; + push @branch, $_; + next; + } + + next if $cost > $least_cost; + + if ($cost == $least_cost) { + push @branch, $_; + next; + } + + @branch = ($_); + $least_cost = $cost; + } + + return $branch[0]; + + # XXX return a random @branch with minimum cost instead of the first one; + # return $branch[0] if @branch == 1; +} + + +1; diff --git a/admin/systempreferences.pl b/admin/systempreferences.pl index f7a7d05..8b37919 100755 --- a/admin/systempreferences.pl +++ b/admin/systempreferences.pl @@ -203,6 +203,7 @@ $tabsysprefs{DisplayClearScreenButton} = "Circulation"; $tabsysprefs{AllowAllMessageDeletion} = "Circulation"; $tabsysprefs{OverdueNoticeBcc} = "Circulation"; $tabsysprefs{OverduesBlockCirc} = "Circulation"; +$tabsysprefs{UseTransportCostMatrix} = "Circulation"; # Staff Client diff --git a/admin/transport-cost-matrix.pl b/admin/transport-cost-matrix.pl new file mode 100755 index 0000000..725257c --- /dev/null +++ b/admin/transport-cost-matrix.pl @@ -0,0 +1,125 @@ +#!/usr/bin/perl +# Copyright 2000-2002 Katipo Communications +# copyright 2010 BibLibre +# +# 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 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; +use CGI; +use C4::Context; +use C4::Output; +use C4::Auth; +use C4::Koha; +use C4::Debug; +use C4::Branch; # GetBranches +use C4::HoldsQueue qw(TransportCostMatrix UpdateTransportCostMatrix); + +use Data::Dumper; + +my $input = new CGI; + +my ($template, $loggedinuser, $cookie) + = get_template_and_user({template_name => "admin/transport-cost-matrix.tmpl", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => {parameters => 1}, + debug => 1, + }); +my $use_transport_cost_matrix = C4::Context->preference("UseTransportCostMatrix"); + +my $update = $input->param('op') eq 'set-cost-matrix'; + +my ($cost_matrix, $have_matrix); +unless ($update) { + $cost_matrix = TransportCostMatrix(); + $have_matrix = keys %$cost_matrix if $cost_matrix; +} + +my $branches = GetBranches(); +my @branchloop = map { code => $_, + name => $branches->{$_}->{'branchname'} }, + sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname} } + keys %$branches; +my (@branchfromloop, @cost, @errors); +foreach my $branchfrom ( @branchloop ) { + my $fromcode = $branchfrom->{code}; + + my %from_row = ( code => $fromcode, name => $branchfrom->{name} ); + foreach my $branchto ( @branchloop ) { + my $tocode = $branchto->{code}; + + my %from_to_input_def = ( code => $tocode, name => $branchto->{name} ); + push @{ $from_row{branchtoloop} }, \%from_to_input_def; + + if ($fromcode eq $tocode) { + $from_to_input_def{skip} = 1; + next; + } + + (my $from_to = "${fromcode}_${tocode}") =~ s/\W//go; + $from_to_input_def{id} = $from_to; + my $input_name = "cost_$from_to"; + my $disable_name = "disable_$from_to"; + + if ($update) { + my $value = $from_to_input_def{value} = $input->param($input_name); + if ( $input->param($disable_name) ) { + $from_to_input_def{disabled} = 1; + } + else { + push @errors, "Invalid value for $from_row{name} -> $from_to_input_def{name}" + unless $value =~ /\d/o && $value >= 0.0; + } + } + else { + if ($have_matrix) { + if ( my $cell = $cost_matrix->{$tocode}{$fromcode} ) { + $from_to_input_def{value} = $cell->{cost}; + $from_to_input_def{disabled} = 1 if $cell->{disable_transfer}; + } + } else { + $from_to_input_def{disabled} = 1; + } + } + } + +# die Dumper(\%from_row); + push @branchfromloop, \%from_row; +} + +if ($update && !@errors) { + my @update_recs = map { + my $from = $_->{code}; + map { frombranch => $from, tobranch => $_->{code}, cost => $_->{value}, disable_transfer => $_->{disabled} || 0 }, + grep { $_->{code} ne $from } + @{ $_->{branchtoloop} }; + } @branchfromloop; + + UpdateTransportCostMatrix(\@update_recs); +} + +$template->param( + branchloop => \@branchloop, + branchfromloop => \@branchfromloop, + WARNING_transport_cost_matrix_off => !$use_transport_cost_matrix, + errors => \@errors, +); +output_html_with_http_headers $input, $cookie, $template->output; + +exit 0; + diff --git a/circ/view_holdsqueue.pl b/circ/view_holdsqueue.pl index 4b51423..cf529f0 100755 --- a/circ/view_holdsqueue.pl +++ b/circ/view_holdsqueue.pl @@ -31,7 +31,7 @@ use C4::Biblio; use C4::Items; use C4::Koha; # GetItemTypes use C4::Branch; # GetBranches -use C4::Dates qw/format_date/; +use C4::HoldsQueue qw(GetHoldsQueueItems); my $query = new CGI; my ( $template, $loggedinuser, $cookie ) = get_template_and_user( @@ -51,6 +51,7 @@ my $branchlimit = $params->{'branchlimit'}; my $itemtypeslimit = $params->{'itemtypeslimit'}; if ( $run_report ) { + # XXX GetHoldsQueueItems() does not support $itemtypeslimit! my $items = GetHoldsQueueItems($branchlimit, $itemtypeslimit); $template->param( branch => $branchlimit, @@ -76,36 +77,5 @@ $template->param( itemtypeloop => \@itemtypesloop, ); -sub GetHoldsQueueItems { - my ($branchlimit,$itemtypelimit) = @_; - my $dbh = C4::Context->dbh; - - my @bind_params = (); - my $query = q/SELECT tmp_holdsqueue.*, biblio.author, items.ccode, items.location, items.enumchron, items.cn_sort, biblioitems.publishercode,biblio.copyrightdate,biblioitems.publicationyear,biblioitems.pages,biblioitems.size,biblioitems.publicationyear,biblioitems.isbn,items.copynumber - FROM tmp_holdsqueue - JOIN biblio USING (biblionumber) - LEFT JOIN biblioitems USING (biblionumber) - LEFT JOIN items USING ( itemnumber) - /; - if ($branchlimit) { - $query .=" WHERE tmp_holdsqueue.holdingbranch = ?"; - push @bind_params, $branchlimit; - } - $query .= " ORDER BY ccode, location, cn_sort, author, title, pickbranch, reservedate"; - my $sth = $dbh->prepare($query); - $sth->execute(@bind_params); - my $items = []; - while ( my $row = $sth->fetchrow_hashref ){ - $row->{reservedate} = format_date($row->{reservedate}); - my $record = GetMarcBiblio($row->{biblionumber}); - if ($record){ - $row->{subtitle} = GetRecordValue('subtitle',$record,'')->[0]->{subfield}; - $row->{parts} = GetRecordValue('parts',$record,'')->[0]->{subfield}; - $row->{numbers} = GetRecordValue('numbers',$record,'')->[0]->{subfield}; - } - push @$items, $row; - } - return $items; -} # writing the template output_html_with_http_headers $query, $cookie, $template->output; diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index 69304e1..b620800 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -2808,6 +2808,22 @@ CREATE TABLE `fieldmapping` ( -- koha to keyword mapping ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- +-- Table structure for table `transport_cost` +-- + +DROP TABLE IF EXISTS transport_cost; +CREATE TABLE transport_cost ( + frombranch varchar(10) NOT NULL, + tobranch varchar(10) NOT NULL, + cost decimal(6,2) NOT NULL, + disable_transfer tinyint(1) NOT NULL DEFAULT 0, + CHECK ( frombranch <> tobranch ), -- a dud check, mysql does not support that + PRIMARY KEY (frombranch, tobranch), + CONSTRAINT transport_cost_ibfk_1 FOREIGN KEY (frombranch) REFERENCES branches (branchcode) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT transport_cost_ibfk_2 FOREIGN KEY (tobranch) REFERENCES branches (`branchcode`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- -- Table structure for table `biblioimages` -- diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index 430585a..d0bf8f0 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -317,6 +317,7 @@ INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('OpacHiddenItems','','This syspref allows to define custom rules for hiding specific items at opac. See docs/opac/OpacHiddenItems.txt for more informations.','','Textarea'); INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('numSearchRSSResults',50,'Specify the maximum number of results to display on a RSS page of results',NULL,'Integer'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('OpacRenewalBranch','checkoutbranch','Choose how the branch for an OPAC renewal is recorded in statistics','itemhomebranch|patronhomebranch|checkoutbranch|null','Choice'); +INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('UseTransportCostMatrix',0,"Use Transport Cost Matrix when filling holds",'','YesNo'); INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('BasketConfirmations', '1', 'When closing or reopening a basket,', 'always ask for confirmation.|do not ask for confirmation.', 'Choice'); INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES ('MARCAuthorityControlField008', '|| aca||aabn | a|a d', NULL, NULL, 'Textarea'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpenLibraryCovers',0,'If ON Openlibrary book covers will be show',NULL,'YesNo'); diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index c6b6063..88596fa 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -5684,6 +5684,28 @@ if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) { SetVersion($DBversion); } + + + +$DBversion = "3.09.00.XXX"; +if (C4::Context->preference("Version") < TransformToNum($DBversion)) { + $dbh->do("INSERT INTO `systempreferences` (variable,value,explanation,options,type) VALUES('UseTransportCostMatrix',0,'Use Transport Cost Matrix when filling holds','','YesNo')"); + + $dbh->do("CREATE TABLE `transport_cost` ( + `frombranch` varchar(10) NOT NULL, + `tobranch` varchar(10) NOT NULL, + `cost` decimal(6,2) NOT NULL, + `disable_transfer` tinyint(1) NOT NULL DEFAULT 0, + CHECK ( `frombranch` <> `tobranch` ), -- a dud check, mysql does not support that + PRIMARY KEY (`frombranch`, `tobranch`), + CONSTRAINT `transport_cost_ibfk_1` FOREIGN KEY (`frombranch`) REFERENCES `branches` (`branchcode`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `transport_cost_ibfk_2` FOREIGN KEY (`tobranch`) REFERENCES `branches` (`branchcode`) ON DELETE CASCADE ON UPDATE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8"); + + print "Upgrade to $DBversion done (creating `transport_cost` table; adding UseTransportCostMatrix systempref, in circulation)\n"; + SetVersion ($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt index 3906c46..6ceb6d7 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt @@ -50,6 +50,8 @@
Define extended attributes (identifiers and statistical categories) for patron records
Library transfer limits
Limit the ability to transfer items between libraries based on the library sending, the library receiving, and the item type involved. These rules only go into effect if the preference UseBranchTransferLimits is set to ON.
+
Transport Cost Matrix
+
Define transport costs between branches
Item circulation alerts
Define rules for check-in and checkout notifications for combinations of libraries, patron categories, and item types
Cities and towns
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref index a758b19..81b40b1 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -171,6 +171,12 @@ Circulation: itemtype: item type - . - + - pref: UseTransportCostMatrix + choices: + yes: Use + no: "Don't use" + - Transport Cost Matrix for calculating optimal holds filling between branches. + - - Use the checkout and fines rules of - pref: CircControl type: choice diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/transport-cost-matrix.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/transport-cost-matrix.tt new file mode 100644 index 0000000..8d12419 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/transport-cost-matrix.tt @@ -0,0 +1,131 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Administration › Transport Cost Matrix +[% INCLUDE 'doc-head-close.inc' %] + + + + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +
+ +
+
+
+

+ Defining transport costs between libraries +

+[% IF ( WARNING_transport_cost_matrix_off ) %] +
Because the "UseTransportCostMatrix" system preference is currently not enabled, Transport Cost Matrix is not being used. Go here if you wish to enable this feature.
+[% END %] + +
+
+ +
+
+

Costs are decimal values 0 to some arbitrarymax value (1 or 100), 0 being minimum (no) cost.

+

Red cells signify no transfer allowed

+

Click on the cell to edit

+
+
    + [% FOR e IN errors %] +
  • [% e %]
  • + [% END %] +
+ + + + [% FOR b IN branchloop %] + + [% END %] + + [% FOR bf IN branchfromloop %] + + + [% FOR bt IN bf.branchtoloop %] + + [% END %] + + [% END %] +
From \ To[% b.name %]
[% bf.name %] + [% IF bt.skip %] +   + [% ELSE %] + [% IF bt.disabled %] +
+ [% ELSE %] +
+ [% END %] +
[% bt.disabled ? ' ' : bt.value %]
+ + [% IF bt.disabled %] + + [% END %] +
+ [% END %] +
+
+ +
+
+
+
+
+[% INCLUDE 'admin-menu.inc' %] +
+
+[% INCLUDE 'intranet-bottom.inc' %] diff --git a/misc/cronjobs/holds/build_holds_queue.pl b/misc/cronjobs/holds/build_holds_queue.pl index d920c04..9ec9825 100755 --- a/misc/cronjobs/holds/build_holds_queue.pl +++ b/misc/cronjobs/holds/build_holds_queue.pl @@ -5,7 +5,6 @@ #----------------------------------- # FIXME: add command-line options for verbosity and summary # FIXME: expand perldoc, explain intended logic -# FIXME: refactor all subroutines into C4 for testability use strict; use warnings; @@ -16,387 +15,7 @@ BEGIN { eval { require "$FindBin::Bin/../kohalib.pl" }; } -use C4::Context; -use C4::Search; -use C4::Items; -use C4::Branch; -use C4::Circulation; -use C4::Members; -use C4::Biblio; +use C4::HoldsQueue qw(CreateQueue); -use List::Util qw(shuffle); +CreateQueue(); -my $bibs_with_pending_requests = GetBibsWithPendingHoldRequests(); - -my $dbh = C4::Context->dbh; -$dbh->do("DELETE FROM tmp_holdsqueue"); # clear the old table for new info -$dbh->do("DELETE FROM hold_fill_targets"); - -my $total_bibs = 0; -my $total_requests = 0; -my $total_available_items = 0; -my $num_items_mapped = 0; - -my @branches_to_use = _get_branches_to_pull_from(); - -foreach my $biblionumber (@$bibs_with_pending_requests) { - $total_bibs++; - my $hold_requests = GetPendingHoldRequestsForBib($biblionumber); - my $available_items = GetItemsAvailableToFillHoldRequestsForBib($biblionumber, @branches_to_use); - $total_requests += scalar(@$hold_requests); - $total_available_items += scalar(@$available_items); - my $item_map = MapItemsToHoldRequests($hold_requests, $available_items, @branches_to_use); - - (defined($item_map)) or next; - - my $item_map_size = scalar(keys %$item_map); - $num_items_mapped += $item_map_size; - CreatePicklistFromItemMap($item_map); - AddToHoldTargetMap($item_map); - if (($item_map_size < scalar(@$hold_requests )) and - ($item_map_size < scalar(@$available_items))) { - # DOUBLE CHECK, but this is probably OK - unfilled item-level requests - # FIXME - #warn "unfilled requests for $biblionumber"; - #warn Dumper($hold_requests), Dumper($available_items), Dumper($item_map); - } -} - -exit 0; - -=head1 FUNCTIONS - -=head2 GetBibsWithPendingHoldRequests - - my $biblionumber_aref = GetBibsWithPendingHoldRequests(); - -Return an arrayref of the biblionumbers of all bibs -that have one or more unfilled hold requests. - -=cut - -sub GetBibsWithPendingHoldRequests { - my $dbh = C4::Context->dbh; - - my $bib_query = "SELECT DISTINCT biblionumber - FROM reserves - WHERE found IS NULL - AND priority > 0 - AND reservedate <= CURRENT_DATE() - AND suspend = 0 - "; - my $sth = $dbh->prepare($bib_query); - - $sth->execute(); - my $biblionumbers = $sth->fetchall_arrayref(); - - return [ map { $_->[0] } @$biblionumbers ]; -} - -=head2 GetPendingHoldRequestsForBib - - my $requests = GetPendingHoldRequestsForBib($biblionumber); - -Returns an arrayref of hashrefs to pending, unfilled hold requests -on the bib identified by $biblionumber. The following keys -are present in each hashref: - - biblionumber - borrowernumber - itemnumber - priority - branchcode - reservedate - reservenotes - borrowerbranch - -The arrayref is sorted in order of increasing priority. - -=cut - -sub GetPendingHoldRequestsForBib { - my $biblionumber = shift; - - my $dbh = C4::Context->dbh; - - my $request_query = "SELECT biblionumber, borrowernumber, itemnumber, priority, reserves.branchcode, - reservedate, reservenotes, borrowers.branchcode AS borrowerbranch - FROM reserves - JOIN borrowers USING (borrowernumber) - WHERE biblionumber = ? - AND found IS NULL - AND priority > 0 - AND reservedate <= CURRENT_DATE() - AND suspend = 0 - ORDER BY priority"; - my $sth = $dbh->prepare($request_query); - $sth->execute($biblionumber); - - my $requests = $sth->fetchall_arrayref({}); - return $requests; - -} - -=head2 GetItemsAvailableToFillHoldRequestsForBib - - my $available_items = GetItemsAvailableToFillHoldRequestsForBib($biblionumber); - -Returns an arrayref of items available to fill hold requests -for the bib identified by C<$biblionumber>. An item is available -to fill a hold request if and only if: - - * it is not on loan - * it is not withdrawn - * it is not marked notforloan - * it is not currently in transit - * it is not lost - * it is not sitting on the hold shelf - -=cut - -sub GetItemsAvailableToFillHoldRequestsForBib { - my $biblionumber = shift; - my @branches_to_use = @_; - - my $dbh = C4::Context->dbh; - my $items_query = "SELECT itemnumber, homebranch, holdingbranch, itemtypes.itemtype AS itype - FROM items "; - - if (C4::Context->preference('item-level_itypes')) { - $items_query .= "LEFT JOIN itemtypes ON (itemtypes.itemtype = items.itype) "; - } else { - $items_query .= "JOIN biblioitems USING (biblioitemnumber) - LEFT JOIN itemtypes USING (itemtype) "; - } - $items_query .= "WHERE items.notforloan = 0 - AND holdingbranch IS NOT NULL - AND itemlost = 0 - AND wthdrawn = 0"; - $items_query .= " AND damaged = 0 " unless C4::Context->preference('AllowHoldsOnDamagedItems'); - $items_query .= " AND items.onloan IS NULL - AND (itemtypes.notforloan IS NULL OR itemtypes.notforloan = 0) - AND itemnumber NOT IN ( - SELECT itemnumber - FROM reserves - WHERE biblionumber = ? - AND itemnumber IS NOT NULL - AND (found IS NOT NULL OR priority = 0) - ) - AND items.biblionumber = ?"; - my @params = ($biblionumber, $biblionumber); - if ($#branches_to_use > -1) { - $items_query .= " AND holdingbranch IN (" . join (",", map { "?" } @branches_to_use) . ")"; - push @params, @branches_to_use; - } - my $sth = $dbh->prepare($items_query); - $sth->execute(@params); - - my $items = $sth->fetchall_arrayref({}); - $items = [ grep { my @transfers = GetTransfers($_->{itemnumber}); $#transfers == -1; } @$items ]; - map { my $rule = GetBranchItemRule($_->{homebranch}, $_->{itype}); $_->{holdallowed} = $rule->{holdallowed}; $rule->{holdallowed} != 0 } @$items; - return [ grep { $_->{holdallowed} != 0 } @$items ]; -} - -=head2 MapItemsToHoldRequests - - MapItemsToHoldRequests($hold_requests, $available_items); - -=cut - -sub MapItemsToHoldRequests { - my $hold_requests = shift; - my $available_items = shift; - my @branches_to_use = @_; - - # handle trival cases - return unless scalar(@$hold_requests) > 0; - return unless scalar(@$available_items) > 0; - - # identify item-level requests - my %specific_items_requested = map { $_->{itemnumber} => 1 } - grep { defined($_->{itemnumber}) } - @$hold_requests; - - # group available items by itemnumber - my %items_by_itemnumber = map { $_->{itemnumber} => $_ } @$available_items; - - # items already allocated - my %allocated_items = (); - - # map of items to hold requests - my %item_map = (); - - # figure out which item-level requests can be filled - my $num_items_remaining = scalar(@$available_items); - foreach my $request (@$hold_requests) { - last if $num_items_remaining == 0; - - # is this an item-level request? - if (defined($request->{itemnumber})) { - # fill it if possible; if not skip it - if (exists $items_by_itemnumber{$request->{itemnumber}} and - not exists $allocated_items{$request->{itemnumber}}) { - $item_map{$request->{itemnumber}} = { - borrowernumber => $request->{borrowernumber}, - biblionumber => $request->{biblionumber}, - holdingbranch => $items_by_itemnumber{$request->{itemnumber}}->{holdingbranch}, - pickup_branch => $request->{branchcode}, - item_level => 1, - reservedate => $request->{reservedate}, - reservenotes => $request->{reservenotes}, - }; - $allocated_items{$request->{itemnumber}}++; - $num_items_remaining--; - } - } else { - # it's title-level request that will take up one item - $num_items_remaining--; - } - } - - # group available items by branch - my %items_by_branch = (); - foreach my $item (@$available_items) { - push @{ $items_by_branch{ $item->{holdingbranch} } }, $item unless exists $allocated_items{ $item->{itemnumber} }; - } - - # now handle the title-level requests - $num_items_remaining = scalar(@$available_items) - scalar(keys %allocated_items); - foreach my $request (@$hold_requests) { - last if $num_items_remaining <= 0; - next if defined($request->{itemnumber}); # already handled these - - # look for local match first - my $pickup_branch = $request->{branchcode}; - if (exists $items_by_branch{$pickup_branch} and - not ($items_by_branch{$pickup_branch}->[0]->{holdallowed} == 1 and - $request->{borrowerbranch} ne $items_by_branch{$pickup_branch}->[0]->{homebranch}) - ) { - my $item = pop @{ $items_by_branch{$pickup_branch} }; - delete $items_by_branch{$pickup_branch} if scalar(@{ $items_by_branch{$pickup_branch} }) == 0; - $item_map{$item->{itemnumber}} = { - borrowernumber => $request->{borrowernumber}, - biblionumber => $request->{biblionumber}, - holdingbranch => $pickup_branch, - pickup_branch => $pickup_branch, - item_level => 0, - reservedate => $request->{reservedate}, - reservenotes => $request->{reservenotes}, - }; - $num_items_remaining--; - } else { - my @pull_branches = (); - if ($#branches_to_use > -1) { - @pull_branches = @branches_to_use; - } else { - @pull_branches = sort keys %items_by_branch; - } - foreach my $branch (@pull_branches) { - next unless exists $items_by_branch{$branch} and - not ($items_by_branch{$branch}->[0]->{holdallowed} == 1 and - $request->{borrowerbranch} ne $items_by_branch{$branch}->[0]->{homebranch}); - my $item = pop @{ $items_by_branch{$branch} }; - delete $items_by_branch{$branch} if scalar(@{ $items_by_branch{$branch} }) == 0; - $item_map{$item->{itemnumber}} = { - borrowernumber => $request->{borrowernumber}, - biblionumber => $request->{biblionumber}, - holdingbranch => $branch, - pickup_branch => $pickup_branch, - item_level => 0, - reservedate => $request->{reservedate}, - reservenotes => $request->{reservenotes}, - }; - $num_items_remaining--; - last; - } - } - } - return \%item_map; -} - -=head2 CreatePickListFromItemMap - -=cut - -sub CreatePicklistFromItemMap { - my $item_map = shift; - - my $dbh = C4::Context->dbh; - - my $sth_load=$dbh->prepare(" - INSERT INTO tmp_holdsqueue (biblionumber,itemnumber,barcode,surname,firstname,phone,borrowernumber, - cardnumber,reservedate,title, itemcallnumber, - holdingbranch,pickbranch,notes, item_level_request) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) - "); - - foreach my $itemnumber (sort keys %$item_map) { - my $mapped_item = $item_map->{$itemnumber}; - my $biblionumber = $mapped_item->{biblionumber}; - my $borrowernumber = $mapped_item->{borrowernumber}; - my $pickbranch = $mapped_item->{pickup_branch}; - my $holdingbranch = $mapped_item->{holdingbranch}; - my $reservedate = $mapped_item->{reservedate}; - my $reservenotes = $mapped_item->{reservenotes}; - my $item_level = $mapped_item->{item_level}; - - my $item = GetItem($itemnumber); - my $barcode = $item->{barcode}; - my $itemcallnumber = $item->{itemcallnumber}; - - my $borrower = GetMember('borrowernumber'=>$borrowernumber); - my $cardnumber = $borrower->{'cardnumber'}; - my $surname = $borrower->{'surname'}; - my $firstname = $borrower->{'firstname'}; - my $phone = $borrower->{'phone'}; - - my $bib = GetBiblioData($biblionumber); - my $title = $bib->{title}; - - $sth_load->execute($biblionumber, $itemnumber, $barcode, $surname, $firstname, $phone, $borrowernumber, - $cardnumber, $reservedate, $title, $itemcallnumber, - $holdingbranch, $pickbranch, $reservenotes, $item_level); - } -} - -=head2 AddToHoldTargetMap - -=cut - -sub AddToHoldTargetMap { - my $item_map = shift; - - my $dbh = C4::Context->dbh; - - my $insert_sql = q( - INSERT INTO hold_fill_targets (borrowernumber, biblionumber, itemnumber, source_branchcode, item_level_request) - VALUES (?, ?, ?, ?, ?) - ); - my $sth_insert = $dbh->prepare($insert_sql); - - foreach my $itemnumber (keys %$item_map) { - my $mapped_item = $item_map->{$itemnumber}; - $sth_insert->execute($mapped_item->{borrowernumber}, $mapped_item->{biblionumber}, $itemnumber, - $mapped_item->{holdingbranch}, $mapped_item->{item_level}); - } -} - -=head2 _get_branches_to_pull_from - -Query system preferences to get ordered list of -branches to use to fill hold requests. - -=cut - -sub _get_branches_to_pull_from { - my @branches_to_use = (); - - my $static_branch_list = C4::Context->preference("StaticHoldsQueueWeight"); - if ($static_branch_list) { - @branches_to_use = map { s/^\s+//; s/\s+$//; $_; } split /,/, $static_branch_list; - } - - @branches_to_use = shuffle(@branches_to_use) if C4::Context->preference("RandomizeHoldsQueueWeight"); - - return @branches_to_use; -} diff --git a/t/db_dependent/HoldsQueue.t b/t/db_dependent/HoldsQueue.t new file mode 100755 index 0000000..0198269 --- /dev/null +++ b/t/db_dependent/HoldsQueue.t @@ -0,0 +1,175 @@ +#!/usr/bin/perl + +# Test C4::HoldsQueue::CreateQueue() for both transport cost matrix +# and StaticHoldsQueueWeight array (no RandomizeHoldsQueueWeight, no point) +# Wraps tests in transaction that's rolled back, so no data is destroyed +# MySQL WARNING: This makes sense only if your tables are InnoDB, otherwise +# transactions are not supported and mess is left behind + +use strict; +use warnings; +use C4::Context; + +use Data::Dumper; + +use Test::More tests => 18; + +BEGIN { + use FindBin; + use lib $FindBin::Bin; + use_ok('C4::Reserves'); + use_ok('C4::HoldsQueue'); +} + +my $TITLE = "Test Holds Queue XXX"; +# Pick a plausible borrower. Easier than creating one. +my $BORROWER_QRY = <dbh; +my $borrower = $dbh->selectrow_hashref($BORROWER_QRY); +my $borrowernumber = $borrower->{borrowernumber}; +# Set special (for this test) branches +my $borrower_branchcode = $borrower->{branchcode}; +my @other_branches = grep { $_ ne $borrower_branchcode } @{ $dbh->selectcol_arrayref("SELECT branchcode FROM branches") }; +my $least_cost_branch_code = pop @other_branches + or BAIL_OUT("No point testing only one branch..."); +my $itemtype = $dbh->selectrow_array("SELECT min(itemtype) FROM itemtypes WHERE notforloan = 0") + or BAIL_OUT("No adequate itemtype"); + +# Start transaction +$dbh->{AutoCommit} = 0; +$dbh->{RaiseError} = 1; + +#Set up the stage +# Sysprefs and cost matrix +$dbh->do("UPDATE systempreferences SET value = ? WHERE variable = 'StaticHoldsQueueWeight'", undef, + join( ',', @other_branches, $borrower_branchcode, $least_cost_branch_code)); +$dbh->do("UPDATE systempreferences SET value = '0' WHERE variable = 'RandomizeHoldsQueueWeight'"); + +$dbh->do("DELETE FROM transport_cost"); +my $transport_cost_insert_sth = $dbh->prepare("insert into transport_cost (frombranch, tobranch, cost) values (?, ?, ?)"); +# Favour $least_cost_branch_code +$transport_cost_insert_sth->execute($borrower_branchcode, $least_cost_branch_code, 0.2); +$transport_cost_insert_sth->execute($least_cost_branch_code, $borrower_branchcode, 0.2); +my @b = @other_branches; +while ( my $b1 = shift @b ) { + foreach my $b2 ($borrower_branchcode, $least_cost_branch_code, @b) { + $transport_cost_insert_sth->execute($b1, $b2, 0.5); + $transport_cost_insert_sth->execute($b2, $b1, 0.5); + } +} + + +# Loanable items - all possible combinations of homebranch and holdingbranch +$dbh->do("INSERT INTO biblio (frameworkcode, author, title, datecreated) + VALUES ('SER', 'Koha test', '$TITLE', '2011-02-01')"); +my $biblionumber = $dbh->selectrow_array("SELECT biblionumber FROM biblio WHERE title = '$TITLE'") + or BAIL_OUT("Cannot find newly created biblio record"); +$dbh->do("INSERT INTO biblioitems (biblionumber, marcxml, itemtype) + VALUES ($biblionumber, '', '$itemtype')"); +my $biblioitemnumber = $dbh->selectrow_array("SELECT biblioitemnumber FROM biblioitems WHERE biblionumber = $biblionumber") + or BAIL_OUT("Cannot find newly created biblioitems record"); + +my $items_insert_sth = $dbh->prepare("INSERT INTO items (biblionumber, biblioitemnumber, barcode, homebranch, holdingbranch, notforloan, damaged, itemlost, wthdrawn, onloan, itype) + VALUES ($biblionumber, $biblioitemnumber, ?, ?, ?, 0, 0, 0, 0, NULL, '$itemtype')"); # CURRENT_DATE - 3)"); +my $first_barcode = int(rand(1000000000000)); # XXX +my $barcode = $first_barcode; +foreach ( $borrower_branchcode, $least_cost_branch_code, @other_branches ) { + $items_insert_sth->execute($barcode++, $borrower_branchcode, $_); + $items_insert_sth->execute($barcode++, $_, $_); + $items_insert_sth->execute($barcode++, $_, $borrower_branchcode); +} + +# Remove existing reserves, makes debugging easier +$dbh->do("DELETE FROM reserves"); +my $constraint = undef; +my $bibitems = undef; +my $priority = 1; +# Make a reserve +AddReserve ( $borrower_branchcode, $borrowernumber, $biblionumber, $constraint, $bibitems, $priority ); +# $resdate, $expdate, $notes, $title, $checkitem, $found +$dbh->do("UPDATE reserves SET reservedate = reservedate - 1"); + +# Tests +my $use_cost_matrix_sth = $dbh->prepare("UPDATE systempreferences SET value = ? WHERE variable = 'UseTransportCostMatrix'"); +my $test_sth = $dbh->prepare("SELECT * FROM hold_fill_targets + JOIN tmp_holdsqueue USING (borrowernumber, biblionumber, itemnumber) + JOIN items USING (itemnumber) + WHERE borrowernumber = $borrowernumber"); + +# We have a book available homed in borrower branch, no point fiddling with AutomaticItemReturn +test_queue ('take from homebranch', 0, $borrower_branchcode, $borrower_branchcode); +test_queue ('take from homebranch', 1, $borrower_branchcode, $borrower_branchcode); + +$dbh->do("DELETE FROM tmp_holdsqueue"); +$dbh->do("DELETE FROM hold_fill_targets"); +$dbh->do("DELETE FROM issues WHERE itemnumber IN (SELECT itemnumber FROM items WHERE homebranch = '$borrower_branchcode' AND holdingbranch = '$borrower_branchcode')"); +$dbh->do("DELETE FROM items WHERE homebranch = '$borrower_branchcode' AND holdingbranch = '$borrower_branchcode'"); +# test_queue will flush +$dbh->do("UPDATE systempreferences SET value = 1 WHERE variable = 'AutomaticItemReturn'"); +# Not sure how to make this test more difficult - holding branch does not matter +test_queue ('take from holdingbranch AutomaticItemReturn on', 0, $borrower_branchcode, undef); +test_queue ('take from holdingbranch AutomaticItemReturn on', 1, $borrower_branchcode, $least_cost_branch_code); + +$dbh->do("DELETE FROM tmp_holdsqueue"); +$dbh->do("DELETE FROM hold_fill_targets"); +$dbh->do("DELETE FROM issues WHERE itemnumber IN (SELECT itemnumber FROM items WHERE homebranch = '$borrower_branchcode')"); +$dbh->do("DELETE FROM items WHERE homebranch = '$borrower_branchcode'"); +$dbh->do("UPDATE systempreferences SET value = 0 WHERE variable = 'AutomaticItemReturn'"); +# We have a book available held in borrower branch +test_queue ('take from holdingbranch', 0, $borrower_branchcode, $borrower_branchcode); +test_queue ('take from holdingbranch', 1, $borrower_branchcode, $borrower_branchcode); + +$dbh->do("DELETE FROM tmp_holdsqueue"); +$dbh->do("DELETE FROM hold_fill_targets"); +$dbh->do("DELETE FROM issues WHERE itemnumber IN (SELECT itemnumber FROM items WHERE holdingbranch = '$borrower_branchcode')"); +$dbh->do("DELETE FROM items WHERE holdingbranch = '$borrower_branchcode'"); +# No book available in borrower branch, pick according to the rules +# Frst branch from StaticHoldsQueueWeight +test_queue ('take from lowest cost branch', 0, $borrower_branchcode, $other_branches[0]); +test_queue ('take from lowest cost branch', 1, $borrower_branchcode, $least_cost_branch_code); +my $queue = C4::HoldsQueue::GetHoldsQueueItems($least_cost_branch_code) || []; +my $queue_item = $queue->[0]; +ok( $queue_item + && $queue_item->{pickbranch} eq $borrower_branchcode + && $queue_item->{holdingbranch} eq $least_cost_branch_code, "GetHoldsQueueItems" ) + or diag( "Expected item for pick $borrower_branchcode, hold $least_cost_branch_code, got ".Dumper($queue_item) ); + +# XXX All this tests are for borrower branch pick-up. +# Maybe needs expanding to homebranch or holdingbranch pick-up. + +# Cleanup +$dbh->rollback; + +exit; + +sub test_queue { + my ($test_name, $use_cost_matrix, $pick_branch, $hold_branch) = @_; + + $test_name = "$test_name (".($use_cost_matrix ? "" : "don't ")."use cost matrix)"; + + $use_cost_matrix_sth->execute($use_cost_matrix); + C4::Context->clear_syspref_cache(); + C4::HoldsQueue::CreateQueue(); + + my $results = $dbh->selectall_arrayref($test_sth, { Slice => {} }); # should be only one + my $r = $results->[0]; + + my $ok = is( $r->{pickbranch}, $pick_branch, "$test_name pick up branch"); + $ok &&= is( $r->{holdingbranch}, $hold_branch, "$test_name holding branch") + if $hold_branch; + + diag( "Wrong pick-up/hold for first target (pick_branch, hold_branch, reserves, hold_fill_targets, tmp_holdsqueue): " + . Dumper ($pick_branch, $hold_branch, map dump_records($_), qw(reserves hold_fill_targets tmp_holdsqueue)) ) + unless $ok; +} + +sub dump_records { + my ($tablename) = @_; + return $dbh->selectall_arrayref("SELECT * from $tablename where borrowernumber = ?", { Slice => {} }, $borrowernumber); +} + + -- 1.7.9.5 From colin.campbell at ptfs-europe.com Wed Sep 5 14:12:23 2012 From: colin.campbell at ptfs-europe.com (Colin Campbell) Date: Wed, 5 Sep 2012 13:12:23 +0100 Subject: [Koha-patches] [PATCH] Bug 8251 Do not try to debar patrons if returns are not overdue Message-ID: <1346847143-28385-1-git-send-email-colin.campbell@ptfs-europe.com> If a period of suspension is configured in the issuing rules a calculation to debar the patron was called on all returns It should be limited to overdue returns Renamed _FixFineDaysOnReturn subroutine to _debar_user_on_return which is more descriptive of its purpose Removed some unnecessary or duplicated processing Changed visibility of $today so it didnt need calculating twice Removed declaration of a datedue variable that is never used --- C4/Circulation.pm | 89 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 8647943..a46dd1d 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -1579,8 +1579,8 @@ sub AddReturn { } # case of a return of document (deal with issues and holdingbranch) - if ($doreturn) { my $today = DateTime->now( time_zone => C4::Context->tz() ); + if ($doreturn) { my $datedue = $issue->{date_due}; $borrower or warn "AddReturn without current borrower"; my $circControlBranch; @@ -1590,7 +1590,6 @@ sub AddReturn { # FIXME: check issuedate > returndate, factoring in holidays #$circControlBranch = _GetCircControlBranch($item,$borrower) unless ( $item->{'issuedate'} eq C4::Dates->today('iso') );; $circControlBranch = _GetCircControlBranch($item,$borrower); - my $datedue = $issue->{date_due}; $issue->{'overdue'} = DateTime->compare($issue->{'date_due'}, $today ) == -1 ? 1 : 0; } @@ -1654,9 +1653,12 @@ sub AddReturn { my $fix = _FixOverduesOnReturn($borrowernumber, $item->{itemnumber}, $exemptfine, $dropbox); defined($fix) or warn "_FixOverduesOnReturn($borrowernumber, $item->{itemnumber}...) failed!"; # zero is OK, check defined - # fix fine days - my $debardate = _FixFineDaysOnReturn( $borrower, $item, $issue->{date_due} ); - $messages->{'Debarred'} = $debardate if ($debardate); + if ( $issue->{overdue} && $issue->{date_due} ) { +# fix fine days + my $debardate = + _debar_user_on_return( $borrower, $item, $issue->{date_due}, $today ); + $messages->{Debarred} = $debardate if ($debardate); + } } # find reserves..... @@ -1781,26 +1783,27 @@ sub MarkIssueReturned { $sth_del->execute($borrowernumber, $itemnumber); } -=head2 _FixFineDaysOnReturn +=head2 _debar_user_on_return - &_FixFineDaysOnReturn($borrower, $item, $datedue); + _debar_user_on_return($borrower, $item, $datedue, today); C<$borrower> borrower hashref C<$item> item hashref -C<$datedue> date due +C<$datedue> date due DateTime object -Internal function, called only by AddReturn that calculate and update the user fine days, and debars him +C<$today> DateTime object representing the return time + +Internal function, called only by AddReturn that calculates and updates + the user fine days, and debars him if necessary. + +Should only be called for overdue returns =cut -sub _FixFineDaysOnReturn { - my ( $borrower, $item, $datedue ) = @_; - return unless ($datedue); - - my $dt_due = dt_from_string( $datedue ); - my $dt_today = DateTime->now( time_zone => C4::Context->tz() ); +sub _debar_user_on_return { + my ( $borrower, $item, $dt_due, $dt_today ) = @_; my $branchcode = _GetCircControlBranch( $item, $borrower ); my $calendar = Koha::Calendar->new( branchcode => $branchcode ); @@ -1809,35 +1812,41 @@ sub _FixFineDaysOnReturn { my $deltadays = $calendar->days_between( $dt_due, $dt_today ); my $circcontrol = C4::Context::preference('CircControl'); - my $issuingrule = GetIssuingRule( $borrower->{categorycode}, $item->{itype}, $branchcode ); - my $finedays = $issuingrule->{finedays}; - my $unit = $issuingrule->{lengthunit}; - - # exit if no finedays defined - return unless $finedays; - # finedays is in days, so hourly loans must multiply by 24 - # thus 1 hour late equals 1 day suspension * finedays rate - $finedays = $finedays * 24 if ($unit eq 'hours'); - - # grace period is measured in the same units as the loan - my $grace = DateTime::Duration->new( $unit => $issuingrule->{firstremind} ); - - if ( ( $deltadays - $grace )->is_positive ) { # you can't compare DateTime::Durations with logical operators - my $new_debar_dt = $dt_today->clone()->add_duration( $deltadays * $finedays ); - my $borrower_debar_dt = dt_from_string( $borrower->{debarred} ); - # check to see if the current debar date is a valid date - if ( $borrower->{debarred} && $borrower_debar_dt ) { - # if so, is it before the new date? update only if true - if ( DateTime->compare( $borrower_debar_dt, $new_debar_dt ) == -1 ) { - C4::Members::DebarMember( $borrower->{borrowernumber}, $new_debar_dt->ymd() ); - return $new_debar_dt->ymd(); + my $issuingrule = + GetIssuingRule( $borrower->{categorycode}, $item->{itype}, $branchcode ); + my $finedays = $issuingrule->{finedays}; + my $unit = $issuingrule->{lengthunit}; + + if ($finedays) { + + # finedays is in days, so hourly loans must multiply by 24 + # thus 1 hour late equals 1 day suspension * finedays rate + $finedays = $finedays * 24 if ( $unit eq 'hours' ); + + # grace period is measured in the same units as the loan + my $grace = + DateTime::Duration->new( $unit => $issuingrule->{firstremind} ); + if ( $deltadays->subtract($grace)->is_positive() ) { + + my $new_debar_dt = + $dt_today->clone()->add_duration( $deltadays * $finedays ); + if ( $borrower->{debarred} ) { + my $borrower_debar_dt = dt_from_string( $borrower->{debarred} ); + + # Update patron only if new date > old + if ( DateTime->compare( $borrower_debar_dt, $new_debar_dt ) != + -1 ) + { + return; + } + } - # if the borrower's debar date is not set or valid, debar them - } else { - C4::Members::DebarMember( $borrower->{borrowernumber}, $new_debar_dt->ymd() ); + C4::Members::DebarMember( $borrower->{borrowernumber}, + $new_debar_dt->ymd() ); return $new_debar_dt->ymd(); } } + return; } =head2 _FixOverduesOnReturn -- 1.7.12.146.g16d26b1 From colin.campbell at ptfs-europe.com Wed Sep 5 15:11:39 2012 From: colin.campbell at ptfs-europe.com (Colin Campbell) Date: Wed, 5 Sep 2012 14:11:39 +0100 Subject: [Koha-patches] [PATCH] Bug 8727 Minor stylistic change to help text Message-ID: <1346850699-28859-1-git-send-email-colin.campbell@ptfs-europe.com> indexing not indexation some minor grammatical changes --- misc/migration_tools/rebuild_zebra_sliced.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misc/migration_tools/rebuild_zebra_sliced.sh b/misc/migration_tools/rebuild_zebra_sliced.sh index f1b73f9..3874d08 100755 --- a/misc/migration_tools/rebuild_zebra_sliced.sh +++ b/misc/migration_tools/rebuild_zebra_sliced.sh @@ -5,9 +5,9 @@ usage() { cat < In order to be valid XHTML ampersands must be encoded ("&"). --- .../intranet-tmpl/prog/en/modules/acqui/parcel.tt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcel.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcel.tt index 1b6543a..23a7866 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcel.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcel.tt @@ -392,7 +392,7 @@ - Clear + Clear -- 1.7.9.5 From matted-34813 at mypacks.net Wed Sep 5 18:48:37 2012 From: matted-34813 at mypacks.net (matted-34813 at mypacks.net) Date: Wed, 5 Sep 2012 11:48:37 -0500 (GMT-05:00) Subject: [Koha-patches] adjust-Reserves.t-test-for-resdate-and-expdate-and-t.patch Message-ID: <1618134.1346863717545.JavaMail.root@wamui-hunyo.atl.sa.earthlink.net> This patch adds the resdate and expdate argument to the AddReserve test. I also adjusted it to create a bilio, and item for that biblio and delete them at the end of the test, so its not dependent on external test data. It gets rid of Illegal metric data warning from misaligned arguments. TestPlan: koha at biblio:~/kohaclone$ prove t/db_dependent/Reserves.t t/db_dependent/Reserves.t .. 1/4 # # Creating biblio instance for testing. # Creating item instance for testing. # Deleting item testing instance. # Deleting biblio testing instance. t/db_dependent/Reserves.t .. ok All tests successful. Files=1, Tests=4, 2 wallclock secs ( 0.03 usr 0.01 sys + 0.59 cusr 0.04 csys = 0.67 CPU) Result: PASS From matted-34813 at mypacks.net Wed Sep 5 18:35:42 2012 From: matted-34813 at mypacks.net (wajasu) Date: Wed, 5 Sep 2012 11:35:42 -0500 Subject: [PATCH] adjust Reserves.t test for resdate and expdate and test setup/teardown Message-ID: --- t/db_dependent/Reserves.t | 59 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/t/db_dependent/Reserves.t b/t/db_dependent/Reserves.t index ca4d42e..d3428b3 100755 --- a/t/db_dependent/Reserves.t +++ b/t/db_dependent/Reserves.t @@ -5,6 +5,9 @@ use warnings; use C4::Branch; use Test::More tests => 4; +use MARC::Record; +use C4::Biblio; +use C4::Items; BEGIN { use FindBin; @@ -12,6 +15,20 @@ BEGIN { use_ok('C4::Reserves'); } +# Setup Test------------------------ +# Helper biblio. +diag("\nCreating biblio instance for testing."); +my ($bibnum, $title, $bibitemnum) = create_helper_biblio(); + +# Helper item for that biblio. +diag("Creating item instance for testing."); +my ($item_bibnum, $item_bibitemnum, $itemnumber) = AddItem({ homebranch => 'CPL', holdingbranch => 'CPL' } , $bibnum); + +# Modify item; setting barcode. +my $testbarcode = '97531'; +ModItem({ barcode => $testbarcode }, $bibnum, $itemnumber); + +# Get a borrower my $dbh = C4::Context->dbh; my $query = qq/SELECT borrowernumber FROM borrowers @@ -20,27 +37,16 @@ my $sth = $dbh->prepare($query); $sth->execute; my $borrower = $sth->fetchrow_hashref; -$query = qq/SELECT biblionumber, title, itemnumber, barcode - FROM biblio - LEFT JOIN items USING (biblionumber) - WHERE barcode <> "" - AND barcode IS NOT NULL - LIMIT 1/; -$sth = $dbh->prepare($query); -$sth->execute; -my $biblio = $sth->fetchrow_hashref; - - my $borrowernumber = $borrower->{'borrowernumber'}; -my $biblionumber = $biblio->{'biblionumber'}; -my $itemnumber = $biblio->{'itemnumber'}; -my $barcode = $biblio->{'barcode'}; +my $biblionumber = $bibnum; +my $barcode = $testbarcode; my $constraint = 'a'; my $bibitems = ''; my $priority = '1'; +my $resdate = undef; +my $expdate = undef; my $notes = ''; -my $title = $biblio->{'title'}; my $checkitem = undef; my $found = undef; @@ -48,7 +54,7 @@ my @branches = GetBranchesLoop(); my $branch = $branches[0][0]{value}; AddReserve($branch, $borrowernumber, $biblionumber, - $constraint, $bibitems, $priority, $notes, + $constraint, $bibitems, $priority, $resdate, $expdate, $notes, $title, $checkitem, $found); my ($status, $reserve, $all_reserves) = CheckReserves($itemnumber, $barcode); @@ -60,3 +66,24 @@ ok($status eq "Reserved", "CheckReserves Test 2"); ($status, $reserve, $all_reserves) = CheckReserves(undef, $barcode); ok($status eq "Reserved", "CheckReserves Test 3"); + +# Teardown Test--------------------- +# Delete item. +diag("Deleting item testing instance."); +DelItem($dbh, $bibnum, $itemnumber); + +# Delete helper Biblio. +diag("Deleting biblio testing instance."); +DelBiblio($bibnum); + +# Helper method to set up a Biblio. +sub create_helper_biblio { + my $bib = MARC::Record->new(); + my $title = 'Silence in the library'; + $bib->append_fields( + MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'), + MARC::Field->new('245', ' ', ' ', a => $title), + ); + return ($bibnum, $title, $bibitemnum) = AddBiblio($bib, ''); +} + -- 1.7.11.4 ------=_Part_2868_5036580.1346863717269-- From nengard at bywatersolutions.com Wed Sep 5 21:14:49 2012 From: nengard at bywatersolutions.com (Nicole C. Engard) Date: Wed, 5 Sep 2012 15:14:49 -0400 Subject: [Koha-patches] [PATCH] Bug 6716: Update borrowers tables altaddress definiton Message-ID: <1346872489-2192-1-git-send-email-nengard@bywatersolutions.com> The comment on the altcontactaddress3 said it was the 3rd address line but in reality it was the city. This fixes the documentation of that field --- installer/data/mysql/kohastructure.sql | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index 91d42a2..e1ef469 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -256,7 +256,7 @@ CREATE TABLE `borrowers` ( -- this table includes information about your patrons `altcontactsurname` varchar(255) default NULL, -- surname or last name of the alternate contact for the patron/borrower `altcontactaddress1` varchar(255) default NULL, -- the first address line for the alternate contact for the patron/borrower `altcontactaddress2` varchar(255) default NULL, -- the second address line for the alternate contact for the patron/borrower - `altcontactaddress3` varchar(255) default NULL, -- the third address line for the alternate contact for the patron/borrower + `altcontactaddress3` varchar(255) default NULL, -- the city for the alternate contact for the patron/borrower `altcontactstate` text default NULL, -- the city and state for the alternate contact for the patron/borrower `altcontactzipcode` varchar(50) default NULL, -- the zipcode for the alternate contact for the patron/borrower `altcontactcountry` text default NULL, -- the country for the alternate contact for the patron/borrower @@ -727,7 +727,7 @@ CREATE TABLE `deletedborrowers` ( -- stores data related to the patrons/borrower `altcontactsurname` varchar(255) default NULL, -- surname or last name of the alternate contact for the patron/borrower `altcontactaddress1` varchar(255) default NULL, -- the first address line for the alternate contact for the patron/borrower `altcontactaddress2` varchar(255) default NULL, -- the second address line for the alternate contact for the patron/borrower - `altcontactaddress3` varchar(255) default NULL, -- the third address line for the alternate contact for the patron/borrower + `altcontactaddress3` varchar(255) default NULL, -- the city for the alternate contact for the patron/borrower `altcontactstate` text default NULL, -- the city and state for the alternate contact for the patron/borrower `altcontactzipcode` varchar(50) default NULL, -- the zipcode for the alternate contact for the patron/borrower `altcontactcountry` text default NULL, -- the country for the alternate contact for the patron/borrower -- 1.7.2.3 From srdjan at catalyst.net.nz Thu Sep 6 01:53:21 2012 From: srdjan at catalyst.net.nz (Srdjan) Date: Thu, 6 Sep 2012 11:53:21 +1200 Subject: [Koha-patches] [PATCH] bug_8034: Restored network printer maintenance and selection In-Reply-To: References: Message-ID: <1346889201-6733-1-git-send-email-srdjan@catalyst.net.nz> This patch is just for restoring printer maintenance and selection, not for priting itself. It is just a preparation step. * Added UsePrintQueues syspref. If set to No, no printer info will be displayed/used * Database changes: - printers table PRIMARY KEY is now printqueue. It is more natural. We should really have a synthetic id, but printqueue is good enough - branches.branchprinter is a FOREIGN KEY to printers.printqueue * Created C4::Auth::get_user_printer() function that will return appropriate printer. In order of preference: - session selected - logged in branch branchprinter * Moved printer functions to C4::Printer * Restored printer maint/selection in admin zone UsePrintQueues permitting * Restored printer selection in circ/selectbranchprinter.pl UsePrintQueues permitting Signed-off-by: Jared Camins-Esakov --- C4/Auth.pm | 49 +++++-- C4/Context.pm | 2 +- C4/Koha.pm | 40 ------ C4/Printer.pm | 152 ++++++++++++++++++++ admin/branches.pl | 30 ++-- admin/printers.pl | 39 ++--- circ/circulation.pl | 12 +- circ/returns.pl | 8 -- circ/selectbranchprinter.pl | 49 ++++--- installer/data/mysql/kohastructure.sql | 16 ++- installer/data/mysql/sysprefs.sql | 1 + installer/data/mysql/updatedatabase.pl | 12 ++ .../intranet-tmpl/prog/en/includes/header.inc | 3 + .../prog/en/modules/admin/admin-home.tt | 4 +- .../prog/en/modules/admin/branches.tt | 20 +-- .../en/modules/admin/preferences/circulation.pref | 6 + .../prog/en/modules/admin/printers.tt | 13 +- .../prog/en/modules/circ/circulation.tt | 2 - .../prog/en/modules/circ/selectbranchprinter.tt | 5 +- t/db_dependent/lib/KohaTest/Koha.pm | 2 - t/db_dependent/lib/KohaTest/Printer.pm | 26 ++++ 21 files changed, 330 insertions(+), 161 deletions(-) create mode 100644 C4/Printer.pm create mode 100644 t/db_dependent/lib/KohaTest/Printer.pm diff --git a/C4/Auth.pm b/C4/Auth.pm index 59c9955..71abfb3 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -28,6 +28,7 @@ require Exporter; use C4::Context; use C4::Templates; # to get the template use C4::Branch; # GetBranches +use C4::Printer qw(GetPrinterDetails); use C4::VirtualShelves; use POSIX qw/strftime/; use List::MoreUtils qw/ any /; @@ -46,7 +47,8 @@ BEGIN { $debug = $ENV{DEBUG}; @ISA = qw(Exporter); @EXPORT = qw(&checkauth &get_template_and_user &haspermission &get_user_subpermissions); - @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions); + @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw + &get_all_subpermissions &get_user_subpermissions &get_user_printer); %EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] ); $ldap = C4::Context->config('useldapserver') || 0; $cas = C4::Context->preference('casAuthentication'); @@ -310,6 +312,9 @@ sub get_template_and_user { $template->param(dateformat_iso => 1); } + my $userenv = C4::Context->userenv; + my $userenv_branch = $userenv ? $userenv->{"branch"} : undef; + # these template parameters are set the same regardless of $in->{'type'} $template->param( "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1, @@ -317,9 +322,9 @@ sub get_template_and_user { GoogleJackets => C4::Context->preference("GoogleJackets"), OpenLibraryCovers => C4::Context->preference("OpenLibraryCovers"), KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"), - LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"), - LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"), - LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", + LoginBranchcode => ($userenv?$userenv_branch:"insecure"), + LoginFirstname => ($userenv?$userenv->{"firstname"}:"Bel"), + LoginSurname => $userenv?$userenv->{"surname"}:"Inconnu", TagsEnabled => C4::Context->preference("TagsEnabled"), hide_marc => C4::Context->preference("hide_marc"), item_level_itypes => C4::Context->preference('item-level_itypes'), @@ -344,7 +349,7 @@ sub get_template_and_user { IntranetNav => C4::Context->preference("IntranetNav"), IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"), LibraryName => C4::Context->preference("LibraryName"), - LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"), + LoginBranchname => ($userenv?$userenv->{"branchname"}:"insecure"), advancedMARCEditor => C4::Context->preference("advancedMARCEditor"), canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'), intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"), @@ -364,6 +369,14 @@ sub get_template_and_user { AllowMultipleCovers => C4::Context->preference('AllowMultipleCovers'), EnableBorrowerFiles => C4::Context->preference('EnableBorrowerFiles'), ); + if ( C4::Context->preference('UsePrintQueues') ) { + my $printer = get_user_printer(); + my $printer_rec = $printer ? GetPrinterDetails($printer) : {}; + $template->param( + UsePrintQueues => 1, + PrinterName => $printer_rec->{printername}, + ); + } } else { warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' ); @@ -382,8 +395,8 @@ sub get_template_and_user { my $opac_name = ''; if (($opac_search_limit =~ /branch:(\w+)/ && $opac_limit_override) || $in->{'query'}->param('limit') =~ /branch:(\w+)/){ $opac_name = $1; # opac_search_limit is a branch, so we use it. - } elsif (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv && C4::Context->userenv->{'branch'}) { - $opac_name = C4::Context->userenv->{'branch'}; + } elsif (C4::Context->preference("SearchMyLibraryFirst") && $userenv_branch) { + $opac_name = $userenv_branch } $template->param( opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"), @@ -393,7 +406,7 @@ sub get_template_and_user { CalendarFirstDayOfWeek => (C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday")?0:1, LibraryName => "" . C4::Context->preference("LibraryName"), LibraryNameTitle => "" . $LibraryNameTitle, - LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"", + LoginBranchname => $userenv?$userenv->{"branchname"}:"", OPACAmazonCoverImages => C4::Context->preference("OPACAmazonCoverImages"), OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"), OpacHighlightedWords => C4::Context->preference("OpacHighlightedWords"), @@ -424,7 +437,7 @@ sub get_template_and_user { RequestOnOpac => C4::Context->preference("RequestOnOpac"), 'Version' => C4::Context->preference('Version'), hidelostitems => C4::Context->preference("hidelostitems"), - mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '', + mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && $userenv) ? $userenv_branch : '', opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"), opacbookbag => "" . C4::Context->preference("opacbookbag"), opaccredits => "" . C4::Context->preference("opaccredits"), @@ -1644,8 +1657,24 @@ sub getborrowernumber { return 0; } +=head2 get_user_printer + + $printer = get_user_printer(); + + Returns printer queue that is to be used for the logged in user + +=cut + +sub get_user_printer { + my $userenv = C4::Context->userenv or return; + if (my $printer = $userenv->{branchprinter}) { + return $printer; + } + my $branchname = $userenv->{branch} or return; + my $branch = GetBranchDetail($branchname) or return; + return $branch->{branchprinter}; +} -END { } # module clean-up code here (global destructor) 1; __END__ diff --git a/C4/Context.pm b/C4/Context.pm index 9ce6c74..717863f 100644 --- a/C4/Context.pm +++ b/C4/Context.pm @@ -998,7 +998,7 @@ sub userenv { =head2 set_userenv C4::Context->set_userenv($usernum, $userid, $usercnum, $userfirstname, - $usersurname, $userbranch, $userflags, $emailaddress); + $usersurname, $userbranch, $userflags, $emailaddress, $branchprinter); Establish a hash of user environment variables. diff --git a/C4/Koha.pm b/C4/Koha.pm index 26106cf..1e1a412 100644 --- a/C4/Koha.pm +++ b/C4/Koha.pm @@ -39,7 +39,6 @@ BEGIN { @EXPORT = qw( &slashifyDate &subfield_is_koha_internal_p - &GetPrinters &GetPrinter &GetItemTypes &getitemtypeinfo &GetCcodes &GetSupportName &GetSupportList @@ -600,45 +599,6 @@ sub getImageSets { return \@imagesets; } -=head2 GetPrinters - - $printers = &GetPrinters(); - @queues = keys %$printers; - -Returns information about existing printer queues. - -C<$printers> is a reference-to-hash whose keys are the print queues -defined in the printers table of the Koha database. The values are -references-to-hash, whose keys are the fields in the printers table. - -=cut - -sub GetPrinters { - my %printers; - my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare("select * from printers"); - $sth->execute; - while ( my $printer = $sth->fetchrow_hashref ) { - $printers{ $printer->{'printqueue'} } = $printer; - } - return ( \%printers ); -} - -=head2 GetPrinter - - $printer = GetPrinter( $query, $printers ); - -=cut - -sub GetPrinter ($$) { - my ( $query, $printers ) = @_; # get printer for this query from printers - my $printer = $query->param('printer'); - my %cookie = $query->cookie('userenv'); - ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' ); - ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] ); - return $printer; -} - =head2 getnbpages Returns the number of pages to display in a pagination bar, given the number diff --git a/C4/Printer.pm b/C4/Printer.pm new file mode 100644 index 0000000..fc4d326 --- /dev/null +++ b/C4/Printer.pm @@ -0,0 +1,152 @@ +#!/usr/bin/perl + +package C4::Printer; + +# Copyright 2012 Catalyst IT +# +# 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 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; + +use C4::Context; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +BEGIN { + $VERSION = 3.07.00.049; + require Exporter; + @ISA = qw(Exporter); + @EXPORT = qw( + ); + @EXPORT_OK = qw( + &GetPrinters &SearchPrinters &GetPrinterDetails + &AddPrinter &UpdatePrinter &DeletePrinter + ); +} + +=head1 NAME + +C4::Printer - functions that deal with printer selection + +=head1 SYNOPSIS + + use C4::Printer; + +=head1 DESCRIPTION + +This module provides functions to select printer for slips etc. + +TODO: Move SQL from admin/printers.pl to this module + +=head1 FUNCTIONS + +=head2 GetPrinters + + $printers = &GetPrinters(); + @queues = keys %$printers; + +Returns information about existing printer queues. + +C<$printers> is a reference-to-hash whose keys are the print queues +defined in the printers table of the Koha database. The values are +references-to-hash, whose keys are the fields in the printers table. + +=cut + +sub GetPrinters { + my %printers; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("select * from printers"); + $sth->execute; + while ( my $printer = $sth->fetchrow_hashref ) { + $printers{ $printer->{'printqueue'} } = $printer; + } + return ( \%printers ); +} + +=head2 SearchPrinters + + $printers = SearchPrinters( $searchstring ); + +=cut + +sub SearchPrinters { + my ($searchstring)=@_; + $searchstring=~ s/\'/\\\'/g; + my @data=split(' ',$searchstring); + my $sth = C4::Context->dbh->prepare(" + SELECT * FROM printers + WHERE (printername like ?) ORDER BY printername + "); + $sth->execute("$data[0]%"); + return $sth->fetchall_arrayref({}); +} + + +=head2 GetPrinterDetails + + $printer_rec = GetPrinterDetails( $printqueue ); + +=cut + +sub GetPrinterDetails { + my ( $printer ) = @_; + my $dbh = C4::Context->dbh; + my $printername = $dbh->selectrow_hashref('SELECT * FROM printers WHERE printqueue = ?', undef, $printer); + return $printername; +} + +=head2 AddPrinter + + AddPrinter( $data ); + +=cut + +sub AddPrinter { + my ( $data ) = @_; + my $dbh = C4::Context->dbh; + $dbh->do("INSERT INTO printers (printername,printqueue,printtype) VALUES (?,?,?)", undef, + $data->{printername}, $data->{printqueue}, $data->{printtype}); +} + +=head2 UpdatePrinter + + UpdatePrinter( $printqueue, $data ); + +=cut + +sub UpdatePrinter { + my ( $printqueue, $data ) = @_; + my $dbh = C4::Context->dbh; + $dbh->do("UPDATE printers SET printername = ?, printtype = ? WHERE printqueue = ?", undef, + $data->{printername}, $data->{printtype}, $printqueue); +} + +=head2 DeletePrinter + + DeletePrinter( $printqueue ); + +=cut + +sub DeletePrinter { + my ( $printqueue ) = @_; + my $dbh = C4::Context->dbh; + $dbh->do("DELETE FROM printers WHERE printqueue = ?", undef, + $printqueue); +} + +1; diff --git a/admin/branches.pl b/admin/branches.pl index d172ea6..711fa0a 100755 --- a/admin/branches.pl +++ b/admin/branches.pl @@ -45,6 +45,7 @@ use C4::Context; use C4::Output; use C4::Koha; use C4::Branch; +use C4::Printer qw(GetPrinters); # Fixed variables my $script_name = "/cgi-bin/koha/admin/branches.pl"; @@ -226,12 +227,9 @@ sub default { sub editbranchform { my ($branchcode,$innertemplate) = @_; - # initiate the scrolling-list to select the printers - my $printers = GetPrinters(); - my @printerloop; + my $data; my $oldprinter = ""; - if ($branchcode) { $data = GetBranchInfo($branchcode); $data = $data->[0]; @@ -241,15 +239,21 @@ sub editbranchform { _branch_to_template($data, $innertemplate); } - foreach my $thisprinter ( keys %$printers ) { - push @printerloop, { - value => $thisprinter, - selected => ( $oldprinter eq $printers->{$thisprinter} ), - branchprinter => $printers->{$thisprinter}->{'printqueue'}, - }; + if ( C4::Context->preference('UsePrintQueues') ) { + # initiate the scrolling-list to select the printers + my $printers = GetPrinters(); + my @printerloop; + foreach my $thisprinter ( keys %$printers ) { + push @printerloop, { + value => $thisprinter, + selected => ( $oldprinter eq $printers->{$thisprinter} ), + branchprinter => $printers->{$thisprinter}->{'printername'}, + }; + } + + $innertemplate->param( printerloop => \@printerloop ); } - $innertemplate->param( printerloop => \@printerloop ); # make the checkboxes..... # # We export a "categoryloop" array to the template, each element of which @@ -308,6 +312,7 @@ sub branchinfotable { my ($branchcode,$innertemplate) = @_; my $branchinfo = $branchcode ? GetBranchInfo($branchcode) : GetBranchInfo(); + my $printers = GetPrinters(); my @loop_data = (); foreach my $branch (@$branchinfo) { # @@ -367,6 +372,9 @@ sub branchinfotable { $row{'branch_name'} = $branch->{'branchname'}; $row{'branch_code'} = $branch->{'branchcode'}; $row{'value'} = $branch->{'branchcode'}; + if (my $printer = $branch->{'branchprinter'}) { + $row{'branchprintername'} = $printers->{$printer}->{'printername'}; + } push @loop_data, \%row; } diff --git a/admin/printers.pl b/admin/printers.pl index c7e7492..1cce350 100755 --- a/admin/printers.pl +++ b/admin/printers.pl @@ -43,19 +43,7 @@ use CGI; use C4::Context; use C4::Output; use C4::Auth; - -sub StringSearch { - my ($searchstring,$type)=@_; # why bother with $type if we don't use it?! - $searchstring=~ s/\'/\\\'/g; - my @data=split(' ',$searchstring); - my $sth = C4::Context->dbh->prepare(" - SELECT printername,printqueue,printtype from printers - WHERE (printername like ?) order by printername - "); - $sth->execute("$data[0]%"); - my $data=$sth->fetchall_arrayref({}); - return (scalar(@$data),$data); -} +use C4::Printer qw(GetPrinterDetails SearchPrinters AddPrinter UpdatePrinter DeletePrinter); my $input = new CGI; my $searchfield=$input->param('searchfield'); @@ -89,33 +77,31 @@ if ($op eq 'add_form') { #---- if primkey exists, it's a modify action, so read values to modify... my $data; if ($searchfield) { - my $sth=$dbh->prepare("SELECT printername,printqueue,printtype from printers where printername=?"); - $sth->execute($searchfield); - $data=$sth->fetchrow_hashref; + $data=GetPrinterDetails($searchfield); } $template->param(printqueue => $data->{'printqueue'}, + printername => $data->{'printername'}, printtype => $data->{'printtype'}); # END $OP eq ADD_FORM ################## ADD_VALIDATE ################################## # called by add_form, used to insert/modify data in DB } elsif ($op eq 'add_validate') { $template->param(add_validate => 1); + my $params = $input->Vars; if ($input->param('add')){ - my $sth=$dbh->prepare("INSERT INTO printers (printername,printqueue,printtype) VALUES (?,?,?)"); - $sth->execute($input->param('printername'),$input->param('printqueue'),$input->param('printtype')); + AddPrinter($params); } else { - my $sth=$dbh->prepare("UPDATE printers SET printqueue=?,printtype=? WHERE printername=?"); - $sth->execute($input->param('printqueue'),$input->param('printtype'),$input->param('printername')); + UpdatePrinter($params->{printqueue}, $params); } # END $OP eq ADD_VALIDATE ################## DELETE_CONFIRM ################################## # called by default form, used to confirm deletion of data in DB } elsif ($op eq 'delete_confirm') { $template->param(delete_confirm => 1); - my $sth=$dbh->prepare("select printername,printqueue,printtype from printers where printername=?"); + my $sth=$dbh->prepare("select printername,printqueue,printtype from printers where printqueue=?"); $sth->execute($searchfield); - my $data=$sth->fetchrow_hashref; + my $data=GetPrinterDetails($searchfield); $template->param(printqueue => $data->{'printqueue'}, printtype => $data->{'printtype'}); # END $OP eq DELETE_CONFIRM @@ -123,15 +109,16 @@ if ($op eq 'add_form') { # called by delete_confirm, used to effectively confirm deletion of data in DB } elsif ($op eq 'delete_confirmed') { $template->param(delete_confirmed => 1); - my $sth=$dbh->prepare("delete from printers where printername=?"); - $sth->execute($searchfield); + DeletePrinter($searchfield); # END $OP eq DELETE_CONFIRMED ################## DEFAULT ########################################### } else { # DEFAULT $template->param(else => 1); - my ($count,$results)=StringSearch($searchfield,'web'); + $searchfield ||= $input->param('description') || ""; + my $results=SearchPrinters($searchfield); + my $count = $results ? scalar(@$results) : 0; my $max = ($offset+$pagesize < $count) ? $offset+$pagesize : $count; - my @loop = (@$results)[$offset..$max]; + my @loop = (@$results)[$offset..$max-1]; $template->param(loop => \@loop); diff --git a/circ/circulation.pl b/circ/circulation.pl index 78ac1a4..54138ef 100755 --- a/circ/circulation.pl +++ b/circ/circulation.pl @@ -29,7 +29,7 @@ use C4::Print; use C4::Auth qw/:DEFAULT get_session/; use C4::Dates qw/format_date/; use C4::Branch; # GetBranches -use C4::Koha; # GetPrinter +use C4::Koha; use C4::Circulation; use C4::Overdues qw/CheckBorrowerDebarred/; use C4::Members; @@ -67,12 +67,6 @@ if ($branch){ $session->param('branchname', GetBranchName($branch)); } -my $printer = $query->param('printer'); -if ($printer){ - # update our session so the userenv is updated - $session->param('branchprinter', $printer); -} - if (!C4::Context->userenv && !$branch){ if ($session->param('branch') eq 'NO_LIBRARY_SET'){ # no branch set we can't issue @@ -102,8 +96,6 @@ $findborrower =~ s|,| |g; my $borrowernumber = $query->param('borrowernumber'); $branch = C4::Context->userenv->{'branch'}; -$printer = C4::Context->userenv->{'branchprinter'}; - # If AutoLocation is not activated, we show the Circulation Parameters to chage settings of librarian if (C4::Context->preference("AutoLocation") != 1) { @@ -665,8 +657,6 @@ $template->param( borrowernumber => $borrowernumber, branch => $branch, branchname => GetBranchName($borrower->{'branchcode'}), - printer => $printer, - printername => $printer, firstname => $borrower->{'firstname'}, surname => $borrower->{'surname'}, showname => $borrower->{'showname'}, diff --git a/circ/returns.pl b/circ/returns.pl index 743bf18..0efa712 100755 --- a/circ/returns.pl +++ b/circ/returns.pl @@ -36,7 +36,6 @@ use C4::Context; use C4::Auth qw/:DEFAULT get_session/; use C4::Output; use C4::Circulation; -use C4::Print; use C4::Reserves; use C4::Biblio; use C4::Items; @@ -73,15 +72,10 @@ my ( $template, $librarian, $cookie ) = get_template_and_user( ##################### #Global vars my $branches = GetBranches(); -my $printers = GetPrinters(); -my $printer = C4::Context->userenv ? C4::Context->userenv->{'branchprinter'} : ""; my $overduecharges = (C4::Context->preference('finesMode') && C4::Context->preference('finesMode') ne 'off'); my $userenv_branch = C4::Context->userenv->{'branch'} || ''; -# -# Some code to handle the error if there is no branch or printer setting..... -# # Set up the item stack .... my %returneditems; @@ -601,9 +595,7 @@ foreach ( sort { $a <=> $b } keys %returneditems ) { $template->param( riloop => \@riloop, genbrname => $branches->{$userenv_branch}->{'branchname'}, - genprname => $printers->{$printer}->{'printername'}, branchname => $branches->{$userenv_branch}->{'branchname'}, - printer => $printer, errmsgloop => \@errmsgloop, exemptfine => $exemptfine, dropboxmode => $dropboxmode, diff --git a/circ/selectbranchprinter.pl b/circ/selectbranchprinter.pl index b5adcfc..67034b7 100755 --- a/circ/selectbranchprinter.pl +++ b/circ/selectbranchprinter.pl @@ -23,10 +23,10 @@ use CGI; use C4::Context; use C4::Output; -use C4::Auth qw/:DEFAULT get_session/; -use C4::Print; # GetPrinters +use C4::Auth qw/:DEFAULT get_session get_user_printer/; use C4::Koha; use C4::Branch; # GetBranches GetBranchesLoop +use C4::Printer qw(GetPrinters); # this will be the script that chooses branch and printer settings.... @@ -56,9 +56,10 @@ my $userenv_printer = C4::Context->userenv->{'branchprinter'} || ''; my @updated; # $session lddines here are doing the updating -if ($branch and $branches->{$branch}) { +my $branch_rec = $branch ? $branches->{$branch} : undef; +if ($branch_rec) { if (! $userenv_branch or $userenv_branch ne $branch ) { - my $branchname = GetBranchName($branch); + my $branchname = $branch_rec->{branchname}; $template->param(LoginBranchname => $branchname); # update template for new branch $template->param(LoginBranchcode => $branch); # update template for new branch $session->param('branchname', $branchname); # update sesssion in DB @@ -67,6 +68,8 @@ if ($branch and $branches->{$branch}) { updated_branch => 1, old_branch => $userenv_branch, }; + $printer ||= $branch_rec->{branchprinter}; + undef $userenv_printer; } # else branch the same, no update } else { $branch = $userenv_branch; # fallback value @@ -87,7 +90,7 @@ if ($printer) { }; } # else printer is the same, no update } else { - $printer = $userenv_printer; # fallback value + $printer = get_user_printer(); # fallback value } $template->param(updated => \@updated) if (scalar @updated); @@ -96,21 +99,6 @@ unless ($branches->{$branch}) { $branch = (keys %$branches)[0]; # if branch didn't really exist, then replace it w/ one that does } -my @printkeys = sort keys %$printers; -if (scalar(@printkeys) == 1 or not $printers->{$printer}) { - $printer = $printkeys[0]; # if printer didn't really exist, or there is only 1 anyway, then replace it w/ one that does -} - -my @printerloop; -foreach ( @printkeys ) { - next unless ($_); # skip printer if blank. - push @printerloop, { - selected => ( $_ eq $printer ), - name => $printers->{$_}->{'printername'}, - value => $_, - }; -} - my @recycle_loop; foreach ($query->param()) { $_ or next; # disclude blanks @@ -133,9 +121,28 @@ if (scalar @updated and not scalar @recycle_loop) { $template->param( referer => $referer, - printerloop => \@printerloop, branchloop => GetBranchesLoop($branch), recycle_loop=> \@recycle_loop, ); +if ( C4::Context->preference('UsePrintQueues') ) { + my @printkeys = keys %$printers; + if (scalar(@printkeys) == 1 or not $printers->{$printer}) { + $printer = $printkeys[0]; # if printer didn't really exist, or there is only 1 anyway, then replace it w/ one that does + } + + my @printerloop; + foreach ( @printkeys ) { + push @printerloop, { + selected => ( $_ eq $printer ), + name => $printers->{$_}->{'printername'}, + value => $_, + }; + } + + $template->param( + printerloop => \@printerloop, + ); +} + output_html_with_http_headers $query, $cookie, $template->output; diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index 91d42a2..048a7ea 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -364,10 +364,11 @@ CREATE TABLE `branches` ( -- information about your libraries or branches are st `branchurl` mediumtext, -- the URL for your library or branch's website `issuing` tinyint(4) default NULL, -- unused in Koha `branchip` varchar(15) default NULL, -- the IP address for your library or branch - `branchprinter` varchar(100) default NULL, -- unused in Koha + `branchprinter` varchar(20) default NULL, `branchnotes` mediumtext, -- notes related to your library or branch opac_info text, -- HTML that displays in OPAC PRIMARY KEY (`branchcode`) + FOREIGN KEY (branchprinter) REFERENCES printers (printqueue) ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- @@ -1570,12 +1571,13 @@ CREATE TABLE `pending_offline_operations` ( -- Table structure for table `printers` -- -DROP TABLE IF EXISTS `printers`; -CREATE TABLE `printers` ( - `printername` varchar(40) NOT NULL default '', - `printqueue` varchar(20) default NULL, - `printtype` varchar(20) default NULL, - PRIMARY KEY (`printername`) +DROP TABLE IF EXISTS printers; +CREATE TABLE printers ( + printername varchar(40) NOT NULL default '', + printqueue varchar(20) NOT NULL, + printtype varchar(20) default NULL, + PRIMARY KEY (printqueue), + UNIQUE (printername) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index 35f92cc..f21b071 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -366,6 +366,7 @@ INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ( INSERT INTO systempreferences (variable,value,explanation,type) VALUES('EnableBorrowerFiles','0','If enabled, allows librarians to upload and attach arbitrary files to a borrower record.','YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('UpdateTotalIssuesOnCirc','0','Whether to update the totalissues field in the biblio on each circ.',NULL,'YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('IntranetSlipPrinterJS','','Use this JavaScript for printing slips. Define at least function printThenClose(). For use e.g. with Firefox PlugIn jsPrintSetup, see http://jsprintsetup.mozdev.org/','','Free'); +INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('UsePrintQueues','0',NULL,NULL,'YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpacSuppressionByIPRange','','Restrict the suppression to IP adresses outside of the IP range','','free'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('PrefillItem','0','When a new item is added, should it be prefilled with last created item values?','','YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('SubfieldsToUseWhenPrefill','','Define a list of subfields to use when prefilling items (separated by space)','','Free'); diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index 25cd367..3649a78 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -5696,6 +5696,18 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) { SetVersion($DBversion); } + + +$DBversion = "3.09.00.XXX"; +if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) { + $dbh->do("ALTER TABLE printers DROP PRIMARY KEY, MODIFY printqueue varchar(20) NOT NULL PRIMARY KEY, ADD UNIQUE (printername)"); + $dbh->do("ALTER TABLE branches MODIFY branchprinter varchar(20) NULL, ADD FOREIGN KEY (branchprinter) REFERENCES printers (printqueue) ON UPDATE CASCADE"); + $dbh->do("INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('UsePrintQueues','0',NULL,NULL,'YesNo')"); + + print "Upgrade to $DBversion done (Add borrowers.default_printqueue and 'UsePrintQueues' syspref)\n"; + SetVersion($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc index 3d71d45..86fe8cc 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc @@ -57,6 +57,9 @@ [% LoginBranchname %] [% END %] + [% IF UsePrintQueues %] + - [% PrinterName %] + [% END %] [% IF ( IndependantBranches ) %] [% IF ( CAN_user_management || CAN_user_editcatalogue_edit_catalogue ) %] ( Set library ) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt index 3906c46..d2e0fc5 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt @@ -101,8 +101,8 @@
[% IF ( NoZebra ) %]
Stop words
Words ignored during search.
[% END %] - + [% IF UsePrintQueues %]
Network Printers
+
Printers (UNIX paths).
[% END %]
Z39.50 client targets
Define which servers to query for MARC data in the integrated Z39.50 client.
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt index 15e8064..bcb918f 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt @@ -143,10 +143,10 @@ tinyMCE.init({
  • Can be entered as a single IP, or a subnet such as 192.168.1.*
  • - +[% END %]
  • @@ -198,7 +198,9 @@ tinyMCE.init({ Address Properties IP - +[% IF UsePrintQueues %] + Printer +[% END %]   [% FOREACH branche IN branches %] @@ -250,9 +252,11 @@ tinyMCE.init({ [% branche.branchip %] - +[% IF UsePrintQueues %] + + [% branche.branchprintername %] + +[% END %] Edit diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref index a9ed6f7..23cd7ee 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -110,6 +110,12 @@ Circulation: yes: Do no: "Do not" - update a bibliographic record's total issues count whenever an item is issued (WARNING! This increases server load significantly; if performance is a concern, use the update_totalissues.pl cron job to update the total issues count). + - + - pref: UsePrintQueues + choices: + yes: "Use" + no: "Don't use" + - server print queues. Checkout Policy: - - pref: AllowNotForLoanOverride diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt index 7dfb80b..d1007a3 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt @@ -89,22 +89,17 @@
    [% IF ( searchfield ) %] + [% ELSE %] [% END %]
    -
      [% IF ( searchfield ) %] -
    1. - Printer name: - [% searchfield %] -
    2. - [% ELSE %] +
      1. - +
      2. - [% END %]
      3. @@ -190,7 +185,7 @@ [% loo.printername %] [% loo.printqueue %] [% loo.printtype %] - Edit Delete + Edit Delete [% END %] [% ELSE %]
        No printers defined.
        [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt index 8724e9d..0dc95a8 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt @@ -457,7 +457,6 @@ No patron matched [% message %] Patron selection - @@ -515,7 +514,6 @@ No patron matched [% message %]
    [% END %] - [% IF ( CHARGES ) %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/selectbranchprinter.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/selectbranchprinter.tt index acad21c..e742f81 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/selectbranchprinter.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/selectbranchprinter.tt @@ -28,7 +28,7 @@ Updated:
      [% IF ( update.updated_branch ) %]
    • Library: [% update.old_branch or "?" %] ⇒ [% update.LoginBranchcode or "?" %]
    • [% ELSIF ( update.updated_printer ) %] - +
    • Printer: [% update.old_printer or "?" %] ⇒ [% update.new_printer or "?" %]
    • [% ELSE %]
    • ERROR - unknown
    • [% END %] @@ -63,7 +63,6 @@ Updated:
        [% END %] [% END %] - + [% END %]
        diff --git a/t/db_dependent/lib/KohaTest/Koha.pm b/t/db_dependent/lib/KohaTest/Koha.pm index 13a145a..0dda47a 100644 --- a/t/db_dependent/lib/KohaTest/Koha.pm +++ b/t/db_dependent/lib/KohaTest/Koha.pm @@ -28,8 +28,6 @@ sub methods : Test( 1 ) { _getImagesFromDirectory _getSubdirectoryNames getImageSets - GetPrinters - GetPrinter getnbpages getallthemes getFacets diff --git a/t/db_dependent/lib/KohaTest/Printer.pm b/t/db_dependent/lib/KohaTest/Printer.pm new file mode 100644 index 0000000..4caef06 --- /dev/null +++ b/t/db_dependent/lib/KohaTest/Printer.pm @@ -0,0 +1,26 @@ +package KohaTest::Printer; +use base qw( KohaTest ); + +use strict; +use warnings; + +use Test::More; + +use C4::Printer; +sub testing_class { 'C4::Printer' } + +sub methods : Test( 1 ) { + my $self = shift; + my @methods = qw( + GetPrinters + SearchPrinters + GetPrinterDetails + AddPrinter + UpdatePrinter + DeletePrinter + ); + + can_ok( $self->testing_class, @methods ); +} + +1; -- 1.7.9.5 From fridolyn.somers at biblibre.com Thu Sep 6 14:11:00 2012 From: fridolyn.somers at biblibre.com (Fridolyn SOMERS) Date: Thu, 6 Sep 2012 14:11:00 +0200 Subject: [Koha-patches] [PATCH] Bug 8462: OpacHiddenItems and hidelostitems hide items, but query result count is off Message-ID: <1346933460-17787-1-git-send-email-fridolyn.somers@biblibre.com> --- C4/Search.pm | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/C4/Search.pm b/C4/Search.pm index 3edf118..f2f33b6 100644 --- a/C4/Search.pm +++ b/C4/Search.pm @@ -1766,10 +1766,7 @@ sub searchResults { } } } # notforloan, item level and biblioitem level - if ($items_count > 0) { - next if $is_opac && $hideatopac_count >= $items_count; - next if $hidelostitems && $itemlost_count >= $items_count; - } + my ( $availableitemscount, $onloanitemscount, $otheritemscount ); for my $key ( sort keys %$onloan_items ) { (++$onloanitemscount > $maxitems) and last; -- 1.7.9.5 From oleonard at myacpl.org Thu Sep 6 21:07:02 2012 From: oleonard at myacpl.org (Owen Leonard) Date: Thu, 6 Sep 2012 15:07:02 -0400 Subject: [Koha-patches] [PATCH] Bug 8730 - browse overlaying powered by Message-ID: <1346958423-23352-1-git-send-email-oleonard@myacpl.org> This patch adds a "z-index" property to the CSS for the browse results menu so that it will appear on top of the "Powered by Koha" link. --- koha-tmpl/opac-tmpl/prog/en/css/opac.css | 1 + 1 file changed, 1 insertion(+) diff --git a/koha-tmpl/opac-tmpl/prog/en/css/opac.css b/koha-tmpl/opac-tmpl/prog/en/css/opac.css index 414b776..f6f6096 100644 --- a/koha-tmpl/opac-tmpl/prog/en/css/opac.css +++ b/koha-tmpl/opac-tmpl/prog/en/css/opac.css @@ -2230,6 +2230,7 @@ a.koha_url { display: none; background-color:#F3F3F3; padding-bottom:10px; + z-index: 100; } -- 1.7.9.5 From nengard at bywatersolutions.com Thu Sep 6 06:23:03 2012 From: nengard at bywatersolutions.com (Nicole C. Engard) Date: Thu, 6 Sep 2012 00:23:03 -0400 Subject: [Koha-patches] [PATCH] Bug 6716 - Fix alt contact field documentation Message-ID: <1346905383-4029-1-git-send-email-nengard@bywatersolutions.com> The borrowers table has the wrong documentation for the alternate address3 and city fields. This fixes that. --- installer/data/mysql/kohastructure.sql | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index 91d42a2..305d3ea 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -256,8 +256,8 @@ CREATE TABLE `borrowers` ( -- this table includes information about your patrons `altcontactsurname` varchar(255) default NULL, -- surname or last name of the alternate contact for the patron/borrower `altcontactaddress1` varchar(255) default NULL, -- the first address line for the alternate contact for the patron/borrower `altcontactaddress2` varchar(255) default NULL, -- the second address line for the alternate contact for the patron/borrower - `altcontactaddress3` varchar(255) default NULL, -- the third address line for the alternate contact for the patron/borrower - `altcontactstate` text default NULL, -- the city and state for the alternate contact for the patron/borrower + `altcontactaddress3` varchar(255) default NULL, -- the city for the alternate contact for the patron/borrower + `altcontactstate` text default NULL, -- the state for the alternate contact for the patron/borrower `altcontactzipcode` varchar(50) default NULL, -- the zipcode for the alternate contact for the patron/borrower `altcontactcountry` text default NULL, -- the country for the alternate contact for the patron/borrower `altcontactphone` varchar(50) default NULL, -- the phone number for the alternate contact for the patron/borrower @@ -727,8 +727,8 @@ CREATE TABLE `deletedborrowers` ( -- stores data related to the patrons/borrower `altcontactsurname` varchar(255) default NULL, -- surname or last name of the alternate contact for the patron/borrower `altcontactaddress1` varchar(255) default NULL, -- the first address line for the alternate contact for the patron/borrower `altcontactaddress2` varchar(255) default NULL, -- the second address line for the alternate contact for the patron/borrower - `altcontactaddress3` varchar(255) default NULL, -- the third address line for the alternate contact for the patron/borrower - `altcontactstate` text default NULL, -- the city and state for the alternate contact for the patron/borrower + `altcontactaddress3` varchar(255) default NULL, -- the city for the alternate contact for the patron/borrower + `altcontactstate` text default NULL, -- the state for the alternate contact for the patron/borrower `altcontactzipcode` varchar(50) default NULL, -- the zipcode for the alternate contact for the patron/borrower `altcontactcountry` text default NULL, -- the country for the alternate contact for the patron/borrower `altcontactphone` varchar(50) default NULL, -- the phone number for the alternate contact for the patron/borrower -- 1.7.2.3 From oleonard at myacpl.org Thu Sep 6 21:22:25 2012 From: oleonard at myacpl.org (Owen Leonard) Date: Thu, 6 Sep 2012 15:22:25 -0400 Subject: [Koha-patches] [PATCH] Bug 8711 - alternating colors messed up on reports Message-ID: <1346959345-23613-1-git-send-email-oleonard@myacpl.org> Patch adds the "zebra-striping" option to the table sorter initialization and changes the row color CSS to match the tablesorter's to prevent conflicts. --- .../en/modules/reports/guided_reports_start.tt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reports/guided_reports_start.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/reports/guided_reports_start.tt index 21d8235..f081894 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/reports/guided_reports_start.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reports/guided_reports_start.tt @@ -43,6 +43,7 @@ $(document).ready(function(){ [% END %] // call the tablesorter plugin $("#table_reports").tablesorter({ + widgets : ['zebra'], sortList: [[1,0]], headers: { 6: { sorter: false}, @@ -151,7 +152,7 @@ canned reports and writing custom SQL reports.

        [% FOREACH savedreport IN savedreports %] -[% UNLESS ( loop.odd ) %][% ELSE %][% END %] +[% UNLESS ( loop.odd ) %][% ELSE %][% END %] [% savedreport.id %] [% savedreport.report_name %] [% savedreport.type %] -- 1.7.9.5 From oleonard at myacpl.org Fri Sep 7 17:24:35 2012 From: oleonard at myacpl.org (Owen Leonard) Date: Fri, 7 Sep 2012 11:24:35 -0400 Subject: [Koha-patches] [PATCH] Bug 8677 - table overlapping to the right on holds waiting Message-ID: <1347031475-26454-1-git-send-email-oleonard@myacpl.org> Converting layout to 100% flexible width to allow table to expand as needed. --- .../prog/en/modules/circ/waitingreserves.tt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/waitingreserves.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/waitingreserves.tt index ea0faef..f9d2258 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/waitingreserves.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/waitingreserves.tt @@ -32,7 +32,7 @@ $.tablesorter.addParser({ › Holds awaiting pickup
    -
    +
    -- 1.7.9.5 From oleonard at myacpl.org Fri Sep 7 18:14:49 2012 From: oleonard at myacpl.org (Owen Leonard) Date: Fri, 7 Sep 2012 12:14:49 -0400 Subject: [Koha-patches] [PATCH] Bug 8598 - No patron image or home library on Files & Statistics tabs Message-ID: <1347034490-27053-1-git-send-email-oleonard@myacpl.org> Adding the necessary code for patron image, patron home library, patron category description, and patron extended attributes to show in the left-hand sidebar. --- members/files.pl | 18 ++++++++++++++++++ members/statistics.pl | 17 ++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/members/files.pl b/members/files.pl index f011c77..84bc41e 100755 --- a/members/files.pl +++ b/members/files.pl @@ -23,8 +23,10 @@ use warnings; use CGI; use C4::Auth; +use C4::Branch; use C4::Output; use C4::Members; +use C4::Members::Attributes qw(GetBorrowerAttributes); use C4::Debug; use Koha::DateUtils; @@ -100,6 +102,22 @@ else { } $template->param( + categoryname => $data->{'description'}, + branchname => GetBranchName($data->{'branchcode'}), + ); + + if (C4::Context->preference('ExtendedPatronAttributes')) { + my $attributes = GetBorrowerAttributes($borrowernumber); + $template->param( + ExtendedPatronAttributes => 1, + extendedattributes => $attributes + ); + } + + my ($picture, $dberror) = GetPatronImage($data->{'cardnumber'}); + $template->param( picture => 1 ) if $picture; + + $template->param( files => Koha::Borrower::Files->new( borrowernumber => $borrowernumber ) ->GetFilesInfo(), diff --git a/members/statistics.pl b/members/statistics.pl index 7125560..d48cdde 100755 --- a/members/statistics.pl +++ b/members/statistics.pl @@ -29,6 +29,7 @@ use C4::Branch; use C4::Context; use C4::Members; use C4::Members::Statistics; +use C4::Members::Attributes qw(GetBorrowerAttributes); use C4::Output; my $input = new CGI; @@ -56,7 +57,10 @@ if ( not defined $borrower ) { foreach my $key ( keys %$borrower ) { $template->param( $key => $borrower->{$key} ); } - +$template->param( + categoryname => $borrower->{'description'}, + branchname => GetBranchName($borrower->{'branchcode'}), +); # Construct column names my $fields = C4::Members::Statistics::get_fields(); our @statistic_column_names = split '\|', $fields; @@ -80,6 +84,17 @@ my $count_total_issues = $total->{count_total_issues_today} || 0; my $count_total_issues_returned = $total->{count_total_issues_returned_today} || 0; my $count_total_actual_state = ($count_total_precedent_state - $count_total_issues_returned + $count_total_issues); +if (C4::Context->preference('ExtendedPatronAttributes')) { + my $attributes = GetBorrowerAttributes($borrowernumber); + $template->param( + ExtendedPatronAttributes => 1, + extendedattributes => $attributes + ); +} + +my ($picture, $dberror) = GetPatronImage($borrower->{'cardnumber'}); +$template->param( picture => 1 ) if $picture; + $template->param( statisticsview => 1, datas => $datas, -- 1.7.9.5 From oleonard at myacpl.org Fri Sep 7 19:51:16 2012 From: oleonard at myacpl.org (Owen Leonard) Date: Fri, 7 Sep 2012 13:51:16 -0400 Subject: [Koha-patches] [PATCH] Bug 8661 - break out additional authors in opac like in staff Message-ID: <1347040276-28311-1-git-send-email-oleonard@myacpl.org> This patch takes the simple route of copying over the markup for displaying authors from the staff client XSL to the OPAC. Signed-off-by: Owen Leonard --- .../prog/en/xslt/MARC21slim2OPACDetail.xsl | 91 +++++++++----------- 1 file changed, 43 insertions(+), 48 deletions(-) diff --git a/koha-tmpl/opac-tmpl/prog/en/xslt/MARC21slim2OPACDetail.xsl b/koha-tmpl/opac-tmpl/prog/en/xslt/MARC21slim2OPACDetail.xsl index 8d54517..cc0d4d9 100644 --- a/koha-tmpl/opac-tmpl/prog/en/xslt/MARC21slim2OPACDetail.xsl +++ b/koha-tmpl/opac-tmpl/prog/en/xslt/MARC21slim2OPACDetail.xsl @@ -137,16 +137,10 @@ - - -
    by - - - - -
    -
    -
    + + + + @@ -873,47 +867,48 @@ + - - + + + +
    - ; - - - - - - n - - - - - - - - /cgi-bin/koha/opac-search.pl?q=an: - - - /cgi-bin/koha/opac-search.pl?q=au: - - - - - - - - - - [ - - - - - ] - - + + + Author(s): + Additional author(s): + + + + + /cgi-bin/koha/opac-search.pl?q=an: + + + /cgi-bin/koha/opac-search.pl?q=au: + + + + + + + + + + [ + + + + + ] + + + + .; + - . +
    +
    -- 1.7.9.5 From matted-34813 at mypacks.net Sat Sep 8 06:49:49 2012 From: matted-34813 at mypacks.net (matted-34813 at mypacks.net) Date: Fri, 7 Sep 2012 23:49:49 -0500 (GMT-05:00) Subject: [Koha-patches] Bug-8447- Can't save new recors/ indicator error/Make-sure-we-have-enough-subfield Message-ID: <22103295.1347079789593.JavaMail.root@wamui-june.atl.sa.earthlink.net> Here is a use case that caused the problem (for historical reference and testing for QA) In admin set sysprefs AutoCreateAuthorities - generate BiblioAddsAuthorities - allow dontmerge don't LinkerModule Default LinkerOptions broader_headings Catalogueing -> Z3950 search Subject [Bible.] Library of Congress[x] Import the one with: 1 Corinthians : 2005 080282577X (cloth:alkpaper) 2005052170 Edit koha itemtype books 942c, and 003 Save ( and get software error mentioning indicators) With the patch, we fixend how the biblio is merged with existing data. wajasu From jcamins at cpbibliography.com Fri Sep 7 16:58:53 2012 From: jcamins at cpbibliography.com (Jared Camins-Esakov) Date: Fri, 7 Sep 2012 10:58:53 -0400 Subject: [PATCH] [SIGNED-OFF] Bug 8447: Make sure we have enough subfields for broader_headings Message-ID: Thanks to wajasu for providing the debugging information, as I cannot seem to duplicate the problem, even seeing where it could come from. Fix corrects cases where upon save, a software error complaing about indicators being wrong as a side effect of a poorly autogenerated biblio/authority record. Behvior with this fix causes existing bilio data to to be preserved while linking to the authority via subtag 9. Signed-off-by: wajasu --- C4/Linker/Default.pm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/C4/Linker/Default.pm b/C4/Linker/Default.pm index 653dabd..445b408 100644 --- a/C4/Linker/Default.pm +++ b/C4/Linker/Default.pm @@ -56,17 +56,16 @@ sub get_link { if ( !defined $authid && $self->{'broader_headings'} ) { my $field = $heading->field(); - my @subfields = $field->subfields(); + my @subfields = grep { $_->[0] ne '9' } $field->subfields(); if ( scalar @subfields > 1 ) { pop @subfields; - $field->replace_with( + $field = MARC::Field->new( $field->tag, $field->indicator(1), $field->indicator(2), - map { $_[0] => $_[1] } @subfields - ) - ); + map { $_->[0] => $_->[1] } @subfields + ); ( $authid, $fuzzy ) = $self->get_link( C4::Heading->new_from_bib_field($field), $behavior ); -- 1.7.11.4 ------=_Part_8041_12838168.1347079789321-- From matted-34813 at mypacks.net Sun Sep 9 21:55:23 2012 From: matted-34813 at mypacks.net (matted-34813 at mypacks.net) Date: Sun, 9 Sep 2012 14:55:23 -0500 (GMT-05:00) Subject: [Koha-patches] SIGNED-OFF-Bug-8744-Thesaurus-in-authorities-should-not-lock-fields Message-ID: <30320385.1347220523521.JavaMail.root@wamui-june.atl.sa.earthlink.net> Now one can create heirarchies of geographical names inthe authority 551 $a field for example. This was needed to support bug 8211 for expanded search functionaility. wajasu From jcamins at cpbibliography.com Sun Sep 9 14:55:12 2012 From: jcamins at cpbibliography.com (Jared Camins-Esakov) Date: Sun, 9 Sep 2012 08:55:12 -0400 Subject: [PATCH] [SIGNED-OFF] Bug 8744: Thesaurus in authorities should not lock fields Message-ID: Although fields in the authority editor should support thesaurus control, they should never be locked, as authority records commonly refer to other records that may not be in use locally (and therefore might not be in the authority file). Test plan: 1) Make sure a 5xx field in one of your authority frameworks is thesaurus-controlled. 2) Note that you cannot edit the field directly. 3) Apply patch. 4) Note that you can now edit the field irectly. Signed-off-by: wajasu --- authorities/authorities.pl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/authorities/authorities.pl b/authorities/authorities.pl index 5b6afbd..661e465 100755 --- a/authorities/authorities.pl +++ b/authorities/authorities.pl @@ -194,9 +194,8 @@ sub create_input { id=\"".$subfield_data{id}."\" name=\"".$subfield_data{id}."\" value=\"$value\" - class=\"input_marceditor readonly\" - tabindex=\"1\" - readonly=\"readonly\" \/> + class=\"input_marceditor\" + tabindex=\"1\" \/> {$tag}->{$subfield}->{authtypecode}."'); return false;\" tabindex=\"1\" title=\"Tag Editor\">... "; -- 1.7.11.4 ------=_Part_2512_10030794.1347220523217-- From matted-34813 at mypacks.net Mon Sep 10 02:20:22 2012 From: matted-34813 at mypacks.net (matted-34813 at mypacks.net) Date: Sun, 9 Sep 2012 19:20:22 -0500 (GMT-05:00) Subject: [Koha-patches] SIGNED-OFF-Bug-8211-Add-exploded-search-options.patch Message-ID: <18785378.1347236422979.JavaMail.root@wamui-june.atl.sa.earthlink.net> After pulling master i applied these: 0001-Bug-8744-Thesaurus-in-authorities-should-not-lock-fi.patch 0002-Bug-8649-Add-unit-test-for-C4-Search.patch 0003-bug-8649-make-sure-C4-Context-is-fully-mocked-up-bef.patch 0004-bug-8649-make-sure-we-can-exit-if-a-test-fails.patch 0005-bug-8649-create-a-temp-directory-for-Zebra-for-C4-Se.patch 0006-bug-8649-quiet-a-variable-use-warning.patch 0007-Bug-8211-Add-exploded-search-options.patch You need 8744 to be able to edit Geographical names in 551 (unlock them) 8649 has Search.t tests 1) I had created the authority heirarchy as comment#2 described. 2) I used z3950 to search LOC specifiying "United States" in subject heading, and chose "1000 things to love about America" and added an item. I navigated to its 651 Geographic name heading and clicked the editor icon for the 'a' subtag, and used the authority search plugin ( $a = United States ) and found the record that was created in step #1). choose it. I finished out the biblio and added an item. 3) I did the same thing as step 2) but for "Arizona" and "Phoenix" subject headingg, linking with the authority plugin as well. Pick a book for Arizona accordingly. 4) I ran rebuild-zebra and did my searches as described in comment #3) in the OPAC search. su-br:Arizona su-na:Arizona and su-rl:Arizona Each returned results. For me: I also had Phoenix in the su-br-Arizona search, and jcamins said there must be a subject heading in my chosen biblio for Phoenix. There was, so I edited my biblio, to remove the Restaurant -- Arizona TOPICAL TERM SUBJECT HEADING. After that I requeried su-br: Arizona, and the Phoenix item did not show up as we wanted. So there is an added restriction to be sure other subject heading don't interfere with results. wajasu From srdjan at catalyst.net.nz Mon Sep 10 03:16:43 2012 From: srdjan at catalyst.net.nz (Srdjan) Date: Mon, 10 Sep 2012 13:16:43 +1200 Subject: [Koha-patches] [PATCH] Bug 7993: Save reports with Group/Subgroup hierarchy In-Reply-To: References: Message-ID: <1347239803-21035-1-git-send-email-srdjan@catalyst.net.nz> This should make saved reports more manageable. Group/Subgroup hierarchy is stored in authorised_values, categories REPORT_GROUP and REPORT_SUBGROUP, connected by REPORT_SUBGROUP.lib_opac -> REPORT_GROUP.authorised_value Database changes: * authorised_values: expanded category to 16 chars * created default set of REPORT_GROUP authorised values to match hardcoded report areas * reports_dictionary: replaced area int with report_area text, converted values * saved_sql: added report_area, report_group and report_subgroup; report_area is not currently used, saved for the record C4/Reports/Guided.pm: * Replaced Area numeric values with the mnemonic codes * get_report_areas(): returns hardcoded areas list * created get_report_areas(): returns full hierarchy (groups with belonging subgroups) * save_report(): changed iterface, accepts fields hashref as input * update_sql(): changed iterface, accepts id and fields hashref as input * get_saved_reports():] - join to authorised_values to pick group and subgroup name - accept group and subgroup filter params * get_saved_report(): - changed iterface, return record hashref - join to authorised_values to pick group and subgroup name * build_authorised_value_list(): new sub, moved code from reports/guided_reports.pl * Updated interfaces in: cronjobs/runreport.pl, svc/report, opac/svc/report: get_saved_report() reports/dictionary.pl: get_report_areas() reports/guided_reports.pl reports/guided_reports_start.tt: * Reports list: - added group/subgroup filter - display area/group/subgroup for the reports * Create report wizard: - carry area to the end - select group and subgroup when saving the report; group defaults to area, useful when report groups match areas * Update report and Create from SQL: added group/subgroup * Amended reports/guided_reports.pl accordingly Conflicts: C4/Reports/Guided.pm admin/authorised_values.pl installer/data/mysql/kohastructure.sql installer/data/mysql/updatedatabase.pl koha-tmpl/intranet-tmpl/prog/en/modules/reports/dictionary.tmpl koha-tmpl/intranet-tmpl/prog/en/modules/reports/guided_reports_start.tmpl misc/cronjobs/runreport.pl reports/dictionary.pl reports/guided_reports.pl --- C4/Reports/Guided.pm | 432 +++++++++++------- admin/authorised_values.pl | 23 +- installer/data/mysql/kohastructure.sql | 11 +- installer/data/mysql/updatedatabase.pl | 32 ++ .../prog/en/modules/reports/dictionary.tt | 4 +- .../en/modules/reports/guided_reports_start.tt | 144 +++++- misc/cronjobs/runreport.pl | 22 +- opac/svc/report | 48 +- reports/dictionary.pl | 292 ++++++------ reports/guided_reports.pl | 482 +++++++++++--------- svc/report | 55 +-- 11 files changed, 949 insertions(+), 596 deletions(-) diff --git a/C4/Reports/Guided.pm b/C4/Reports/Guided.pm index e5c28f3..377a55a 100644 --- a/C4/Reports/Guided.pm +++ b/C4/Reports/Guided.pm @@ -26,7 +26,8 @@ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); use C4::Context; use C4::Dates qw/format_date format_date_in_iso/; use C4::Templates qw/themelanguage/; -use C4::Dates; +use C4::Koha; +use C4::Output; use XML::Simple; use XML::Dumper; use C4::Debug; @@ -34,75 +35,82 @@ use C4::Debug; # use Data::Dumper; BEGIN { - # set the version for version checking + # set the version for version checking $VERSION = 3.07.00.049; - require Exporter; - @ISA = qw(Exporter); - @EXPORT = qw( - get_report_types get_report_areas get_columns build_query get_criteria - save_report get_saved_reports execute_query get_saved_report create_compound run_compound - get_column_type get_distinct_values save_dictionary get_from_dictionary - delete_definition delete_report format_results get_sql - nb_rows update_sql - ); + require Exporter; + @ISA = qw(Exporter); + @EXPORT = qw( + get_report_types get_report_areas get_report_groups get_columns build_query get_criteria + save_report get_saved_reports execute_query get_saved_report create_compound run_compound + get_column_type get_distinct_values save_dictionary get_from_dictionary + delete_definition delete_report format_results get_sql + nb_rows update_sql build_authorised_value_list + ); } -our %table_areas; -$table_areas{'1'} = - [ 'borrowers', 'statistics','items', 'biblioitems' ]; # circulation -$table_areas{'2'} = [ 'items', 'biblioitems', 'biblio' ]; # catalogue -$table_areas{'3'} = [ 'borrowers' ]; # patrons -$table_areas{'4'} = ['aqorders', 'biblio', 'items']; # acquisitions -$table_areas{'5'} = [ 'borrowers', 'accountlines' ]; # accounts -our %keys; -$keys{'1'} = [ - 'statistics.borrowernumber=borrowers.borrowernumber', - 'items.itemnumber = statistics.itemnumber', - 'biblioitems.biblioitemnumber = items.biblioitemnumber' -]; -$keys{'2'} = [ - 'items.biblioitemnumber=biblioitems.biblioitemnumber', - 'biblioitems.biblionumber=biblio.biblionumber' -]; -$keys{'3'} = [ ]; -$keys{'4'} = [ - 'aqorders.biblionumber=biblio.biblionumber', - 'biblio.biblionumber=items.biblionumber' -]; -$keys{'5'} = ['borrowers.borrowernumber=accountlines.borrowernumber']; +=item get_report_areas() -# have to do someting here to know if its dropdown, free text, date etc +This will return a list of all the available report areas -our %criteria; -# reports on circulation -$criteria{'1'} = [ - 'statistics.type', 'borrowers.categorycode', - 'statistics.branch', - 'biblioitems.publicationyear|date', - 'items.dateaccessioned|date' -]; -# reports on catalogue -$criteria{'2'} = - [ 'items.itemnumber|textrange', 'items.biblionumber|textrange', 'items.barcode|textrange', - 'biblio.frameworkcode', 'items.holdingbranch', 'items.homebranch', - 'biblio.datecreated|daterange', 'biblio.timestamp|daterange', 'items.onloan|daterange', - 'items.ccode', 'items.itemcallnumber|textrange', 'items.itype', - 'items.itemlost', 'items.location' ]; -# reports on borrowers -$criteria{'3'} = ['borrowers.branchcode', 'borrowers.categorycode']; -# reports on acquisition -$criteria{'4'} = ['aqorders.datereceived|date']; - -# reports on accounting -$criteria{'5'} = ['borrowers.branchcode', 'borrowers.categorycode']; +=cut + +my @REPORT_AREA = ( + [CIRC => "Circulation"], + [CAT => "Catalogue"], + [PAT => "Patrons"], + [ACQ => "Acquisition"], + [ACC => "Accounts"], +); +my $AREA_NAME_SQL_SNIPPET + = "CASE report_area " . + join (" ", map "WHEN '$_->[0]' THEN '$_->[1]'", @REPORT_AREA) . + " END AS areaname"; +sub get_report_areas { + return \@REPORT_AREA +} + +my %table_areas = ( + CIRC => [ 'borrowers', 'statistics', 'items', 'biblioitems' ], + CAT => [ 'items', 'biblioitems', 'biblio' ], + PAT => ['borrowers'], + ACQ => [ 'aqorders', 'biblio', 'items' ], + ACC => [ 'borrowers', 'accountlines' ], +); +my %keys = ( + CIRC => [ 'statistics.borrowernumber=borrowers.borrowernumber', + 'items.itemnumber = statistics.itemnumber', + 'biblioitems.biblioitemnumber = items.biblioitemnumber' ], + CAT => [ 'items.biblioitemnumber=biblioitems.biblioitemnumber', + 'biblioitems.biblionumber=biblio.biblionumber' ], + PAT => [], + ACQ => [ 'aqorders.biblionumber=biblio.biblionumber', + 'biblio.biblionumber=items.biblionumber' ], + ACC => ['borrowers.borrowernumber=accountlines.borrowernumber'], +); + +# have to do someting here to know if its dropdown, free text, date etc +my %criteria = ( + CIRC => [ 'statistics.type', 'borrowers.categorycode', 'statistics.branch', + 'biblioitems.publicationyear|date', 'items.dateaccessioned|date' ], + CAT => [ 'items.itemnumber|textrange', 'items.biblionumber|textrange', + 'items.barcode|textrange', 'biblio.frameworkcode', + 'items.holdingbranch', 'items.homebranch', + 'biblio.datecreated|daterange', 'biblio.timestamp|daterange', + 'items.onloan|daterange', 'items.ccode', + 'items.itemcallnumber|textrange', 'items.itype', 'items.itemlost', + 'items.location' ], + PAT => [ 'borrowers.branchcode', 'borrowers.categorycode' ], + ACQ => ['aqorders.datereceived|date'], + ACC => [ 'borrowers.branchcode', 'borrowers.categorycode' ], +); # Adds itemtypes to criteria, according to the syspref -if (C4::Context->preference('item-level_itypes')) { - unshift @{ $criteria{'1'} }, 'items.itype'; - unshift @{ $criteria{'2'} }, 'items.itype'; +if ( C4::Context->preference('item-level_itypes') ) { + unshift @{ $criteria{'CIRC'} }, 'items.itype'; + unshift @{ $criteria{'CAT'} }, 'items.itype'; } else { - unshift @{ $criteria{'1'} }, 'biblioitems.itemtype'; - unshift @{ $criteria{'2'} }, 'biblioitems.itemtype'; + unshift @{ $criteria{'CIRC'} }, 'biblioitems.itemtype'; + unshift @{ $criteria{'CAT'} }, 'biblioitems.itemtype'; } =head1 NAME @@ -145,26 +153,33 @@ sub get_report_types { } -=item get_report_areas() +=item get_report_groups() -This will return a list of all the available report areas +This will return a list of all the available report areas with groups =cut -sub get_report_areas { +sub get_report_groups { my $dbh = C4::Context->dbh(); - # FIXME these should be in the database - my @reports = ( 'Circulation', 'Catalog', 'Patrons', 'Acquisitions', 'Accounts'); - my @reports2; - for ( my $i = 0 ; $i < 5 ; $i++ ) { - my %hashrep; - $hashrep{id} = $i + 1; - $hashrep{name} = $reports[$i]; - push @reports2, \%hashrep; + my $groups = GetAuthorisedValues('REPORT_GROUP'); + my $subgroups = GetAuthorisedValues('REPORT_SUBGROUP'); + + my %groups_with_subgroups = map { $_->{authorised_value} => { + name => $_->{lib}, + groups => {} + } } @$groups; + foreach (@$subgroups) { + my $sg = $_->{authorised_value}; + my $g = $_->{lib_opac} + or warn( qq{REPORT_SUBGROUP "$sg" without REPORT_GROUP (lib_opac)} ), + next; + my $g_sg = $groups_with_subgroups{$g} + or warn( qq{REPORT_SUBGROUP "$sg" with invalid REPORT_GROUP "$g"} ), + next; + $g_sg->{subgroups}{$sg} = $_->{lib}; } - return ( \@reports2 ); - + return \%groups_with_subgroups } =item get_all_tables() @@ -196,8 +211,10 @@ This will return a list of all columns for a report area sub get_columns { # this calls the internal fucntion _get_columns - my ($area,$cgi) = @_; - my $tables = $table_areas{$area}; + my ( $area, $cgi ) = @_; + my $tables = $table_areas{$area} + or die qq{Unsuported report area "$area"}; + my @allcolumns; my $first = 1; foreach my $table (@$tables) { @@ -383,7 +400,7 @@ sub nb_rows($) { =item execute_query - ($results, $total, $error) = execute_query($sql, $offset, $limit) + ($results, $error) = execute_query($sql, $offset, $limit) When passed C<$sql>, this function returns an array ref containing a result set @@ -505,37 +522,47 @@ Returns id of the newly created report =cut sub save_report { - my ( $borrowernumber, $sql, $name, $type, $notes, $cache_expiry, $public ) = @_; - $cache_expiry ||= 300; + my ($fields) = @_; + my $borrowernumber = $fields->{borrowernumber}; + my $sql = $fields->{sql}; + my $name = $fields->{name}; + my $type = $fields->{type}; + my $notes = $fields->{notes}; + my $area = $fields->{area}; + my $group = $fields->{group}; + my $subgroup = $fields->{subgroup}; + my $cache_expiry = $fields->{cache_expiry} || 300; + my $public = $fields->{public}; + my $dbh = C4::Context->dbh(); - $sql =~ s/(\s*\;\s*)$//; # removes trailing whitespace and /;/ - my $query = -"INSERT INTO saved_sql (borrowernumber,date_created,last_modified,savedsql,report_name,type,notes,cache_expiry, public) VALUES (?,now(),now(),?,?,?,?,?,?)"; - $dbh->do( $query, undef, $borrowernumber, $sql, $name, $type, $notes, $cache_expiry, $public ); + $sql =~ s/(\s*\;\s*)$//; # removes trailing whitespace and /;/ + my $query = "INSERT INTO saved_sql (borrowernumber,date_created,last_modified,savedsql,report_name,report_area,report_group,report_subgroup,type,notes,cache_expiry,public) VALUES (?,now(),now(),?,?,?,?,?,?,?,?,?)"; + $dbh->do($query, undef, $borrowernumber, $sql, $name, $area, $group, $subgroup, $type, $notes, $cache_expiry, $public); + my $id = $dbh->selectrow_array("SELECT max(id) FROM saved_sql WHERE borrowernumber=? AND report_name=?", undef, $borrowernumber, $name); return $id; } sub update_sql { - my $id = shift || croak "No Id given"; - my $sql = shift; - my $reportname = shift; - my $notes = shift; - my $cache_expiry = shift; - my $public = shift; - - # not entirely a magic number, Cache::Memcached::Set assumed any expiry >= (60*60*24*30) is an absolute unix timestamp (rather than relative seconds) + my $id = shift || croak "No Id given"; + my $fields = shift; + my $sql = $fields->{sql}; + my $name = $fields->{name}; + my $notes = $fields->{notes}; + my $group = $fields->{group}; + my $subgroup = $fields->{subgroup}; + my $cache_expiry = $fields->{cache_expiry}; + my $public = $fields->{public}; + if( $cache_expiry >= 2592000 ){ die "Please specify a cache expiry less than 30 days\n"; } - my $dbh = C4::Context->dbh(); - $sql =~ s/(\s*\;\s*)$//; # removes trailing whitespace and /;/ - my $query = "UPDATE saved_sql SET savedsql = ?, last_modified = now(), report_name = ?, notes = ?, cache_expiry = ?, public = ? WHERE id = ? "; - my $sth = $dbh->prepare($query); - $sth->execute( $sql, $reportname, $notes, $cache_expiry, $public, $id ); - $sth->finish(); + my $dbh = C4::Context->dbh(); + $sql =~ s/(\s*\;\s*)$//; # removes trailing whitespace and /;/ + my $query = "UPDATE saved_sql SET savedsql = ?, last_modified = now(), report_name = ?, report_group = ?, report_subgroup = ?, notes = ?, cache_expiry = ?, public = ? WHERE id = ? "; + $dbh->do($query, undef, $sql, $name, $group, $subgroup, $notes, $cache_expiry, $public, $id ); } sub store_results { @@ -582,30 +609,34 @@ sub format_results { } sub delete_report { - my ( $id ) = @_; - my $dbh = C4::Context->dbh(); - my $query = "DELETE FROM saved_sql WHERE id = ?"; - my $sth = $dbh->prepare($query); - $sth->execute($id); + my ($id) = @_; + my $dbh = C4::Context->dbh(); + my $query = "DELETE FROM saved_sql WHERE id = ?"; + my $sth = $dbh->prepare($query); + $sth->execute($id); } -# $filter is either { date => $d, author => $a, keyword => $kw } -# or $keyword. Optional. + +my $SAVED_REPORTS_BASE_QRY = < $d, author => $a, keyword => $kw, } +# or $keyword. Optional. my ($filter) = @_; $filter = { keyword => $filter } if $filter && !ref( $filter ); + my ($group, $subgroup) = @_; my $dbh = C4::Context->dbh(); + my $query = $SAVED_REPORTS_BASE_QRY; my (@cond, at args); - my $query = "SELECT saved_sql.id, report_id, report, - date_run, date_created, last_modified, savedsql, last_run, - report_name, type, notes, - borrowernumber, surname as borrowersurname, firstname as borrowerfirstname, - cache_expiry, public - FROM saved_sql - LEFT JOIN saved_reports ON saved_reports.report_id = saved_sql.id - LEFT OUTER JOIN borrowers USING (borrowernumber)"; if ($filter) { if (my $date = $filter->{date}) { $date = format_date_in_iso($date); @@ -629,6 +660,14 @@ sub get_saved_reports { savedsql LIKE ?"; push @args, $keyword, $keyword, $keyword, $keyword; } + if ($filter->{group}) { + push @cond, "report_group = ?"; + push @args, $filter->{group}; + } + if ($filter->{subgroup}) { + push @cond, "report_subgroup = ?"; + push @args, $filter->{subgroup}; + } } $query .= " WHERE ".join( " AND ", map "($_)", @cond ) if @cond; $query .= " ORDER by date_created"; @@ -642,7 +681,6 @@ sub get_saved_reports { sub get_saved_report { my $dbh = C4::Context->dbh(); my $query; - my $sth; my $report_arg; if ($#_ == 0 && ref $_[0] ne 'HASH') { ($report_arg) = @_; @@ -661,10 +699,7 @@ sub get_saved_report { } else { return; } - $sth = $dbh->prepare($query); - $sth->execute($report_arg); - my $data = $sth->fetchrow_hashref(); - return ( $data->{'savedsql'}, $data->{'type'}, $data->{'report_name'}, $data->{'notes'}, $data->{'cache_expiry'}, $data->{'public'}, $data->{'id'} ); + return $dbh->selectrow_hashref($query, undef, $report_arg); } =item create_compound($masterID,$subreportID) @@ -674,22 +709,27 @@ This will take 2 reports and create a compound report using both of them =cut sub create_compound { - my ($masterID,$subreportID) = @_; - my $dbh = C4::Context->dbh(); - # get the reports - my ($mastersql,$mastertype) = get_saved_report($masterID); - my ($subsql,$subtype) = get_saved_report($subreportID); - - # now we have to do some checking to see how these two will fit together - # or if they will - my ($mastertables,$subtables); - if ($mastersql =~ / from (.*) where /i){ - $mastertables = $1; - } - if ($subsql =~ / from (.*) where /i){ - $subtables = $1; - } - return ($mastertables,$subtables); + my ( $masterID, $subreportID ) = @_; + my $dbh = C4::Context->dbh(); + + # get the reports + my $master = get_saved_report($masterID); + my $mastersql = $master->{savedsql}; + my $mastertype = $master->{type}; + my $sub = get_saved_report($subreportID); + my $subsql = $master->{savedsql}; + my $subtype = $master->{type}; + + # now we have to do some checking to see how these two will fit together + # or if they will + my ( $mastertables, $subtables ); + if ( $mastersql =~ / from (.*) where /i ) { + $mastertables = $1; + } + if ( $subsql =~ / from (.*) where /i ) { + $subtables = $1; + } + return ( $mastertables, $subtables ); } =item get_column_type($column) @@ -739,43 +779,41 @@ sub get_distinct_values { } sub save_dictionary { - my ($name,$description,$sql,$area) = @_; - my $dbh = C4::Context->dbh(); - my $query = "INSERT INTO reports_dictionary (name,description,saved_sql,area,date_created,date_modified) + my ( $name, $description, $sql, $area ) = @_; + my $dbh = C4::Context->dbh(); + my $query = "INSERT INTO reports_dictionary (name,description,saved_sql,report_area,date_created,date_modified) VALUES (?,?,?,?,now(),now())"; my $sth = $dbh->prepare($query); $sth->execute($name,$description,$sql,$area) || return 0; return 1; } +my $DICTIONARY_BASE_QRY = <dbh(); - my $query = "SELECT * FROM reports_dictionary"; - if ($area){ - $query.= " WHERE area = ?"; - } - elsif ($id){ - $query.= " WHERE id = ?" - } - my $sth = $dbh->prepare($query); - if ($id){ - $sth->execute($id); - } - elsif ($area) { - $sth->execute($area); - } - else { - $sth->execute(); - } - my @loop; - my @reports = ( 'Circulation', 'Catalog', 'Patrons', 'Acquisitions', 'Accounts'); - while (my $data = $sth->fetchrow_hashref()){ - $data->{'areaname'}=$reports[$data->{'area'}-1]; - push @loop,$data; - - } - return (\@loop); + my ( $area, $id ) = @_; + my $dbh = C4::Context->dbh(); + my $query = $DICTIONARY_BASE_QRY; + if ($area) { + $query .= " WHERE report_area = ?"; + } elsif ($id) { + $query .= " WHERE id = ?"; + } + my $sth = $dbh->prepare($query); + if ($id) { + $sth->execute($id); + } elsif ($area) { + $sth->execute($area); + } else { + $sth->execute(); + } + my @loop; + while ( my $data = $sth->fetchrow_hashref() ) { + push @loop, $data; + } + return ( \@loop ); } sub delete_definition { @@ -815,6 +853,72 @@ sub _get_column_defs { close COLUMNS; return \%columns; } + +=item build_authorised_value_list($authorised_value) + +Returns an arrayref - hashref pair. The hashref consists of +various code => name lists depending on the $authorised_value. +The arrayref is the hashref keys, in appropriate order + +=cut + +sub build_authorised_value_list { + my ( $authorised_value ) = @_; + + my $dbh = C4::Context->dbh; + my @authorised_values; + my %authorised_lib; + + # builds list, depending on authorised value... + if ( $authorised_value eq "branches" ) { + my $branches = GetBranchesLoop(); + foreach my $thisbranch (@$branches) { + push @authorised_values, $thisbranch->{value}; + $authorised_lib{ $thisbranch->{value} } = $thisbranch->{branchname}; + } + } elsif ( $authorised_value eq "itemtypes" ) { + my $sth = $dbh->prepare("SELECT itemtype,description FROM itemtypes ORDER BY description"); + $sth->execute; + while ( my ( $itemtype, $description ) = $sth->fetchrow_array ) { + push @authorised_values, $itemtype; + $authorised_lib{$itemtype} = $description; + } + } elsif ( $authorised_value eq "cn_source" ) { + my $class_sources = GetClassSources(); + my $default_source = C4::Context->preference("DefaultClassificationSource"); + foreach my $class_source ( sort keys %$class_sources ) { + next + unless $class_sources->{$class_source}->{'used'} + or ( $class_source eq $default_source ); + push @authorised_values, $class_source; + $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'}; + } + } elsif ( $authorised_value eq "categorycode" ) { + my $sth = $dbh->prepare("SELECT categorycode, description FROM categories ORDER BY description"); + $sth->execute; + while ( my ( $categorycode, $description ) = $sth->fetchrow_array ) { + push @authorised_values, $categorycode; + $authorised_lib{$categorycode} = $description; + } + + #---- "true" authorised value + } else { + my $authorised_values_sth = $dbh->prepare("SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib"); + + $authorised_values_sth->execute($authorised_value); + + while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) { + push @authorised_values, $value; + $authorised_lib{$value} = $lib; + + # For item location, we show the code and the libelle + $authorised_lib{$value} = $lib; + } + } + + return (\@authorised_values, \%authorised_lib); +} + 1; __END__ diff --git a/admin/authorised_values.pl b/admin/authorised_values.pl index 949e488..17917ce 100755 --- a/admin/authorised_values.pl +++ b/admin/authorised_values.pl @@ -188,17 +188,18 @@ output_html_with_http_headers $input, $cookie, $template->output; exit 0; sub default_form { - # build categories list - my $sth = $dbh->prepare("select distinct category from authorised_values"); - $sth->execute; - my @category_list; - my %categories; # a hash, to check that some hardcoded categories exist. - while ( my ($category) = $sth->fetchrow_array) { - push(@category_list,$category); - $categories{$category} = 1; - } - # push koha system categories - foreach (qw(Asort1 Asort2 Bsort1 Bsort2 SUGGEST DAMAGED LOST)) { + # build categories list + my $sth = $dbh->prepare("select distinct category from authorised_values"); + $sth->execute; + my @category_list; + my %categories; # a hash, to check that some hardcoded categories exist. + while ( my ($category) = $sth->fetchrow_array ) { + push( @category_list, $category ); + $categories{$category} = 1; + } + + # push koha system categories + foreach (qw(Asort1 Asort2 Bsort1 Bsort2 SUGGEST DAMAGED LOST REPORT_GROUP REPORT_SUBGROUP)) { push @category_list, $_ unless $categories{$_}; } diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index 91d42a2..ff1c0f1 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -97,7 +97,7 @@ CREATE TABLE `auth_types` ( DROP TABLE IF EXISTS `authorised_values`; CREATE TABLE `authorised_values` ( -- stores values for authorized values categories and values `id` int(11) NOT NULL auto_increment, -- unique key, used to identify the authorized value - `category` varchar(10) NOT NULL default '', -- key used to identify the authorized value category + `category` varchar(16) NOT NULL default '', -- key used to identify the authorized value category `authorised_value` varchar(80) NOT NULL default '', -- code use to identify the authorized value `lib` varchar(80) default NULL, -- authorized value description as printed in the staff client `lib_opac` VARCHAR(80) default NULL, -- authorized value description as printed in the OPAC @@ -1626,8 +1626,9 @@ CREATE TABLE reports_dictionary ( -- definitions (or snippets of SQL) stored for `date_created` datetime default NULL, -- date and time this definition was created `date_modified` datetime default NULL, -- date and time this definition was last modified `saved_sql` text, -- SQL snippet for us in reports - `area` int(11) default NULL, -- Koha module this definition is for (1 = Circulation, 2 = Catalog, 3 = Patrons, 4 = Acquistions, 5 = Accounts) - PRIMARY KEY (`id`) + report_area varchar(6) DEFAULT NULL, -- Koha module this definition is for Circulation, Catalog, Patrons, Acquistions, Accounts) + PRIMARY KEY (id), + KEY dictionary_area_idx (report_area) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- @@ -1725,7 +1726,11 @@ CREATE TABLE saved_sql ( -- saved sql reports `notes` text, -- the notes or description given to this report `cache_expiry` int NOT NULL default 300, `public` boolean NOT NULL default FALSE, + report_area varchar(6) default NULL, + report_group varchar(80) default NULL, + report_subgroup varchar(80) default NULL, PRIMARY KEY (`id`), + KEY sql_area_group_idx (report_group, report_subgroup), KEY boridx (`borrowernumber`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index 25cd367..3f713eb 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -5696,6 +5696,38 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) { SetVersion($DBversion); } + + +$DBversion = "3.09.00.XXX"; +if (C4::Context->preference("Version") < TransformToNum($DBversion)) { + $dbh->do("ALTER TABLE authorised_values MODIFY category varchar(16) NOT NULL DEFAULT '';"); + $dbh->do("INSERT INTO authorised_values (category, authorised_value, lib) VALUES + ('REPORT_GROUP', 'CIRC', 'Circulation'), + ('REPORT_GROUP', 'CAT', 'Catalog'), + ('REPORT_GROUP', 'PAT', 'Patrons'), + ('REPORT_GROUP', 'ACQ', 'Acquisitions'), + ('REPORT_GROUP', 'ACC', 'Accounts');"); + + $dbh->do("ALTER TABLE reports_dictionary ADD report_area varchar(6) DEFAULT NULL;"); + $dbh->do("UPDATE reports_dictionary SET report_area = CASE area + WHEN 1 THEN 'CIRC' + WHEN 2 THEN 'CAT' + WHEN 3 THEN 'PAT' + WHEN 4 THEN 'ACQ' + WHEN 5 THEN 'ACC' + END;"); + $dbh->do("ALTER TABLE reports_dictionary DROP area;"); + $dbh->do("ALTER TABLE reports_dictionary ADD KEY dictionary_area_idx (report_area);"); + + $dbh->do("ALTER TABLE saved_sql ADD report_area varchar(6) DEFAULT NULL;"); + $dbh->do("ALTER TABLE saved_sql ADD report_group varchar(80) DEFAULT NULL;"); + $dbh->do("ALTER TABLE saved_sql ADD report_subgroup varchar(80) DEFAULT NULL;"); + $dbh->do("ALTER TABLE saved_sql ADD KEY sql_area_group_idx (report_group, report_subgroup);"); + + print "Upgrade to $DBversion done saved_sql new fields report_group and report_area; authorised_values.category 16 char \n"; + SetVersion($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reports/dictionary.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/reports/dictionary.tt index 102a6ee..2e15834 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/reports/dictionary.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reports/dictionary.tt @@ -33,7 +33,7 @@ [% IF ( areas ) %] - Filter by area [% FOREACH area IN areas %] [% IF ( area.selected ) %] @@ -103,7 +103,7 @@
    1. - [% FOREACH area IN areas %] [% END %] diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reports/guided_reports_start.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/reports/guided_reports_start.tt index 21d8235..aa6a627 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/reports/guided_reports_start.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reports/guided_reports_start.tt @@ -24,6 +24,25 @@ + [% END %] +
    2. +
    3. + [% END %]
    @@ -553,6 +604,11 @@ canned reports and writing custom SQL reports.

    [% END %] [% IF ( create ) %] +
    Create report from SQL @@ -561,6 +617,31 @@ canned reports and writing custom SQL reports.

    [% IF ( reportname ) %] [% ELSE %][% END %] + [% IF groups_with_subgroups %] +
  • +
  • + [% END %] [% IF (public) %]
  • [% ELSE %] @@ -645,6 +726,11 @@ Sub report: @@ -652,6 +738,31 @@ Sub report: + [% IF groups_with_subgroups %] +
  • +
  • + [% END %] [% IF (public) %]
  • [% ELSE %] @@ -719,12 +830,43 @@ Sub report:

    Filter

      +
    1. + + +
    2. [% INCLUDE 'date-format.inc' %]
      diff --git a/misc/cronjobs/runreport.pl b/misc/cronjobs/runreport.pl index 6a8cce7..690f216 100755 --- a/misc/cronjobs/runreport.pl +++ b/misc/cronjobs/runreport.pl @@ -186,14 +186,26 @@ unless (scalar(@ARGV)) { ($verbose) and print scalar(@ARGV), " argument(s) after options: " . join(" ", @ARGV) . "\n"; -foreach my $report (@ARGV) { - my ($sql, $type) = get_saved_report($report); - unless ($sql) { - carp "ERROR: No saved report $report found"; +foreach my $report_id (@ARGV) { + my $report = get_saved_report($report_id); + unless ($report) { + warn "ERROR: No saved report $report_id found"; next; } + my $sql => $report->{savedsql}; + my $report_name => $report->{report_name}; + my $type => $report->{type}; + $verbose and print "SQL: $sql\n\n"; - # my $results = execute_query($sql, undef, 0, 99999, $format, $report); + if (defined($report_name) and $report_name ne "") + { + $subject = $report_name ; + } + else + { + $subject = 'Koha Saved Report'; + } + # my $results = execute_query($sql, undef, 0, 99999, $format, $report_id); my ($sth) = execute_query($sql); # execute_query(sql, , 0, 20, , ) my $count = scalar($sth->rows); diff --git a/opac/svc/report b/opac/svc/report index 86b7a00..38cbd42 100755 --- a/opac/svc/report +++ b/opac/svc/report @@ -31,38 +31,34 @@ my $query = CGI->new(); my $report_id = $query->param('id'); my $report_name = $query->param('name'); -my $cache; -my $sql; -my $type; -my $notes; -my $cache_expiry; -my $public; +my $report_rec = get_saved_report( $report_name ? { 'name' => $report_name } : { 'id' => $report_id } ); +die "Sorry this report is not public\n" unless $report_rec->{public}; -( $sql, $type, $report_name, $notes, $cache_expiry, $public, $report_id ) = - get_saved_report($report_name ? { 'name' => $report_name } : { 'id' => $report_id } ); -die "Sorry this report is not public\n" unless $public; -if (Koha::Cache->is_cache_active) { - $cache = Koha::Cache->new( - ); - my $page = $cache->get_from_cache("opac:report:$report_id"); - if ($page) { - print $query->header; - print $page; - exit; - } +my $cache_active = Koha::Cache->is_cache_active; +my ($cache_key, $cache, $json_text); +if ($cache_active) { + $cache_key = "opac:report:".($report_name ? "name:$report_name" : "id:$report_id"); + $cache = Koha::Cache->new(); + $json_text = $cache->get_from_cache($cache_key); } -print $query->header; -if ($sql) { +unless ($json_text) { my $offset = 0; my $limit = C4::Context->preference("SvcMaxReportRows") || 10; - my ( $sth, $errors ) = execute_query( $sql, $offset, $limit ); - my $lines = $sth->fetchall_arrayref; - my $json_text = to_json($lines); - print $json_text; + my ( $sth, $errors ) = execute_query( $report_rec->{savedsql}, $offset, $limit ); + if ($sth) { + my $lines = $sth->fetchall_arrayref; + $json_text = to_json($lines); - if (Koha::Cache->is_cache_active) { - $cache->set_in_cache( "opac:report:$report_id", $json_text, $cache_expiry ); + if ($cache_active) { + $cache->set_in_cache( $cache_key, $json_text, $report_rec->{cache_expiry} ); + } + } + else { + $json_text = to_json($errors); } } + +print $query->header; +print $json_text; diff --git a/reports/dictionary.pl b/reports/dictionary.pl index 4f94d69..aae96c9 100755 --- a/reports/dictionary.pl +++ b/reports/dictionary.pl @@ -37,11 +37,12 @@ my $input = new CGI; my $referer = $input->referer(); my $phase = $input->param('phase') || 'View Dictionary'; -my $area = $input->param('areas') || ''; -my $no_html = 0; # this will be set if we dont want to print out an html::template -my ( $template, $borrowernumber, $cookie ) = get_template_and_user( - { - template_name => "reports/dictionary.tmpl", +my $definition_name = $input->param('definition_name'); +my $definition_description = $input->param('definition_description'); +my $area = $input->param('area') || ''; +my $no_html = 0; # this will be set if we dont want to print out an html::template +my ( $template, $borrowernumber, $cookie ) = get_template_and_user( + { template_name => "reports/dictionary.tmpl", query => $input, type => "intranet", authnotrequired => 0, @@ -51,78 +52,71 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user( ); if ($phase eq 'View Dictionary'){ - # view the dictionary we use to set up abstract variables such as all borrowers over fifty who live in a certain town - my $areas = get_report_areas(); - foreach (@{ $areas }) { - $_->{selected} = 1 if $_->{id} eq $area; # mark active area - } - my $definitions = get_from_dictionary($area); - $template->param( 'areas' => $areas , - 'start_dictionary' => 1, - 'definitions' => $definitions, - ); -} -elsif ($phase eq 'Add New Definition'){ - # display form allowing them to add a new definition - $template->param( 'new_dictionary' => 1, - ); + # view the dictionary we use to set up abstract variables such as all borrowers over fifty who live in a certain town + my $definitions = get_from_dictionary($area); + $template->param( + 'areas'=> areas(), + 'start_dictionary' => 1, + 'definitions' => $definitions, + ); +} elsif ( $phase eq 'Add New Definition' ) { + + # display form allowing them to add a new definition + $template->param( 'new_dictionary' => 1, ); } -elsif ($phase eq 'New Term step 2'){ - # Choosing the area - my $areas = C4::Reports::Guided::get_report_areas(); - my $definition_name=$input->param('definition_name'); - my $definition_description=$input->param('definition_description'); - $template->param( 'step_2' => 1, - 'areas' => $areas, - 'definition_name' => $definition_name, - 'definition_description' => $definition_description, - ); +elsif ( $phase eq 'New Term step 2' ) { + + # Choosing the area + $template->param( + 'step_2' => 1, + 'areas' => areas(), + 'definition_name' => $definition_name, + 'definition_description' => $definition_description, + ); } -elsif ($phase eq 'New Term step 3'){ - # Choosing the columns - my $area = $input->param('areas'); - my $columns = get_columns($area,$input); - my $definition_name=$input->param('definition_name'); - my $definition_description=$input->param('definition_description'); - $template->param( 'step_3' => 1, - 'area' => $area, - 'columns' => $columns, - 'definition_name' => $definition_name, - 'definition_description' => $definition_description, - ); +elsif ( $phase eq 'New Term step 3' ) { + + # Choosing the columns + my $columns = get_columns( $area, $input ); + $template->param( + 'step_3' => 1, + 'area' => $area, + 'columns' => $columns, + 'definition_name' => $definition_name, + 'definition_description' => $definition_description, + ); } -elsif ($phase eq 'New Term step 4'){ - # Choosing the values - my $area=$input->param('area'); - my $definition_name=$input->param('definition_name'); - my $definition_description=$input->param('definition_description'); - my @columns = $input->param('columns'); - my $columnstring = join (',', at columns); - my @column_loop; - foreach my $column (@columns){ - my %tmp_hash; - $tmp_hash{'name'}=$column; - my $type =get_column_type($column); - if ($type eq 'distinct'){ - my $values = get_distinct_values($column); - $tmp_hash{'values'} = $values; - $tmp_hash{'distinct'} = 1; - - } - if ($type eq 'DATE' || $type eq 'DATETIME'){ - $tmp_hash{'date'}=1; - } - if ($type eq 'TEXT' || $type eq 'MEDIUMTEXT'){ - $tmp_hash{'text'}=1; - } -# else { -# warn $type;# -# } - push @column_loop,\%tmp_hash; - } +elsif ( $phase eq 'New Term step 4' ) { + + # Choosing the values + my @columns = $input->param('columns'); + my $columnstring = join( ',', @columns ); + my @column_loop; + foreach my $column (@columns) { + my %tmp_hash; + $tmp_hash{'name'} = $column; + my $type = get_column_type($column); + if ( $type eq 'distinct' ) { + my $values = get_distinct_values($column); + $tmp_hash{'values'} = $values; + $tmp_hash{'distinct'} = 1; + + } + if ( $type eq 'DATE' || $type eq 'DATETIME' ) { + $tmp_hash{'date'} = 1; + } + if ( $type eq 'TEXT' ) { + $tmp_hash{'text'} = 1; + } + + # else { + # warn $type;# + # } + push @column_loop, \%tmp_hash; + } $template->param( 'step_4' => 1, 'area' => $area, @@ -134,83 +128,78 @@ elsif ($phase eq 'New Term step 4'){ ); } -elsif ($phase eq 'New Term step 5'){ - # Confirmation screen - my $areas = C4::Reports::Guided::get_report_areas(); - my $area = $input->param('area'); - my $areaname = $areas->[$area - 1]->{'name'}; - my $columnstring = $input->param('columnstring'); - my $definition_name=$input->param('definition_name'); - my $definition_description=$input->param('definition_description'); - my @criteria = $input->param('criteria_column'); - my $query_criteria; - my @criteria_loop; - foreach my $crit (@criteria) { - my $value = $input->param( $crit . "_value" ); - if ($value) { - my %tmp_hash; - $tmp_hash{'name'}=$crit; - $tmp_hash{'value'} = $value; - push @criteria_loop,\%tmp_hash; - if ($value =~ C4::Dates->regexp(C4::Context->preference('dateformat'))) { - my $date = C4::Dates->new($value); - $value = $date->output("iso"); - } - $query_criteria .= " AND $crit='$value'"; - } - $value = $input->param( $crit . "_start_value" ); - if ($value) { - my %tmp_hash; - $tmp_hash{'name'}="$crit Start"; - $tmp_hash{'value'} = $value; - push @criteria_loop,\%tmp_hash; - if ($value =~ C4::Dates->regexp(C4::Context->preference('dateformat'))) { - my $date = C4::Dates->new($value); - $value = $date->output("iso"); - } - $query_criteria .= " AND $crit >= '$value'"; - } - $value = $input->param( $crit . "_end_value" ); - if ($value) { - my %tmp_hash; - $tmp_hash{'name'}="$crit End"; - $tmp_hash{'value'} = $value; - push @criteria_loop,\%tmp_hash; - if ($value =~ C4::Dates->regexp(C4::Context->preference('dateformat'))) { - my $date = C4::Dates->new($value); - $value = $date->output("iso"); - } - $query_criteria .= " AND $crit <= '$value'"; - } - } - $template->param( 'step_5' => 1, - 'area' => $area, - 'areaname' => $areaname, - 'definition_name' => $definition_name, - 'definition_description' => $definition_description, - 'query' => $query_criteria, - 'columnstring' => $columnstring, - 'criteria_loop' => \@criteria_loop, - ); +elsif ( $phase eq 'New Term step 5' ) { + # Confirmation screen + my $columnstring = $input->param('columnstring'); + my @criteria = $input->param('criteria_column'); + my $query_criteria; + my @criteria_loop; + + foreach my $crit (@criteria) { + my $value = $input->param( $crit . "_value" ); + if ($value) { + my %tmp_hash; + $tmp_hash{'name'} = $crit; + $tmp_hash{'value'} = $value; + push @criteria_loop, \%tmp_hash; + if ( $value =~ C4::Dates->regexp( C4::Context->preference('dateformat') ) ) { + my $date = C4::Dates->new($value); + $value = $date->output("iso"); + } + $query_criteria .= " AND $crit='$value'"; + } + $value = $input->param( $crit . "_start_value" ); + if ($value) { + my %tmp_hash; + $tmp_hash{'name'} = "$crit Start"; + $tmp_hash{'value'} = $value; + push @criteria_loop, \%tmp_hash; + if ( $value =~ C4::Dates->regexp( C4::Context->preference('dateformat') ) ) { + my $date = C4::Dates->new($value); + $value = $date->output("iso"); + } + $query_criteria .= " AND $crit >= '$value'"; + } + $value = $input->param( $crit . "_end_value" ); + if ($value) { + my %tmp_hash; + $tmp_hash{'name'} = "$crit End"; + $tmp_hash{'value'} = $value; + push @criteria_loop, \%tmp_hash; + if ( $value =~ C4::Dates->regexp( C4::Context->preference('dateformat') ) ) { + my $date = C4::Dates->new($value); + $value = $date->output("iso"); + } + $query_criteria .= " AND $crit <= '$value'"; + } + } + my %report_areas = map @$_, get_report_areas(); + $template->param( + 'step_5' => 1, + 'area' => $area, + 'areaname' => $report_areas{$area}, + 'definition_name' => $definition_name, + 'definition_description' => $definition_description, + 'query' => $query_criteria, + 'columnstring' => $columnstring, + 'criteria_loop' => \@criteria_loop, + ); } -elsif ($phase eq 'New Term step 6'){ - # Saving - my $area = $input->param('area'); - my $definition_name=$input->param('definition_name'); - my $definition_description=$input->param('definition_description'); - my $sql=$input->param('sql'); - save_dictionary($definition_name,$definition_description,$sql,$area); - $no_html=1; - print $input->redirect("/cgi-bin/koha/reports/dictionary.pl?phase=View%20Dictionary"); - +elsif ( $phase eq 'New Term step 6' ) { + # Saving + my $area = $input->param('area'); + my $sql = $input->param('sql'); + save_dictionary( $definition_name, $definition_description, $sql, $area ); + $no_html = 1; + print $input->redirect("/cgi-bin/koha/reports/dictionary.pl?phase=View%20Dictionary"); + +} elsif ( $phase eq 'Delete Definition' ) { + $no_html = 1; + my $id = $input->param('id'); + delete_definition($id); + print $input->redirect("/cgi-bin/koha/reports/dictionary.pl?phase=View%20Dictionary"); } -elsif ($phase eq 'Delete Definition'){ - $no_html=1; - my $id = $input->param('id'); - delete_definition($id); - print $input->redirect("/cgi-bin/koha/reports/dictionary.pl?phase=View%20Dictionary"); - } $template->param( 'referer' => $referer ); @@ -218,3 +207,16 @@ $template->param( 'referer' => $referer ); if (!$no_html){ output_html_with_http_headers $input, $cookie, $template->output; } + +sub areas { + my $areas = get_report_areas(); + my @a; + foreach (@$areas) { + push @a, { + id => $_->[0], + name => $_->[1], + selected => ($_->[0] eq $area), + }; + } + return \@a; +} diff --git a/reports/guided_reports.pl b/reports/guided_reports.pl index 25a2ffb..85247d3 100755 --- a/reports/guided_reports.pl +++ b/reports/guided_reports.pl @@ -18,14 +18,15 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. use strict; -#use warnings; FIXME - Bug 2505 +use warnings; + use CGI; use Text::CSV; use URI::Escape; use C4::Reports::Guided; use C4::Auth qw/:DEFAULT get_session/; use C4::Output; -use C4::Dates; +use C4::Dates qw/format_date/; use C4::Debug; use C4::Branch; # XXX subfield_is_koha_internal_p use Koha::Cache; @@ -69,7 +70,7 @@ my $session = $cookie ? get_session($cookie->value) : undef; my $filter; if ( $input->param("filter_set") ) { $filter = {}; - $filter->{$_} = $input->param("filter_$_") foreach qw/date author keyword/; + $filter->{$_} = $input->param("filter_$_") foreach qw/date author keyword group subgroup/; $session->param('report_filter', $filter) if $session; $template->param( 'filter_set' => 1 ); } @@ -86,21 +87,27 @@ if ( !$phase ) { elsif ( $phase eq 'Build new' ) { # build a new report $template->param( 'build1' => 1 ); - $template->param( 'areas' => get_report_areas(), 'usecache' => $usecache, 'cache_expiry' => 300, 'public' => '0' ); -} -elsif ( $phase eq 'Use saved' ) { + my $areas = get_report_areas(); + $template->param( + 'areas' => [map { id => $_->[0], name => $_->[1] }, @$areas], + 'usecache' => $usecache, + 'cache_expiry' => 300, + 'public' => '0', + ); +} elsif ( $phase eq 'Use saved' ) { + # use a saved report # get list of reports and display them + my $group = $input->param('group'); + my $subgroup = $input->param('subgroup'); + $filter->{group} = $group; + $filter->{subgroup} = $subgroup; $template->param( 'saved1' => 1, 'savedreports' => get_saved_reports($filter), 'usecache' => $usecache, + 'groups_with_subgroups'=> groups_with_subgroups($group, $subgroup), ); - if ($filter) { - while ( my ($k, $v) = each %$filter ) { - $template->param( "filter_$k" => $v ) if $v; - } - } } elsif ( $phase eq 'Delete Saved') { @@ -114,30 +121,33 @@ elsif ( $phase eq 'Delete Saved') { elsif ( $phase eq 'Show SQL'){ - my $id = $input->param('reports'); - my ($sql,$type,$reportname,$notes) = get_saved_report($id); - $template->param( + my $id = $input->param('reports'); + my $report = get_saved_report($id); + $template->param( 'id' => $id, - 'reportname' => $reportname, - 'notes' => $notes, - 'sql' => $sql, - 'showsql' => 1, + 'reportname' => $report->{report_name}, + 'notes' => $report->{notes}, + 'sql' => $report->{savedsql}, + 'showsql' => 1, ); } elsif ( $phase eq 'Edit SQL'){ my $id = $input->param('reports'); - my ($sql,$type,$reportname,$notes, $cache_expiry, $public) = get_saved_report($id); + my $report = get_saved_report($id); + my $group = $report->{report_group}; + my $subgroup = $report->{report_subgroup}; $template->param( - 'sql' => $sql, - 'reportname' => $reportname, - 'notes' => $notes, + 'sql' => $report->{savedsql}, + 'reportname' => $report->{report_name}, + 'groups_with_subgroups' => groups_with_subgroups($group, $subgroup), + 'notes' => $report->{notes}, 'id' => $id, - 'cache_expiry' => $cache_expiry, - 'public' => $public, + 'cache_expiry' => $report->{cache_expiry}, + 'public' => $report->{public}, 'usecache' => $usecache, - 'editsql' => 1, + 'editsql' => 1, ); } @@ -145,6 +155,8 @@ elsif ( $phase eq 'Update SQL'){ my $id = $input->param('id'); my $sql = $input->param('sql'); my $reportname = $input->param('reportname'); + my $group = $input->param('group'); + my $subgroup = $input->param('subgroup'); my $notes = $input->param('notes'); my $cache_expiry = $input->param('cache_expiry'); my $cache_expiry_units = $input->param('cache_expiry_units'); @@ -178,16 +190,22 @@ elsif ( $phase eq 'Update SQL'){ 'errors' => \@errors, 'sql' => $sql, ); - } - else { - update_sql( $id, $sql, $reportname, $notes, $cache_expiry, $public ); + } else { + update_sql( $id, { + sql => $sql, + name => $reportname, + group => $group, + subgroup => $subgroup, + notes => $notes, + cache_expiry => $cache_expiry, + public => $public, + } ); $template->param( 'save_successful' => 1, 'reportname' => $reportname, 'id' => $id, ); } - } elsif ($phase eq 'retrieve results') { @@ -229,7 +247,7 @@ elsif ( $phase eq 'Report on this Area' ) { # they have choosen a new report and the area to report on $template->param( 'build2' => 1, - 'area' => $input->param('areas'), + 'area' => $input->param('area'), 'types' => get_report_types(), 'cache_expiry' => $cache_expiry, 'public' => $input->param('public'), @@ -276,42 +294,42 @@ elsif ( $phase eq 'Choose these criteria' ) { my $area = $input->param('area'); my $type = $input->param('type'); my $column = $input->param('column'); - my @definitions = $input->param('definition'); - my $definition = join (',', at definitions); + my @definitions = $input->param('definition'); + my $definition = join (',', at definitions); my @criteria = $input->param('criteria_column'); - my $query_criteria; + my $query_criteria; foreach my $crit (@criteria) { my $value = $input->param( $crit . "_value" ); - - # If value is not defined, then it may be range values - if (!defined $value) { - - my $fromvalue = $input->param( "from_" . $crit . "_value" ); - my $tovalue = $input->param( "to_" . $crit . "_value" ); - - # If the range values are dates - if ($fromvalue =~ C4::Dates->regexp('syspref') && $tovalue =~ C4::Dates->regexp('syspref')) { - $fromvalue = C4::Dates->new($fromvalue)->output("iso"); - $tovalue = C4::Dates->new($tovalue)->output("iso"); - } - - if ($fromvalue && $tovalue) { - $query_criteria .= " AND $crit >= '$fromvalue' AND $crit <= '$tovalue'"; - } - - } else { - - # If value is a date - if ($value =~ C4::Dates->regexp('syspref')) { - $value = C4::Dates->new($value)->output("iso"); - } - # don't escape runtime parameters, they'll be at runtime - if ($value =~ /<<.*>>/) { - $query_criteria .= " AND $crit=$value"; + + # If value is not defined, then it may be range values + if (!defined $value) { + + my $fromvalue = $input->param( "from_" . $crit . "_value" ); + my $tovalue = $input->param( "to_" . $crit . "_value" ); + + # If the range values are dates + if ($fromvalue =~ C4::Dates->regexp('syspref') && $tovalue =~ C4::Dates->regexp('syspref')) { + $fromvalue = C4::Dates->new($fromvalue)->output("iso"); + $tovalue = C4::Dates->new($tovalue)->output("iso"); + } + + if ($fromvalue && $tovalue) { + $query_criteria .= " AND $crit >= '$fromvalue' AND $crit <= '$tovalue'"; + } + } else { - $query_criteria .= " AND $crit='$value'"; + + # If value is a date + if ($value =~ C4::Dates->regexp('syspref')) { + $value = C4::Dates->new($value)->output("iso"); + } + # don't escape runtime parameters, they'll be at runtime + if ($value =~ /<<.*>>/) { + $query_criteria .= " AND $crit=$value"; + } else { + $query_criteria .= " AND $crit='$value'"; + } } - } } $template->param( 'build5' => 1, @@ -413,6 +431,7 @@ elsif ( $phase eq 'Build report' ) { build_query( \@columns, $query_criteria, $query_orderby, $area, $totals, $definition ); $template->param( 'showreport' => 1, + 'area' => $area, 'sql' => $sql, 'type' => $type, 'cache_expiry' => $input->param('cache_expiry'), @@ -421,23 +440,29 @@ elsif ( $phase eq 'Build report' ) { } elsif ( $phase eq 'Save' ) { - # Save the report that has just been built + # Save the report that has just been built + my $area = $input->param('area'); my $sql = $input->param('sql'); my $type = $input->param('type'); $template->param( 'save' => 1, + 'area' => $area, 'sql' => $sql, 'type' => $type, 'cache_expiry' => $input->param('cache_expiry'), 'public' => $input->param('public'), + 'groups_with_subgroups' => groups_with_subgroups($area), # in case we have a report group that matches area ); } elsif ( $phase eq 'Save Report' ) { - # save the sql pasted in by a user - my $sql = $input->param('sql'); - my $name = $input->param('reportname'); - my $type = $input->param('types'); + # save the sql pasted in by a user + my $area = $input->param('area'); + my $group = $input->param('group'); + my $subgroup = $input->param('subgroup'); + my $sql = $input->param('sql'); + my $name = $input->param('reportname'); + my $type = $input->param('types'); my $notes = $input->param('notes'); my $cache_expiry = $input->param('cache_expiry'); my $cache_expiry_units = $input->param('cache_expiry_units'); @@ -455,7 +480,7 @@ elsif ( $phase eq 'Save Report' ) { } } # check $cache_expiry isnt too large, Memcached::set requires it to be less than 30 days or it will be treated as if it were an absolute time stamp - if( $cache_expiry >= 2592000 ){ + if( $cache_expiry && $cache_expiry >= 2592000 ){ push @errors, {cache_expiry => $cache_expiry}; } ## FIXME this is AFTER entering a name to save the report under @@ -463,7 +488,7 @@ elsif ( $phase eq 'Save Report' ) { push @errors, {sqlerr => $1}; } elsif ($sql !~ /^(SELECT)/i) { - push @errors, {queryerr => 1}; + push @errors, {queryerr => "No SELECT"}; } if (@errors) { $template->param( @@ -477,161 +502,174 @@ elsif ( $phase eq 'Save Report' ) { ); } else { - my $id = save_report( $borrowernumber, $sql, $name, $type, $notes, $cache_expiry, $public ); - $template->param( - 'save_successful' => 1, - 'reportname' => $name, - 'id' => $id, - ); + save_report( { + borrowernumber => $borrowernumber, + sql => $sql, + name => $name, + area => $area, + group => $group, + subgroup => $subgroup, + type => $type, + notes => $notes, + cache_expiry => $cache_expiry, + public => $public, + } ); + $template->param( 'save_successful' => 1, ); } } elsif ($phase eq 'Run this report'){ # execute a saved report - my $limit = 20; # page size. # TODO: move to DB or syspref? - my $offset = 0; - my $report = $input->param('reports'); + my $limit = 20; # page size. # TODO: move to DB or syspref? + my $offset = 0; + my $report_id = $input->param('reports'); my @sql_params = $input->param('sql_params'); # offset algorithm if ($input->param('page')) { $offset = ($input->param('page') - 1) * $limit; } - my ($sql,$type,$name,$notes) = get_saved_report($report); - unless ($sql) { - push @errors, {no_sql_for_id=>$report}; - } - my @rows = (); - # if we have at least 1 parameter, and it's not filled, then don't execute but ask for parameters - if ($sql =~ /<>/,$sql; - my @tmpl_parameters; - for(my $i=0;$i<($#split/2);$i++) { - my ($text,$authorised_value) = split /\|/,$split[$i*2+1]; - my $input; - my $labelid; - if ($authorised_value eq "date") { - $input = 'date'; - } - elsif ($authorised_value) { - my $dbh=C4::Context->dbh; - my @authorised_values; - my %authorised_lib; - # builds list, depending on authorised value... - if ( $authorised_value eq "branches" ) { - my $branches = GetBranchesLoop(); - foreach my $thisbranch (@$branches) { - push @authorised_values, $thisbranch->{value}; - $authorised_lib{$thisbranch->{value}} = $thisbranch->{branchname}; - } + + my ( $sql, $type, $name, $notes ); + if (my $report = get_saved_report($report_id)) { + $sql = $report->{savedsql}; + $name = $report->{report_name}; + $notes = $report->{notes}; + + my @rows = (); + # if we have at least 1 parameter, and it's not filled, then don't execute but ask for parameters + if ($sql =~ /<>/,$sql; + my @tmpl_parameters; + for(my $i=0;$i<($#split/2);$i++) { + my ($text,$authorised_value) = split /\|/,$split[$i*2+1]; + my $input; + my $labelid; + if ($authorised_value eq "date") { + $input = 'date'; } - elsif ( $authorised_value eq "itemtypes" ) { - my $sth = $dbh->prepare("SELECT itemtype,description FROM itemtypes ORDER BY description"); - $sth->execute; - while ( my ( $itemtype, $description ) = $sth->fetchrow_array ) { - push @authorised_values, $itemtype; - $authorised_lib{$itemtype} = $description; + elsif ($authorised_value) { + my $dbh=C4::Context->dbh; + my @authorised_values; + my %authorised_lib; + # builds list, depending on authorised value... + if ( $authorised_value eq "branches" ) { + my $branches = GetBranchesLoop(); + foreach my $thisbranch (@$branches) { + push @authorised_values, $thisbranch->{value}; + $authorised_lib{$thisbranch->{value}} = $thisbranch->{branchname}; + } } - } - elsif ( $authorised_value eq "cn_source" ) { - my $class_sources = GetClassSources(); - my $default_source = C4::Context->preference("DefaultClassificationSource"); - foreach my $class_source (sort keys %$class_sources) { - next unless $class_sources->{$class_source}->{'used'} or - ($class_source eq $default_source); - push @authorised_values, $class_source; - $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'}; + elsif ( $authorised_value eq "itemtypes" ) { + my $sth = $dbh->prepare("SELECT itemtype,description FROM itemtypes ORDER BY description"); + $sth->execute; + while ( my ( $itemtype, $description ) = $sth->fetchrow_array ) { + push @authorised_values, $itemtype; + $authorised_lib{$itemtype} = $description; + } } - } - elsif ( $authorised_value eq "categorycode" ) { - my $sth = $dbh->prepare("SELECT categorycode, description FROM categories ORDER BY description"); - $sth->execute; - while ( my ( $categorycode, $description ) = $sth->fetchrow_array ) { - push @authorised_values, $categorycode; - $authorised_lib{$categorycode} = $description; + elsif ( $authorised_value eq "cn_source" ) { + my $class_sources = GetClassSources(); + my $default_source = C4::Context->preference("DefaultClassificationSource"); + foreach my $class_source (sort keys %$class_sources) { + next unless $class_sources->{$class_source}->{'used'} or + ($class_source eq $default_source); + push @authorised_values, $class_source; + $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'}; + } } + elsif ( $authorised_value eq "categorycode" ) { + my $sth = $dbh->prepare("SELECT categorycode, description FROM categories ORDER BY description"); + $sth->execute; + while ( my ( $categorycode, $description ) = $sth->fetchrow_array ) { + push @authorised_values, $categorycode; + $authorised_lib{$categorycode} = $description; + } + + #---- "true" authorised value + } + else { + my $authorised_values_sth = $dbh->prepare("SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib"); - #---- "true" authorised value - } - else { - my $authorised_values_sth = $dbh->prepare("SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib"); - - $authorised_values_sth->execute( $authorised_value); + $authorised_values_sth->execute( $authorised_value); - while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) { - push @authorised_values, $value; - $authorised_lib{$value} = $lib; - # For item location, we show the code and the libelle - $authorised_lib{$value} = $lib; + while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) { + push @authorised_values, $value; + $authorised_lib{$value} = $lib; + # For item location, we show the code and the libelle + $authorised_lib{$value} = $lib; + } } - } - $labelid = $text; - $labelid =~ s/\W//g; - $input =CGI::scrolling_list( # FIXME: factor out scrolling_list - -name => "sql_params", - -id => "sql_params_".$labelid, - -values => \@authorised_values, + $labelid = $text; + $labelid =~ s/\W//g; + $input =CGI::scrolling_list( # FIXME: factor out scrolling_list + -name => "sql_params", + -id => "sql_params_".$labelid, + -values => \@authorised_values, # -default => $value, - -labels => \%authorised_lib, - -override => 1, - -size => 1, - -multiple => 0, - -tabindex => 1, - ); - - } else { - $input = "text"; + -labels => \%authorised_lib, + -override => 1, + -size => 1, + -multiple => 0, + -tabindex => 1, + ); + } else { + $input = "text"; + } + push @tmpl_parameters, {'entry' => $text, 'input' => $input, 'labelid' => $labelid }; } - push @tmpl_parameters, {'entry' => $text, 'input' => $input, 'labelid' => $labelid }; - } - $template->param('sql' => $sql, - 'name' => $name, - 'sql_params' => \@tmpl_parameters, - 'enter_params' => 1, - 'reports' => $report, - ); - } else { - # OK, we have parameters, or there are none, we run the report - # if there were parameters, replace before running - # split on ??. Each odd (2,4,6,...) entry should be a parameter to fill - my @split = split /<<|>>/,$sql; - my @tmpl_parameters; - for(my $i=0;$i<$#split/2;$i++) { - my $quoted = C4::Context->dbh->quote($sql_params[$i]); - # if there are special regexp chars, we must \ them - $split[$i*2+1] =~ s/(\||\?|\.|\*|\(|\)|\%)/\\$1/g; - $sql =~ s/<<$split[$i*2+1]>>/$quoted/; - } - my ($sth, $errors) = execute_query($sql, $offset, $limit); - my $total = nb_rows($sql) || 0; - unless ($sth) { - die "execute_query failed to return sth for report $report: $sql"; + $template->param('sql' => $sql, + 'name' => $name, + 'sql_params' => \@tmpl_parameters, + 'enter_params' => 1, + 'reports' => $report_id, + ); } else { - my $headref = $sth->{NAME} || []; - my @headers = map { +{ cell => $_ } } @$headref; - $template->param(header_row => \@headers); - while (my $row = $sth->fetchrow_arrayref()) { - my @cells = map { +{ cell => $_ } } @$row; - push @rows, { cells => \@cells }; + # OK, we have parameters, or there are none, we run the report + # if there were parameters, replace before running + # split on ??. Each odd (2,4,6,...) entry should be a parameter to fill + my @split = split /<<|>>/,$sql; + my @tmpl_parameters; + for(my $i=0;$i<$#split/2;$i++) { + my $quoted = C4::Context->dbh->quote($sql_params[$i]); + # if there are special regexp chars, we must \ them + $split[$i*2+1] =~ s/(\||\?|\.|\*|\(|\)|\%)/\\$1/g; + $sql =~ s/<<$split[$i*2+1]>>/$quoted/; + } + my ($sth, $errors) = execute_query($sql, $offset, $limit); + my $total = nb_rows($sql) || 0; + unless ($sth) { + die "execute_query failed to return sth for report $report_id: $sql"; + } else { + my $headref = $sth->{NAME} || []; + my @headers = map { +{ cell => $_ } } @$headref; + $template->param(header_row => \@headers); + while (my $row = $sth->fetchrow_arrayref()) { + my @cells = map { +{ cell => $_ } } @$row; + push @rows, { cells => \@cells }; + } } - } - my $totpages = int($total/$limit) + (($total % $limit) > 0 ? 1 : 0); - my $url = "/cgi-bin/koha/reports/guided_reports.pl?reports=$report&phase=Run%20this%20report"; - if (@sql_params) { - $url = join('&sql_params=', $url, map { URI::Escape::uri_escape($_) } @sql_params); + my $totpages = int($total/$limit) + (($total % $limit) > 0 ? 1 : 0); + my $url = "/cgi-bin/koha/reports/guided_reports.pl?reports=$report_id&phase=Run%20this%20report"; + if (@sql_params) { + $url = join('&sql_params=', $url, map { URI::Escape::uri_escape($_) } @sql_params); + } + $template->param( + 'results' => \@rows, + 'sql' => $sql, + 'id' => $report_id, + 'execute' => 1, + 'name' => $name, + 'notes' => $notes, + 'errors' => $errors, + 'pagination_bar' => pagination_bar($url, $totpages, $input->param('page')), + 'unlimited_total' => $total, + ); } - $template->param( - 'results' => \@rows, - 'sql' => $sql, - 'id' => $report, - 'execute' => 1, - 'name' => $name, - 'notes' => $notes, - 'errors' => $errors, - 'pagination_bar' => pagination_bar($url, $totpages, $input->param('page')), - 'unlimited_total' => $total, - ); + } + else { + push @errors, { no_sql_for_id => $report_id }; } } @@ -681,16 +719,26 @@ elsif ($phase eq 'Export'){ ); } -elsif ($phase eq 'Create report from SQL') { - # allow the user to paste in sql - if ($input->param('sql')) { +elsif ( $phase eq 'Create report from SQL' ) { + + my ($group, $subgroup); + # allow the user to paste in sql + if ( $input->param('sql') ) { + $group = $input->param('report_group'); + $subgroup = $input->param('report_subgroup'); $template->param( 'sql' => $input->param('sql'), 'reportname' => $input->param('reportname'), 'notes' => $input->param('notes'), ); } - $template->param('create' => 1, 'public' => '0', 'cache_expiry' => 300, 'usecache' => $usecache); + $template->param( + 'create' => 1, + 'groups_with_subgroups' => groups_with_subgroups($group, $subgroup), + 'public' => '0', + 'cache_expiry' => 300, + 'usecache' => $usecache, + ); } elsif ($phase eq 'Create Compound Report'){ @@ -729,3 +777,29 @@ $template->param( 'referer' => $input->referer(), ); output_html_with_http_headers $input, $cookie, $template->output; + +sub groups_with_subgroups { + my ($group, $subgroup) = @_; + + my $groups_with_subgroups = get_report_groups(); + my @g_sg; + while (my ($g_id, $v) = each %$groups_with_subgroups) { + my @subgroups; + if (my $sg = $v->{subgroups}) { + while (my ($sg_id, $n) = each %$sg) { + push @subgroups, { + id => $sg_id, + name => $n, + selected => ($group && $g_id eq $group && $subgroup && $sg_id eq $subgroup ), + }; + } + } + push @g_sg, { + id => $g_id, + name => $v->{name}, + selected => ($group && $g_id eq $group), + subgroups => \@subgroups, + }; + } + return \@g_sg; +} diff --git a/svc/report b/svc/report index b0507bd..654ef15 100755 --- a/svc/report +++ b/svc/report @@ -33,13 +33,6 @@ my $query = CGI->new(); my $report_id = $query->param('id'); my $report_name = $query->param('name'); -my $cache; -my $sql; -my $type; -my $notes; -my $cache_expiry; -my $public; - my ( $template, $loggedinuser, $cookie ) = get_template_and_user( { template_name => "intranet-main.tmpl", @@ -50,39 +43,31 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( } ); -if (Koha::Cache->is_cache_active) { - if ($report_name) { # When retrieving by name, we have to hit the - # database to get the ID before we can check - # the cache. Yuck. - ( $sql, $type, $report_name, $notes, $cache_expiry, $public, $report_id ) = - get_saved_report( { 'name' => $report_name } ); - } - +my $cache_active = Koha::Cache->is_cache_active; +my ($cache_key, $cache, $json_text); +if ($cache_active) { + $cache_key = "intranet:report:".($report_name ? "name:$report_name" : "id:$report_id"); $cache = Koha::Cache->new(); - my $page = $cache->get_from_cache("intranet:report:$report_id"); - if ($page) { - print $query->header; - print $page; - exit; - } + $json_text = $cache->get_from_cache($cache_key); } -print $query->header; - -# $public isnt used for intranet -unless ($sql) { - ( $sql, $type, $report_name, $notes, $cache_expiry, $public, $report_id ) = - get_saved_report($report_name ? { 'name' => $report_name } : { 'id' => $report_id } ); -} -if ($sql) { +unless ($json_text) { + my $report_rec = get_saved_report($report_name ? { 'name' => $report_name } : { 'id' => $report_id }); my $offset = 0; my $limit = C4::Context->preference("SvcMaxReportRows") || 10; - my ( $sth, $errors ) = execute_query( $sql, $offset, $limit ); - my $lines = $sth->fetchall_arrayref; - my $json_text = to_json($lines); - print $json_text; + my ( $sth, $errors ) = execute_query( $report_rec->{savedsql}, $offset, $limit ); + if ($sth) { + my $lines = $sth->fetchall_arrayref; + $json_text = to_json($lines); - if (Koha::Cache->is_cache_active) { - $cache->set_in_cache( "intranet:report:$report_id", $json_text, $cache_expiry ); + if ($cache_active) { + $cache->set_in_cache( $cache_key, $json_text, $report_rec->{cache_expiry} ); + } + } + else { + $json_text = to_json($errors); } } + +print $query->header; +print $json_text; -- 1.7.9.5 From srdjan at catalyst.net.nz Mon Sep 10 03:55:11 2012 From: srdjan at catalyst.net.nz (Srdjan) Date: Mon, 10 Sep 2012 13:55:11 +1200 Subject: [Koha-patches] [PATCH] bug_8034: Restored network printer maintenance and selection In-Reply-To: References: Message-ID: <1347242111-22540-1-git-send-email-srdjan@catalyst.net.nz> This patch is just for restoring printer maintenance and selection, not for priting itself. It is just a preparation step. * Added UsePrintQueues syspref. If set to No, no printer info will be displayed/used * Database changes: - printers table PRIMARY KEY is now printqueue. It is more natural. We should really have a synthetic id, but printqueue is good enough - branches.branchprinter is a FOREIGN KEY to printers.printqueue * Created C4::Auth::get_user_printer() function that will return appropriate printer. In order of preference: - session selected - logged in branch branchprinter * Moved printer functions to C4::Printer * Restored printer maint/selection in admin zone UsePrintQueues permitting * Restored printer selection in circ/selectbranchprinter.pl UsePrintQueues permitting Signed-off-by: Jared Camins-Esakov --- C4/Auth.pm | 49 +++++-- C4/Context.pm | 2 +- C4/Koha.pm | 40 ------ C4/Printer.pm | 148 +++++++++++++++++++ admin/branches.pl | 30 ++-- admin/printers.pl | 152 ++++++++++---------- circ/circulation.pl | 12 +- circ/returns.pl | 8 -- circ/selectbranchprinter.pl | 49 ++++--- installer/data/mysql/kohastructure.sql | 16 ++- installer/data/mysql/sysprefs.sql | 1 + installer/data/mysql/updatedatabase.pl | 12 ++ .../intranet-tmpl/prog/en/includes/admin-menu.inc | 1 + .../intranet-tmpl/prog/en/includes/header.inc | 3 + .../prog/en/modules/admin/admin-home.tt | 4 +- .../prog/en/modules/admin/branches.tt | 20 +-- .../en/modules/admin/preferences/circulation.pref | 6 + .../prog/en/modules/admin/printers.tt | 33 +---- .../prog/en/modules/circ/circulation.tt | 2 - .../prog/en/modules/circ/selectbranchprinter.tt | 5 +- t/db_dependent/lib/KohaTest/Koha.pm | 2 - t/db_dependent/lib/KohaTest/Printer.pm | 26 ++++ 22 files changed, 393 insertions(+), 228 deletions(-) create mode 100644 C4/Printer.pm create mode 100644 t/db_dependent/lib/KohaTest/Printer.pm diff --git a/C4/Auth.pm b/C4/Auth.pm index 59c9955..71abfb3 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -28,6 +28,7 @@ require Exporter; use C4::Context; use C4::Templates; # to get the template use C4::Branch; # GetBranches +use C4::Printer qw(GetPrinterDetails); use C4::VirtualShelves; use POSIX qw/strftime/; use List::MoreUtils qw/ any /; @@ -46,7 +47,8 @@ BEGIN { $debug = $ENV{DEBUG}; @ISA = qw(Exporter); @EXPORT = qw(&checkauth &get_template_and_user &haspermission &get_user_subpermissions); - @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions); + @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw + &get_all_subpermissions &get_user_subpermissions &get_user_printer); %EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] ); $ldap = C4::Context->config('useldapserver') || 0; $cas = C4::Context->preference('casAuthentication'); @@ -310,6 +312,9 @@ sub get_template_and_user { $template->param(dateformat_iso => 1); } + my $userenv = C4::Context->userenv; + my $userenv_branch = $userenv ? $userenv->{"branch"} : undef; + # these template parameters are set the same regardless of $in->{'type'} $template->param( "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1, @@ -317,9 +322,9 @@ sub get_template_and_user { GoogleJackets => C4::Context->preference("GoogleJackets"), OpenLibraryCovers => C4::Context->preference("OpenLibraryCovers"), KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"), - LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"), - LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"), - LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", + LoginBranchcode => ($userenv?$userenv_branch:"insecure"), + LoginFirstname => ($userenv?$userenv->{"firstname"}:"Bel"), + LoginSurname => $userenv?$userenv->{"surname"}:"Inconnu", TagsEnabled => C4::Context->preference("TagsEnabled"), hide_marc => C4::Context->preference("hide_marc"), item_level_itypes => C4::Context->preference('item-level_itypes'), @@ -344,7 +349,7 @@ sub get_template_and_user { IntranetNav => C4::Context->preference("IntranetNav"), IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"), LibraryName => C4::Context->preference("LibraryName"), - LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"), + LoginBranchname => ($userenv?$userenv->{"branchname"}:"insecure"), advancedMARCEditor => C4::Context->preference("advancedMARCEditor"), canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'), intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"), @@ -364,6 +369,14 @@ sub get_template_and_user { AllowMultipleCovers => C4::Context->preference('AllowMultipleCovers'), EnableBorrowerFiles => C4::Context->preference('EnableBorrowerFiles'), ); + if ( C4::Context->preference('UsePrintQueues') ) { + my $printer = get_user_printer(); + my $printer_rec = $printer ? GetPrinterDetails($printer) : {}; + $template->param( + UsePrintQueues => 1, + PrinterName => $printer_rec->{printername}, + ); + } } else { warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' ); @@ -382,8 +395,8 @@ sub get_template_and_user { my $opac_name = ''; if (($opac_search_limit =~ /branch:(\w+)/ && $opac_limit_override) || $in->{'query'}->param('limit') =~ /branch:(\w+)/){ $opac_name = $1; # opac_search_limit is a branch, so we use it. - } elsif (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv && C4::Context->userenv->{'branch'}) { - $opac_name = C4::Context->userenv->{'branch'}; + } elsif (C4::Context->preference("SearchMyLibraryFirst") && $userenv_branch) { + $opac_name = $userenv_branch } $template->param( opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"), @@ -393,7 +406,7 @@ sub get_template_and_user { CalendarFirstDayOfWeek => (C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday")?0:1, LibraryName => "" . C4::Context->preference("LibraryName"), LibraryNameTitle => "" . $LibraryNameTitle, - LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"", + LoginBranchname => $userenv?$userenv->{"branchname"}:"", OPACAmazonCoverImages => C4::Context->preference("OPACAmazonCoverImages"), OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"), OpacHighlightedWords => C4::Context->preference("OpacHighlightedWords"), @@ -424,7 +437,7 @@ sub get_template_and_user { RequestOnOpac => C4::Context->preference("RequestOnOpac"), 'Version' => C4::Context->preference('Version'), hidelostitems => C4::Context->preference("hidelostitems"), - mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '', + mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && $userenv) ? $userenv_branch : '', opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"), opacbookbag => "" . C4::Context->preference("opacbookbag"), opaccredits => "" . C4::Context->preference("opaccredits"), @@ -1644,8 +1657,24 @@ sub getborrowernumber { return 0; } +=head2 get_user_printer + + $printer = get_user_printer(); + + Returns printer queue that is to be used for the logged in user + +=cut + +sub get_user_printer { + my $userenv = C4::Context->userenv or return; + if (my $printer = $userenv->{branchprinter}) { + return $printer; + } + my $branchname = $userenv->{branch} or return; + my $branch = GetBranchDetail($branchname) or return; + return $branch->{branchprinter}; +} -END { } # module clean-up code here (global destructor) 1; __END__ diff --git a/C4/Context.pm b/C4/Context.pm index 0a56aa8..7835497 100644 --- a/C4/Context.pm +++ b/C4/Context.pm @@ -978,7 +978,7 @@ sub userenv { =head2 set_userenv C4::Context->set_userenv($usernum, $userid, $usercnum, $userfirstname, - $usersurname, $userbranch, $userflags, $emailaddress); + $usersurname, $userbranch, $userflags, $emailaddress, $branchprinter); Establish a hash of user environment variables. diff --git a/C4/Koha.pm b/C4/Koha.pm index 26106cf..1e1a412 100644 --- a/C4/Koha.pm +++ b/C4/Koha.pm @@ -39,7 +39,6 @@ BEGIN { @EXPORT = qw( &slashifyDate &subfield_is_koha_internal_p - &GetPrinters &GetPrinter &GetItemTypes &getitemtypeinfo &GetCcodes &GetSupportName &GetSupportList @@ -600,45 +599,6 @@ sub getImageSets { return \@imagesets; } -=head2 GetPrinters - - $printers = &GetPrinters(); - @queues = keys %$printers; - -Returns information about existing printer queues. - -C<$printers> is a reference-to-hash whose keys are the print queues -defined in the printers table of the Koha database. The values are -references-to-hash, whose keys are the fields in the printers table. - -=cut - -sub GetPrinters { - my %printers; - my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare("select * from printers"); - $sth->execute; - while ( my $printer = $sth->fetchrow_hashref ) { - $printers{ $printer->{'printqueue'} } = $printer; - } - return ( \%printers ); -} - -=head2 GetPrinter - - $printer = GetPrinter( $query, $printers ); - -=cut - -sub GetPrinter ($$) { - my ( $query, $printers ) = @_; # get printer for this query from printers - my $printer = $query->param('printer'); - my %cookie = $query->cookie('userenv'); - ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' ); - ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] ); - return $printer; -} - =head2 getnbpages Returns the number of pages to display in a pagination bar, given the number diff --git a/C4/Printer.pm b/C4/Printer.pm new file mode 100644 index 0000000..22d8659 --- /dev/null +++ b/C4/Printer.pm @@ -0,0 +1,148 @@ +#!/usr/bin/perl + +package C4::Printer; + +# Copyright 2012 Catalyst IT +# +# 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 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; + +use C4::Context; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +BEGIN { + $VERSION = 3.07.00.049; + require Exporter; + @ISA = qw(Exporter); + @EXPORT = qw( + ); + @EXPORT_OK = qw( + &GetPrinters &SearchPrinters &GetPrinterDetails + &AddPrinter &UpdatePrinter &DeletePrinter + ); +} + +=head1 NAME + +C4::Printer - functions that deal with printer selection + +=head1 SYNOPSIS + + use C4::Printer; + +=head1 DESCRIPTION + +This module provides functions to select printer for slips etc. + +TODO: Move SQL from admin/printers.pl to this module + +=head1 FUNCTIONS + +=head2 GetPrinters + + $printers = &GetPrinters(); + @queues = keys %$printers; + +Returns information about existing printer queues. + +C<$printers> is a reference-to-hash whose keys are the print queues +defined in the printers table of the Koha database. The values are +references-to-hash, whose keys are the fields in the printers table. + +=cut + +sub GetPrinters { + my %printers; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("select * from printers"); + $sth->execute; + while ( my $printer = $sth->fetchrow_hashref ) { + $printers{ $printer->{'printqueue'} } = $printer; + } + return ( \%printers ); +} + +=head2 SearchPrinters + + $printers = SearchPrinters( $searchstring ); + +=cut + +sub SearchPrinters { + my ($searchstring)=@_; + $searchstring .= '%'; + return C4::Context->dbh->selectall_arrayref(" + SELECT * FROM printers + WHERE printername like ? OR printqueue like ? ORDER BY printername + ", {Slice => {}}, $searchstring, $searchstring); +} + +=head2 GetPrinterDetails + + $printer_rec = GetPrinterDetails( $printqueue ); + +=cut + +sub GetPrinterDetails { + my ( $printer ) = @_; + my $dbh = C4::Context->dbh; + my $printername = $dbh->selectrow_hashref('SELECT * FROM printers WHERE printqueue = ?', undef, $printer); + return $printername; +} + +=head2 AddPrinter + + AddPrinter( $data ); + +=cut + +sub AddPrinter { + my ( $data ) = @_; + my $dbh = C4::Context->dbh; + $dbh->do("INSERT INTO printers (printername,printqueue,printtype) VALUES (?,?,?)", undef, + $data->{printername}, $data->{printqueue}, $data->{printtype}); +} + +=head2 UpdatePrinter + + UpdatePrinter( $printqueue, $data ); + +=cut + +sub UpdatePrinter { + my ( $printqueue, $data ) = @_; + my $dbh = C4::Context->dbh; + $dbh->do("UPDATE printers SET printername = ?, printtype = ? WHERE printqueue = ?", undef, + $data->{printername}, $data->{printtype}, $printqueue); +} + +=head2 DeletePrinter + + DeletePrinter( $printqueue ); + +=cut + +sub DeletePrinter { + my ( $printqueue ) = @_; + my $dbh = C4::Context->dbh; + $dbh->do("DELETE FROM printers WHERE printqueue = ?", undef, + $printqueue); +} + +1; diff --git a/admin/branches.pl b/admin/branches.pl index d172ea6..3671f43 100755 --- a/admin/branches.pl +++ b/admin/branches.pl @@ -45,6 +45,7 @@ use C4::Context; use C4::Output; use C4::Koha; use C4::Branch; +use C4::Printer qw(GetPrinters); # Fixed variables my $script_name = "/cgi-bin/koha/admin/branches.pl"; @@ -226,12 +227,9 @@ sub default { sub editbranchform { my ($branchcode,$innertemplate) = @_; - # initiate the scrolling-list to select the printers - my $printers = GetPrinters(); - my @printerloop; + my $data; my $oldprinter = ""; - if ($branchcode) { $data = GetBranchInfo($branchcode); $data = $data->[0]; @@ -241,15 +239,21 @@ sub editbranchform { _branch_to_template($data, $innertemplate); } - foreach my $thisprinter ( keys %$printers ) { - push @printerloop, { - value => $thisprinter, - selected => ( $oldprinter eq $printers->{$thisprinter} ), - branchprinter => $printers->{$thisprinter}->{'printqueue'}, - }; + if ( C4::Context->preference('UsePrintQueues') ) { + # initiate the scrolling-list to select the printers + my $printers = GetPrinters(); + my @printerloop; + foreach my $thisprinter ( keys %$printers ) { + push @printerloop, { + value => $thisprinter, + selected => ( $oldprinter eq $thisprinter ), + branchprinter => $printers->{$thisprinter}->{'printername'}, + }; + } + + $innertemplate->param( printerloop => \@printerloop ); } - $innertemplate->param( printerloop => \@printerloop ); # make the checkboxes..... # # We export a "categoryloop" array to the template, each element of which @@ -308,6 +312,7 @@ sub branchinfotable { my ($branchcode,$innertemplate) = @_; my $branchinfo = $branchcode ? GetBranchInfo($branchcode) : GetBranchInfo(); + my $printers = GetPrinters(); my @loop_data = (); foreach my $branch (@$branchinfo) { # @@ -367,6 +372,9 @@ sub branchinfotable { $row{'branch_name'} = $branch->{'branchname'}; $row{'branch_code'} = $branch->{'branchcode'}; $row{'value'} = $branch->{'branchcode'}; + if (my $printer = $branch->{'branchprinter'}) { + $row{'branchprintername'} = $printers->{$printer}->{'printername'}; + } push @loop_data, \%row; } diff --git a/admin/printers.pl b/admin/printers.pl index c7e7492..701f9fe 100755 --- a/admin/printers.pl +++ b/admin/printers.pl @@ -7,17 +7,17 @@ # ALGO : # this script use an $op to know what to do. # if $op is empty or none of the above values, -# - the default screen is build (with all records, or filtered datas). -# - the user can clic on add, modify or delete record. +# - the default screen is build (with all records, or filtered datas). +# - the user can clic on add, modify or delete record. # if $op=add_form -# - if primkey exists, this is a modification,so we read the $primkey record -# - builds the add/modify form +# - if primkey exists, this is a modification,so we read the $primkey record +# - builds the add/modify form # if $op=add_validate -# - the user has just send datas, so we create/modify the record +# - the user has just send datas, so we create/modify the record # if $op=delete_form -# - we show the record having primkey=$primkey and ask for deletion validation form +# - we show the record having primkey=$primkey and ask for deletion validation form # if $op=delete_confirm -# - we delete the record having primkey=$primkey +# - we delete the record having primkey=$primkey # Copyright 2000-2002 Katipo Communications @@ -43,19 +43,7 @@ use CGI; use C4::Context; use C4::Output; use C4::Auth; - -sub StringSearch { - my ($searchstring,$type)=@_; # why bother with $type if we don't use it?! - $searchstring=~ s/\'/\\\'/g; - my @data=split(' ',$searchstring); - my $sth = C4::Context->dbh->prepare(" - SELECT printername,printqueue,printtype from printers - WHERE (printername like ?) order by printername - "); - $sth->execute("$data[0]%"); - my $data=$sth->fetchall_arrayref({}); - return (scalar(@$data),$data); -} +use C4::Printer qw(GetPrinterDetails SearchPrinters AddPrinter UpdatePrinter DeletePrinter); my $input = new CGI; my $searchfield=$input->param('searchfield'); @@ -68,83 +56,95 @@ my $op = $input->param('op'); $searchfield=~ s/\,//g; my ($template, $loggedinuser, $cookie) = get_template_and_user({ - template_name => "admin/printers.tmpl", - query => $input, - type => "intranet", - authnotrequired => 0, + template_name => "admin/printers.tmpl", + query => $input, + type => "intranet", + authnotrequired => 0, flagsrequired => {parameters => 'parameters_remaining_permissions'}, - debug => 1, + debug => 1, }); -$template->param(searchfield => $searchfield, - script_name => $script_name); - #start the page and read in includes my $dbh = C4::Context->dbh; +my $list_printers = 1; ################## ADD_FORM ################################## # called by default. Used to create form to add or modify a record if ($op eq 'add_form') { - $template->param(add_form => 1); - #---- if primkey exists, it's a modify action, so read values to modify... - my $data; - if ($searchfield) { - my $sth=$dbh->prepare("SELECT printername,printqueue,printtype from printers where printername=?"); - $sth->execute($searchfield); - $data=$sth->fetchrow_hashref; - } - - $template->param(printqueue => $data->{'printqueue'}, - printtype => $data->{'printtype'}); - # END $OP eq ADD_FORM + $list_printers = 0; + $template->param(add_form => 1); + #---- if primkey exists, it's a modify action, so read values to modify... + my $data; + if ($searchfield) { + $data=GetPrinterDetails($searchfield); + } + + $template->param( + printqueue => $data->{'printqueue'}, + printername => $data->{'printername'}, + printtype => $data->{'printtype'} + ); +# END $OP eq ADD_FORM ################## ADD_VALIDATE ################################## # called by add_form, used to insert/modify data in DB } elsif ($op eq 'add_validate') { - $template->param(add_validate => 1); - if ($input->param('add')){ - my $sth=$dbh->prepare("INSERT INTO printers (printername,printqueue,printtype) VALUES (?,?,?)"); - $sth->execute($input->param('printername'),$input->param('printqueue'),$input->param('printtype')); - } else { - my $sth=$dbh->prepare("UPDATE printers SET printqueue=?,printtype=? WHERE printername=?"); - $sth->execute($input->param('printqueue'),$input->param('printtype'),$input->param('printername')); - } - # END $OP eq ADD_VALIDATE + my $params = $input->Vars; + if ($input->param('add')){ + AddPrinter($params); + } else { + UpdatePrinter($params->{printqueue}, $params); + } + $template->param(add_validate => 1); + $searchfield = ''; +# END $OP eq ADD_VALIDATE ################## DELETE_CONFIRM ################################## # called by default form, used to confirm deletion of data in DB } elsif ($op eq 'delete_confirm') { - $template->param(delete_confirm => 1); - my $sth=$dbh->prepare("select printername,printqueue,printtype from printers where printername=?"); - $sth->execute($searchfield); - my $data=$sth->fetchrow_hashref; - $template->param(printqueue => $data->{'printqueue'}, - printtype => $data->{'printtype'}); - # END $OP eq DELETE_CONFIRM + $list_printers = 0; + $template->param(delete_confirm => 1); + my $data=GetPrinterDetails($searchfield); + $template->param( + printqueue => $data->{'printqueue'}, + printtype => $data->{'printtype'}, + ); +# END $OP eq DELETE_CONFIRM ################## DELETE_CONFIRMED ################################## # called by delete_confirm, used to effectively confirm deletion of data in DB } elsif ($op eq 'delete_confirmed') { - $template->param(delete_confirmed => 1); - my $sth=$dbh->prepare("delete from printers where printername=?"); - $sth->execute($searchfield); - # END $OP eq DELETE_CONFIRMED + # XXX Delete can fail + DeletePrinter($searchfield); + $template->param(delete_confirmed => 1); + $template->param(list_printers => 1); + $searchfield = ''; +# END $OP eq DELETE_CONFIRMED ################## DEFAULT ########################################### } else { # DEFAULT - $template->param(else => 1); - my ($count,$results)=StringSearch($searchfield,'web'); - my $max = ($offset+$pagesize < $count) ? $offset+$pagesize : $count; - my @loop = (@$results)[$offset..$max]; - - $template->param(loop => \@loop); - - if ($offset>0) { - $template->param(offsetgtzero => 1, - prevpage => $offset-$pagesize); - } - if ($offset+$pagesize<$count) { - $template->param(ltcount => 1, - nextpage => $offset+$pagesize); - } - + $searchfield ||= $input->param('description') || ""; } #---- END $OP eq DEFAULT +if ($list_printers) { + $template->param(list_printers => 1); + my $results=SearchPrinters($searchfield); + my $count = $results ? scalar(@$results) : 0; + my $max = ($offset+$pagesize < $count) ? $offset+$pagesize : $count; + my @loop = (@$results)[$offset..$max-1]; + + $template->param(loop => \@loop); + + if ($offset>0) { + $template->param(offsetgtzero => 1, + prevpage => $offset-$pagesize); + } + if ($offset+$pagesize<$count) { + $template->param(ltcount => 1, + nextpage => $offset+$pagesize); + } +} + +$template->param( + searchfield => $searchfield, + script_name => $script_name +); + output_html_with_http_headers $input, $cookie, $template->output; diff --git a/circ/circulation.pl b/circ/circulation.pl index 78ac1a4..54138ef 100755 --- a/circ/circulation.pl +++ b/circ/circulation.pl @@ -29,7 +29,7 @@ use C4::Print; use C4::Auth qw/:DEFAULT get_session/; use C4::Dates qw/format_date/; use C4::Branch; # GetBranches -use C4::Koha; # GetPrinter +use C4::Koha; use C4::Circulation; use C4::Overdues qw/CheckBorrowerDebarred/; use C4::Members; @@ -67,12 +67,6 @@ if ($branch){ $session->param('branchname', GetBranchName($branch)); } -my $printer = $query->param('printer'); -if ($printer){ - # update our session so the userenv is updated - $session->param('branchprinter', $printer); -} - if (!C4::Context->userenv && !$branch){ if ($session->param('branch') eq 'NO_LIBRARY_SET'){ # no branch set we can't issue @@ -102,8 +96,6 @@ $findborrower =~ s|,| |g; my $borrowernumber = $query->param('borrowernumber'); $branch = C4::Context->userenv->{'branch'}; -$printer = C4::Context->userenv->{'branchprinter'}; - # If AutoLocation is not activated, we show the Circulation Parameters to chage settings of librarian if (C4::Context->preference("AutoLocation") != 1) { @@ -665,8 +657,6 @@ $template->param( borrowernumber => $borrowernumber, branch => $branch, branchname => GetBranchName($borrower->{'branchcode'}), - printer => $printer, - printername => $printer, firstname => $borrower->{'firstname'}, surname => $borrower->{'surname'}, showname => $borrower->{'showname'}, diff --git a/circ/returns.pl b/circ/returns.pl index 743bf18..0efa712 100755 --- a/circ/returns.pl +++ b/circ/returns.pl @@ -36,7 +36,6 @@ use C4::Context; use C4::Auth qw/:DEFAULT get_session/; use C4::Output; use C4::Circulation; -use C4::Print; use C4::Reserves; use C4::Biblio; use C4::Items; @@ -73,15 +72,10 @@ my ( $template, $librarian, $cookie ) = get_template_and_user( ##################### #Global vars my $branches = GetBranches(); -my $printers = GetPrinters(); -my $printer = C4::Context->userenv ? C4::Context->userenv->{'branchprinter'} : ""; my $overduecharges = (C4::Context->preference('finesMode') && C4::Context->preference('finesMode') ne 'off'); my $userenv_branch = C4::Context->userenv->{'branch'} || ''; -# -# Some code to handle the error if there is no branch or printer setting..... -# # Set up the item stack .... my %returneditems; @@ -601,9 +595,7 @@ foreach ( sort { $a <=> $b } keys %returneditems ) { $template->param( riloop => \@riloop, genbrname => $branches->{$userenv_branch}->{'branchname'}, - genprname => $printers->{$printer}->{'printername'}, branchname => $branches->{$userenv_branch}->{'branchname'}, - printer => $printer, errmsgloop => \@errmsgloop, exemptfine => $exemptfine, dropboxmode => $dropboxmode, diff --git a/circ/selectbranchprinter.pl b/circ/selectbranchprinter.pl index b5adcfc..67034b7 100755 --- a/circ/selectbranchprinter.pl +++ b/circ/selectbranchprinter.pl @@ -23,10 +23,10 @@ use CGI; use C4::Context; use C4::Output; -use C4::Auth qw/:DEFAULT get_session/; -use C4::Print; # GetPrinters +use C4::Auth qw/:DEFAULT get_session get_user_printer/; use C4::Koha; use C4::Branch; # GetBranches GetBranchesLoop +use C4::Printer qw(GetPrinters); # this will be the script that chooses branch and printer settings.... @@ -56,9 +56,10 @@ my $userenv_printer = C4::Context->userenv->{'branchprinter'} || ''; my @updated; # $session lddines here are doing the updating -if ($branch and $branches->{$branch}) { +my $branch_rec = $branch ? $branches->{$branch} : undef; +if ($branch_rec) { if (! $userenv_branch or $userenv_branch ne $branch ) { - my $branchname = GetBranchName($branch); + my $branchname = $branch_rec->{branchname}; $template->param(LoginBranchname => $branchname); # update template for new branch $template->param(LoginBranchcode => $branch); # update template for new branch $session->param('branchname', $branchname); # update sesssion in DB @@ -67,6 +68,8 @@ if ($branch and $branches->{$branch}) { updated_branch => 1, old_branch => $userenv_branch, }; + $printer ||= $branch_rec->{branchprinter}; + undef $userenv_printer; } # else branch the same, no update } else { $branch = $userenv_branch; # fallback value @@ -87,7 +90,7 @@ if ($printer) { }; } # else printer is the same, no update } else { - $printer = $userenv_printer; # fallback value + $printer = get_user_printer(); # fallback value } $template->param(updated => \@updated) if (scalar @updated); @@ -96,21 +99,6 @@ unless ($branches->{$branch}) { $branch = (keys %$branches)[0]; # if branch didn't really exist, then replace it w/ one that does } -my @printkeys = sort keys %$printers; -if (scalar(@printkeys) == 1 or not $printers->{$printer}) { - $printer = $printkeys[0]; # if printer didn't really exist, or there is only 1 anyway, then replace it w/ one that does -} - -my @printerloop; -foreach ( @printkeys ) { - next unless ($_); # skip printer if blank. - push @printerloop, { - selected => ( $_ eq $printer ), - name => $printers->{$_}->{'printername'}, - value => $_, - }; -} - my @recycle_loop; foreach ($query->param()) { $_ or next; # disclude blanks @@ -133,9 +121,28 @@ if (scalar @updated and not scalar @recycle_loop) { $template->param( referer => $referer, - printerloop => \@printerloop, branchloop => GetBranchesLoop($branch), recycle_loop=> \@recycle_loop, ); +if ( C4::Context->preference('UsePrintQueues') ) { + my @printkeys = keys %$printers; + if (scalar(@printkeys) == 1 or not $printers->{$printer}) { + $printer = $printkeys[0]; # if printer didn't really exist, or there is only 1 anyway, then replace it w/ one that does + } + + my @printerloop; + foreach ( @printkeys ) { + push @printerloop, { + selected => ( $_ eq $printer ), + name => $printers->{$_}->{'printername'}, + value => $_, + }; + } + + $template->param( + printerloop => \@printerloop, + ); +} + output_html_with_http_headers $query, $cookie, $template->output; diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index 91d42a2..048a7ea 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -364,10 +364,11 @@ CREATE TABLE `branches` ( -- information about your libraries or branches are st `branchurl` mediumtext, -- the URL for your library or branch's website `issuing` tinyint(4) default NULL, -- unused in Koha `branchip` varchar(15) default NULL, -- the IP address for your library or branch - `branchprinter` varchar(100) default NULL, -- unused in Koha + `branchprinter` varchar(20) default NULL, `branchnotes` mediumtext, -- notes related to your library or branch opac_info text, -- HTML that displays in OPAC PRIMARY KEY (`branchcode`) + FOREIGN KEY (branchprinter) REFERENCES printers (printqueue) ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- @@ -1570,12 +1571,13 @@ CREATE TABLE `pending_offline_operations` ( -- Table structure for table `printers` -- -DROP TABLE IF EXISTS `printers`; -CREATE TABLE `printers` ( - `printername` varchar(40) NOT NULL default '', - `printqueue` varchar(20) default NULL, - `printtype` varchar(20) default NULL, - PRIMARY KEY (`printername`) +DROP TABLE IF EXISTS printers; +CREATE TABLE printers ( + printername varchar(40) NOT NULL default '', + printqueue varchar(20) NOT NULL, + printtype varchar(20) default NULL, + PRIMARY KEY (printqueue), + UNIQUE (printername) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index 35f92cc..f21b071 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -366,6 +366,7 @@ INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ( INSERT INTO systempreferences (variable,value,explanation,type) VALUES('EnableBorrowerFiles','0','If enabled, allows librarians to upload and attach arbitrary files to a borrower record.','YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('UpdateTotalIssuesOnCirc','0','Whether to update the totalissues field in the biblio on each circ.',NULL,'YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('IntranetSlipPrinterJS','','Use this JavaScript for printing slips. Define at least function printThenClose(). For use e.g. with Firefox PlugIn jsPrintSetup, see http://jsprintsetup.mozdev.org/','','Free'); +INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('UsePrintQueues','0',NULL,NULL,'YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpacSuppressionByIPRange','','Restrict the suppression to IP adresses outside of the IP range','','free'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('PrefillItem','0','When a new item is added, should it be prefilled with last created item values?','','YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('SubfieldsToUseWhenPrefill','','Define a list of subfields to use when prefilling items (separated by space)','','Free'); diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index 25cd367..3649a78 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -5696,6 +5696,18 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) { SetVersion($DBversion); } + + +$DBversion = "3.09.00.XXX"; +if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) { + $dbh->do("ALTER TABLE printers DROP PRIMARY KEY, MODIFY printqueue varchar(20) NOT NULL PRIMARY KEY, ADD UNIQUE (printername)"); + $dbh->do("ALTER TABLE branches MODIFY branchprinter varchar(20) NULL, ADD FOREIGN KEY (branchprinter) REFERENCES printers (printqueue) ON UPDATE CASCADE"); + $dbh->do("INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('UsePrintQueues','0',NULL,NULL,'YesNo')"); + + print "Upgrade to $DBversion done (Add borrowers.default_printqueue and 'UsePrintQueues' syspref)\n"; + SetVersion($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc index 72162d3..7da6f1f 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc @@ -59,6 +59,7 @@
      Additional parameters
        + [% IF UsePrintQueues %]
      • Network Printers
      • [% END %] [% IF ( NoZebra ) %]
      • Stop words
      • [% END %]
      • Z39.50 client targets
      • diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc index 3d71d45..7d3aa24 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc @@ -57,6 +57,9 @@ [% LoginBranchname %] [% END %] + [% IF UsePrintQueues && PrinterName %] + - [% PrinterName %] + [% END %] [% IF ( IndependantBranches ) %] [% IF ( CAN_user_management || CAN_user_editcatalogue_edit_catalogue ) %] ( Set library ) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt index 3906c46..d2e0fc5 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt @@ -101,8 +101,8 @@
        [% IF ( NoZebra ) %]
        Stop words
        Words ignored during search.
        [% END %] - + [% IF UsePrintQueues %]
        Network Printers
        +
        Printers (UNIX paths).
        [% END %]
        Z39.50 client targets
        Define which servers to query for MARC data in the integrated Z39.50 client.
        diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt index 15e8064..bcb918f 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt @@ -143,10 +143,10 @@ tinyMCE.init({
      • Can be entered as a single IP, or a subnet such as 192.168.1.*
      • - +[% END %]
    @@ -198,7 +198,9 @@ tinyMCE.init({ Address Properties IP - +[% IF UsePrintQueues %] + Printer +[% END %]   [% FOREACH branche IN branches %] @@ -250,9 +252,11 @@ tinyMCE.init({ [% branche.branchip %] - +[% IF UsePrintQueues %] + + [% branche.branchprintername %] + +[% END %] Edit diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref index a9ed6f7..23cd7ee 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -110,6 +110,12 @@ Circulation: yes: Do no: "Do not" - update a bibliographic record's total issues count whenever an item is issued (WARNING! This increases server load significantly; if performance is a concern, use the update_totalissues.pl cron job to update the total issues count). + - + - pref: UsePrintQueues + choices: + yes: "Use" + no: "Don't use" + - server print queues. Checkout Policy: - - pref: AllowNotForLoanOverride diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt index 7dfb80b..06fd229 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt @@ -4,7 +4,7 @@ [% IF ( add_validate ) %] Printers › Printer added[% END %] [% IF ( delete_confirm ) %] Printers › Confirm deletion of printer '[% searchfield %]'[% END %] [% IF ( delete_confirmed ) %] Printers › Printer deleted[% END %] -[% IF ( else ) %]Printers[% END %] +[% IF ( list_printers ) %]Printers[% END %] [% INCLUDE 'doc-head-close.inc' %] [% IF ( add_form ) %] + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] +
    Home &rs= aquo; Administration &rsa= quo; Did you mean?
    + +
    + +
    +
    +
    +

    Did you mean?

    + +
    + Please put the Did you mean? plugins in order by sign= ificance, from + most significant to least significant, and check the box to en= able those + plugins that you want to use. +
    + +
    + OPAC + [% PROCESS pluginlist plugins=3DOPACpluginlist %] +
    +
    + Intranet + [% PROCESS pluginlist plugins=3DINTRApluginlist %] +
    +
    + + +
    +
    +
    +[% INCLUDE 'admin-menu.inc' %] +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/opac-tmpl/prog/en/lib/jquery/jquery-ui.css b/koha-tm= pl/opac-tmpl/prog/en/lib/jquery/jquery-ui.css index 234b987..fe76cd3 100644 --- a/koha-tmpl/opac-tmpl/prog/en/lib/jquery/jquery-ui.css +++ b/koha-tmpl/opac-tmpl/prog/en/lib/jquery/jquery-ui.css @@ -1,5 +1,5 @@ /*! - * jQuery UI CSS Framework 1.8.20 + * jQuery UI CSS Framework 1.8.23 * * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -39,7 +39,7 @@ =20 =20 /*! - * jQuery UI CSS Framework 1.8.20 + * jQuery UI CSS Framework 1.8.23 * * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -56,26 +56,26 @@ .ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; } .ui-widget .ui-widget { font-size: 1em; } .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget butto= n { font-family: Verdana,Arial,sans-serif; font-size: 1em; } -.ui-widget-content { border: 1px solid #B9D8D9; background: #ffffff; color= : #222222; } +.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(im= ages/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; } .ui-widget-content a { color: #222222; } -.ui-widget-header { border: 1px solid #B9D8D9; background: #E6F0F2 none; c= olor: #222222; font-weight: bold; } +.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(ima= ges/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222= 222; font-weight: bold; } .ui-widget-header a { color: #222222; } =20 /* Interaction states ----------------------------------*/ -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header= .ui-state-default { border: 1px solid #B9D8D9; background: #F4F8F9 none; f= ont-weight: normal; color: #555555; } +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header= .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(ima= ges/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal;= color: #555555; } .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited= { color: #555555; text-decoration: none; } -.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui= -state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widg= et-header .ui-state-focus { border: 1px solid #B9D8D9; background: #E6F0F2 = none; font-weight: normal; color: #212121; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui= -state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widg= et-header .ui-state-focus { border: 1px solid #999999; background: #dadada = url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: = normal; color: #212121; } .ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decorati= on: none; } -.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .= ui-state-active { border: 1px solid #aaaaaa; background: #ffffff none; font= -weight: normal; color: #212121; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .= ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images= /ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; co= lor: #212121; } .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { = color: #212121; text-decoration: none; } .ui-widget :active { outline: none; } =20 /* Interaction Cues ----------------------------------*/ -.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-he= ader .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee; = color: #363636; } +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-he= ader .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee u= rl(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636= ; } .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget= -header .ui-state-highlight a { color: #363636; } -.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui= -state-error {border: 1px solid #cd0a0a; background: #fef1ec; color: #cd0a0= a; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui= -state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-= bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; } .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header= .ui-state-error a { color: #cd0a0a; } .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-= header .ui-state-error-text { color: #cd0a0a; } .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-= header .ui-priority-primary { font-weight: bold; } @@ -285,7 +285,7 @@ /* Overlays */ .ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40= x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=3D30); } .ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaa= aa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .3= 0;filter:Alpha(Opacity=3D30); -moz-border-radius: 8px; -khtml-border-radius= : 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*! - * jQuery UI Autocomplete 1.8.20 + * jQuery UI Autocomplete 1.8.23 * * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -293,20 +293,13 @@ * * http://docs.jquery.com/UI/Autocomplete#theming */ -.ui-autocomplete { position: absolute; cursor: default; -webkit-box-shadow= : 2px 2px 2px rgba(0,0,0,.3); -moz-box-shadow: 2px 2px 2px rgba(0,0,0,.3); = box-shadow: 2px 2px 2px rgba(0,0,0,.3); } - -.ui-autocomplete.ui-widget-content .ui-state-hover { border: 1px solid #B9= D8D9; background: #E6F0F2 none; font-weight: normal; color: #212121; -} - -.ui-autocomplete-loading { - background: #FFF url("../../img/loading-small.gif") right center no-re= peat; -} +.ui-autocomplete { position: absolute; cursor: default; } =20 /* workarounds */ * html .ui-autocomplete { width:1px; } /* without this, the menu expands t= o 100% in IE6 */ =20 /* - * jQuery UI Menu 1.8.20 + * jQuery UI Menu 1.8.23 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -315,40 +308,60 @@ * http://docs.jquery.com/UI/Menu#theming */ .ui-menu { - list-style:none; - padding: 2px; - margin: 0; - display:block; - float: left; -} -.ui-menu li { - list-style:none; + list-style:none; + padding: 2px; + margin: 0; + display:block; + float: left; } .ui-menu .ui-menu { - margin-top: -3px; + margin-top: -3px; } .ui-menu .ui-menu-item { - margin:0; - padding: 0; + margin:0; + padding: 0; zoom: 1; - float: left; - clear: left; - width: 100%; + float: left; + clear: left; + width: 100%; } .ui-menu .ui-menu-item a { - text-decoration:none; - display:block; - padding:.2em .4em; - line-height:1.5; - zoom:1; + text-decoration:none; + display:block; + padding:.2em .4em; + line-height:1.5; + zoom:1; } .ui-menu .ui-menu-item a.ui-state-hover, .ui-menu .ui-menu-item a.ui-state-active { - font-weight: normal; - margin: -1px; + font-weight: normal; + margin: -1px; } /*! - * jQuery UI Tabs 1.8.20 + * jQuery UI Slider 1.8.23 + * + * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Slider#theming + */ +.ui-slider { position: relative; text-align: left; } +.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2e= m; height: 1.2em; cursor: default; } +.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .= 7em; display: block; border: 0; background-position: 0 0; } + +.ui-slider-horizontal { height: .8em; } +.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; = } +.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } +.ui-slider-horizontal .ui-slider-range-min { left: 0; } +.ui-slider-horizontal .ui-slider-range-max { right: 0; } + +.ui-slider-vertical { width: .8em; height: 100px; } +.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margi= n-bottom: -.6em; } +.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } +.ui-slider-vertical .ui-slider-range-min { bottom: 0; } +.ui-slider-vertical .ui-slider-range-max { top: 0; }/*! + * jQuery UI Tabs 1.8.23 * * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -366,7 +379,7 @@ .ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.= 4em; background: none; } .ui-tabs .ui-tabs-hide { display: none !important; } /*! - * jQuery UI Datepicker 1.8.20 + * jQuery UI Datepicker 1.8.23 * * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. @@ -388,8 +401,8 @@ .ui-datepicker select.ui-datepicker-month-year {width: 100%;} .ui-datepicker select.ui-datepicker-month, .ui-datepicker select.ui-datepicker-year { width: 49%;} -.ui-datepicker table {width: 100%; font-size: .9em; border : 0; border-col= lapse: collapse; margin:0 0 .4em; } -.ui-datepicker th { background : transparent none; padding: .7em .3em; tex= t-align: center; font-weight: bold; border: 0; } +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: colla= pse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: b= old; border: 0; } .ui-datepicker td { border: 0; padding: 1px; } .ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2e= m; text-align: right; text-decoration: none; } .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin:= .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom= : 0; } @@ -423,8 +436,6 @@ =20 /* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ .ui-datepicker-cover { - display: none; /*sorry for IE5*/ - display/**/: block; /*sorry for IE5*/ position: absolute; /*must have*/ z-index: -1; /*must have*/ filter: mask(); /*must have*/ diff --git a/koha-tmpl/opac-tmpl/prog/en/lib/jquery/jquery-ui.js b/koha-tmp= l/opac-tmpl/prog/en/lib/jquery/jquery-ui.js index 6996eae..635f4c6 100644 --- a/koha-tmpl/opac-tmpl/prog/en/lib/jquery/jquery-ui.js +++ b/koha-tmpl/opac-tmpl/prog/en/lib/jquery/jquery-ui.js @@ -1,29 +1,37 @@ -/*! jQuery UI - v1.8.21 - 2012-06-05 +/*! jQuery UI - v1.8.23 - 2012-08-15 * https://github.com/jquery/jquery-ui * Includes: jquery.ui.core.js * Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ -(function(a,b){function c(b,c){var e=3Db.nodeName.toLowerCase();if("area"= =3D=3D=3De){var f=3Db.parentNode,g=3Df.name,h;return!b.href||!g||f.nodeName= .toLowerCase()!=3D=3D"map"?!1:(h=3Da("img[usemap=3D#"+g+"]")[0],!!h&&d(h))}= return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"=3D=3De= ?b.href||c:c)&&d(b)}function d(b){return!a(b).parents().andSelf().filter(fu= nction(){return a.curCSS(this,"visibility")=3D=3D=3D"hidden"||a.expr.filter= s.hidden(this)}).length}a.ui=3Da.ui||{};if(a.ui.version)return;a.extend(a.u= i,{version:"1.8.21",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMM= AND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35= ,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD= _DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_= SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,= TAB:9,UP:38,WINDOWS:91}}),a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus= :a.fn.focus,focus:function(b,c){return typeof b=3D=3D"number"?this.each(fun= ction(){var d=3Dthis;setTimeout(function(){a(d).focus(),c&&c.call(d)},b)}):= this._focus.apply(this,arguments)},scrollParent:function(){var b;return a.b= rowser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.tes= t(this.css("position"))?b=3Dthis.parents().filter(function(){return/(relati= ve|absolute|fixed)/.test(a.curCSS(this,"position",1))&&/(auto|scroll)/.test= (a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"o= verflow-x",1))}).eq(0):b=3Dthis.parents().filter(function(){return/(auto|sc= roll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.cur= CSS(this,"overflow-x",1))}).eq(0),/fixed/.test(this.css("position"))||!b.le= ngth?a(document):b},zIndex:function(c){if(c!=3D=3Db)return this.css("zIndex= ",c);if(this.length){var d=3Da(this[0]),e,f;while(d.length&&d[0]!=3D=3Ddocu= ment){e=3Dd.css("position");if(e=3D=3D=3D"absolute"||e=3D=3D=3D"relative"||= e=3D=3D=3D"fixed"){f=3DparseInt(d.css("zIndex"),10);if(!isNaN(f)&&f!=3D=3D0= )return f}d=3Dd.parent()}}return 0},disableSelection:function(){return this= .bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelecti= on",function(a){a.preventDefault()})},enableSelection:function(){return thi= s.unbind(".ui-disableSelection")}}),a.each(["Width","Height"],function(c,d)= {function h(b,c,d,f){return a.each(e,function(){c-=3DparseFloat(a.curCSS(b,= "padding"+this,!0))||0,d&&(c-=3DparseFloat(a.curCSS(b,"border"+this+"Width"= ,!0))||0),f&&(c-=3DparseFloat(a.curCSS(b,"margin"+this,!0))||0)}),c}var e= =3Dd=3D=3D=3D"Width"?["Left","Right"]:["Top","Bottom"],f=3Dd.toLowerCase(),= g=3D{innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.f= n.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=3Dfunction(c){re= turn c=3D=3D=3Db?g["inner"+d].call(this):this.each(function(){a(this).css(f= ,h(this,c)+"px")})},a.fn["outer"+d]=3Dfunction(b,c){return typeof b!=3D"num= ber"?g["outer"+d].call(this,b):this.each(function(){a(this).css(f,h(this,b,= !0,c)+"px")})}}),a.extend(a.expr[":"],{data:function(b,c,d){return!!a.data(= b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},ta= bbable:function(b){var d=3Da.attr(b,"tabindex"),e=3DisNaN(d);return(e||d>= =3D0)&&c(b,!e)}}),a(function(){var b=3Ddocument.body,c=3Db.appendChild(c=3D= document.createElement("div"));c.offsetHeight,a.extend(c.style,{minHeight:"= 100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=3Dc.offs= etHeight=3D=3D=3D100,a.support.selectstart=3D"onselectstart"in c,b.removeCh= ild(c).style.display=3D"none"}),a.extend(a.ui,{plugin:{add:function(b,c,d){= var e=3Da.ui[b].prototype;for(var f in d)e.plugins[f]=3De.plugins[f]||[],e.= plugins[f].push([c,d[f]])},call:function(a,b,c){var d=3Da.plugins[b];if(!d|= |!a.element[0].parentNode)return;for(var e=3D0;e0?!0:(b[d]=3D1,e=3Db[d]>0,b[d]=3D0,e)},isOverAxis:function(a,b,c){return= a>b&&a").outerWidth(1).jquery||a.each([= "Width","Height"],function(c,d){function h(b,c,d,f){return a.each(e,functio= n(){c-=3DparseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=3DparseFloat(a= .curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=3DparseFloat(a.curCSS(b,"ma= rgin"+this,!0))||0)}),c}var e=3Dd=3D=3D=3D"Width"?["Left","Right"]:["Top","= Bottom"],f=3Dd.toLowerCase(),g=3D{innerWidth:a.fn.innerWidth,innerHeight:a.= fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.f= n["inner"+d]=3Dfunction(c){return c=3D=3D=3Db?g["inner"+d].call(this):this.= each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=3Dfunction= (b,c){return typeof b!=3D"number"?g["outer"+d].call(this,b):this.each(funct= ion(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:a.= expr.createPseudo?a.expr.createPseudo(function(b){return function(c){return= !!a.data(c,b)}}):function(b,c,d){return!!a.data(b,d[3])},focusable:function= (b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=3D= a.attr(b,"tabindex"),e=3DisNaN(d);return(e||d>=3D0)&&c(b,!e)}}),a(function(= ){var b=3Ddocument.body,c=3Db.appendChild(c=3Ddocument.createElement("div")= );c.offsetHeight,a.extend(c.style,{minHeight:"100px",height:"auto",padding:= 0,borderWidth:0}),a.support.minHeight=3Dc.offsetHeight=3D=3D=3D100,a.suppor= t.selectstart=3D"onselectstart"in c,b.removeChild(c).style.display=3D"none"= }),a.curCSS||(a.curCSS=3Da.css),a.extend(a.ui,{plugin:{add:function(b,c,d){= var e=3Da.ui[b].prototype;for(var f in d)e.plugins[f]=3De.plugins[f]||[],e.= plugins[f].push([c,d[f]])},call:function(a,b,c){var d=3Da.plugins[b];if(!d|= |!a.element[0].parentNode)return;for(var e=3D0;e0?!0:(b[d]=3D1,e=3Db[d]>0,b[d]=3D0,e)},isOverAxis:function(a,b,c){return= a>b&&a=3D9||!!b.button?this._mouseStarted?(this._mouseDr= ag(b),b.preventDefault()):(this._mouseDistanceMet(b)&&this._mouseDelayMet(b= )&&(this._mouseStarted=3Dthis._mouseStart(this._mouseDownEvent,b)!=3D=3D!1,= this._mouseStarted?this._mouseDrag(b):this._mouseUp(b)),!this._mouseStarted= ):this._mouseUp(b)},_mouseUp:function(b){return a(document).unbind("mousemo= ve."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widget= Name,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=3D!1,b.= target=3D=3Dthis._mouseDownEvent.target&&a.data(b.target,this.widgetName+".= preventClickEvent",!0),this._mouseStop(b)),!1},_mouseDistanceMet:function(a= ){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(thi= s._mouseDownEvent.pageY-a.pageY))>=3Dthis.options.distance},_mouseDelayMet:= function(a){return this.mouseDelayMet},_mouseStart:function(a){},_mouseDrag= :function(a){},_mouseStop:function(a){},_mouseCapture:function(a){return!0}= })})(jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05 +(function(a,b){var c=3D!1;a(document).mouseup(function(a){c=3D!1}),a.widge= t("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseIni= t:function(){var b=3Dthis;this.element.bind("mousedown."+this.widgetName,fu= nction(a){return b._mouseDown(a)}).bind("click."+this.widgetName,function(c= ){if(!0=3D=3D=3Da.data(c.target,b.widgetName+".preventClickEvent"))return a= .removeData(c.target,b.widgetName+".preventClickEvent"),c.stopImmediateProp= agation(),!1}),this.started=3D!1},_mouseDestroy:function(){this.element.unb= ind("."+this.widgetName),this._mouseMoveDelegate&&a(document).unbind("mouse= move."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widg= etName,this._mouseUpDelegate)},_mouseDown:function(b){if(c)return;this._mou= seStarted&&this._mouseUp(b),this._mouseDownEvent=3Db;var d=3Dthis,e=3Db.whi= ch=3D=3D1,f=3Dtypeof this.options.cancel=3D=3D"string"&&b.target.nodeName?a= (b.target).closest(this.options.cancel).length:!1;if(!e||f||!this._mouseCap= ture(b))return!0;this.mouseDelayMet=3D!this.options.delay,this.mouseDelayMe= t||(this._mouseDelayTimer=3DsetTimeout(function(){d.mouseDelayMet=3D!0},thi= s.options.delay));if(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)){thi= s._mouseStarted=3Dthis._mouseStart(b)!=3D=3D!1;if(!this._mouseStarted)retur= n b.preventDefault(),!0}return!0=3D=3D=3Da.data(b.target,this.widgetName+".= preventClickEvent")&&a.removeData(b.target,this.widgetName+".preventClickEv= ent"),this._mouseMoveDelegate=3Dfunction(a){return d._mouseMove(a)},this._m= ouseUpDelegate=3Dfunction(a){return d._mouseUp(a)},a(document).bind("mousem= ove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetN= ame,this._mouseUpDelegate),b.preventDefault(),c=3D!0,!0},_mouseMove:functio= n(b){return!a.browser.msie||document.documentMode>=3D9||!!b.button?this._mo= useStarted?(this._mouseDrag(b),b.preventDefault()):(this._mouseDistanceMet(= b)&&this._mouseDelayMet(b)&&(this._mouseStarted=3Dthis._mouseStart(this._mo= useDownEvent,b)!=3D=3D!1,this._mouseStarted?this._mouseDrag(b):this._mouseU= p(b)),!this._mouseStarted):this._mouseUp(b)},_mouseUp:function(b){return a(= document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbi= nd("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(t= his._mouseStarted=3D!1,b.target=3D=3Dthis._mouseDownEvent.target&&a.data(b.= target,this.widgetName+".preventClickEvent",!0),this._mouseStop(b)),!1},_mo= useDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pa= geX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=3Dthis.options.= distance},_mouseDelayMet:function(a){return this.mouseDelayMet},_mouseStart= :function(a){},_mouseDrag:function(a){},_mouseStop:function(a){},_mouseCapt= ure:function(a){return!0}})})(jQuery);;/*! jQuery UI - v1.8.23 - 2012-08-15 * https://github.com/jquery/jquery-ui * Includes: jquery.ui.position.js * Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ -(function(a,b){a.ui=3Da.ui||{};var c=3D/left|center|right/,d=3D/top|center= |bottom/,e=3D"center",f=3D{},g=3Da.fn.position,h=3Da.fn.offset;a.fn.positio= n=3Dfunction(b){if(!b||!b.of)return g.apply(this,arguments);b=3Da.extend({}= ,b);var h=3Da(b.of),i=3Dh[0],j=3D(b.collision||"flip").split(" "),k=3Db.off= set?b.offset.split(" "):[0,0],l,m,n;return i.nodeType=3D=3D=3D9?(l=3Dh.widt= h(),m=3Dh.height(),n=3D{top:0,left:0}):i.setTimeout?(l=3Dh.width(),m=3Dh.he= ight(),n=3D{top:h.scrollTop(),left:h.scrollLeft()}):i.preventDefault?(b.at= =3D"left top",l=3Dm=3D0,n=3D{top:b.of.pageY,left:b.of.pageX}):(l=3Dh.outerW= idth(),m=3Dh.outerHeight(),n=3Dh.offset()),a.each(["my","at"],function(){va= r a=3D(b[this]||"").split(" ");a.length=3D=3D=3D1&&(a=3Dc.test(a[0])?a.conc= at([e]):d.test(a[0])?[e].concat(a):[e,e]),a[0]=3Dc.test(a[0])?a[0]:e,a[1]= =3Dd.test(a[1])?a[1]:e,b[this]=3Da}),j.length=3D=3D=3D1&&(j[1]=3Dj[0]),k[0]= =3DparseInt(k[0],10)||0,k.length=3D=3D=3D1&&(k[1]=3Dk[0]),k[1]=3DparseInt(k= [1],10)||0,b.at[0]=3D=3D=3D"right"?n.left+=3Dl:b.at[0]=3D=3D=3De&&(n.left+= =3Dl/2),b.at[1]=3D=3D=3D"bottom"?n.top+=3Dm:b.at[1]=3D=3D=3De&&(n.top+=3Dm/= 2),n.left+=3Dk[0],n.top+=3Dk[1],this.each(function(){var c=3Da(this),d=3Dc.= outerWidth(),g=3Dc.outerHeight(),h=3DparseInt(a.curCSS(this,"marginLeft",!0= ))||0,i=3DparseInt(a.curCSS(this,"marginTop",!0))||0,o=3Dd+h+(parseInt(a.cu= rCSS(this,"marginRight",!0))||0),p=3Dg+i+(parseInt(a.curCSS(this,"marginBot= tom",!0))||0),q=3Da.extend({},n),r;b.my[0]=3D=3D=3D"right"?q.left-=3Dd:b.my= [0]=3D=3D=3De&&(q.left-=3Dd/2),b.my[1]=3D=3D=3D"bottom"?q.top-=3Dg:b.my[1]= =3D=3D=3De&&(q.top-=3Dg/2),f.fractions||(q.left=3DMath.round(q.left),q.top= =3DMath.round(q.top)),r=3D{left:q.left-h,top:q.top-i},a.each(["left","top"]= ,function(c,e){a.ui.position[j[c]]&&a.ui.position[j[c]][e](q,{targetWidth:l= ,targetHeight:m,elemWidth:d,elemHeight:g,collisionPosition:r,collisionWidth= :o,collisionHeight:p,offset:k,my:b.my,at:b.at})}),a.fn.bgiframe&&c.bgiframe= (),c.offset(a.extend(q,{using:b.using}))})},a.ui.position=3D{fit:{left:func= tion(b,c){var d=3Da(window),e=3Dc.collisionPosition.left+c.collisionWidth-d= .width()-d.scrollLeft();b.left=3De>0?b.left-e:Math.max(b.left-c.collisionPo= sition.left,b.left)},top:function(b,c){var d=3Da(window),e=3Dc.collisionPos= ition.top+c.collisionHeight-d.height()-d.scrollTop();b.top=3De>0?b.top-e:Ma= th.max(b.top-c.collisionPosition.top,b.top)}},flip:{left:function(b,c){if(c= .at[0]=3D=3D=3De)return;var d=3Da(window),f=3Dc.collisionPosition.left+c.co= llisionWidth-d.width()-d.scrollLeft(),g=3Dc.my[0]=3D=3D=3D"left"?-c.elemWid= th:c.my[0]=3D=3D=3D"right"?c.elemWidth:0,h=3Dc.at[0]=3D=3D=3D"left"?c.targe= tWidth:-c.targetWidth,i=3D-2*c.offset[0];b.left+=3Dc.collisionPosition.left= <0?g+h+i:f>0?g+h+i:0},top:function(b,c){if(c.at[1]=3D=3D=3De)return;var d= =3Da(window),f=3Dc.collisionPosition.top+c.collisionHeight-d.height()-d.scr= ollTop(),g=3Dc.my[1]=3D=3D=3D"top"?-c.elemHeight:c.my[1]=3D=3D=3D"bottom"?c= .elemHeight:0,h=3Dc.at[1]=3D=3D=3D"top"?c.targetHeight:-c.targetHeight,i=3D= -2*c.offset[1];b.top+=3Dc.collisionPosition.top<0?g+h+i:f>0?g+h+i:0}}},a.of= fset.setOffset||(a.offset.setOffset=3Dfunction(b,c){/static/.test(a.curCSS(= b,"position"))&&(b.style.position=3D"relative");var d=3Da(b),e=3Dd.offset()= ,f=3DparseInt(a.curCSS(b,"top",!0),10)||0,g=3DparseInt(a.curCSS(b,"left",!0= ),10)||0,h=3D{top:c.top-e.top+f,left:c.left-e.left+g};"using"in c?c.using.c= all(b,h):d.css(h)},a.fn.offset=3Dfunction(b){var c=3Dthis[0];return!c||!c.o= wnerDocument?null:b?a.isFunction(b)?this.each(function(c){a(this).offset(b.= call(this,c,a(this).offset()))}):this.each(function(){a.offset.setOffset(th= is,b)}):h.call(this)}),function(){var b=3Ddocument.getElementsByTagName("bo= dy")[0],c=3Ddocument.createElement("div"),d,e,g,h,i;d=3Ddocument.createElem= ent(b?"div":"body"),g=3D{visibility:"hidden",width:0,height:0,border:0,marg= in:0,background:"none"},b&&a.extend(g,{position:"absolute",left:"-1000px",t= op:"-1000px"});for(var j in g)d.style[j]=3Dg[j];d.appendChild(c),e=3Db||doc= ument.documentElement,e.insertBefore(d,e.firstChild),c.style.cssText=3D"pos= ition: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width:= 201px;",h=3Da(c).offset(function(a,b){return b}).offset(),d.innerHTML=3D""= ,e.removeChild(d),i=3Dh.top+h.left+(b?2e3:0),f.fractions=3Di>21&&i<22}()})(= jQuery);;/*! jQuery UI - v1.8.21 - 2012-06-05 +(function(a,b){a.ui=3Da.ui||{};var c=3D/left|center|right/,d=3D/top|center= |bottom/,e=3D"center",f=3D{},g=3Da.fn.position,h=3Da.fn.offset;a.fn.positio= n=3Dfunction(b){if(!b||!b.of)return g.apply(this,arguments);b=3Da.extend({}= ,b);var h=3Da(b.of),i=3Dh[0],j=3D(b.collision||"flip").split(" "),k=3Db.off= set?b.offset.split(" "):[0,0],l,m,n;return i.nodeType=3D=3D=3D9?(l=3Dh.widt= h(),m=3Dh.height(),n=3D{top:0,left:0}):i.setTimeout?(l=3Dh.width(),m=3Dh.he= ight(),n=3D{top:h.scrollTop(),left:h.scrollLeft()}):i.preventDefault?(b.at= =3D"left top",l=3Dm=3D0,n=3D{top:b.of.pageY,left:b.of.pageX}):(l=3Dh.outerW= idth(),m=3Dh.outerHeight(),n=3Dh.offset()),a.each(["my","at"],function(){va= r a=3D(b[this]||"").split(" ");a.length=3D=3D=3D1&&(a=3Dc.test(a[0])?a.conc= at([e]):d.test(a[0])?[e].concat(a):[e,e]),a[0]=3Dc.test(a[0])?a[0]:e,a[1]= =3Dd.test(a[1])?a[1]:e,b[this]=3Da}),j.length=3D=3D=3D1&&(j[1]=3Dj[0]),k[0]= =3DparseInt(k[0],10)||0,k.length=3D=3D=3D1&&(k[1]=3Dk[0]),k[1]=3DparseInt(k= [1],10)||0,b.at[0]=3D=3D=3D"right"?n.left+=3Dl:b.at[0]=3D=3D=3De&&(n.left+= =3Dl/2),b.at[1]=3D=3D=3D"bottom"?n.top+=3Dm:b.at[1]=3D=3D=3De&&(n.top+=3Dm/= 2),n.left+=3Dk[0],n.top+=3Dk[1],this.each(function(){var c=3Da(this),d=3Dc.= outerWidth(),g=3Dc.outerHeight(),h=3DparseInt(a.curCSS(this,"marginLeft",!0= ))||0,i=3DparseInt(a.curCSS(this,"marginTop",!0))||0,o=3Dd+h+(parseInt(a.cu= rCSS(this,"marginRight",!0))||0),p=3Dg+i+(parseInt(a.curCSS(this,"marginBot= tom",!0))||0),q=3Da.extend({},n),r;b.my[0]=3D=3D=3D"right"?q.left-=3Dd:b.my= [0]=3D=3D=3De&&(q.left-=3Dd/2),b.my[1]=3D=3D=3D"bottom"?q.top-=3Dg:b.my[1]= =3D=3D=3De&&(q.top-=3Dg/2),f.fractions||(q.left=3DMath.round(q.left),q.top= =3DMath.round(q.top)),r=3D{left:q.left-h,top:q.top-i},a.each(["left","top"]= ,function(c,e){a.ui.position[j[c]]&&a.ui.position[j[c]][e](q,{targetWidth:l= ,targetHeight:m,elemWidth:d,elemHeight:g,collisionPosition:r,collisionWidth= :o,collisionHeight:p,offset:k,my:b.my,at:b.at})}),a.fn.bgiframe&&c.bgiframe= (),c.offset(a.extend(q,{using:b.using}))})},a.ui.position=3D{fit:{left:func= tion(b,c){var d=3Da(window),e=3Dc.collisionPosition.left+c.collisionWidth-d= .width()-d.scrollLeft();b.left=3De>0?b.left-e:Math.max(b.left-c.collisionPo= sition.left,b.left)},top:function(b,c){var d=3Da(window),e=3Dc.collisionPos= ition.top+c.collisionHeight-d.height()-d.scrollTop();b.top=3De>0?b.top-e:Ma= th.max(b.top-c.collisionPosition.top,b.top)}},flip:{left:function(b,c){if(c= .at[0]=3D=3D=3De)return;var d=3Da(window),f=3Dc.collisionPosition.left+c.co= llisionWidth-d.width()-d.scrollLeft(),g=3Dc.my[0]=3D=3D=3D"left"?-c.elemWid= th:c.my[0]=3D=3D=3D"right"?c.elemWidth:0,h=3Dc.at[0]=3D=3D=3D"left"?c.targe= tWidth:-c.targetWidth,i=3D-2*c.offset[0];b.left+=3Dc.collisionPosition.left= <0?g+h+i:f>0?g+h+i:0},top:function(b,c){if(c.at[1]=3D=3D=3De)return;var d= =3Da(window),f=3Dc.collisionPosition.top+c.collisionHeight-d.height()-d.scr= ollTop(),g=3Dc.my[1]=3D=3D=3D"top"?-c.elemHeight:c.my[1]=3D=3D=3D"bottom"?c= .elemHeight:0,h=3Dc.at[1]=3D=3D=3D"top"?c.targetHeight:-c.targetHeight,i=3D= -2*c.offset[1];b.top+=3Dc.collisionPosition.top<0?g+h+i:f>0?g+h+i:0}}},a.of= fset.setOffset||(a.offset.setOffset=3Dfunction(b,c){/static/.test(a.curCSS(= b,"position"))&&(b.style.position=3D"relative");var d=3Da(b),e=3Dd.offset()= ,f=3DparseInt(a.curCSS(b,"top",!0),10)||0,g=3DparseInt(a.curCSS(b,"left",!0= ),10)||0,h=3D{top:c.top-e.top+f,left:c.left-e.left+g};"using"in c?c.using.c= all(b,h):d.css(h)},a.fn.offset=3Dfunction(b){var c=3Dthis[0];return!c||!c.o= wnerDocument?null:b?a.isFunction(b)?this.each(function(c){a(this).offset(b.= call(this,c,a(this).offset()))}):this.each(function(){a.offset.setOffset(th= is,b)}):h.call(this)}),a.curCSS||(a.curCSS=3Da.css),function(){var b=3Ddocu= ment.getElementsByTagName("body")[0],c=3Ddocument.createElement("div"),d,e,= g,h,i;d=3Ddocument.createElement(b?"div":"body"),g=3D{visibility:"hidden",w= idth:0,height:0,border:0,margin:0,background:"none"},b&&a.extend(g,{positio= n:"absolute",left:"-1000px",top:"-1000px"});for(var j in g)d.style[j]=3Dg[j= ];d.appendChild(c),e=3Db||document.documentElement,e.insertBefore(d,e.first= Child),c.style.cssText=3D"position: absolute; left: 10.7432222px; top: 10.4= 32325px; height: 30px; width: 201px;",h=3Da(c).offset(function(a,b){return = b}).offset(),d.innerHTML=3D"",e.removeChild(d),i=3Dh.top+h.left+(b?2e3:0),f= .fractions=3Di>21&&i<22}()})(jQuery);;/*! jQuery UI - v1.8.23 - 2012-08-15 +* https://github.com/jquery/jquery-ui +* Includes: jquery.ui.sortable.js +* Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ +(function(a,b){a.widget("ui.sortable",a.ui.mouse,{widgetEventPrefix:"sort"= ,ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,= cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelpe= rSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeho= lder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"defa= ult",tolerance:"intersect",zIndex:1e3},_create:function(){var a=3Dthis.opti= ons;this.containerCache=3D{},this.element.addClass("ui-sortable"),this.refr= esh(),this.floating=3Dthis.items.length?a.axis=3D=3D=3D"x"||/left|right/.te= st(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0]= .item.css("display")):!1,this.offset=3Dthis.element.offset(),this._mouseIni= t(),this.ready=3D!0},destroy:function(){a.Widget.prototype.destroy.call(thi= s),this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouse= Destroy();for(var b=3Dthis.items.length-1;b>=3D0;b--)this.items[b].item.rem= oveData(this.widgetName+"-item");return this},_setOption:function(b,c){b=3D= =3D=3D"disabled"?(this.options[b]=3Dc,this.widget()[c?"addClass":"removeCla= ss"]("ui-sortable-disabled")):a.Widget.prototype._setOption.apply(this,argu= ments)},_mouseCapture:function(b,c){var d=3Dthis;if(this.reverting)return!1= ;if(this.options.disabled||this.options.type=3D=3D"static")return!1;this._r= efreshItems(b);var e=3Dnull,f=3Dthis,g=3Da(b.target).parents().each(functio= n(){if(a.data(this,d.widgetName+"-item")=3D=3Df)return e=3Da(this),!1});a.d= ata(b.target,d.widgetName+"-item")=3D=3Df&&(e=3Da(b.target));if(!e)return!1= ;if(this.options.handle&&!c){var h=3D!1;a(this.options.handle,e).find("*").= andSelf().each(function(){this=3D=3Db.target&&(h=3D!0)});if(!h)return!1}ret= urn this.currentItem=3De,this._removeCurrentsFromItems(),!0},_mouseStart:fu= nction(b,c,d){var e=3Dthis.options,f=3Dthis;this.currentContainer=3Dthis,th= is.refreshPositions(),this.helper=3Dthis._createHelper(b),this._cacheHelper= Proportions(),this._cacheMargins(),this.scrollParent=3Dthis.helper.scrollPa= rent(),this.offset=3Dthis.currentItem.offset(),this.offset=3D{top:this.offs= et.top-this.margins.top,left:this.offset.left-this.margins.left},a.extend(t= his.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.to= p},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this= .helper.css("position","absolute"),this.cssPosition=3Dthis.helper.css("posi= tion"),this.originalPosition=3Dthis._generatePosition(b),this.originalPageX= =3Db.pageX,this.originalPageY=3Db.pageY,e.cursorAt&&this._adjustOffsetFromH= elper(e.cursorAt),this.domPosition=3D{prev:this.currentItem.prev()[0],paren= t:this.currentItem.parent()[0]},this.helper[0]!=3Dthis.currentItem[0]&&this= .currentItem.hide(),this._createPlaceholder(),e.containment&&this._setConta= inment(),e.cursor&&(a("body").css("cursor")&&(this._storedCursor=3Da("body"= ).css("cursor")),a("body").css("cursor",e.cursor)),e.opacity&&(this.helper.= css("opacity")&&(this._storedOpacity=3Dthis.helper.css("opacity")),this.hel= per.css("opacity",e.opacity)),e.zIndex&&(this.helper.css("zIndex")&&(this._= storedZIndex=3Dthis.helper.css("zIndex")),this.helper.css("zIndex",e.zIndex= )),this.scrollParent[0]!=3Ddocument&&this.scrollParent[0].tagName!=3D"HTML"= &&(this.overflowOffset=3Dthis.scrollParent.offset()),this._trigger("start",= b,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProport= ions();if(!d)for(var g=3Dthis.containers.length-1;g>=3D0;g--)this.container= s[g]._trigger("activate",b,f._uiHash(this));return a.ui.ddmanager&&(a.ui.dd= manager.current=3Dthis),a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.pr= epareOffsets(this,b),this.dragging=3D!0,this.helper.addClass("ui-sortable-h= elper"),this._mouseDrag(b),!0},_mouseDrag:function(b){this.position=3Dthis.= _generatePosition(b),this.positionAbs=3Dthis._convertPositionTo("absolute")= ,this.lastPositionAbs||(this.lastPositionAbs=3Dthis.positionAbs);if(this.op= tions.scroll){var c=3Dthis.options,d=3D!1;this.scrollParent[0]!=3Ddocument&= &this.scrollParent[0].tagName!=3D"HTML"?(this.overflowOffset.top+this.scrol= lParent[0].offsetHeight-b.pageY=3D0;e--){var f=3Dthis.i= tems[e],g=3Df.item[0],h=3Dthis._intersectsWithPointer(f);if(!h)continue;if(= g!=3Dthis.currentItem[0]&&this.placeholder[h=3D=3D1?"next":"prev"]()[0]!=3D= g&&!a.ui.contains(this.placeholder[0],g)&&(this.options.type=3D=3D"semi-dyn= amic"?!a.ui.contains(this.element[0],g):!0)){this.direction=3Dh=3D=3D1?"dow= n":"up";if(this.options.tolerance=3D=3D"pointer"||this._intersectsWithSides= (f))this._rearrange(b,f);else break;this._trigger("change",b,this._uiHash()= );break}}return this._contactContainers(b),a.ui.ddmanager&&a.ui.ddmanager.d= rag(this,b),this._trigger("sort",b,this._uiHash()),this.lastPositionAbs=3Dt= his.positionAbs,!1},_mouseStop:function(b,c){if(!b)return;a.ui.ddmanager&&!= this.options.dropBehaviour&&a.ui.ddmanager.drop(this,b);if(this.options.rev= ert){var d=3Dthis,e=3Dd.placeholder.offset();d.reverting=3D!0,a(this.helper= ).animate({left:e.left-this.offset.parent.left-d.margins.left+(this.offsetP= arent[0]=3D=3Ddocument.body?0:this.offsetParent[0].scrollLeft),top:e.top-th= is.offset.parent.top-d.margins.top+(this.offsetParent[0]=3D=3Ddocument.body= ?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,f= unction(){d._clear(b)})}else this._clear(b,c);return!1},cancel:function(){v= ar b=3Dthis;if(this.dragging){this._mouseUp({target:null}),this.options.hel= per=3D=3D"original"?this.currentItem.css(this._storedCSS).removeClass("ui-s= ortable-helper"):this.currentItem.show();for(var c=3Dthis.containers.length= -1;c>=3D0;c--)this.containers[c]._trigger("deactivate",null,b._uiHash(this)= ),this.containers[c].containerCache.over&&(this.containers[c]._trigger("out= ",null,b._uiHash(this)),this.containers[c].containerCache.over=3D0)}return = this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].pare= ntNode.removeChild(this.placeholder[0]),this.options.helper!=3D"original"&&= this.helper&&this.helper[0].parentNode&&this.helper.remove(),a.extend(this,= {helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.= prev?a(this.domPosition.prev).after(this.currentItem):a(this.domPosition.pa= rent).prepend(this.currentItem)),this},serialize:function(b){var c=3Dthis._= getItemsAsjQuery(b&&b.connected),d=3D[];return b=3Db||{},a(c).each(function= (){var c=3D(a(b.item||this).attr(b.attribute||"id")||"").match(b.expression= ||/(.+)[-=3D_](.+)/);c&&d.push((b.key||c[1]+"[]")+"=3D"+(b.key&&b.expressio= n?c[1]:c[2]))}),!d.length&&b.key&&d.push(b.key+"=3D"),d.join("&")},toArray:= function(b){var c=3Dthis._getItemsAsjQuery(b&&b.connected),d=3D[];return b= =3Db||{},c.each(function(){d.push(a(b.item||this).attr(b.attribute||"id")||= "")}),d},_intersectsWith:function(a){var b=3Dthis.positionAbs.left,c=3Db+th= is.helperProportions.width,d=3Dthis.positionAbs.top,e=3Dd+this.helperPropor= tions.height,f=3Da.left,g=3Df+a.width,h=3Da.top,i=3Dh+a.height,j=3Dthis.off= set.click.top,k=3Dthis.offset.click.left,l=3Dd+j>h&&d+jf&&b+ka[this.floating?"width":"height"]?l:f0?"down":"up")},_getDragHorizontalDirection:function(){= var a=3Dthis.positionAbs.left-this.lastPositionAbs.left;return a!=3D0&&(a>0= ?"right":"left")},refresh:function(a){return this._refreshItems(a),this.ref= reshPositions(),this},_connectWith:function(){var a=3Dthis.options;return a= .connectWith.constructor=3D=3DString?[a.connectWith]:a.connectWith},_getIte= msAsjQuery:function(b){var c=3Dthis,d=3D[],e=3D[],f=3Dthis._connectWith();i= f(f&&b)for(var g=3Df.length-1;g>=3D0;g--){var h=3Da(f[g]);for(var i=3Dh.len= gth-1;i>=3D0;i--){var j=3Da.data(h[i],this.widgetName);j&&j!=3Dthis&&!j.opt= ions.disabled&&e.push([a.isFunction(j.options.items)?j.options.items.call(j= .element):a(j.options.items,j.element).not(".ui-sortable-helper").not(".ui-= sortable-placeholder"),j])}}e.push([a.isFunction(this.options.items)?this.o= ptions.items.call(this.element,null,{options:this.options,item:this.current= Item}):a(this.options.items,this.element).not(".ui-sortable-helper").not(".= ui-sortable-placeholder"),this]);for(var g=3De.length-1;g>=3D0;g--)e[g][0].= each(function(){d.push(this)});return a(d)},_removeCurrentsFromItems:functi= on(){var a=3Dthis.currentItem.find(":data("+this.widgetName+"-item)");for(v= ar b=3D0;b=3D0;g--){var h=3Da(f[= g]);for(var i=3Dh.length-1;i>=3D0;i--){var j=3Da.data(h[i],this.widgetName)= ;j&&j!=3Dthis&&!j.options.disabled&&(e.push([a.isFunction(j.options.items)?= j.options.items.call(j.element[0],b,{item:this.currentItem}):a(j.options.it= ems,j.element),j]),this.containers.push(j))}}for(var g=3De.length-1;g>=3D0;= g--){var k=3De[g][1],l=3De[g][0];for(var i=3D0,m=3Dl.length;i=3D0;c--){var d=3Dthis.items[c];if(d.instance!=3Dt= his.currentContainer&&this.currentContainer&&d.item[0]!=3Dthis.currentItem[= 0])continue;var e=3Dthis.options.toleranceElement?a(this.options.toleranceE= lement,d.item):d.item;b||(d.width=3De.outerWidth(),d.height=3De.outerHeight= ());var f=3De.offset();d.left=3Df.left,d.top=3Df.top}if(this.options.custom= &&this.options.custom.refreshContainers)this.options.custom.refreshContaine= rs.call(this);else for(var c=3Dthis.containers.length-1;c>=3D0;c--){var f= =3Dthis.containers[c].element.offset();this.containers[c].containerCache.le= ft=3Df.left,this.containers[c].containerCache.top=3Df.top,this.containers[c= ].containerCache.width=3Dthis.containers[c].element.outerWidth(),this.conta= iners[c].containerCache.height=3Dthis.containers[c].element.outerHeight()}r= eturn this},_createPlaceholder:function(b){var c=3Db||this,d=3Dc.options;if= (!d.placeholder||d.placeholder.constructor=3D=3DString){var e=3Dd.placehold= er;d.placeholder=3D{element:function(){var b=3Da(document.createElement(c.c= urrentItem[0].nodeName)).addClass(e||c.currentItem[0].className+" ui-sortab= le-placeholder").removeClass("ui-sortable-helper")[0];return e||(b.style.vi= sibility=3D"hidden"),b},update:function(a,b){if(e&&!d.forcePlaceholderSize)= return;b.height()||b.height(c.currentItem.innerHeight()-parseInt(c.currentI= tem.css("paddingTop")||0,10)-parseInt(c.currentItem.css("paddingBottom")||0= ,10)),b.width()||b.width(c.currentItem.innerWidth()-parseInt(c.currentItem.= css("paddingLeft")||0,10)-parseInt(c.currentItem.css("paddingRight")||0,10)= )}}}c.placeholder=3Da(d.placeholder.element.call(c.element,c.currentItem)),= c.currentItem.after(c.placeholder),d.placeholder.update(c,c.placeholder)},_= contactContainers:function(b){var c=3Dnull,d=3Dnull;for(var e=3Dthis.contai= ners.length-1;e>=3D0;e--){if(a.ui.contains(this.currentItem[0],this.contain= ers[e].element[0]))continue;if(this._intersectsWith(this.containers[e].cont= ainerCache)){if(c&&a.ui.contains(this.containers[e].element[0],c.element[0]= ))continue;c=3Dthis.containers[e],d=3De}else this.containers[e].containerCa= che.over&&(this.containers[e]._trigger("out",b,this._uiHash(this)),this.con= tainers[e].containerCache.over=3D0)}if(!c)return;if(this.containers.length= =3D=3D=3D1)this.containers[d]._trigger("over",b,this._uiHash(this)),this.co= ntainers[d].containerCache.over=3D1;else if(this.currentContainer!=3Dthis.c= ontainers[d]){var f=3D1e4,g=3Dnull,h=3Dthis.positionAbs[this.containers[d].= floating?"left":"top"];for(var i=3Dthis.items.length-1;i>=3D0;i--){if(!a.ui= .contains(this.containers[d].element[0],this.items[i].item[0]))continue;var= j=3Dthis.containers[d].floating?this.items[i].item.offset().left:this.item= s[i].item.offset().top;Math.abs(j-h)0?"down":"up")}if(!g&&!this.options.dropOnEmpty)retu= rn;this.currentContainer=3Dthis.containers[d],g?this._rearrange(b,g,null,!0= ):this._rearrange(b,null,this.containers[d].element,!0),this._trigger("chan= ge",b,this._uiHash()),this.containers[d]._trigger("change",b,this._uiHash(t= his)),this.options.placeholder.update(this.currentContainer,this.placeholde= r),this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers= [d].containerCache.over=3D1}},_createHelper:function(b){var c=3Dthis.option= s,d=3Da.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b,this.curre= ntItem])):c.helper=3D=3D"clone"?this.currentItem.clone():this.currentItem;r= eturn d.parents("body").length||a(c.appendTo!=3D"parent"?c.appendTo:this.cu= rrentItem[0].parentNode)[0].appendChild(d[0]),d[0]=3D=3Dthis.currentItem[0]= &&(this._storedCSS=3D{width:this.currentItem[0].style.width,height:this.cur= rentItem[0].style.height,position:this.currentItem.css("position"),top:this= .currentItem.css("top"),left:this.currentItem.css("left")}),(d[0].style.wid= th=3D=3D""||c.forceHelperSize)&&d.width(this.currentItem.width()),(d[0].sty= le.height=3D=3D""||c.forceHelperSize)&&d.height(this.currentItem.height()),= d},_adjustOffsetFromHelper:function(b){typeof b=3D=3D"string"&&(b=3Db.split= (" ")),a.isArray(b)&&(b=3D{left:+b[0],top:+b[1]||0}),"left"in b&&(this.offs= et.click.left=3Db.left+this.margins.left),"right"in b&&(this.offset.click.l= eft=3Dthis.helperProportions.width-b.right+this.margins.left),"top"in b&&(t= his.offset.click.top=3Db.top+this.margins.top),"bottom"in b&&(this.offset.c= lick.top=3Dthis.helperProportions.height-b.bottom+this.margins.top)},_getPa= rentOffset:function(){this.offsetParent=3Dthis.helper.offsetParent();var b= =3Dthis.offsetParent.offset();this.cssPosition=3D=3D"absolute"&&this.scroll= Parent[0]!=3Ddocument&&a.ui.contains(this.scrollParent[0],this.offsetParent= [0])&&(b.left+=3Dthis.scrollParent.scrollLeft(),b.top+=3Dthis.scrollParent.= scrollTop());if(this.offsetParent[0]=3D=3Ddocument.body||this.offsetParent[= 0].tagName&&this.offsetParent[0].tagName.toLowerCase()=3D=3D"html"&&a.brows= er.msie)b=3D{top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css= ("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("bor= derLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition= =3D=3D"relative"){var a=3Dthis.currentItem.position();return{top:a.top-(par= seInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.l= eft-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft(= )}}return{top:0,left:0}},_cacheMargins:function(){this.margins=3D{left:pars= eInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentIte= m.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperP= roportions=3D{width:this.helper.outerWidth(),height:this.helper.outerHeight= ()}},_setContainment:function(){var b=3Dthis.options;b.containment=3D=3D"pa= rent"&&(b.containment=3Dthis.helper[0].parentNode);if(b.containment=3D=3D"d= ocument"||b.containment=3D=3D"window")this.containment=3D[0-this.offset.rel= ative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.p= arent.top,a(b.containment=3D=3D"document"?document:window).width()-this.hel= perProportions.width-this.margins.left,(a(b.containment=3D=3D"document"?doc= ument:window).height()||document.body.parentNode.scrollHeight)-this.helperP= roportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b= .containment)){var c=3Da(b.containment)[0],d=3Da(b.containment).offset(),e= =3Da(c).css("overflow")!=3D"hidden";this.containment=3D[d.left+(parseInt(a(= c).css("borderLeftWidth"),10)||0)+(parseInt(a(c).css("paddingLeft"),10)||0)= -this.margins.left,d.top+(parseInt(a(c).css("borderTopWidth"),10)||0)+(pars= eInt(a(c).css("paddingTop"),10)||0)-this.margins.top,d.left+(e?Math.max(c.s= crollWidth,c.offsetWidth):c.offsetWidth)-(parseInt(a(c).css("borderLeftWidt= h"),10)||0)-(parseInt(a(c).css("paddingRight"),10)||0)-this.helperProportio= ns.width-this.margins.left,d.top+(e?Math.max(c.scrollHeight,c.offsetHeight)= :c.offsetHeight)-(parseInt(a(c).css("borderTopWidth"),10)||0)-(parseInt(a(c= ).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.t= op]}},_convertPositionTo:function(b,c){c||(c=3Dthis.position);var d=3Db=3D= =3D"absolute"?1:-1,e=3Dthis.options,f=3Dthis.cssPosition=3D=3D"absolute"&&(= this.scrollParent[0]=3D=3Ddocument||!a.ui.contains(this.scrollParent[0],thi= s.offsetParent[0]))?this.offsetParent:this.scrollParent,g=3D/(html|body)/i.= test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.= parent.top*d-(a.browser.safari&&this.cssPosition=3D=3D"fixed"?0:(this.cssPo= sition=3D=3D"fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),le= ft:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.= safari&&this.cssPosition=3D=3D"fixed"?0:(this.cssPosition=3D=3D"fixed"?-thi= s.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:func= tion(b){var c=3Dthis.options,d=3Dthis.cssPosition=3D=3D"absolute"&&(this.sc= rollParent[0]=3D=3Ddocument||!a.ui.contains(this.scrollParent[0],this.offse= tParent[0]))?this.offsetParent:this.scrollParent,e=3D/(html|body)/i.test(d[= 0].tagName);this.cssPosition=3D=3D"relative"&&(this.scrollParent[0]=3D=3Ddo= cument||this.scrollParent[0]=3D=3Dthis.offsetParent[0])&&(this.offset.relat= ive=3Dthis._getRelativeOffset());var f=3Db.pageX,g=3Db.pageY;if(this.origin= alPosition){this.containment&&(b.pageX-this.offset.click.leftthis.containment[2]&&(f=3Dthis.contain= ment[2]+this.offset.click.left),b.pageY-this.offset.click.top>this.containm= ent[3]&&(g=3Dthis.containment[3]+this.offset.click.top));if(c.grid){var h= =3Dthis.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1= ];g=3Dthis.containment?h-this.offset.click.topthis.containment[3]?h-this.offset.click.topthis.containment[= 2]?i-this.offset.click.left=3D0;f--)a.ui.contains(this= .containers[f].element[0],this.currentItem[0])&&!c&&(d.push(function(a){ret= urn function(b){a._trigger("receive",b,this._uiHash(this))}}.call(this,this= .containers[f])),d.push(function(a){return function(b){a._trigger("update",= b,this._uiHash(this))}}.call(this,this.containers[f])))}for(var f=3Dthis.co= ntainers.length-1;f>=3D0;f--)c||d.push(function(a){return function(b){a._tr= igger("deactivate",b,this._uiHash(this))}}.call(this,this.containers[f])),t= his.containers[f].containerCache.over&&(d.push(function(a){return function(= b){a._trigger("out",b,this._uiHash(this))}}.call(this,this.containers[f])),= this.containers[f].containerCache.over=3D0);this._storedCursor&&a("body").c= ss("cursor",this._storedCursor),this._storedOpacity&&this.helper.css("opaci= ty",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex",this.= _storedZIndex=3D=3D"auto"?"":this._storedZIndex),this.dragging=3D!1;if(this= .cancelHelperRemoval){if(!c){this._trigger("beforeStop",b,this._uiHash());f= or(var f=3D0;f").addClass("ui-autocomplete").appendTo(a(this.options.= appendTo||"body",c)[0]).mousedown(function(c){var d=3Db.menu.element[0];a(c= .target).closest(".ui-menu-item").length||setTimeout(function(){a(document)= .one("mousedown",function(c){c.target!=3D=3Db.element[0]&&c.target!=3D=3Dd&= &!a.ui.contains(d,c.target)&&b.close()})},1),setTimeout(function(){clearTim= eout(b.closing)},13)}).menu({focus:function(a,c){var d=3Dc.item.data("item.= autocomplete");!1!=3D=3Db._trigger("focus",a,{item:d})&&/^key/.test(a.origi= nalEvent.type)&&b.element.val(d.value)},selected:function(a,d){var e=3Dd.it= em.data("item.autocomplete"),f=3Db.previous;b.element[0]!=3D=3Dc.activeElem= ent&&(b.element.focus(),b.previous=3Df,setTimeout(function(){b.previous=3Df= ,b.selectedItem=3De},1)),!1!=3D=3Db._trigger("select",a,{item:e})&&b.elemen= t.val(e.value),b.term=3Db.element.val(),b.close(a),b.selectedItem=3De},blur= :function(a,c){b.menu.element.is(":visible")&&b.element.val()!=3D=3Db.term&= &b.element.val(b.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0= }).hide().data("menu"),a.fn.bgiframe&&this.menu.element.bgiframe(),b.before= unloadHandler=3Dfunction(){b.element.removeAttr("autocomplete")},a(window).= bind("beforeunload",b.beforeunloadHandler)},destroy:function(){this.element= .removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr= ("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup"),this.m= enu.element.remove(),a(window).unbind("beforeunload",this.beforeunloadHandl= er),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widge= t.prototype._setOption.apply(this,arguments),b=3D=3D=3D"source"&&this._init= Source(),b=3D=3D=3D"appendTo"&&this.menu.element.appendTo(a(c||"body",this.= element[0].ownerDocument)[0]),b=3D=3D=3D"disabled"&&c&&this.xhr&&this.xhr.a= bort()},_initSource:function(){var b=3Dthis,c,d;a.isArray(this.options.sour= ce)?(c=3Dthis.options.source,this.source=3Dfunction(b,d){d(a.ui.autocomplet= e.filter(c,b.term))}):typeof this.options.source=3D=3D"string"?(d=3Dthis.op= tions.source,this.source=3Dfunction(c,e){b.xhr&&b.xhr.abort(),b.xhr=3Da.aja= x({url:d,data:c,dataType:"json",success:function(a,b){e(a)},error:function(= ){e([])}})}):this.source=3Dthis.options.source},search:function(a,b){a=3Da!= =3Dnull?a:this.element.val(),this.term=3Dthis.element.val();if(a.length").data("item.autocomplete",c).append(a("").text(c= .label)).appendTo(b)},_move:function(a,b){if(!this.menu.element.is(":visibl= e")){this.search(null,b);return}if(this.menu.first()&&/^previous/.test(a)||= this.menu.last()&&/^next/.test(a)){this.element.val(this.term),this.menu.de= activate();return}this.menu[a](b)},widget:function(){return this.menu.eleme= nt},_keyEvent:function(a,b){if(!this.isMultiLine||this.menu.element.is(":vi= sible"))this._move(a,b),b.preventDefault()}}),a.extend(a.ui.autocomplete,{e= scapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")}= ,filter:function(b,c){var d=3Dnew RegExp(a.ui.autocomplete.escapeRegex(c),"= i");return a.grep(b,function(a){return d.test(a.label||a.value||a)})}})})(j= Query),function(a){a.widget("ui.menu",{_create:function(){var b=3Dthis;this= .element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr= ({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(funct= ion(c){if(!a(c.target).closest(".ui-menu-item a").length)return;c.preventDe= fault(),b.select(c)}),this.refresh()},refresh:function(){var b=3Dthis,c=3Dt= his.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item= ").attr("role","menuitem");c.children("a").addClass("ui-corner-all").attr("= tabindex",-1).mouseenter(function(c){b.activate(c,a(this).parent())}).mouse= leave(function(){b.deactivate()})},activate:function(a,b){this.deactivate()= ;if(this.hasScroll()){var c=3Db.offset().top-this.element.offset().top,d=3D= this.element.scrollTop(),e=3Dthis.element.height();c<0?this.element.scrollT= op(d+c):c>=3De&&this.element.scrollTop(d+c-e+b.height())}this.active=3Db.eq= (0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem"= ).end(),this._trigger("focus",a,{item:b})},deactivate:function(){if(!this.a= ctive)return;this.active.children("a").removeClass("ui-state-hover").remove= Attr("id"),this._trigger("blur"),this.active=3Dnull},next:function(a){this.= move("next",".ui-menu-item:first",a)},previous:function(a){this.move("prev"= ,".ui-menu-item:last",a)},first:function(){return this.active&&!this.active= .prevAll(".ui-menu-item").length},last:function(){return this.active&&!this= .active.nextAll(".ui-menu-item").length},move:function(a,b,c){if(!this.acti= ve){this.activate(c,this.element.children(b));return}var d=3Dthis.active[a+= "All"](".ui-menu-item").eq(0);d.length?this.activate(c,d):this.activate(c,t= his.element.children(b))},nextPage:function(b){if(this.hasScroll()){if(!thi= s.active||this.last()){this.activate(b,this.element.children(".ui-menu-item= :first"));return}var c=3Dthis.active.offset().top,d=3Dthis.element.height()= ,e=3Dthis.element.children(".ui-menu-item").filter(function(){var b=3Da(thi= s).offset().top-c-d+a(this).height();return b<10&&b>-10});e.length||(e=3Dth= is.element.children(".ui-menu-item:last")),this.activate(b,e)}else this.act= ivate(b,this.element.children(".ui-menu-item").filter(!this.active||this.la= st()?":first":":last"))},previousPage:function(b){if(this.hasScroll()){if(!= this.active||this.first()){this.activate(b,this.element.children(".ui-menu-= item:last"));return}var c=3Dthis.active.offset().top,d=3Dthis.element.heigh= t(),e=3Dthis.element.children(".ui-menu-item").filter(function(){var b=3Da(= this).offset().top-c+d-a(this).height();return b<10&&b>-10});e.length||(e= =3Dthis.element.children(".ui-menu-item:first")),this.activate(b,e)}else th= is.activate(b,this.element.children(".ui-menu-item").filter(!this.active||t= his.first()?":last":":first"))},hasScroll:function(){return this.element.he= ight()").addClass("ui-autocomplete").appendTo(a(this.options.= appendTo||"body",c)[0]).mousedown(function(c){var d=3Db.menu.element[0];a(c= .target).closest(".ui-menu-item").length||setTimeout(function(){a(document)= .one("mousedown",function(c){c.target!=3D=3Db.element[0]&&c.target!=3D=3Dd&= &!a.ui.contains(d,c.target)&&b.close()})},1),setTimeout(function(){clearTim= eout(b.closing)},13)}).menu({focus:function(a,c){var d=3Dc.item.data("item.= autocomplete");!1!=3D=3Db._trigger("focus",a,{item:d})&&/^key/.test(a.origi= nalEvent.type)&&b.element.val(d.value)},selected:function(a,d){var e=3Dd.it= em.data("item.autocomplete"),f=3Db.previous;b.element[0]!=3D=3Dc.activeElem= ent&&(b.element.focus(),b.previous=3Df,setTimeout(function(){b.previous=3Df= ,b.selectedItem=3De},1)),!1!=3D=3Db._trigger("select",a,{item:e})&&b.elemen= t.val(e.value),b.term=3Db.element.val(),b.close(a),b.selectedItem=3De},blur= :function(a,c){b.menu.element.is(":visible")&&b.element.val()!=3D=3Db.term&= &b.element.val(b.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0= }).hide().data("menu"),a.fn.bgiframe&&this.menu.element.bgiframe(),b.before= unloadHandler=3Dfunction(){b.element.removeAttr("autocomplete")},a(window).= bind("beforeunload",b.beforeunloadHandler)},destroy:function(){this.element= .removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr= ("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup"),this.m= enu.element.remove(),a(window).unbind("beforeunload",this.beforeunloadHandl= er),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widge= t.prototype._setOption.apply(this,arguments),b=3D=3D=3D"source"&&this._init= Source(),b=3D=3D=3D"appendTo"&&this.menu.element.appendTo(a(c||"body",this.= element[0].ownerDocument)[0]),b=3D=3D=3D"disabled"&&c&&this.xhr&&this.xhr.a= bort()},_initSource:function(){var b=3Dthis,c,d;a.isArray(this.options.sour= ce)?(c=3Dthis.options.source,this.source=3Dfunction(b,d){d(a.ui.autocomplet= e.filter(c,b.term))}):typeof this.options.source=3D=3D"string"?(d=3Dthis.op= tions.source,this.source=3Dfunction(c,e){b.xhr&&b.xhr.abort(),b.xhr=3Da.aja= x({url:d,data:c,dataType:"json",success:function(a,b){e(a)},error:function(= ){e([])}})}):this.source=3Dthis.options.source},search:function(a,b){a=3Da!= =3Dnull?a:this.element.val(),this.term=3Dthis.element.val();if(a.length").data("item.autocomplete",c).append(a("").text(c= .label)).appendTo(b)},_move:function(a,b){if(!this.menu.element.is(":visibl= e")){this.search(null,b);return}if(this.menu.first()&&/^previous/.test(a)||= this.menu.last()&&/^next/.test(a)){this.element.val(this.term),this.menu.de= activate();return}this.menu[a](b)},widget:function(){return this.menu.eleme= nt},_keyEvent:function(a,b){if(!this.isMultiLine||this.menu.element.is(":vi= sible"))this._move(a,b),b.preventDefault()}}),a.extend(a.ui.autocomplete,{e= scapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")}= ,filter:function(b,c){var d=3Dnew RegExp(a.ui.autocomplete.escapeRegex(c),"= i");return a.grep(b,function(a){return d.test(a.label||a.value||a)})}})})(j= Query),function(a){a.widget("ui.menu",{_create:function(){var b=3Dthis;this= .element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr= ({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(funct= ion(c){if(!a(c.target).closest(".ui-menu-item a").length)return;c.preventDe= fault(),b.select(c)}),this.refresh()},refresh:function(){var b=3Dthis,c=3Dt= his.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item= ").attr("role","menuitem");c.children("a").addClass("ui-corner-all").attr("= tabindex",-1).mouseenter(function(c){b.activate(c,a(this).parent())}).mouse= leave(function(){b.deactivate()})},activate:function(a,b){this.deactivate()= ;if(this.hasScroll()){var c=3Db.offset().top-this.element.offset().top,d=3D= this.element.scrollTop(),e=3Dthis.element.height();c<0?this.element.scrollT= op(d+c):c>=3De&&this.element.scrollTop(d+c-e+b.height())}this.active=3Db.eq= (0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem"= ).end(),this._trigger("focus",a,{item:b})},deactivate:function(){if(!this.a= ctive)return;this.active.children("a").removeClass("ui-state-hover").remove= Attr("id"),this._trigger("blur"),this.active=3Dnull},next:function(a){this.= move("next",".ui-menu-item:first",a)},previous:function(a){this.move("prev"= ,".ui-menu-item:last",a)},first:function(){return this.active&&!this.active= .prevAll(".ui-menu-item").length},last:function(){return this.active&&!this= .active.nextAll(".ui-menu-item").length},move:function(a,b,c){if(!this.acti= ve){this.activate(c,this.element.children(b));return}var d=3Dthis.active[a+= "All"](".ui-menu-item").eq(0);d.length?this.activate(c,d):this.activate(c,t= his.element.children(b))},nextPage:function(b){if(this.hasScroll()){if(!thi= s.active||this.last()){this.activate(b,this.element.children(".ui-menu-item= :first"));return}var c=3Dthis.active.offset().top,d=3Dthis.element.height()= ,e=3Dthis.element.children(".ui-menu-item").filter(function(){var b=3Da(thi= s).offset().top-c-d+a(this).height();return b<10&&b>-10});e.length||(e=3Dth= is.element.children(".ui-menu-item:last")),this.activate(b,e)}else this.act= ivate(b,this.element.children(".ui-menu-item").filter(!this.active||this.la= st()?":first":":last"))},previousPage:function(b){if(this.hasScroll()){if(!= this.active||this.first()){this.activate(b,this.element.children(".ui-menu-= item:last"));return}var c=3Dthis.active.offset().top,d=3Dthis.element.heigh= t(),e=3Dthis.element.children(".ui-menu-item").filter(function(){var b=3Da(= this).offset().top-c+d-a(this).height();return b<10&&b>-10});e.length||(e= =3Dthis.element.children(".ui-menu-item:first")),this.activate(b,e)}else th= is.activate(b,this.element.children(".ui-menu-item").filter(!this.active||t= his.first()?":last":":first"))},hasScroll:function(){return this.element.he= ight()",g=3Dd.values&&d.values.length||1,h=3D[];= this._keySliding=3D!1,this._mouseSliding=3D!1,this._animateOff=3D!0,this._h= andleIndex=3Dnull,this._detectOrientation(),this._mouseInit(),this.element.= addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-c= ontent"+" ui-corner-all"+(d.disabled?" ui-slider-disabled ui-disabled":""))= ,this.range=3Da([]),d.range&&(d.range=3D=3D=3D!0&&(d.values||(d.values=3D[t= his._valueMin(),this._valueMin()]),d.values.length&&d.values.length!=3D=3D2= &&(d.values=3D[d.values[0],d.values[0]])),this.range=3Da("
    ").app= endTo(this.element).addClass("ui-slider-range ui-widget-header"+(d.range=3D= =3D=3D"min"||d.range=3D=3D=3D"max"?" ui-slider-range-"+d.range:"")));for(va= r i=3De.length;ic&&(f=3Dc,g=3Da(this),i=3Db)}),c.range=3D=3D=3D!0&&this.values(1)=3D= =3D=3Dc.min&&(i+=3D1,g=3Da(this.handles[i])),j=3Dthis._start(b,i),j=3D=3D= =3D!1?!1:(this._mouseSliding=3D!0,h._handleIndex=3Di,g.addClass("ui-state-a= ctive").focus(),k=3Dg.offset(),l=3D!a(b.target).parents().andSelf().is(".ui= -slider-handle"),this._clickOffset=3Dl?{left:0,top:0}:{left:b.pageX-k.left-= g.width()/2,top:b.pageY-k.top-g.height()/2-(parseInt(g.css("borderTopWidth"= ),10)||0)-(parseInt(g.css("borderBottomWidth"),10)||0)+(parseInt(g.css("mar= ginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(b,i,= e),this._animateOff=3D!0,!0))},_mouseStart:function(a){return!0},_mouseDrag= :function(a){var b=3D{x:a.pageX,y:a.pageY},c=3Dthis._normValueFromMouse(b);= return this._slide(a,this._handleIndex,c),!1},_mouseStop:function(a){return= this.handles.removeClass("ui-state-active"),this._mouseSliding=3D!1,this._= stop(a,this._handleIndex),this._change(a,this._handleIndex),this._handleInd= ex=3Dnull,this._clickOffset=3Dnull,this._animateOff=3D!1,!1},_detectOrienta= tion:function(){this.orientation=3Dthis.options.orientation=3D=3D=3D"vertic= al"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b,c,d,e,f;= return this.orientation=3D=3D=3D"horizontal"?(b=3Dthis.elementSize.width,c= =3Da.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)= ):(b=3Dthis.elementSize.height,c=3Da.y-this.elementOffset.top-(this._clickO= ffset?this._clickOffset.top:0)),d=3Dc/b,d>1&&(d=3D1),d<0&&(d=3D0),this.orie= ntation=3D=3D=3D"vertical"&&(d=3D1-d),e=3Dthis._valueMax()-this._valueMin()= ,f=3Dthis._valueMin()+d*e,this._trimAlignValue(f)},_start:function(a,b){var= c=3D{handle:this.handles[b],value:this.value()};return this.options.values= &&this.options.values.length&&(c.value=3Dthis.values(b),c.values=3Dthis.val= ues()),this._trigger("start",a,c)},_slide:function(a,b,c){var d,e,f;this.op= tions.values&&this.options.values.length?(d=3Dthis.values(b?0:1),this.optio= ns.values.length=3D=3D=3D2&&this.options.range=3D=3D=3D!0&&(b=3D=3D=3D0&&c>= d||b=3D=3D=3D1&&c1){this.options.values[b]=3Dthis._trimAlignValue(c),this._refreshValue(),= this._change(null,b);return}if(!arguments.length)return this._values();if(!= a.isArray(arguments[0]))return this.options.values&&this.options.values.len= gth?this._values(b):this.value();d=3Dthis.options.values,e=3Darguments[0];f= or(f=3D0;f=3Dthis._valueMax())return= this._valueMax();var b=3Dthis.options.step>0?this.options.step:1,c=3D(a-th= is._valueMin())%b,d=3Da-c;return Math.abs(c)*2>=3Db&&(d+=3Dc>0?b:-b),parseF= loat(d.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax= :function(){return this.options.max},_refreshValue:function(){var b=3Dthis.= options.range,c=3Dthis.options,d=3Dthis,e=3Dthis._animateOff?!1:c.animate,f= ,g=3D{},h,i,j,k;this.options.values&&this.options.values.length?this.handle= s.each(function(b,i){f=3D(d.values(b)-d._valueMin())/(d._valueMax()-d._valu= eMin())*100,g[d.orientation=3D=3D=3D"horizontal"?"left":"bottom"]=3Df+"%",a= (this).stop(1,1)[e?"animate":"css"](g,c.animate),d.options.range=3D=3D=3D!0= &&(d.orientation=3D=3D=3D"horizontal"?(b=3D=3D=3D0&&d.range.stop(1,1)[e?"an= imate":"css"]({left:f+"%"},c.animate),b=3D=3D=3D1&&d.range[e?"animate":"css= "]({width:f-h+"%"},{queue:!1,duration:c.animate})):(b=3D=3D=3D0&&d.range.st= op(1,1)[e?"animate":"css"]({bottom:f+"%"},c.animate),b=3D=3D=3D1&&d.range[e= ?"animate":"css"]({height:f-h+"%"},{queue:!1,duration:c.animate}))),h=3Df})= :(i=3Dthis.value(),j=3Dthis._valueMin(),k=3Dthis._valueMax(),f=3Dk!=3D=3Dj?= (i-j)/(k-j)*100:0,g[d.orientation=3D=3D=3D"horizontal"?"left":"bottom"]=3Df= +"%",this.handle.stop(1,1)[e?"animate":"css"](g,c.animate),b=3D=3D=3D"min"&= &this.orientation=3D=3D=3D"horizontal"&&this.range.stop(1,1)[e?"animate":"c= ss"]({width:f+"%"},c.animate),b=3D=3D=3D"max"&&this.orientation=3D=3D=3D"ho= rizontal"&&this.range[e?"animate":"css"]({width:100-f+"%"},{queue:!1,durati= on:c.animate}),b=3D=3D=3D"min"&&this.orientation=3D=3D=3D"vertical"&&this.r= ange.stop(1,1)[e?"animate":"css"]({height:f+"%"},c.animate),b=3D=3D=3D"max"= &&this.orientation=3D=3D=3D"vertical"&&this.range[e?"animate":"css"]({heigh= t:100-f+"%"},{queue:!1,duration:c.animate}))}}),a.extend(a.ui.slider,{versi= on:"1.8.23"})})(jQuery);;/*! jQuery UI - v1.8.23 - 2012-08-15 * https://github.com/jquery/jquery-ui * Includes: jquery.ui.tabs.js * Copyright (c) 2012 AUTHORS.txt; Licensed MIT, GPL */ -(function(a,b){function e(){return++c}function f(){return++d}var c=3D0,d= =3D0;a.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:!1,cookie= :null,collapsible:!1,disable:null,disabled:[],enable:null,event:"click",fx:= null,idPrefix:"ui-tabs-",load:null,panelTemplate:"
    ",remove:null,= select:null,show:null,spinner:"Loading…",tabTemplate:"
  • #{label}
  • "},_create:function(){this.= _tabify(!0)},_setOption:function(a,b){if(a=3D=3D"selected"){if(this.options= .collapsible&&b=3D=3Dthis.options.selected)return;this.select(b)}else this.= options[a]=3Db,this._tabify()},_tabId:function(a){return a.title&&a.title.r= eplace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix= +e()},_sanitizeSelector:function(a){return a.replace(/:/g,"\\:")},_cookie:f= unction(){var b=3Dthis.cookie||(this.cookie=3Dthis.options.cookie.name||"ui= -tabs-"+f());return a.cookie.apply(null,[b].concat(a.makeArray(arguments)))= },_ui:function(a,b){return{tab:a,panel:b,index:this.anchors.index(a)}},_cle= anup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-sta= te-processing").find("span:data(label.tabs)").each(function(){var b=3Da(thi= s);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:functio= n(c){function m(b,c){b.css("display",""),!a.support.opacity&&c.opacity&&b[0= ].style.removeAttribute("filter")}var d=3Dthis,e=3Dthis.options,f=3D/^#.+/;= this.list=3Dthis.element.find("ol,ul").eq(0),this.lis=3Da(" > li:has(a[href= ])",this.list),this.anchors=3Dthis.lis.map(function(){return a("a",this)[0]= }),this.panels=3Da([]),this.anchors.each(function(b,c){var g=3Da(c).attr("h= ref"),h=3Dg.split("#")[0],i;h&&(h=3D=3D=3Dlocation.toString().split("#")[0]= ||(i=3Da("base")[0])&&h=3D=3D=3Di.href)&&(g=3Dc.hash,c.href=3Dg);if(f.test(= g))d.panels=3Dd.panels.add(d.element.find(d._sanitizeSelector(g)));else if(= g&&g!=3D=3D"#"){a.data(c,"href.tabs",g),a.data(c,"load.tabs",g.replace(/#.*= $/,""));var j=3Dd._tabId(c);c.href=3D"#"+j;var k=3Dd.element.find("#"+j);k.= length||(k=3Da(e.panelTemplate).attr("id",j).addClass("ui-tabs-panel ui-wid= get-content ui-corner-bottom").insertAfter(d.panels[b-1]||d.list),k.data("d= estroy.tabs",!0)),d.panels=3Dd.panels.add(k)}else e.disabled.push(b)}),c?(t= his.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"),t= his.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget= -header ui-corner-all"),this.lis.addClass("ui-state-default ui-corner-top")= ,this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom"),e= .selected=3D=3D=3Db?(location.hash&&this.anchors.each(function(a,b){if(b.ha= sh=3D=3Dlocation.hash)return e.selected=3Da,!1}),typeof e.selected!=3D"numb= er"&&e.cookie&&(e.selected=3DparseInt(d._cookie(),10)),typeof e.selected!= =3D"number"&&this.lis.filter(".ui-tabs-selected").length&&(e.selected=3Dthi= s.lis.index(this.lis.filter(".ui-tabs-selected"))),e.selected=3De.selected|= |(this.lis.length?0:-1)):e.selected=3D=3D=3Dnull&&(e.selected=3D-1),e.selec= ted=3De.selected>=3D0&&this.anchors[e.selected]||e.selected<0?e.selected:0,= e.disabled=3Da.unique(e.disabled.concat(a.map(this.lis.filter(".ui-state-di= sabled"),function(a,b){return d.lis.index(a)}))).sort(),a.inArray(e.selecte= d,e.disabled)!=3D-1&&e.disabled.splice(a.inArray(e.selected,e.disabled),1),= this.panels.addClass("ui-tabs-hide"),this.lis.removeClass("ui-tabs-selected= ui-state-active"),e.selected>=3D0&&this.anchors.length&&(d.element.find(d.= _sanitizeSelector(d.anchors[e.selected].hash)).removeClass("ui-tabs-hide"),= this.lis.eq(e.selected).addClass("ui-tabs-selected ui-state-active"),d.elem= ent.queue("tabs",function(){d._trigger("show",null,d._ui(d.anchors[e.select= ed],d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash))[0]))}),= this.load(e.selected)),a(window).bind("unload",function(){d.lis.add(d.ancho= rs).unbind(".tabs"),d.lis=3Dd.anchors=3Dd.panels=3Dnull})):e.selected=3Dthi= s.lis.index(this.lis.filter(".ui-tabs-selected")),this.element[e.collapsibl= e?"addClass":"removeClass"]("ui-tabs-collapsible"),e.cookie&&this._cookie(e= .selected,e.cookie);for(var g=3D0,h;h=3Dthis.lis[g];g++)a(h)[a.inArray(g,e.= disabled)!=3D-1&&!a(h).hasClass("ui-tabs-selected")?"addClass":"removeClass= "]("ui-state-disabled");e.cache=3D=3D=3D!1&&this.anchors.removeData("cache.= tabs"),this.lis.add(this.anchors).unbind(".tabs");if(e.event!=3D=3D"mouseov= er"){var i=3Dfunction(a,b){b.is(":not(.ui-state-disabled)")&&b.addClass("ui= -state-"+a)},j=3Dfunction(a,b){b.removeClass("ui-state-"+a)};this.lis.bind(= "mouseover.tabs",function(){i("hover",a(this))}),this.lis.bind("mouseout.ta= bs",function(){j("hover",a(this))}),this.anchors.bind("focus.tabs",function= (){i("focus",a(this).closest("li"))}),this.anchors.bind("blur.tabs",functio= n(){j("focus",a(this).closest("li"))})}var k,l;e.fx&&(a.isArray(e.fx)?(k=3D= e.fx[0],l=3De.fx[1]):k=3Dl=3De.fx);var n=3Dl?function(b,c){a(b).closest("li= ").addClass("ui-tabs-selected ui-state-active"),c.hide().removeClass("ui-ta= bs-hide").animate(l,l.duration||"normal",function(){m(c,l),d._trigger("show= ",null,d._ui(b,c[0]))})}:function(b,c){a(b).closest("li").addClass("ui-tabs= -selected ui-state-active"),c.removeClass("ui-tabs-hide"),d._trigger("show"= ,null,d._ui(b,c[0]))},o=3Dk?function(a,b){b.animate(k,k.duration||"normal",= function(){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass= ("ui-tabs-hide"),m(b,k),d.element.dequeue("tabs")})}:function(a,b,c){d.lis.= removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),= d.element.dequeue("tabs")};this.anchors.bind(e.event+".tabs",function(){var= b=3Dthis,c=3Da(b).closest("li"),f=3Dd.panels.filter(":not(.ui-tabs-hide)")= ,g=3Dd.element.find(d._sanitizeSelector(b.hash));if(c.hasClass("ui-tabs-sel= ected")&&!e.collapsible||c.hasClass("ui-state-disabled")||c.hasClass("ui-st= ate-processing")||d.panels.filter(":animated").length||d._trigger("select",= null,d._ui(this,g[0]))=3D=3D=3D!1)return this.blur(),!1;e.selected=3Dd.anch= ors.index(this),d.abort();if(e.collapsible){if(c.hasClass("ui-tabs-selected= "))return e.selected=3D-1,e.cookie&&d._cookie(e.selected,e.cookie),d.elemen= t.queue("tabs",function(){o(b,f)}).dequeue("tabs"),this.blur(),!1;if(!f.len= gth)return e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",= function(){n(b,g)}),d.load(d.anchors.index(this)),this.blur(),!1}e.cookie&&= d._cookie(e.selected,e.cookie);if(g.length)f.length&&d.element.queue("tabs"= ,function(){o(b,f)}),d.element.queue("tabs",function(){n(b,g)}),d.load(d.an= chors.index(this));else throw"jQuery UI Tabs: Mismatching fragment identifi= er.";a.browser.msie&&this.blur()}),this.anchors.bind("click.tabs",function(= ){return!1})},_getIndex:function(a){return typeof a=3D=3D"string"&&(a=3Dthi= s.anchors.index(this.anchors.filter("[href$=3D'"+a+"']"))),a},destroy:funct= ion(){var b=3Dthis.options;return this.abort(),this.element.unbind(".tabs")= .removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-col= lapsible").removeData("tabs"),this.list.removeClass("ui-tabs-nav ui-helper-= reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.anchors.each= (function(){var b=3Da.data(this,"href.tabs");b&&(this.href=3Db);var c=3Da(t= his).unbind(".tabs");a.each(["href","load","cache"],function(a,b){c.removeD= ata(b+".tabs")})}),this.lis.unbind(".tabs").add(this.panels).each(function(= ){a.data(this,"destroy.tabs")?a(this).remove():a(this).removeClass(["ui-sta= te-default","ui-corner-top","ui-tabs-selected","ui-state-active","ui-state-= hover","ui-state-focus","ui-state-disabled","ui-tabs-panel","ui-widget-cont= ent","ui-corner-bottom","ui-tabs-hide"].join(" "))}),b.cookie&&this._cookie= (null,b.cookie),this},add:function(c,d,e){e=3D=3D=3Db&&(e=3Dthis.anchors.le= ngth);var f=3Dthis,g=3Dthis.options,h=3Da(g.tabTemplate.replace(/#\{href\}/= g,c).replace(/#\{label\}/g,d)),i=3Dc.indexOf("#")?this._tabId(a("a",h)[0]):= c.replace("#","");h.addClass("ui-state-default ui-corner-top").data("destro= y.tabs",!0);var j=3Df.element.find("#"+i);return j.length||(j=3Da(g.panelTe= mplate).attr("id",i).data("destroy.tabs",!0)),j.addClass("ui-tabs-panel ui-= widget-content ui-corner-bottom ui-tabs-hide"),e>=3Dthis.lis.length?(h.appe= ndTo(this.list),j.appendTo(this.list[0].parentNode)):(h.insertBefore(this.l= is[e]),j.insertBefore(this.panels[e])),g.disabled=3Da.map(g.disabled,functi= on(a,b){return a>=3De?++a:a}),this._tabify(),this.anchors.length=3D=3D1&&(g= .selected=3D0,h.addClass("ui-tabs-selected ui-state-active"),j.removeClass(= "ui-tabs-hide"),this.element.queue("tabs",function(){f._trigger("show",null= ,f._ui(f.anchors[0],f.panels[0]))}),this.load(0)),this._trigger("add",null,= this._ui(this.anchors[e],this.panels[e])),this},remove:function(b){b=3Dthis= ._getIndex(b);var c=3Dthis.options,d=3Dthis.lis.eq(b).remove(),e=3Dthis.pan= els.eq(b).remove();return d.hasClass("ui-tabs-selected")&&this.anchors.leng= th>1&&this.select(b+(b+1=3Db?--a= :a}),this._tabify(),this._trigger("remove",null,this._ui(d.find("a")[0],e[0= ])),this},enable:function(b){b=3Dthis._getIndex(b);var c=3Dthis.options;if(= a.inArray(b,c.disabled)=3D=3D-1)return;return this.lis.eq(b).removeClass("u= i-state-disabled"),c.disabled=3Da.grep(c.disabled,function(a,c){return a!= =3Db}),this._trigger("enable",null,this._ui(this.anchors[b],this.panels[b])= ),this},disable:function(a){a=3Dthis._getIndex(a);var b=3Dthis,c=3Dthis.opt= ions;return a!=3Dc.selected&&(this.lis.eq(a).addClass("ui-state-disabled"),= c.disabled.push(a),c.disabled.sort(),this._trigger("disable",null,this._ui(= this.anchors[a],this.panels[a]))),this},select:function(a){a=3Dthis._getInd= ex(a);if(a=3D=3D-1)if(this.options.collapsible&&this.options.selected!=3D-1= )a=3Dthis.options.selected;else return this;return this.anchors.eq(a).trigg= er(this.options.event+".tabs"),this},load:function(b){b=3Dthis._getIndex(b)= ;var c=3Dthis,d=3Dthis.options,e=3Dthis.anchors.eq(b)[0],f=3Da.data(e,"load= .tabs");this.abort();if(!f||this.element.queue("tabs").length!=3D=3D0&&a.da= ta(e,"cache.tabs")){this.element.dequeue("tabs");return}this.lis.eq(b).addC= lass("ui-state-processing");if(d.spinner){var g=3Da("span",e);g.data("label= .tabs",g.html()).html(d.spinner)}return this.xhr=3Da.ajax(a.extend({},d.aja= xOptions,{url:f,success:function(f,g){c.element.find(c._sanitizeSelector(e.= hash)).html(f),c._cleanup(),d.cache&&a.data(e,"cache.tabs",!0),c._trigger("= load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.success(f,g)}= catch(h){}},error:function(a,f,g){c._cleanup(),c._trigger("load",null,c._ui= (c.anchors[b],c.panels[b]));try{d.ajaxOptions.error(a,f,b,e)}catch(g){}}}))= ,c.element.dequeue("tabs"),this},abort:function(){return this.element.queue= ([]),this.panels.stop(!1,!0),this.element.queue("tabs",this.element.queue("= tabs").splice(-2,2)),this.xhr&&(this.xhr.abort(),delete this.xhr),this._cle= anup(),this},url:function(a,b){return this.anchors.eq(a).removeData("cache.= tabs").data("load.tabs",b),this},length:function(){return this.anchors.leng= th}}),a.extend(a.ui.tabs,{version:"1.8.21"}),a.extend(a.ui.tabs.prototype,{= rotation:null,rotate:function(a,b){var c=3Dthis,d=3Dthis.options,e=3Dc._rot= ate||(c._rotate=3Dfunction(b){clearTimeout(c.rotation),c.rotation=3DsetTime= out(function(){var a=3Dd.selected;c.select(++a
    ",remove:null,= select:null,show:null,spinner:"Loading…",tabTemplate:"
  • #{label}
  • "},_create:function(){this.= _tabify(!0)},_setOption:function(a,b){if(a=3D=3D"selected"){if(this.options= .collapsible&&b=3D=3Dthis.options.selected)return;this.select(b)}else this.= options[a]=3Db,this._tabify()},_tabId:function(a){return a.title&&a.title.r= eplace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix= +e()},_sanitizeSelector:function(a){return a.replace(/:/g,"\\:")},_cookie:f= unction(){var b=3Dthis.cookie||(this.cookie=3Dthis.options.cookie.name||"ui= -tabs-"+f());return a.cookie.apply(null,[b].concat(a.makeArray(arguments)))= },_ui:function(a,b){return{tab:a,panel:b,index:this.anchors.index(a)}},_cle= anup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-sta= te-processing").find("span:data(label.tabs)").each(function(){var b=3Da(thi= s);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:functio= n(c){function m(b,c){b.css("display",""),!a.support.opacity&&c.opacity&&b[0= ].style.removeAttribute("filter")}var d=3Dthis,e=3Dthis.options,f=3D/^#.+/;= this.list=3Dthis.element.find("ol,ul").eq(0),this.lis=3Da(" > li:has(a[href= ])",this.list),this.anchors=3Dthis.lis.map(function(){return a("a",this)[0]= }),this.panels=3Da([]),this.anchors.each(function(b,c){var g=3Da(c).attr("h= ref"),h=3Dg.split("#")[0],i;h&&(h=3D=3D=3Dlocation.toString().split("#")[0]= ||(i=3Da("base")[0])&&h=3D=3D=3Di.href)&&(g=3Dc.hash,c.href=3Dg);if(f.test(= g))d.panels=3Dd.panels.add(d.element.find(d._sanitizeSelector(g)));else if(= g&&g!=3D=3D"#"){a.data(c,"href.tabs",g),a.data(c,"load.tabs",g.replace(/#.*= $/,""));var j=3Dd._tabId(c);c.href=3D"#"+j;var k=3Dd.element.find("#"+j);k.= length||(k=3Da(e.panelTemplate).attr("id",j).addClass("ui-tabs-panel ui-wid= get-content ui-corner-bottom").insertAfter(d.panels[b-1]||d.list),k.data("d= estroy.tabs",!0)),d.panels=3Dd.panels.add(k)}else e.disabled.push(b)}),c?(t= his.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"),t= his.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget= -header ui-corner-all"),this.lis.addClass("ui-state-default ui-corner-top")= ,this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom"),e= .selected=3D=3D=3Db?(location.hash&&this.anchors.each(function(a,b){if(b.ha= sh=3D=3Dlocation.hash)return e.selected=3Da,!1}),typeof e.selected!=3D"numb= er"&&e.cookie&&(e.selected=3DparseInt(d._cookie(),10)),typeof e.selected!= =3D"number"&&this.lis.filter(".ui-tabs-selected").length&&(e.selected=3Dthi= s.lis.index(this.lis.filter(".ui-tabs-selected"))),e.selected=3De.selected|= |(this.lis.length?0:-1)):e.selected=3D=3D=3Dnull&&(e.selected=3D-1),e.selec= ted=3De.selected>=3D0&&this.anchors[e.selected]||e.selected<0?e.selected:0,= e.disabled=3Da.unique(e.disabled.concat(a.map(this.lis.filter(".ui-state-di= sabled"),function(a,b){return d.lis.index(a)}))).sort(),a.inArray(e.selecte= d,e.disabled)!=3D-1&&e.disabled.splice(a.inArray(e.selected,e.disabled),1),= this.panels.addClass("ui-tabs-hide"),this.lis.removeClass("ui-tabs-selected= ui-state-active"),e.selected>=3D0&&this.anchors.length&&(d.element.find(d.= _sanitizeSelector(d.anchors[e.selected].hash)).removeClass("ui-tabs-hide"),= this.lis.eq(e.selected).addClass("ui-tabs-selected ui-state-active"),d.elem= ent.queue("tabs",function(){d._trigger("show",null,d._ui(d.anchors[e.select= ed],d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash))[0]))}),= this.load(e.selected)),a(window).bind("unload",function(){d.lis.add(d.ancho= rs).unbind(".tabs"),d.lis=3Dd.anchors=3Dd.panels=3Dnull})):e.selected=3Dthi= s.lis.index(this.lis.filter(".ui-tabs-selected")),this.element[e.collapsibl= e?"addClass":"removeClass"]("ui-tabs-collapsible"),e.cookie&&this._cookie(e= .selected,e.cookie);for(var g=3D0,h;h=3Dthis.lis[g];g++)a(h)[a.inArray(g,e.= disabled)!=3D-1&&!a(h).hasClass("ui-tabs-selected")?"addClass":"removeClass= "]("ui-state-disabled");e.cache=3D=3D=3D!1&&this.anchors.removeData("cache.= tabs"),this.lis.add(this.anchors).unbind(".tabs");if(e.event!=3D=3D"mouseov= er"){var i=3Dfunction(a,b){b.is(":not(.ui-state-disabled)")&&b.addClass("ui= -state-"+a)},j=3Dfunction(a,b){b.removeClass("ui-state-"+a)};this.lis.bind(= "mouseover.tabs",function(){i("hover",a(this))}),this.lis.bind("mouseout.ta= bs",function(){j("hover",a(this))}),this.anchors.bind("focus.tabs",function= (){i("focus",a(this).closest("li"))}),this.anchors.bind("blur.tabs",functio= n(){j("focus",a(this).closest("li"))})}var k,l;e.fx&&(a.isArray(e.fx)?(k=3D= e.fx[0],l=3De.fx[1]):k=3Dl=3De.fx);var n=3Dl?function(b,c){a(b).closest("li= ").addClass("ui-tabs-selected ui-state-active"),c.hide().removeClass("ui-ta= bs-hide").animate(l,l.duration||"normal",function(){m(c,l),d._trigger("show= ",null,d._ui(b,c[0]))})}:function(b,c){a(b).closest("li").addClass("ui-tabs= -selected ui-state-active"),c.removeClass("ui-tabs-hide"),d._trigger("show"= ,null,d._ui(b,c[0]))},o=3Dk?function(a,b){b.animate(k,k.duration||"normal",= function(){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass= ("ui-tabs-hide"),m(b,k),d.element.dequeue("tabs")})}:function(a,b,c){d.lis.= removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),= d.element.dequeue("tabs")};this.anchors.bind(e.event+".tabs",function(){var= b=3Dthis,c=3Da(b).closest("li"),f=3Dd.panels.filter(":not(.ui-tabs-hide)")= ,g=3Dd.element.find(d._sanitizeSelector(b.hash));if(c.hasClass("ui-tabs-sel= ected")&&!e.collapsible||c.hasClass("ui-state-disabled")||c.hasClass("ui-st= ate-processing")||d.panels.filter(":animated").length||d._trigger("select",= null,d._ui(this,g[0]))=3D=3D=3D!1)return this.blur(),!1;e.selected=3Dd.anch= ors.index(this),d.abort();if(e.collapsible){if(c.hasClass("ui-tabs-selected= "))return e.selected=3D-1,e.cookie&&d._cookie(e.selected,e.cookie),d.elemen= t.queue("tabs",function(){o(b,f)}).dequeue("tabs"),this.blur(),!1;if(!f.len= gth)return e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",= function(){n(b,g)}),d.load(d.anchors.index(this)),this.blur(),!1}e.cookie&&= d._cookie(e.selected,e.cookie);if(g.length)f.length&&d.element.queue("tabs"= ,function(){o(b,f)}),d.element.queue("tabs",function(){n(b,g)}),d.load(d.an= chors.index(this));else throw"jQuery UI Tabs: Mismatching fragment identifi= er.";a.browser.msie&&this.blur()}),this.anchors.bind("click.tabs",function(= ){return!1})},_getIndex:function(a){return typeof a=3D=3D"string"&&(a=3Dthi= s.anchors.index(this.anchors.filter("[href$=3D'"+a+"']"))),a},destroy:funct= ion(){var b=3Dthis.options;return this.abort(),this.element.unbind(".tabs")= .removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-col= lapsible").removeData("tabs"),this.list.removeClass("ui-tabs-nav ui-helper-= reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.anchors.each= (function(){var b=3Da.data(this,"href.tabs");b&&(this.href=3Db);var c=3Da(t= his).unbind(".tabs");a.each(["href","load","cache"],function(a,b){c.removeD= ata(b+".tabs")})}),this.lis.unbind(".tabs").add(this.panels).each(function(= ){a.data(this,"destroy.tabs")?a(this).remove():a(this).removeClass(["ui-sta= te-default","ui-corner-top","ui-tabs-selected","ui-state-active","ui-state-= hover","ui-state-focus","ui-state-disabled","ui-tabs-panel","ui-widget-cont= ent","ui-corner-bottom","ui-tabs-hide"].join(" "))}),b.cookie&&this._cookie= (null,b.cookie),this},add:function(c,d,e){e=3D=3D=3Db&&(e=3Dthis.anchors.le= ngth);var f=3Dthis,g=3Dthis.options,h=3Da(g.tabTemplate.replace(/#\{href\}/= g,c).replace(/#\{label\}/g,d)),i=3Dc.indexOf("#")?this._tabId(a("a",h)[0]):= c.replace("#","");h.addClass("ui-state-default ui-corner-top").data("destro= y.tabs",!0);var j=3Df.element.find("#"+i);return j.length||(j=3Da(g.panelTe= mplate).attr("id",i).data("destroy.tabs",!0)),j.addClass("ui-tabs-panel ui-= widget-content ui-corner-bottom ui-tabs-hide"),e>=3Dthis.lis.length?(h.appe= ndTo(this.list),j.appendTo(this.list[0].parentNode)):(h.insertBefore(this.l= is[e]),j.insertBefore(this.panels[e])),g.disabled=3Da.map(g.disabled,functi= on(a,b){return a>=3De?++a:a}),this._tabify(),this.anchors.length=3D=3D1&&(g= .selected=3D0,h.addClass("ui-tabs-selected ui-state-active"),j.removeClass(= "ui-tabs-hide"),this.element.queue("tabs",function(){f._trigger("show",null= ,f._ui(f.anchors[0],f.panels[0]))}),this.load(0)),this._trigger("add",null,= this._ui(this.anchors[e],this.panels[e])),this},remove:function(b){b=3Dthis= ._getIndex(b);var c=3Dthis.options,d=3Dthis.lis.eq(b).remove(),e=3Dthis.pan= els.eq(b).remove();return d.hasClass("ui-tabs-selected")&&this.anchors.leng= th>1&&this.select(b+(b+1=3Db?--a= :a}),this._tabify(),this._trigger("remove",null,this._ui(d.find("a")[0],e[0= ])),this},enable:function(b){b=3Dthis._getIndex(b);var c=3Dthis.options;if(= a.inArray(b,c.disabled)=3D=3D-1)return;return this.lis.eq(b).removeClass("u= i-state-disabled"),c.disabled=3Da.grep(c.disabled,function(a,c){return a!= =3Db}),this._trigger("enable",null,this._ui(this.anchors[b],this.panels[b])= ),this},disable:function(a){a=3Dthis._getIndex(a);var b=3Dthis,c=3Dthis.opt= ions;return a!=3Dc.selected&&(this.lis.eq(a).addClass("ui-state-disabled"),= c.disabled.push(a),c.disabled.sort(),this._trigger("disable",null,this._ui(= this.anchors[a],this.panels[a]))),this},select:function(a){a=3Dthis._getInd= ex(a);if(a=3D=3D-1)if(this.options.collapsible&&this.options.selected!=3D-1= )a=3Dthis.options.selected;else return this;return this.anchors.eq(a).trigg= er(this.options.event+".tabs"),this},load:function(b){b=3Dthis._getIndex(b)= ;var c=3Dthis,d=3Dthis.options,e=3Dthis.anchors.eq(b)[0],f=3Da.data(e,"load= .tabs");this.abort();if(!f||this.element.queue("tabs").length!=3D=3D0&&a.da= ta(e,"cache.tabs")){this.element.dequeue("tabs");return}this.lis.eq(b).addC= lass("ui-state-processing");if(d.spinner){var g=3Da("span",e);g.data("label= .tabs",g.html()).html(d.spinner)}return this.xhr=3Da.ajax(a.extend({},d.aja= xOptions,{url:f,success:function(f,g){c.element.find(c._sanitizeSelector(e.= hash)).html(f),c._cleanup(),d.cache&&a.data(e,"cache.tabs",!0),c._trigger("= load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.success(f,g)}= catch(h){}},error:function(a,f,g){c._cleanup(),c._trigger("load",null,c._ui= (c.anchors[b],c.panels[b]));try{d.ajaxOptions.error(a,f,b,e)}catch(g){}}}))= ,c.element.dequeue("tabs"),this},abort:function(){return this.element.queue= ([]),this.panels.stop(!1,!0),this.element.queue("tabs",this.element.queue("= tabs").splice(-2,2)),this.xhr&&(this.xhr.abort(),delete this.xhr),this._cle= anup(),this},url:function(a,b){return this.anchors.eq(a).removeData("cache.= tabs").data("load.tabs",b),this},length:function(){return this.anchors.leng= th}}),a.extend(a.ui.tabs,{version:"1.8.23"}),a.extend(a.ui.tabs.prototype,{= rotation:null,rotate:function(a,b){var c=3Dthis,d=3Dthis.options,e=3Dc._rot= ate||(c._rotate=3Dfunction(b){clearTimeout(c.rotation),c.rotation=3DsetTime= out(function(){var a=3Dd.selected;c.select(++a
    '))}function bindHover(a){var b=3D"button, .ui-datepicker-prev, = .ui-datepicker-next, .ui-datepicker-calendar td a";return a.bind("mouseout"= ,function(a){var c=3D$(a.target).closest(b);if(!c.length)return;c.removeCla= ss("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bi= nd("mouseover",function(c){var d=3D$(c.target).closest(b);if($.datepicker._= isDisabledDatepicker(instActive.inline?a.parent()[0]:instActive.input[0])||= !d.length)return;d.parents(".ui-datepicker-calendar").find("a").removeClass= ("ui-state-hover"),d.addClass("ui-state-hover"),d.hasClass("ui-datepicker-p= rev")&&d.addClass("ui-datepicker-prev-hover"),d.hasClass("ui-datepicker-nex= t")&&d.addClass("ui-datepicker-next-hover")})}function extendRemove(a,b){$.= extend(a,b);for(var c in b)if(b[c]=3D=3Dnull||b[c]=3D=3Dundefined)a[c]=3Db[= c];return a}function isArray(a){return a&&($.browser.safari&&typeof a=3D=3D= "object"&&a.length||a.constructor&&a.constructor.toString().match(/\Array\(= \)/))}$.extend($.ui,{datepicker:{version:"1.8.21"}});var PROP_NAME=3D"datep= icker",dpuuid=3D(new Date).getTime(),instActive;$.extend(Datepicker.prototy= pe,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&co= nsole.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpD= iv},setDefaults:function(a){return extendRemove(this._defaults,a||{}),this}= ,_attachDatepicker:function(target,settings){var inlineSettings=3Dnull;for(= var attrName in this._defaults){var attrValue=3Dtarget.getAttribute("date:"= +attrName);if(attrValue){inlineSettings=3DinlineSettings||{};try{inlineSett= ings[attrName]=3Deval(attrValue)}catch(err){inlineSettings[attrName]=3Dattr= Value}}}var nodeName=3Dtarget.nodeName.toLowerCase(),inline=3DnodeName=3D= =3D"div"||nodeName=3D=3D"span";target.id||(this.uuid+=3D1,target.id=3D"dp"+= this.uuid);var inst=3Dthis._newInst($(target),inline);inst.settings=3D$.ext= end({},settings||{},inlineSettings||{}),nodeName=3D=3D"input"?this._connect= Datepicker(target,inst):inline&&this._inlineDatepicker(target,inst)},_newIn= st:function(a,b){var c=3Da[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");retu= rn{id:c,input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,dr= awYear:0,inline:b,dpDiv:b?bindHover($('
    ')):this.dpDiv}},_connectDatepicker:function(a,b){var c=3D$(a);b.app= end=3D$([]),b.trigger=3D$([]);if(c.hasClass(this.markerClassName))return;th= is._attachments(c,b),c.addClass(this.markerClassName).keydown(this._doKeyDo= wn).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicke= r",function(a,c,d){b.settings[c]=3Dd}).bind("getData.datepicker",function(a= ,c){return this._get(b,c)}),this._autoSize(b),$.data(a,PROP_NAME,b),b.setti= ngs.disabled&&this._disableDatepicker(a)},_attachments:function(a,b){var c= =3Dthis._get(b,"appendText"),d=3Dthis._get(b,"isRTL");b.append&&b.append.re= move(),c&&(b.append=3D$(''+c+"= "),a[d?"before":"after"](b.append)),a.unbind("focus",this._showDatepicker),= b.trigger&&b.trigger.remove();var e=3Dthis._get(b,"showOn");(e=3D=3D"focus"= ||e=3D=3D"both")&&a.focus(this._showDatepicker);if(e=3D=3D"button"||e=3D=3D= "both"){var f=3Dthis._get(b,"buttonText"),g=3Dthis._get(b,"buttonImage");b.= trigger=3D$(this._get(b,"buttonImageOnly")?$("").addClass(this._trigg= erClass).attr({src:g,alt:f,title:f}):$('')= .addClass(this._triggerClass).html(g=3D=3D""?f:$("").attr({src:g,alt:= f,title:f}))),a[d?"before":"after"](b.trigger),b.trigger.click(function(){r= eturn $.datepicker._datepickerShowing&&$.datepicker._lastInput=3D=3Da[0]?$.= datepicker._hideDatepicker():$.datepicker._datepickerShowing&&$.datepicker.= _lastInput!=3Da[0]?($.datepicker._hideDatepicker(),$.datepicker._showDatepi= cker(a[0])):$.datepicker._showDatepicker(a[0]),!1})}},_autoSize:function(a)= {if(this._get(a,"autoSize")&&!a.inline){var b=3Dnew Date(2009,11,20),c=3Dth= is._get(a,"dateFormat");if(c.match(/[DM]/)){var d=3Dfunction(a){var b=3D0,c= =3D0;for(var d=3D0;db&&(b=3Da[d].length,c=3Dd);re= turn c};b.setMonth(d(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShor= t"))),b.setDate(d(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20= -b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDat= epicker:function(a,b){var c=3D$(a);if(c.hasClass(this.markerClassName))retu= rn;c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicke= r",function(a,c,d){b.settings[c]=3Dd}).bind("getData.datepicker",function(a= ,c){return this._get(b,c)}),$.data(a,PROP_NAME,b),this._setDate(b,this._get= DefaultDate(b),!0),this._updateDatepicker(b),this._updateAlternate(b),b.set= tings.disabled&&this._disableDatepicker(a),b.dpDiv.css("display","block")},= _dialogDatepicker:function(a,b,c,d,e){var f=3Dthis._dialogInst;if(!f){this.= uuid+=3D1;var g=3D"dp"+this.uuid;this._dialogInput=3D$(''),this._dialogInput.keydown(this._doKeyDown),$("body").append(th= is._dialogInput),f=3Dthis._dialogInst=3Dthis._newInst(this._dialogInput,!1)= ,f.settings=3D{},$.data(this._dialogInput[0],PROP_NAME,f)}extendRemove(f.se= ttings,d||{}),b=3Db&&b.constructor=3D=3DDate?this._formatDate(f,b):b,this._= dialogInput.val(b),this._pos=3De?e.length?e:[e.pageX,e.pageY]:null;if(!this= ._pos){var h=3Ddocument.documentElement.clientWidth,i=3Ddocument.documentEl= ement.clientHeight,j=3Ddocument.documentElement.scrollLeft||document.body.s= crollLeft,k=3Ddocument.documentElement.scrollTop||document.body.scrollTop;t= his._pos=3D[h/2-100+j,i/2-150+k]}return this._dialogInput.css("left",this._= pos[0]+20+"px").css("top",this._pos[1]+"px"),f.settings.onSelect=3Dc,this._= inDialog=3D!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(t= his._dialogInput[0]),$.blockUI&&$.blockUI(this.dpDiv),$.data(this._dialogIn= put[0],PROP_NAME,f),this},_destroyDatepicker:function(a){var b=3D$(a),c=3D$= .data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return;var d=3Da.no= deName.toLowerCase();$.removeData(a,PROP_NAME),d=3D=3D"input"?(c.append.rem= ove(),c.trigger.remove(),b.removeClass(this.markerClassName).unbind("focus"= ,this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",= this._doKeyPress).unbind("keyup",this._doKeyUp)):(d=3D=3D"div"||d=3D=3D"spa= n")&&b.removeClass(this.markerClassName).empty()},_enableDatepicker:functio= n(a){var b=3D$(a),c=3D$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassNa= me))return;var d=3Da.nodeName.toLowerCase();if(d=3D=3D"input")a.disabled=3D= !1,c.trigger.filter("button").each(function(){this.disabled=3D!1}).end().fi= lter("img").css({opacity:"1.0",cursor:""});else if(d=3D=3D"div"||d=3D=3D"sp= an"){var e=3Db.children("."+this._inlineClass);e.children().removeClass("ui= -state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-= year").removeAttr("disabled")}this._disabledInputs=3D$.map(this._disabledIn= puts,function(b){return b=3D=3Da?null:b})},_disableDatepicker:function(a){v= ar b=3D$(a),c=3D$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))re= turn;var d=3Da.nodeName.toLowerCase();if(d=3D=3D"input")a.disabled=3D!0,c.t= rigger.filter("button").each(function(){this.disabled=3D!0}).end().filter("= img").css({opacity:"0.5",cursor:"default"});else if(d=3D=3D"div"||d=3D=3D"s= pan"){var e=3Db.children("."+this._inlineClass);e.children().addClass("ui-s= tate-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-ye= ar").attr("disabled","disabled")}this._disabledInputs=3D$.map(this._disable= dInputs,function(b){return b=3D=3Da?null:b}),this._disabledInputs[this._dis= abledInputs.length]=3Da},_isDisabledDatepicker:function(a){if(!a)return!1;f= or(var b=3D0;b-1}},_doKeyUp:function(a){var b=3D$.datepicker._getInst(a= .target);if(b.input.val()!=3Db.lastVal)try{var c=3D$.datepicker.parseDate($= .datepicker._get(b,"dateFormat"),b.input?b.input.val():null,$.datepicker._g= etFormatConfig(b));c&&($.datepicker._setDateFromField(b),$.datepicker._upda= teAlternate(b),$.datepicker._updateDatepicker(b))}catch(d){$.datepicker.log= (d)}return!0},_showDatepicker:function(a){a=3Da.target||a,a.nodeName.toLowe= rCase()!=3D"input"&&(a=3D$("input",a.parentNode)[0]);if($.datepicker._isDis= abledDatepicker(a)||$.datepicker._lastInput=3D=3Da)return;var b=3D$.datepic= ker._getInst(a);$.datepicker._curInst&&$.datepicker._curInst!=3Db&&($.datep= icker._curInst.dpDiv.stop(!0,!0),b&&$.datepicker._datepickerShowing&&$.date= picker._hideDatepicker($.datepicker._curInst.input[0]));var c=3D$.datepicke= r._get(b,"beforeShow"),d=3Dc?c.apply(a,[a,b]):{};if(d=3D=3D=3D!1)return;ext= endRemove(b.settings,d),b.lastVal=3Dnull,$.datepicker._lastInput=3Da,$.date= picker._setDateFromField(b),$.datepicker._inDialog&&(a.value=3D""),$.datepi= cker._pos||($.datepicker._pos=3D$.datepicker._findPos(a),$.datepicker._pos[= 1]+=3Da.offsetHeight);var e=3D!1;$(a).parents().each(function(){return e|= =3D$(this).css("position")=3D=3D"fixed",!e}),e&&$.browser.opera&&($.datepic= ker._pos[0]-=3Ddocument.documentElement.scrollLeft,$.datepicker._pos[1]-=3D= document.documentElement.scrollTop);var f=3D{left:$.datepicker._pos[0],top:= $.datepicker._pos[1]};$.datepicker._pos=3Dnull,b.dpDiv.empty(),b.dpDiv.css(= {position:"absolute",display:"block",top:"-1000px"}),$.datepicker._updateDa= tepicker(b),f=3D$.datepicker._checkOffset(b,f,e),b.dpDiv.css({position:$.da= tepicker._inDialog&&$.blockUI?"static":e?"fixed":"absolute",display:"none",= left:f.left+"px",top:f.top+"px"});if(!b.inline){var g=3D$.datepicker._get(b= ,"showAnim"),h=3D$.datepicker._get(b,"duration"),i=3Dfunction(){var a=3Db.d= pDiv.find("iframe.ui-datepicker-cover");if(!!a.length){var c=3D$.datepicker= ._getBorders(b.dpDiv);a.css({left:-c[0],top:-c[1],width:b.dpDiv.outerWidth(= ),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex($(a).zIndex()+1),$.datepi= cker._datepickerShowing=3D!0,$.effects&&$.effects[g]?b.dpDiv.show(g,$.datep= icker._get(b,"showOptions"),h,i):b.dpDiv[g||"show"](g?h:null,i),(!g||!h)&&i= (),b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus(),$.date= picker._curInst=3Db}},_updateDatepicker:function(a){var b=3Dthis;b.maxRows= =3D4;var c=3D$.datepicker._getBorders(a.dpDiv);instActive=3Da,a.dpDiv.empty= ().append(this._generateHTML(a));var d=3Da.dpDiv.find("iframe.ui-datepicker= -cover");!d.length||d.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),= height:a.dpDiv.outerHeight()}),a.dpDiv.find("."+this._dayOverClass+" a").mo= useover();var e=3Dthis._getNumberOfMonths(a),f=3De[1],g=3D17;a.dpDiv.remove= Class("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").= width(""),f>1&&a.dpDiv.addClass("ui-datepicker-multi-"+f).css("width",g*f+"= em"),a.dpDiv[(e[0]!=3D1||e[1]!=3D1?"add":"remove")+"Class"]("ui-datepicker-= multi"),a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepick= er-rtl"),a=3D=3D$.datepicker._curInst&&$.datepicker._datepickerShowing&&a.i= nput&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=3Ddocum= ent.activeElement&&a.input.focus();if(a.yearshtml){var h=3Da.yearshtml;setT= imeout(function(){h=3D=3D=3Da.yearshtml&&a.yearshtml&&a.dpDiv.find("select.= ui-datepicker-year:first").replaceWith(a.yearshtml),h=3Da.yearshtml=3Dnull}= ,0)}},_getBorders:function(a){var b=3Dfunction(a){return{thin:1,medium:2,th= ick:3}[a]||a};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b= (a.css("border-top-width")))]},_checkOffset:function(a,b,c){var d=3Da.dpDiv= .outerWidth(),e=3Da.dpDiv.outerHeight(),f=3Da.input?a.input.outerWidth():0,= g=3Da.input?a.input.outerHeight():0,h=3Ddocument.documentElement.clientWidt= h+$(document).scrollLeft(),i=3Ddocument.documentElement.clientHeight+$(docu= ment).scrollTop();return b.left-=3Dthis._get(a,"isRTL")?d-f:0,b.left-=3Dc&&= b.left=3D=3Da.input.offset().left?$(document).scrollLeft():0,b.top-=3Dc&&b.= top=3D=3Da.input.offset().top+g?$(document).scrollTop():0,b.left-=3DMath.mi= n(b.left,b.left+d>h&&h>d?Math.abs(b.left+d-h):0),b.top-=3DMath.min(b.top,b.= top+e>i&&i>e?Math.abs(e+g):0),b},_findPos:function(a){var b=3Dthis._getInst= (a),c=3Dthis._get(b,"isRTL");while(a&&(a.type=3D=3D"hidden"||a.nodeType!=3D= 1||$.expr.filters.hidden(a)))a=3Da[c?"previousSibling":"nextSibling"];var d= =3D$(a).offset();return[d.left,d.top]},_hideDatepicker:function(a){var b=3D= this._curInst;if(!b||a&&b!=3D$.data(a,PROP_NAME))return;if(this._datepicker= Showing){var c=3Dthis._get(b,"showAnim"),d=3Dthis._get(b,"duration"),e=3Dfu= nction(){$.datepicker._tidyDialog(b)};$.effects&&$.effects[c]?b.dpDiv.hide(= c,$.datepicker._get(b,"showOptions"),d,e):b.dpDiv[c=3D=3D"slideDown"?"slide= Up":c=3D=3D"fadeIn"?"fadeOut":"hide"](c?d:null,e),c||e(),this._datepickerSh= owing=3D!1;var f=3Dthis._get(b,"onClose");f&&f.apply(b.input?b.input[0]:nul= l,[b.input?b.input.val():"",b]),this._lastInput=3Dnull,this._inDialog&&(thi= s._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),$.blockUI&&= ($.unblockUI(),$("body").append(this.dpDiv))),this._inDialog=3D!1}},_tidyDi= alog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepic= ker-calendar")},_checkExternalClick:function(a){if(!$.datepicker._curInst)r= eturn;var b=3D$(a.target),c=3D$.datepicker._getInst(b[0]);(b[0].id!=3D$.dat= epicker._mainDivId&&b.parents("#"+$.datepicker._mainDivId).length=3D=3D0&&!= b.hasClass($.datepicker.markerClassName)&&!b.closest("."+$.datepicker._trig= gerClass).length&&$.datepicker._datepickerShowing&&(!$.datepicker._inDialog= ||!$.blockUI)||b.hasClass($.datepicker.markerClassName)&&$.datepicker._curI= nst!=3Dc)&&$.datepicker._hideDatepicker()},_adjustDate:function(a,b,c){var = d=3D$(a),e=3Dthis._getInst(d[0]);if(this._isDisabledDatepicker(d[0]))return= ;this._adjustInstDate(e,b+(c=3D=3D"M"?this._get(e,"showCurrentAtPos"):0),c)= ,this._updateDatepicker(e)},_gotoToday:function(a){var b=3D$(a),c=3Dthis._g= etInst(b[0]);if(this._get(c,"gotoCurrent")&&c.currentDay)c.selectedDay=3Dc.= currentDay,c.drawMonth=3Dc.selectedMonth=3Dc.currentMonth,c.drawYear=3Dc.se= lectedYear=3Dc.currentYear;else{var d=3Dnew Date;c.selectedDay=3Dd.getDate(= ),c.drawMonth=3Dc.selectedMonth=3Dd.getMonth(),c.drawYear=3Dc.selectedYear= =3Dd.getFullYear()}this._notifyChange(c),this._adjustDate(b)},_selectMonthY= ear:function(a,b,c){var d=3D$(a),e=3Dthis._getInst(d[0]);e["selected"+(c=3D= =3D"M"?"Month":"Year")]=3De["draw"+(c=3D=3D"M"?"Month":"Year")]=3DparseInt(= b.options[b.selectedIndex].value,10),this._notifyChange(e),this._adjustDate= (d)},_selectDay:function(a,b,c,d){var e=3D$(a);if($(d).hasClass(this._unsel= ectableClass)||this._isDisabledDatepicker(e[0]))return;var f=3Dthis._getIns= t(e[0]);f.selectedDay=3Df.currentDay=3D$("a",d).html(),f.selectedMonth=3Df.= currentMonth=3Db,f.selectedYear=3Df.currentYear=3Dc,this._selectDate(a,this= ._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))},_clearDate:func= tion(a){var b=3D$(a),c=3Dthis._getInst(b[0]);this._selectDate(b,"")},_selec= tDate:function(a,b){var c=3D$(a),d=3Dthis._getInst(c[0]);b=3Db!=3Dnull?b:th= is._formatDate(d),d.input&&d.input.val(b),this._updateAlternate(d);var e=3D= this._get(d,"onSelect");e?e.apply(d.input?d.input[0]:null,[b,d]):d.input&&d= .input.trigger("change"),d.inline?this._updateDatepicker(d):(this._hideDate= picker(),this._lastInput=3Dd.input[0],typeof d.input[0]!=3D"object"&&d.inpu= t.focus(),this._lastInput=3Dnull)},_updateAlternate:function(a){var b=3Dthi= s._get(a,"altField");if(b){var c=3Dthis._get(a,"altFormat")||this._get(a,"d= ateFormat"),d=3Dthis._getDate(a),e=3Dthis.formatDate(c,d,this._getFormatCon= fig(a));$(b).each(function(){$(this).val(e)})}},noWeekends:function(a){var = b=3Da.getDay();return[b>0&&b<6,""]},iso8601Week:function(a){var b=3Dnew Dat= e(a.getTime());b.setDate(b.getDate()+4-(b.getDay()||7));var c=3Db.getTime()= ;return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1}= ,parseDate:function(a,b,c){if(a=3D=3Dnull||b=3D=3Dnull)throw"Invalid argume= nts";b=3Dtypeof b=3D=3D"object"?b.toString():b+"";if(b=3D=3D"")return null;= var d=3D(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;d=3Dtype= of d!=3D"string"?d:(new Date).getFullYear()%100+parseInt(d,10);var e=3D(c?c= .dayNamesShort:null)||this._defaults.dayNamesShort,f=3D(c?c.dayNames:null)|= |this._defaults.dayNames,g=3D(c?c.monthNamesShort:null)||this._defaults.mon= thNamesShort,h=3D(c?c.monthNames:null)||this._defaults.monthNames,i=3D-1,j= =3D-1,k=3D-1,l=3D-1,m=3D!1,n=3Dfunction(b){var c=3Ds+1-1){j=3D1,k=3Dl;do{v= ar u=3Dthis._getDaysInMonth(i,j-1);if(k<=3Du)break;j++,k-=3Du}while(!0)}var= t=3Dthis._daylightSavingAdjust(new Date(i,j-1,k));if(t.getFullYear()!=3Di|= |t.getMonth()+1!=3Dj||t.getDate()!=3Dk)throw"Invalid date";return t},ATOM:"= yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_85= 0:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy= ",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(71868= 5+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1e7,format= Date:function(a,b,c){if(!b)return"";var d=3D(c?c.dayNamesShort:null)||this.= _defaults.dayNamesShort,e=3D(c?c.dayNames:null)||this._defaults.dayNames,f= =3D(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=3D(c?c.mont= hNames:null)||this._defaults.monthNames,h=3Dfunction(b){var c=3Dm+112?a.getHours()+2:0),a):null},_setDate:function(a,b,c){var d=3D= !b,e=3Da.selectedMonth,f=3Da.selectedYear,g=3Dthis._restrictMinMax(a,this._= determineDate(a,b,new Date));a.selectedDay=3Da.currentDay=3Dg.getDate(),a.d= rawMonth=3Da.selectedMonth=3Da.currentMonth=3Dg.getMonth(),a.drawYear=3Da.s= electedYear=3Da.currentYear=3Dg.getFullYear(),(e!=3Da.selectedMonth||f!=3Da= .selectedYear)&&!c&&this._notifyChange(a),this._adjustInstDate(a),a.input&&= a.input.val(d?"":this._formatDate(a))},_getDate:function(a){var b=3D!a.curr= entYear||a.input&&a.input.val()=3D=3D""?null:this._daylightSavingAdjust(new= Date(a.currentYear,a.currentMonth,a.currentDay));return b},_generateHTML:f= unction(a){var b=3Dnew Date;b=3Dthis._daylightSavingAdjust(new Date(b.getFu= llYear(),b.getMonth(),b.getDate()));var c=3Dthis._get(a,"isRTL"),d=3Dthis._= get(a,"showButtonPanel"),e=3Dthis._get(a,"hideIfNoPrevNext"),f=3Dthis._get(= a,"navigationAsDateFormat"),g=3Dthis._getNumberOfMonths(a),h=3Dthis._get(a,= "showCurrentAtPos"),i=3Dthis._get(a,"stepMonths"),j=3Dg[0]!=3D1||g[1]!=3D1,= k=3Dthis._daylightSavingAdjust(a.currentDay?new Date(a.currentYear,a.curren= tMonth,a.currentDay):new Date(9999,9,9)),l=3Dthis._getMinMaxDate(a,"min"),m= =3Dthis._getMinMaxDate(a,"max"),n=3Da.drawMonth-h,o=3Da.drawYear;n<0&&(n+= =3D12,o--);if(m){var p=3Dthis._daylightSavingAdjust(new Date(m.getFullYear(= ),m.getMonth()-g[0]*g[1]+1,m.getDate()));p=3Dl&&pp)n--,n<0&&(n=3D11,o--)}a.drawMonth=3Dn,a.dr= awYear=3Do;var q=3Dthis._get(a,"prevText");q=3Df?this.formatDate(q,this._da= ylightSavingAdjust(new Date(o,n-i,1)),this._getFormatConfig(a)):q;var r=3Dt= his._canAdjustMonth(a,-1,o,n)?''+q+"":e?"":''+q+"",s=3Dthis._get(a,"nex= tText");s=3Df?this.formatDate(s,this._daylightSavingAdjust(new Date(o,n+i,1= )),this._getFormatConfig(a)):s;var t=3Dthis._canAdjustMonth(a,1,o,n)?''+s+""= :e?"":''+s+"",u=3Dthis._get(a,"currentText"),v=3Dthis._get(a,"gotoCurr= ent")&&a.currentDay?k:b;u=3Df?this.formatDate(u,v,this._getFormatConfig(a))= :u;var w=3Da.inline?"":'
    ":"",y=3DparseInt(this._get(a,"firstDay"),10);y=3Dis= NaN(y)?0:y;var z=3Dthis._get(a,"showWeek"),A=3Dthis._get(a,"dayNames"),B=3D= this._get(a,"dayNamesShort"),C=3Dthis._get(a,"dayNamesMin"),D=3Dthis._get(a= ,"monthNames"),E=3Dthis._get(a,"monthNamesShort"),F=3Dthis._get(a,"beforeSh= owDay"),G=3Dthis._get(a,"showOtherMonths"),H=3Dthis._get(a,"selectOtherMont= hs"),I=3Dthis._get(a,"calculateWeek")||this.iso8601Week,J=3Dthis._getDefaul= tDate(a),K=3D"";for(var L=3D0;L1)switch(N){case 0:Q+=3D" ui-datepicker-group-first",P=3D= " ui-corner-"+(c?"right":"left");break;case g[1]-1:Q+=3D" ui-datepicker-gro= up-last",P=3D" ui-corner-"+(c?"left":"right");break;default:Q+=3D" ui-datep= icker-group-middle",P=3D""}Q+=3D'">'}Q+=3D'
    '+(/all|left/.test(P)&&L=3D=3D= 0?c?t:r:"")+(/all|right/.test(P)&&L=3D=3D0?c?r:t:"")+this._generateMonthYea= rHeader(a,n,o,l,m,L>0||N>0,D,E)+'
    '+"";var R=3Dz?'":"";for(var S=3D0;S<7;S++){var T=3D(S+y)%7;R+= =3D"=3D5?' class=3D"ui-datepicker-week-end"':"")+">"+''+C[T]+""}Q+=3DR+"";var= U=3Dthis._getDaysInMonth(o,n);o=3D=3Da.selectedYear&&n=3D=3Da.selectedMont= h&&(a.selectedDay=3DMath.min(a.selectedDay,U));var V=3D(this._getFirstDayOf= Month(o,n)-y+7)%7,W=3DMath.ceil((V+U)/7),X=3Dj?this.maxRows>W?this.maxRows:= W:W;this.maxRows=3DX;var Y=3Dthis._daylightSavingAdjust(new Date(o,n,1-V));= for(var Z=3D0;Z";var _=3Dz?'":"";for(var S=3D0;S<7;S++)= {var ba=3DF?F.apply(a.input?a.input[0]:null,[Y]):[!0,""],bb=3DY.getMonth()!= =3Dn,bc=3Dbb&&!H||!ba[0]||l&&Ym;_+=3D'
    '+this.= _get(a,"weekHeader")+"
    '+this._get(a,"calculateWeek")(Y)+"= =3D5?" ui-datepicker-week-end":"")+(bb?" ui-datepicker-other-month":"")+(Y.= getTime()=3D=3DO.getTime()&&n=3D=3Da.selectedMonth&&a._keyEvent||J.getTime(= )=3D=3DY.getTime()&&J.getTime()=3D=3DO.getTime()?" "+this._dayOverClass:"")= +(bc?" "+this._unselectableClass+" ui-state-disabled":"")+(bb&&!G?"":" "+ba= [1]+(Y.getTime()=3D=3Dk.getTime()?" "+this._currentClass:"")+(Y.getTime()= =3D=3Db.getTime()?" ui-datepicker-today":""))+'"'+((!bb||G)&&ba[2]?' title= =3D"'+ba[2]+'"':"")+(bc?"":' onclick=3D"DP_jQuery_'+dpuuid+".datepicker._se= lectDay('#"+a.id+"',"+Y.getMonth()+","+Y.getFullYear()+', this);return fals= e;"')+">"+(bb&&!G?" ":bc?''+Y.getDate= ()+"":''+Y.getDate()+"")+"",Y.setDate(Y.getDate()+1),Y=3Dthis._daylightSavingAdjust(Y)}Q+=3D_+""}n++,n>11&&(n=3D0,o++),Q+=3D"
    "+(j?"
    "+(g[0]>0&&N=3D= =3Dg[1]-1?'
    ':""):""),M+=3DQ}K+= =3DM}return K+=3Dx+($.browser.msie&&parseInt($.browser.version,10)<7&&!a.in= line?'':""),a._keyEvent=3D!1,K},_generateMonthYearHeader:fu= nction(a,b,c,d,e,f,g,h){var i=3Dthis._get(a,"changeMonth"),j=3Dthis._get(a,= "changeYear"),k=3Dthis._get(a,"showMonthAfterYear"),l=3D'
    ',m=3D"";if(f||!i)m+=3D''+g[b]+"";else{var n=3Dd&&d.getFullYear()=3D=3Dc,o=3De&&e.getFullY= ear()=3D=3Dc;m+=3D'"}k||(l+=3Dm+(f||!i||!j?" ":""));if(!a= .yearshtml){a.yearshtml=3D"";if(f||!j)l+=3D''+c+"";else{var q=3Dthis._get(a,"yearRange").split(":"),r=3D(new= Date).getFullYear(),s=3Dfunction(a){var b=3Da.match(/c[+-].*/)?c+parseInt(= a.substring(1),10):a.match(/[+-].*/)?r+parseInt(a,10):parseInt(a,10);return= isNaN(b)?r:b},t=3Ds(q[0]),u=3DMath.max(t,s(q[1]||""));t=3Dd?Math.max(t,d.g= etFullYear()):t,u=3De?Math.min(u,e.getFullYear()):u,a.yearshtml+=3D'";for(;t<=3Du;t++)a.yearshtm= l+=3D'";a.yearshtml+=3D"",l+=3Da.yearshtml,a.yearshtml=3Dnull= }}return l+=3Dthis._get(a,"yearSuffix"),k&&(l+=3D(f||!i||!j?" ":"")+m)= ,l+=3D"
    ",l},_adjustInstDate:function(a,b,c){var d=3Da.drawYear+(c=3D= =3D"Y"?b:0),e=3Da.drawMonth+(c=3D=3D"M"?b:0),f=3DMath.min(a.selectedDay,thi= s._getDaysInMonth(d,e))+(c=3D=3D"D"?b:0),g=3Dthis._restrictMinMax(a,this._d= aylightSavingAdjust(new Date(d,e,f)));a.selectedDay=3Dg.getDate(),a.drawMon= th=3Da.selectedMonth=3Dg.getMonth(),a.drawYear=3Da.selectedYear=3Dg.getFull= Year(),(c=3D=3D"M"||c=3D=3D"Y")&&this._notifyChange(a)},_restrictMinMax:fun= ction(a,b){var c=3Dthis._getMinMaxDate(a,"min"),d=3Dthis._getMinMaxDate(a,"= max"),e=3Dc&&bd?d:e,e},_notifyChange:function(a){var= b=3Dthis._get(a,"onChangeMonthYear");b&&b.apply(a.input?a.input[0]:null,[a= .selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){var b= =3Dthis._get(a,"numberOfMonths");return b=3D=3Dnull?[1,1]:typeof b=3D=3D"nu= mber"?[1,b]:b},_getMinMaxDate:function(a,b){return this._determineDate(a,th= is._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._da= ylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:functio= n(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,d){= var e=3Dthis._getNumberOfMonths(a),f=3Dthis._daylightSavingAdjust(new Date(= c,d+(b<0?b:e[0]*e[1]),1));return b<0&&f.setDate(this._getDaysInMonth(f.getF= ullYear(),f.getMonth())),this._isInRange(a,f)},_isInRange:function(a,b){var= c=3Dthis._getMinMaxDate(a,"min"),d=3Dthis._getMinMaxDate(a,"max");return(!= c||b.getTime()>=3Dc.getTime())&&(!d||b.getTime()<=3Dd.getTime())},_getForma= tConfig:function(a){var b=3Dthis._get(a,"shortYearCutoff");return b=3Dtypeo= f b!=3D"string"?b:(new Date).getFullYear()%100+parseInt(b,10),{shortYearCut= off:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,"dayN= ames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(= a,"monthNames")}},_formatDate:function(a,b,c,d){b||(a.currentDay=3Da.select= edDay,a.currentMonth=3Da.selectedMonth,a.currentYear=3Da.selectedYear);var = e=3Db?typeof b=3D=3D"object"?b:this._daylightSavingAdjust(new Date(d,c,b)):= this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentD= ay));return this.formatDate(this._get(a,"dateFormat"),e,this._getFormatConf= ig(a))}}),$.fn.datepicker=3Dfunction(a){if(!this.length)return this;$.datep= icker.initialized||($(document).mousedown($.datepicker._checkExternalClick)= .find("body").append($.datepicker.dpDiv),$.datepicker.initialized=3D!0);var= b=3DArray.prototype.slice.call(arguments,1);return typeof a!=3D"string"||a= !=3D"isDisabled"&&a!=3D"getDate"&&a!=3D"widget"?a=3D=3D"option"&&arguments.= length=3D=3D2&&typeof arguments[1]=3D=3D"string"?$.datepicker["_"+a+"Datepi= cker"].apply($.datepicker,[this[0]].concat(b)):this.each(function(){typeof = a=3D=3D"string"?$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this].= concat(b)):$.datepicker._attachDatepicker(this,a)}):$.datepicker["_"+a+"Dat= epicker"].apply($.datepicker,[this[0]].concat(b))},$.datepicker=3Dnew Datep= icker,$.datepicker.initialized=3D!1,$.datepicker.uuid=3D(new Date).getTime(= ),$.datepicker.version=3D"1.8.21",window["DP_jQuery_"+dpuuid]=3D$})(jQuery)= ;; \ No newline at end of file +(function($,undefined){function Datepicker(){this.debug=3D!1,this._curInst= =3Dnull,this._keyEvent=3D!1,this._disabledInputs=3D[],this._datepickerShowi= ng=3D!1,this._inDialog=3D!1,this._mainDivId=3D"ui-datepicker-div",this._inl= ineClass=3D"ui-datepicker-inline",this._appendClass=3D"ui-datepicker-append= ",this._triggerClass=3D"ui-datepicker-trigger",this._dialogClass=3D"ui-date= picker-dialog",this._disableClass=3D"ui-datepicker-disabled",this._unselect= ableClass=3D"ui-datepicker-unselectable",this._currentClass=3D"ui-datepicke= r-current-day",this._dayOverClass=3D"ui-datepicker-days-cell-over",this.reg= ional=3D[],this.regional[""]=3D{closeText:"Done",prevText:"Prev",nextText:"= Next",currentText:"Today",monthNames:["January","February","March","April",= "May","June","July","August","September","October","November","December"],m= onthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct"= ,"Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday",= "Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sa= t"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFo= rmat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},th= is._defaults=3D{showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate= :null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hide= IfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,cha= ngeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,sh= owWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null= ,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:n= ull,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0= ,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,s= howButtonPanel:!1,autoSize:!1,disabled:!1},$.extend(this._defaults,this.reg= ional[""]),this.dpDiv=3DbindHover($('
    '))}function bindHover(a){var b=3D"button, .ui-datepicker-prev, = .ui-datepicker-next, .ui-datepicker-calendar td a";return a.bind("mouseout"= ,function(a){var c=3D$(a.target).closest(b);if(!c.length)return;c.removeCla= ss("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bi= nd("mouseover",function(c){var d=3D$(c.target).closest(b);if($.datepicker._= isDisabledDatepicker(instActive.inline?a.parent()[0]:instActive.input[0])||= !d.length)return;d.parents(".ui-datepicker-calendar").find("a").removeClass= ("ui-state-hover"),d.addClass("ui-state-hover"),d.hasClass("ui-datepicker-p= rev")&&d.addClass("ui-datepicker-prev-hover"),d.hasClass("ui-datepicker-nex= t")&&d.addClass("ui-datepicker-next-hover")})}function extendRemove(a,b){$.= extend(a,b);for(var c in b)if(b[c]=3D=3Dnull||b[c]=3D=3Dundefined)a[c]=3Db[= c];return a}function isArray(a){return a&&($.browser.safari&&typeof a=3D=3D= "object"&&a.length||a.constructor&&a.constructor.toString().match(/\Array\(= \)/))}$.extend($.ui,{datepicker:{version:"1.8.23"}});var PROP_NAME=3D"datep= icker",dpuuid=3D(new Date).getTime(),instActive;$.extend(Datepicker.prototy= pe,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&co= nsole.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpD= iv},setDefaults:function(a){return extendRemove(this._defaults,a||{}),this}= ,_attachDatepicker:function(target,settings){var inlineSettings=3Dnull;for(= var attrName in this._defaults){var attrValue=3Dtarget.getAttribute("date:"= +attrName);if(attrValue){inlineSettings=3DinlineSettings||{};try{inlineSett= ings[attrName]=3Deval(attrValue)}catch(err){inlineSettings[attrName]=3Dattr= Value}}}var nodeName=3Dtarget.nodeName.toLowerCase(),inline=3DnodeName=3D= =3D"div"||nodeName=3D=3D"span";target.id||(this.uuid+=3D1,target.id=3D"dp"+= this.uuid);var inst=3Dthis._newInst($(target),inline);inst.settings=3D$.ext= end({},settings||{},inlineSettings||{}),nodeName=3D=3D"input"?this._connect= Datepicker(target,inst):inline&&this._inlineDatepicker(target,inst)},_newIn= st:function(a,b){var c=3Da[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");retu= rn{id:c,input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,dr= awYear:0,inline:b,dpDiv:b?bindHover($('
    ')):this.dpDiv}},_connectDatepicker:function(a,b){var c=3D$(a);b.app= end=3D$([]),b.trigger=3D$([]);if(c.hasClass(this.markerClassName))return;th= is._attachments(c,b),c.addClass(this.markerClassName).keydown(this._doKeyDo= wn).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicke= r",function(a,c,d){b.settings[c]=3Dd}).bind("getData.datepicker",function(a= ,c){return this._get(b,c)}),this._autoSize(b),$.data(a,PROP_NAME,b),b.setti= ngs.disabled&&this._disableDatepicker(a)},_attachments:function(a,b){var c= =3Dthis._get(b,"appendText"),d=3Dthis._get(b,"isRTL");b.append&&b.append.re= move(),c&&(b.append=3D$(''+c+"= "),a[d?"before":"after"](b.append)),a.unbind("focus",this._showDatepicker),= b.trigger&&b.trigger.remove();var e=3Dthis._get(b,"showOn");(e=3D=3D"focus"= ||e=3D=3D"both")&&a.focus(this._showDatepicker);if(e=3D=3D"button"||e=3D=3D= "both"){var f=3Dthis._get(b,"buttonText"),g=3Dthis._get(b,"buttonImage");b.= trigger=3D$(this._get(b,"buttonImageOnly")?$("").addClass(this._trigg= erClass).attr({src:g,alt:f,title:f}):$('')= .addClass(this._triggerClass).html(g=3D=3D""?f:$("").attr({src:g,alt:= f,title:f}))),a[d?"before":"after"](b.trigger),b.trigger.click(function(){r= eturn $.datepicker._datepickerShowing&&$.datepicker._lastInput=3D=3Da[0]?$.= datepicker._hideDatepicker():$.datepicker._datepickerShowing&&$.datepicker.= _lastInput!=3Da[0]?($.datepicker._hideDatepicker(),$.datepicker._showDatepi= cker(a[0])):$.datepicker._showDatepicker(a[0]),!1})}},_autoSize:function(a)= {if(this._get(a,"autoSize")&&!a.inline){var b=3Dnew Date(2009,11,20),c=3Dth= is._get(a,"dateFormat");if(c.match(/[DM]/)){var d=3Dfunction(a){var b=3D0,c= =3D0;for(var d=3D0;db&&(b=3Da[d].length,c=3Dd);re= turn c};b.setMonth(d(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShor= t"))),b.setDate(d(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20= -b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDat= epicker:function(a,b){var c=3D$(a);if(c.hasClass(this.markerClassName))retu= rn;c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicke= r",function(a,c,d){b.settings[c]=3Dd}).bind("getData.datepicker",function(a= ,c){return this._get(b,c)}),$.data(a,PROP_NAME,b),this._setDate(b,this._get= DefaultDate(b),!0),this._updateDatepicker(b),this._updateAlternate(b),b.set= tings.disabled&&this._disableDatepicker(a),b.dpDiv.css("display","block")},= _dialogDatepicker:function(a,b,c,d,e){var f=3Dthis._dialogInst;if(!f){this.= uuid+=3D1;var g=3D"dp"+this.uuid;this._dialogInput=3D$(''),t= his._dialogInput.keydown(this._doKeyDown),$("body").append(this._dialogInpu= t),f=3Dthis._dialogInst=3Dthis._newInst(this._dialogInput,!1),f.settings=3D= {},$.data(this._dialogInput[0],PROP_NAME,f)}extendRemove(f.settings,d||{}),= b=3Db&&b.constructor=3D=3DDate?this._formatDate(f,b):b,this._dialogInput.va= l(b),this._pos=3De?e.length?e:[e.pageX,e.pageY]:null;if(!this._pos){var h= =3Ddocument.documentElement.clientWidth,i=3Ddocument.documentElement.client= Height,j=3Ddocument.documentElement.scrollLeft||document.body.scrollLeft,k= =3Ddocument.documentElement.scrollTop||document.body.scrollTop;this._pos=3D= [h/2-100+j,i/2-150+k]}return this._dialogInput.css("left",this._pos[0]+20+"= px").css("top",this._pos[1]+"px"),f.settings.onSelect=3Dc,this._inDialog=3D= !0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialog= Input[0]),$.blockUI&&$.blockUI(this.dpDiv),$.data(this._dialogInput[0],PROP= _NAME,f),this},_destroyDatepicker:function(a){var b=3D$(a),c=3D$.data(a,PRO= P_NAME);if(!b.hasClass(this.markerClassName))return;var d=3Da.nodeName.toLo= werCase();$.removeData(a,PROP_NAME),d=3D=3D"input"?(c.append.remove(),c.tri= gger.remove(),b.removeClass(this.markerClassName).unbind("focus",this._show= Datepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKey= Press).unbind("keyup",this._doKeyUp)):(d=3D=3D"div"||d=3D=3D"span")&&b.remo= veClass(this.markerClassName).empty()},_enableDatepicker:function(a){var b= =3D$(a),c=3D$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return= ;var d=3Da.nodeName.toLowerCase();if(d=3D=3D"input")a.disabled=3D!1,c.trigg= er.filter("button").each(function(){this.disabled=3D!1}).end().filter("img"= ).css({opacity:"1.0",cursor:""});else if(d=3D=3D"div"||d=3D=3D"span"){var e= =3Db.children("."+this._inlineClass);e.children().removeClass("ui-state-dis= abled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").rem= oveAttr("disabled")}this._disabledInputs=3D$.map(this._disabledInputs,funct= ion(b){return b=3D=3Da?null:b})},_disableDatepicker:function(a){var b=3D$(a= ),c=3D$.data(a,PROP_NAME);if(!b.hasClass(this.markerClassName))return;var d= =3Da.nodeName.toLowerCase();if(d=3D=3D"input")a.disabled=3D!0,c.trigger.fil= ter("button").each(function(){this.disabled=3D!0}).end().filter("img").css(= {opacity:"0.5",cursor:"default"});else if(d=3D=3D"div"||d=3D=3D"span"){var = e=3Db.children("."+this._inlineClass);e.children().addClass("ui-state-disab= led"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").attr(= "disabled","disabled")}this._disabledInputs=3D$.map(this._disabledInputs,fu= nction(b){return b=3D=3Da?null:b}),this._disabledInputs[this._disabledInput= s.length]=3Da},_isDisabledDatepicker:function(a){if(!a)return!1;for(var b= =3D0;b-1}},_doKeyUp:function(a){var b=3D$.datepicker._getInst(a.target);= if(b.input.val()!=3Db.lastVal)try{var c=3D$.datepicker.parseDate($.datepick= er._get(b,"dateFormat"),b.input?b.input.val():null,$.datepicker._getFormatC= onfig(b));c&&($.datepicker._setDateFromField(b),$.datepicker._updateAlterna= te(b),$.datepicker._updateDatepicker(b))}catch(d){$.datepicker.log(d)}retur= n!0},_showDatepicker:function(a){a=3Da.target||a,a.nodeName.toLowerCase()!= =3D"input"&&(a=3D$("input",a.parentNode)[0]);if($.datepicker._isDisabledDat= epicker(a)||$.datepicker._lastInput=3D=3Da)return;var b=3D$.datepicker._get= Inst(a);$.datepicker._curInst&&$.datepicker._curInst!=3Db&&($.datepicker._c= urInst.dpDiv.stop(!0,!0),b&&$.datepicker._datepickerShowing&&$.datepicker._= hideDatepicker($.datepicker._curInst.input[0]));var c=3D$.datepicker._get(b= ,"beforeShow"),d=3Dc?c.apply(a,[a,b]):{};if(d=3D=3D=3D!1)return;extendRemov= e(b.settings,d),b.lastVal=3Dnull,$.datepicker._lastInput=3Da,$.datepicker._= setDateFromField(b),$.datepicker._inDialog&&(a.value=3D""),$.datepicker._po= s||($.datepicker._pos=3D$.datepicker._findPos(a),$.datepicker._pos[1]+=3Da.= offsetHeight);var e=3D!1;$(a).parents().each(function(){return e|=3D$(this)= .css("position")=3D=3D"fixed",!e}),e&&$.browser.opera&&($.datepicker._pos[0= ]-=3Ddocument.documentElement.scrollLeft,$.datepicker._pos[1]-=3Ddocument.d= ocumentElement.scrollTop);var f=3D{left:$.datepicker._pos[0],top:$.datepick= er._pos[1]};$.datepicker._pos=3Dnull,b.dpDiv.empty(),b.dpDiv.css({position:= "absolute",display:"block",top:"-1000px"}),$.datepicker._updateDatepicker(b= ),f=3D$.datepicker._checkOffset(b,f,e),b.dpDiv.css({position:$.datepicker._= inDialog&&$.blockUI?"static":e?"fixed":"absolute",display:"none",left:f.lef= t+"px",top:f.top+"px"});if(!b.inline){var g=3D$.datepicker._get(b,"showAnim= "),h=3D$.datepicker._get(b,"duration"),i=3Dfunction(){var a=3Db.dpDiv.find(= "iframe.ui-datepicker-cover");if(!!a.length){var c=3D$.datepicker._getBorde= rs(b.dpDiv);a.css({left:-c[0],top:-c[1],width:b.dpDiv.outerWidth(),height:b= .dpDiv.outerHeight()})}};b.dpDiv.zIndex($(a).zIndex()+1),$.datepicker._date= pickerShowing=3D!0,$.effects&&$.effects[g]?b.dpDiv.show(g,$.datepicker._get= (b,"showOptions"),h,i):b.dpDiv[g||"show"](g?h:null,i),(!g||!h)&&i(),b.input= .is(":visible")&&!b.input.is(":disabled")&&b.input.focus(),$.datepicker._cu= rInst=3Db}},_updateDatepicker:function(a){var b=3Dthis;b.maxRows=3D4;var c= =3D$.datepicker._getBorders(a.dpDiv);instActive=3Da,a.dpDiv.empty().append(= this._generateHTML(a)),this._attachHandlers(a);var d=3Da.dpDiv.find("iframe= .ui-datepicker-cover");!d.length||d.css({left:-c[0],top:-c[1],width:a.dpDiv= .outerWidth(),height:a.dpDiv.outerHeight()}),a.dpDiv.find("."+this._dayOver= Class+" a").mouseover();var e=3Dthis._getNumberOfMonths(a),f=3De[1],g=3D17;= a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepic= ker-multi-4").width(""),f>1&&a.dpDiv.addClass("ui-datepicker-multi-"+f).css= ("width",g*f+"em"),a.dpDiv[(e[0]!=3D1||e[1]!=3D1?"add":"remove")+"Class"]("= ui-datepicker-multi"),a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"= ]("ui-datepicker-rtl"),a=3D=3D$.datepicker._curInst&&$.datepicker._datepick= erShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.inp= ut[0]!=3Ddocument.activeElement&&a.input.focus();if(a.yearshtml){var h=3Da.= yearshtml;setTimeout(function(){h=3D=3D=3Da.yearshtml&&a.yearshtml&&a.dpDiv= .find("select.ui-datepicker-year:first").replaceWith(a.yearshtml),h=3Da.yea= rshtml=3Dnull},0)}},_getBorders:function(a){var b=3Dfunction(a){return{thin= :1,medium:2,thick:3}[a]||a};return[parseFloat(b(a.css("border-left-width"))= ),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){v= ar d=3Da.dpDiv.outerWidth(),e=3Da.dpDiv.outerHeight(),f=3Da.input?a.input.o= uterWidth():0,g=3Da.input?a.input.outerHeight():0,h=3Ddocument.documentElem= ent.clientWidth+(c?0:$(document).scrollLeft()),i=3Ddocument.documentElement= .clientHeight+(c?0:$(document).scrollTop());return b.left-=3Dthis._get(a,"i= sRTL")?d-f:0,b.left-=3Dc&&b.left=3D=3Da.input.offset().left?$(document).scr= ollLeft():0,b.top-=3Dc&&b.top=3D=3Da.input.offset().top+g?$(document).scrol= lTop():0,b.left-=3DMath.min(b.left,b.left+d>h&&h>d?Math.abs(b.left+d-h):0),= b.top-=3DMath.min(b.top,b.top+e>i&&i>e?Math.abs(e+g):0),b},_findPos:functio= n(a){var b=3Dthis._getInst(a),c=3Dthis._get(b,"isRTL");while(a&&(a.type=3D= =3D"hidden"||a.nodeType!=3D1||$.expr.filters.hidden(a)))a=3Da[c?"previousSi= bling":"nextSibling"];var d=3D$(a).offset();return[d.left,d.top]},_hideDate= picker:function(a){var b=3Dthis._curInst;if(!b||a&&b!=3D$.data(a,PROP_NAME)= )return;if(this._datepickerShowing){var c=3Dthis._get(b,"showAnim"),d=3Dthi= s._get(b,"duration"),e=3Dfunction(){$.datepicker._tidyDialog(b)};$.effects&= &$.effects[c]?b.dpDiv.hide(c,$.datepicker._get(b,"showOptions"),d,e):b.dpDi= v[c=3D=3D"slideDown"?"slideUp":c=3D=3D"fadeIn"?"fadeOut":"hide"](c?d:null,e= ),c||e(),this._datepickerShowing=3D!1;var f=3Dthis._get(b,"onClose");f&&f.a= pply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]),this._lastInput= =3Dnull,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0= ",top:"-100px"}),$.blockUI&&($.unblockUI(),$("body").append(this.dpDiv))),t= his._inDialog=3D!1}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dial= ogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a)= {if(!$.datepicker._curInst)return;var b=3D$(a.target),c=3D$.datepicker._get= Inst(b[0]);(b[0].id!=3D$.datepicker._mainDivId&&b.parents("#"+$.datepicker.= _mainDivId).length=3D=3D0&&!b.hasClass($.datepicker.markerClassName)&&!b.cl= osest("."+$.datepicker._triggerClass).length&&$.datepicker._datepickerShowi= ng&&(!$.datepicker._inDialog||!$.blockUI)||b.hasClass($.datepicker.markerCl= assName)&&$.datepicker._curInst!=3Dc)&&$.datepicker._hideDatepicker()},_adj= ustDate:function(a,b,c){var d=3D$(a),e=3Dthis._getInst(d[0]);if(this._isDis= abledDatepicker(d[0]))return;this._adjustInstDate(e,b+(c=3D=3D"M"?this._get= (e,"showCurrentAtPos"):0),c),this._updateDatepicker(e)},_gotoToday:function= (a){var b=3D$(a),c=3Dthis._getInst(b[0]);if(this._get(c,"gotoCurrent")&&c.c= urrentDay)c.selectedDay=3Dc.currentDay,c.drawMonth=3Dc.selectedMonth=3Dc.cu= rrentMonth,c.drawYear=3Dc.selectedYear=3Dc.currentYear;else{var d=3Dnew Dat= e;c.selectedDay=3Dd.getDate(),c.drawMonth=3Dc.selectedMonth=3Dd.getMonth(),= c.drawYear=3Dc.selectedYear=3Dd.getFullYear()}this._notifyChange(c),this._a= djustDate(b)},_selectMonthYear:function(a,b,c){var d=3D$(a),e=3Dthis._getIn= st(d[0]);e["selected"+(c=3D=3D"M"?"Month":"Year")]=3De["draw"+(c=3D=3D"M"?"= Month":"Year")]=3DparseInt(b.options[b.selectedIndex].value,10),this._notif= yChange(e),this._adjustDate(d)},_selectDay:function(a,b,c,d){var e=3D$(a);i= f($(d).hasClass(this._unselectableClass)||this._isDisabledDatepicker(e[0]))= return;var f=3Dthis._getInst(e[0]);f.selectedDay=3Df.currentDay=3D$("a",d).= html(),f.selectedMonth=3Df.currentMonth=3Db,f.selectedYear=3Df.currentYear= =3Dc,this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.cu= rrentYear))},_clearDate:function(a){var b=3D$(a),c=3Dthis._getInst(b[0]);th= is._selectDate(b,"")},_selectDate:function(a,b){var c=3D$(a),d=3Dthis._getI= nst(c[0]);b=3Db!=3Dnull?b:this._formatDate(d),d.input&&d.input.val(b),this.= _updateAlternate(d);var e=3Dthis._get(d,"onSelect");e?e.apply(d.input?d.inp= ut[0]:null,[b,d]):d.input&&d.input.trigger("change"),d.inline?this._updateD= atepicker(d):(this._hideDatepicker(),this._lastInput=3Dd.input[0],typeof d.= input[0]!=3D"object"&&d.input.focus(),this._lastInput=3Dnull)},_updateAlter= nate:function(a){var b=3Dthis._get(a,"altField");if(b){var c=3Dthis._get(a,= "altFormat")||this._get(a,"dateFormat"),d=3Dthis._getDate(a),e=3Dthis.forma= tDate(c,d,this._getFormatConfig(a));$(b).each(function(){$(this).val(e)})}}= ,noWeekends:function(a){var b=3Da.getDay();return[b>0&&b<6,""]},iso8601Week= :function(a){var b=3Dnew Date(a.getTime());b.setDate(b.getDate()+4-(b.getDa= y()||7));var c=3Db.getTime();return b.setMonth(0),b.setDate(1),Math.floor(M= ath.round((c-b)/864e5)/7)+1},parseDate:function(a,b,c){if(a=3D=3Dnull||b=3D= =3Dnull)throw"Invalid arguments";b=3Dtypeof b=3D=3D"object"?b.toString():b+= "";if(b=3D=3D"")return null;var d=3D(c?c.shortYearCutoff:null)||this._defau= lts.shortYearCutoff;d=3Dtypeof d!=3D"string"?d:(new Date).getFullYear()%100= +parseInt(d,10);var e=3D(c?c.dayNamesShort:null)||this._defaults.dayNamesSh= ort,f=3D(c?c.dayNames:null)||this._defaults.dayNames,g=3D(c?c.monthNamesSho= rt:null)||this._defaults.monthNamesShort,h=3D(c?c.monthNames:null)||this._d= efaults.monthNames,i=3D-1,j=3D-1,k=3D-1,l=3D-1,m=3D!1,n=3Dfunction(b){var c= =3Ds+1-1){j=3D1,k=3Dl;do{var u=3Dthis._getDaysInMonth(i,j-1);if(k<=3Du)bre= ak;j++,k-=3Du}while(!0)}var t=3Dthis._daylightSavingAdjust(new Date(i,j-1,k= ));if(t.getFullYear()!=3Di||t.getMonth()+1!=3Dj||t.getDate()!=3Dk)throw"Inv= alid date";return t},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd= ",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, = d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy= -mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(= 4.925))*24*60*60*1e7,formatDate:function(a,b,c){if(!b)return"";var d=3D(c?c= .dayNamesShort:null)||this._defaults.dayNamesShort,e=3D(c?c.dayNames:null)|= |this._defaults.dayNames,f=3D(c?c.monthNamesShort:null)||this._defaults.mon= thNamesShort,g=3D(c?c.monthNames:null)||this._defaults.monthNames,h=3Dfunct= ion(b){var c=3Dm+112?a.getHours()+2:0),a):null},_setDa= te:function(a,b,c){var d=3D!b,e=3Da.selectedMonth,f=3Da.selectedYear,g=3Dth= is._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=3Da.c= urrentDay=3Dg.getDate(),a.drawMonth=3Da.selectedMonth=3Da.currentMonth=3Dg.= getMonth(),a.drawYear=3Da.selectedYear=3Da.currentYear=3Dg.getFullYear(),(e= !=3Da.selectedMonth||f!=3Da.selectedYear)&&!c&&this._notifyChange(a),this._= adjustInstDate(a),a.input&&a.input.val(d?"":this._formatDate(a))},_getDate:= function(a){var b=3D!a.currentYear||a.input&&a.input.val()=3D=3D""?null:thi= s._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay)= );return b},_attachHandlers:function(a){var b=3Dthis._get(a,"stepMonths"),c= =3D"#"+a.id.replace(/\\\\/g,"\\");a.dpDiv.find("[data-handler]").map(functi= on(){var a=3D{prev:function(){window["DP_jQuery_"+dpuuid].datepicker._adjus= tDate(c,-b,"M")},next:function(){window["DP_jQuery_"+dpuuid].datepicker._ad= justDate(c,+b,"M")},hide:function(){window["DP_jQuery_"+dpuuid].datepicker.= _hideDatepicker()},today:function(){window["DP_jQuery_"+dpuuid].datepicker.= _gotoToday(c)},selectDay:function(){return window["DP_jQuery_"+dpuuid].date= picker._selectDay(c,+this.getAttribute("data-month"),+this.getAttribute("da= ta-year"),this),!1},selectMonth:function(){return window["DP_jQuery_"+dpuui= d].datepicker._selectMonthYear(c,this,"M"),!1},selectYear:function(){return= window["DP_jQuery_"+dpuuid].datepicker._selectMonthYear(c,this,"Y"),!1}};$= (this).bind(this.getAttribute("data-event"),a[this.getAttribute("data-handl= er")])})},_generateHTML:function(a){var b=3Dnew Date;b=3Dthis._daylightSavi= ngAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=3Dthis._= get(a,"isRTL"),d=3Dthis._get(a,"showButtonPanel"),e=3Dthis._get(a,"hideIfNo= PrevNext"),f=3Dthis._get(a,"navigationAsDateFormat"),g=3Dthis._getNumberOfM= onths(a),h=3Dthis._get(a,"showCurrentAtPos"),i=3Dthis._get(a,"stepMonths"),= j=3Dg[0]!=3D1||g[1]!=3D1,k=3Dthis._daylightSavingAdjust(a.currentDay?new Da= te(a.currentYear,a.currentMonth,a.currentDay):new Date(9999,9,9)),l=3Dthis.= _getMinMaxDate(a,"min"),m=3Dthis._getMinMaxDate(a,"max"),n=3Da.drawMonth-h,= o=3Da.drawYear;n<0&&(n+=3D12,o--);if(m){var p=3Dthis._daylightSavingAdjust(= new Date(m.getFullYear(),m.getMonth()-g[0]*g[1]+1,m.getDate()));p=3Dl&&pp)n--,n<0&&(n=3D11,o-= -)}a.drawMonth=3Dn,a.drawYear=3Do;var q=3Dthis._get(a,"prevText");q=3Df?thi= s.formatDate(q,this._daylightSavingAdjust(new Date(o,n-i,1)),this._getForma= tConfig(a)):q;var r=3Dthis._canAdjustMonth(a,-1,o,n)?'= '+q+"":e?"":''+q+"",s=3Dthis._get(a,"nextText");s=3Df?this.= formatDate(s,this._daylightSavingAdjust(new Date(o,n+i,1)),this._getFormatC= onfig(a)):s;var t=3Dthis._canAdjustMonth(a,1,o,n)?''+s+"= ":e?"":''+s+"",u=3Dthis._get(a,"currentText"),v=3Dthis._get= (a,"gotoCurrent")&&a.currentDay?k:b;u=3Df?this.formatDate(u,v,this._getForm= atConfig(a)):u;var w=3Da.inline?"":'",x= =3Dd?'
    '+(c?w:"")+= (this._isInRange(a,v)?'":"")+(c?"":w)+"
    ":"",y=3Dpars= eInt(this._get(a,"firstDay"),10);y=3DisNaN(y)?0:y;var z=3Dthis._get(a,"show= Week"),A=3Dthis._get(a,"dayNames"),B=3Dthis._get(a,"dayNamesShort"),C=3Dthi= s._get(a,"dayNamesMin"),D=3Dthis._get(a,"monthNames"),E=3Dthis._get(a,"mont= hNamesShort"),F=3Dthis._get(a,"beforeShowDay"),G=3Dthis._get(a,"showOtherMo= nths"),H=3Dthis._get(a,"selectOtherMonths"),I=3Dthis._get(a,"calculateWeek"= )||this.iso8601Week,J=3Dthis._getDefaultDate(a),K=3D"";for(var L=3D0;L1)switch(N){case 0= :Q+=3D" ui-datepicker-group-first",P=3D" ui-corner-"+(c?"right":"left");bre= ak;case g[1]-1:Q+=3D" ui-datepicker-group-last",P=3D" ui-corner-"+(c?"left"= :"right");break;default:Q+=3D" ui-datepicker-group-middle",P=3D""}Q+=3D'">'= }Q+=3D'
    '+(/all|left/.test(P)&&L=3D=3D0?c?t:r:"")+(/all|right/.test(P)&&L= =3D=3D0?c?r:t:"")+this._generateMonthYearHeader(a,n,o,l,m,L>0||N>0,D,E)+''+"";var R=3Dz?'":"";for= (var S=3D0;S<7;S++){var T=3D(S+y)%7;R+=3D"=3D5?' class=3D"u= i-datepicker-week-end"':"")+">"+''+C[T]+""}Q+=3DR+"";var U=3Dthis._getDaysInMonth(o,n);o=3D= =3Da.selectedYear&&n=3D=3Da.selectedMonth&&(a.selectedDay=3DMath.min(a.sele= ctedDay,U));var V=3D(this._getFirstDayOfMonth(o,n)-y+7)%7,W=3DMath.ceil((V+= U)/7),X=3Dj?this.maxRows>W?this.maxRows:W:W;this.maxRows=3DX;var Y=3Dthis._= daylightSavingAdjust(new Date(o,n,1-V));for(var Z=3D0;Z";= var _=3Dz?'":"";for(var S=3D0;S<7;S++){var ba=3DF?F.apply(a.input?a.input= [0]:null,[Y]):[!0,""],bb=3DY.getMonth()!=3Dn,bc=3Dbb&&!H||!ba[0]||l&&Ym;_+=3D'",Y.setDate(Y.getDate()+1),Y=3Dthis._daylightSav= ingAdjust(Y)}Q+=3D_+""}n++,n>11&&(n=3D0,o++),Q+=3D"
    '+this._get(a,"weekHeader")+"
    '+this._get(a,"calculateWee= k")(Y)+"=3D5?" ui-datepicker-week-end":"")+(b= b?" ui-datepicker-other-month":"")+(Y.getTime()=3D=3DO.getTime()&&n=3D=3Da.= selectedMonth&&a._keyEvent||J.getTime()=3D=3DY.getTime()&&J.getTime()=3D=3D= O.getTime()?" "+this._dayOverClass:"")+(bc?" "+this._unselectableClass+" ui= -state-disabled":"")+(bb&&!G?"":" "+ba[1]+(Y.getTime()=3D=3Dk.getTime()?" "= +this._currentClass:"")+(Y.getTime()=3D=3Db.getTime()?" ui-datepicker-today= ":""))+'"'+((!bb||G)&&ba[2]?' title=3D"'+ba[2]+'"':"")+(bc?"":' data-handle= r=3D"selectDay" data-event=3D"click" data-month=3D"'+Y.getMonth()+'" data-y= ear=3D"'+Y.getFullYear()+'"')+">"+(bb&&!G?" ":bc?''+Y.getDate()+"":''= +Y.getDate()+"")+"
    "+(= j?"
    "+(g[0]>0&&N=3D=3Dg[1]-1?'
    <= /div>':""):""),M+=3DQ}K+=3DM}return K+=3Dx+($.browser.msie&&parseInt($.brow= ser.version,10)<7&&!a.inline?'':""),a._keyEvent=3D!1,K},_ge= nerateMonthYearHeader:function(a,b,c,d,e,f,g,h){var i=3Dthis._get(a,"change= Month"),j=3Dthis._get(a,"changeYear"),k=3Dthis._get(a,"showMonthAfterYear")= ,l=3D'
    ',m=3D"";if(f||!i)m+=3D''+g[b]+"";else{var n=3Dd&&d.getFullYear()= =3D=3Dc,o=3De&&e.getFullYear()=3D=3Dc;m+=3D'"}k||(l+=3Dm+(f||!i||!j?" ":""));if(!a.yearshtml){a.ye= arshtml=3D"";if(f||!j)l+=3D''+c+"= ";else{var q=3Dthis._get(a,"yearRange").split(":"),r=3D(new Date).getFullYe= ar(),s=3Dfunction(a){var b=3Da.match(/c[+-].*/)?c+parseInt(a.substring(1),1= 0):a.match(/[+-].*/)?r+parseInt(a,10):parseInt(a,10);return isNaN(b)?r:b},t= =3Ds(q[0]),u=3DMath.max(t,s(q[1]||""));t=3Dd?Math.max(t,d.getFullYear()):t,= u=3De?Math.min(u,e.getFullYear()):u,a.yearshtml+=3D'",l+=3Da.yearshtml= ,a.yearshtml=3Dnull}}return l+=3Dthis._get(a,"yearSuffix"),k&&(l+=3D(f||!i|= |!j?" ":"")+m),l+=3D"
    ",l},_adjustInstDate:function(a,b,c){var d= =3Da.drawYear+(c=3D=3D"Y"?b:0),e=3Da.drawMonth+(c=3D=3D"M"?b:0),f=3DMath.mi= n(a.selectedDay,this._getDaysInMonth(d,e))+(c=3D=3D"D"?b:0),g=3Dthis._restr= ictMinMax(a,this._daylightSavingAdjust(new Date(d,e,f)));a.selectedDay=3Dg.= getDate(),a.drawMonth=3Da.selectedMonth=3Dg.getMonth(),a.drawYear=3Da.selec= tedYear=3Dg.getFullYear(),(c=3D=3D"M"||c=3D=3D"Y")&&this._notifyChange(a)},= _restrictMinMax:function(a,b){var c=3Dthis._getMinMaxDate(a,"min"),d=3Dthis= ._getMinMaxDate(a,"max"),e=3Dc&&bd?d:e,e},_notifyCha= nge:function(a){var b=3Dthis._get(a,"onChangeMonthYear");b&&b.apply(a.input= ?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:= function(a){var b=3Dthis._get(a,"numberOfMonths");return b=3D=3Dnull?[1,1]:= typeof b=3D=3D"number"?[1,b]:b},_getMinMaxDate:function(a,b){return this._d= etermineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){r= eturn 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstD= ayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:f= unction(a,b,c,d){var e=3Dthis._getNumberOfMonths(a),f=3Dthis._daylightSavin= gAdjust(new Date(c,d+(b<0?b:e[0]*e[1]),1));return b<0&&f.setDate(this._getD= aysInMonth(f.getFullYear(),f.getMonth())),this._isInRange(a,f)},_isInRange:= function(a,b){var c=3Dthis._getMinMaxDate(a,"min"),d=3Dthis._getMinMaxDate(= a,"max");return(!c||b.getTime()>=3Dc.getTime())&&(!d||b.getTime()<=3Dd.getT= ime())},_getFormatConfig:function(a){var b=3Dthis._get(a,"shortYearCutoff")= ;return b=3Dtypeof b!=3D"string"?b:(new Date).getFullYear()%100+parseInt(b,= 10),{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:= this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),mont= hNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,d){b||(a.curr= entDay=3Da.selectedDay,a.currentMonth=3Da.selectedMonth,a.currentYear=3Da.s= electedYear);var e=3Db?typeof b=3D=3D"object"?b:this._daylightSavingAdjust(= new Date(d,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.curren= tMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),e,th= is._getFormatConfig(a))}}),$.fn.datepicker=3Dfunction(a){if(!this.length)re= turn this;$.datepicker.initialized||($(document).mousedown($.datepicker._ch= eckExternalClick).find("body").append($.datepicker.dpDiv),$.datepicker.init= ialized=3D!0);var b=3DArray.prototype.slice.call(arguments,1);return typeof= a!=3D"string"||a!=3D"isDisabled"&&a!=3D"getDate"&&a!=3D"widget"?a=3D=3D"op= tion"&&arguments.length=3D=3D2&&typeof arguments[1]=3D=3D"string"?$.datepic= ker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b)):this.each(f= unction(){typeof a=3D=3D"string"?$.datepicker["_"+a+"Datepicker"].apply($.d= atepicker,[this].concat(b)):$.datepicker._attachDatepicker(this,a)}):$.date= picker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b))},$.datep= icker=3Dnew Datepicker,$.datepicker.initialized=3D!1,$.datepicker.uuid=3D(n= ew Date).getTime(),$.datepicker.version=3D"1.8.23",window["DP_jQuery_"+dpuu= id]=3D$})(jQuery);; \ No newline at end of file diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt b/koha-tmp= l/opac-tmpl/prog/en/modules/opac-results.tt index 321d662..905f378 100644 --- a/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt +++ b/koha-tmpl/opac-tmpl/prog/en/modules/opac-results.tt @@ -285,7 +285,7 @@ $(document).ready(function(){
    - [% IF ( DidYouMeanFromAuthorities ) %] + [% IF ( DidYouMean ) %]
    Not what you expected? Check for suggestions
    [% END %] =20 diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/text/explodedterms.tt b/ko= ha-tmpl/opac-tmpl/prog/en/modules/text/explodedterms.tt new file mode 100644 index 0000000..6f394ad --- /dev/null +++ b/koha-tmpl/opac-tmpl/prog/en/modules/text/explodedterms.tt @@ -0,0 +1,8 @@ +[%- SWITCH index -%] +[%- CASE 'su-na' -%] +Search also for narrower subjects +[%- CASE 'su-br' -%] +Search also for broader subjects +[%- CASE 'su-rl' -%] +Search also for related subjects +[%- END -%] diff --git a/opac/opac-search.pl b/opac/opac-search.pl index 216de2f..5c04a59 100755 --- a/opac/opac-search.pl +++ b/opac/opac-search.pl @@ -828,7 +828,7 @@ if (C4::Context->preference('GoogleIndicTransliteration= ')) { $template->param('GoogleIndicTransliteration' =3D> 1); } =20 -$template->{VARS}->{DidYouMeanFromAuthorities} =3D C4::Context->preference= ('DidYouMeanFromAuthorities'); +$template->{VARS}->{DidYouMean} =3D C4::Context->preference('OPACdidyoumea= n') =3D~ m/enable/; =20 $template->param( borrowernumber =3D> $borrowernumber); output_with_http_headers $cgi, $cookie, $template->output, $content_type; diff --git a/opac/svc/suggestion b/opac/svc/suggestion index 7234ffd..9c08565 100755 --- a/opac/svc/suggestion +++ b/opac/svc/suggestion @@ -80,12 +80,20 @@ my ( $template, $loggedinuser, $cookie ) =3D get_templa= te_and_user( } ); =20 -unless ( C4::Context->preference('DidYouMeanFromAuthorities') ) { +my @plugins =3D (); + +my $pluginsconfig =3D from_json(C4::Context->preference('OPACdidyoumean') = || '[]'); + +foreach my $plugin (@$pluginsconfig) { + push @plugins, $plugin->{name} if ($plugin->{enabled}); +} + +unless ( @plugins ) { print $query->header; exit; } =20 -my $suggestor =3D Koha::SuggestionEngine->new( { plugins =3D> ('AuthorityF= ile') } ); +my $suggestor =3D Koha::SuggestionEngine->new( { plugins =3D> \@plugins } = ); =20 my $suggestions =3D $suggestor->get_suggestions( { search =3D> $search, count =3D> $count } = ); diff --git a/t/SuggestionEngine_ExplodedTerms.t b/t/SuggestionEngine_Explod= edTerms.t new file mode 100755 index 0000000..d597aa1 --- /dev/null +++ b/t/SuggestionEngine_ExplodedTerms.t @@ -0,0 +1,31 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More; + +BEGIN { + use_ok('Koha::SuggestionEngine'); +} + +my $suggestor =3D Koha::SuggestionEngine->new( { plugins =3D> ( 'ExplodedT= erms' ) } ); +is(ref($suggestor), 'Koha::SuggestionEngine', 'Created suggestion engine')= ; + +my $result =3D $suggestor->get_suggestions({search =3D> 'Cookery'}); + +ok((grep { $_->{'search'} eq 'su-na=3DCookery' } @$result) && (grep { $_->= {'search'} eq 'su-br=3DCookery' } @$result) && (grep { $_->{'search'} eq 's= u-rl=3DCookery' } @$result), "Suggested correct alternatives for keyword se= arch 'Cookery'"); + +$result =3D $suggestor->get_suggestions({search =3D> 'su:Cookery'}); + +ok((grep { $_->{'search'} eq 'su-na=3DCookery' } @$result) && (grep { $_->= {'search'} eq 'su-br=3DCookery' } @$result) && (grep { $_->{'search'} eq 's= u-rl=3DCookery' } @$result), "Suggested correct alternatives for subject se= arch 'Cookery'"); + +$result =3D $suggestor->get_suggestions({search =3D> 'nt:Cookery'}); + +is(scalar @$result, 0, "No suggestions for fielded search"); + +$result =3D $suggestor->get_suggestions({search =3D> 'ccl=3Dsu:Cookery'}); + +is(scalar @$result, 0, "No suggestions for CCL search"); + +done_testing(); --=20 1.7.11.4 ------=_Part_3872_9533057.1347300578501-- From tomascohen at gmail.com Mon Sep 10 20:30:58 2012 From: tomascohen at gmail.com (Tomas Cohen Arazi) Date: Mon, 10 Sep 2012 15:30:58 -0300 Subject: [Koha-patches] [PATCH] Bug 8750 - Chronological terms authorities not correctly indexed Message-ID: <1347301858-25356-1-git-send-email-tomascohen@gmail.com> There was no entry in authority's record.abs for indexing chronological terms. They ccouldn't be searched and (obviously) linked. I've added those entries using the index names defined in authorities/etc/bib1.att Regards To+ Sponsored-by: Universidad Nacional de C?rdoba --- .../marc_defs/marc21/authorities/record.abs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/etc/zebradb/marc_defs/marc21/authorities/record.abs b/etc/zebradb/marc_defs/marc21/authorities/record.abs index dcd2c24..e76107c 100644 --- a/etc/zebradb/marc_defs/marc21/authorities/record.abs +++ b/etc/zebradb/marc_defs/marc21/authorities/record.abs @@ -46,6 +46,12 @@ melm 130 Title-uniform:w,Title-uniform:p,Title-uniform:s,Heading:w,Heading:p,Hea melm 431 Title-uniform-see-from:w,Title-uniform-see-from:p,Title-uniform-see-from:s,See-from:w,See-from:p,See-from:s,Match:w,Match:p melm 531 Title-uniform-see-also-from:w,Title-uniform-see-also-from:p,Title-uniform-see-also-from:s,See-also-from:w,See-also-from:p,See-also-from:s,Match:w,Match:p +# Chronological Term +melm 148$a Chronological-term-heading:w,Chronological-term-heading:p,Chronological-term-heading:s,Chronological-term:w,Chronological-term:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s +melm 148 Chronological-term:w,Chronological-term:p,Chronological-term:s,Heading:w,Heading:p,Heading:s +melm 448 Chronological-term-see-from:w,Chronological-term-see-from:p,Chronological-term-see-from:s,See-from:w,See-from:p,See-from:s +melm 548 Chronological-term-see-also-from:w,Chronological-term-see-also-from:p,Chronological-term-see-also-from:s,See-also-from:w,See-also-from:p,See-also-from:s + # Topical Term melm 150$a Subject-topical-heading:w,Subject-topical-heading:p,Subject-topical-heading:s,Subject-topical:w,Subject-topical:p,Heading:w,Heading:p,Heading:s,Heading-Main:w,Heading-Main:p,Heading-Main:s,Match:w,Match:p,Match-heading:w,Match-heading:p melm 150 Subject-topical:w,Subject-topical:p,Subject-topical:s,Heading:w,Heading:p,Heading:s,Match:w,Match:p,Match-heading:w,Match-heading:p -- 1.7.9.5 From matted-34813 at mypacks.net Mon Sep 10 23:54:50 2012 From: matted-34813 at mypacks.net (matted-34813 at mypacks.net) Date: Mon, 10 Sep 2012 16:54:50 -0500 (GMT-05:00) Subject: [Koha-patches] SIGNED-OFF-Bug-8215-Course-Reserves Message-ID: <29194467.1347314090885.JavaMail.root@wamui-june.atl.sa.earthlink.net> This patch adds a new feature enabling course reserves. - courses - course-reserves (items reserved for courses) - course-instructors (Teacher patron type) - visibility in holdings views Alot of work. Congratulations khall on your persistence. wajasu From colin.campbell at ptfs-europe.com Tue Sep 11 20:09:37 2012 From: colin.campbell at ptfs-europe.com (Colin Campbell) Date: Tue, 11 Sep 2012 19:09:37 +0100 Subject: [Koha-patches] [PATCH] Bug 8761 Dont inadvertantly use slices Message-ID: <1347386977-12117-1-git-send-email-colin.campbell@ptfs-europe.com> Assignment to a single element slice is better written as a scalar - This generates a compile time warning as it can lead to odd behaviour see perldiag for details This corrects some cases which were added in a recent commit --- C4/Circulation.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/C4/Circulation.pm b/C4/Circulation.pm index 8bd53b9..273c6f3 100644 --- a/C4/Circulation.pm +++ b/C4/Circulation.pm @@ -982,16 +982,16 @@ sub CanBookBeIssued { # Index points to the next value my $restrictionyear = 0; if (($take <= $#values) && ($take >= 0)){ - $restrictionyear += @values[$take]; + $restrictionyear += $values[$take]; } if ($restrictionyear > 0) { if ( $borrower->{'dateofbirth'} ) { my @alloweddate = split /-/,$borrower->{'dateofbirth'} ; - @alloweddate[0] += $restrictionyear; + $alloweddate[0] += $restrictionyear; #Prevent runime eror on leap year (invalid date) - if ((@alloweddate[1] == 2) && (@alloweddate[2] == 29)) { - @alloweddate[2] = 28; + if (($alloweddate[1] == 2) && ($alloweddate[2] == 29)) { + $alloweddate[2] = 28; } if ( Date_to_Days(Today) < Date_to_Days(@alloweddate) -1 ) { -- 1.7.12.146.g16d26b1 From melia at test.bywatersolutions.com Tue Sep 11 23:44:57 2012 From: melia at test.bywatersolutions.com (Melia Meggs) Date: Tue, 11 Sep 2012 16:44:57 -0500 Subject: [Koha-patches] [PATCH] Bug 7143: Adding Kathryn Tyree to the history and About page. Message-ID: <1347399897-16036-1-git-send-email-melia@test.bywatersolutions.com> --- docs/history.txt | 3 ++- koha-tmpl/intranet-tmpl/prog/en/modules/about.tt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/history.txt b/docs/history.txt index 4a58f9d..eb06f03 100644 --- a/docs/history.txt +++ b/docs/history.txt @@ -627,4 +627,5 @@ July 3 2012 Mark Tompsett becomes the 186th developer to have a patch pushed July 7 2012 Stacey Walker becomes the 187th developer to have a patch pushed July 7 2012 Mirko Tietgen becomes the 188th developer to have a patch pushed July 22 2012 Koha 3.8.3 released releases -July 22 2012 Koha 3.6.7 released releases \ No newline at end of file +July 22 2012 Koha 3.6.7 released releases +September 3 2012 Kathryn Tyree becomes the 189th developer to have a patch pushed diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt index 11d6834..742fbb0 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt @@ -314,7 +314,8 @@
  • Steve Tonnesen (early MARC work, Virtual Bookshelves concept, KohaCD)
  • Bruno Toumi
  • Andrei V. Toutoukine
  • -
  • Darrell Ulm
  • +
  • Kathryn Tyree
  • +
  • Darrell Ulm
  • Universidad ORT Uruguay (Ernesto Silva, Andres Tarallo)
  • Marc Veron
  • Aleksa Vujicic
  • -- 1.7.2.5 From srdjan at catalyst.net.nz Wed Sep 12 03:20:09 2012 From: srdjan at catalyst.net.nz (Srdjan) Date: Wed, 12 Sep 2012 13:20:09 +1200 Subject: [Koha-patches] [PATCH] bug_8034: Restored network printer maintenance and selection In-Reply-To: References: Message-ID: <1347412809-20447-1-git-send-email-srdjan@catalyst.net.nz> This patch is just for restoring printer maintenance and selection, not for priting itself. It is just a preparation step. * Added UsePrintQueues syspref. If set to No, no printer info will be displayed/used * Database changes: - printers table PRIMARY KEY is now printqueue. It is more natural. We should really have a synthetic id, but printqueue is good enough - branches.branchprinter is a FOREIGN KEY to printers.printqueue * Created C4::Auth::get_user_printer() function that will return appropriate printer. In order of preference: - session selected - logged in branch branchprinter * Moved printer functions to C4::Printer * Restored printer maint/selection in admin zone UsePrintQueues permitting * Restored printer selection in circ/selectbranchprinter.pl UsePrintQueues permitting Signed-off-by: Jared Camins-Esakov --- C4/Auth.pm | 49 +++++-- C4/Context.pm | 2 +- C4/Koha.pm | 40 ------ C4/Printer.pm | 148 +++++++++++++++++++ admin/branches.pl | 30 ++-- admin/printers.pl | 152 ++++++++++---------- circ/circulation.pl | 12 +- circ/returns.pl | 8 -- circ/selectbranchprinter.pl | 49 ++++--- installer/data/mysql/kohastructure.sql | 16 ++- installer/data/mysql/sysprefs.sql | 1 + installer/data/mysql/updatedatabase.pl | 12 ++ .../intranet-tmpl/prog/en/includes/admin-menu.inc | 1 + .../intranet-tmpl/prog/en/includes/header.inc | 3 + .../prog/en/modules/admin/admin-home.tt | 4 +- .../prog/en/modules/admin/branches.tt | 20 +-- .../en/modules/admin/preferences/circulation.pref | 6 + .../prog/en/modules/admin/printers.tt | 33 +---- .../prog/en/modules/circ/circulation.tt | 2 - .../prog/en/modules/circ/selectbranchprinter.tt | 5 +- t/db_dependent/lib/KohaTest/Koha.pm | 2 - t/db_dependent/lib/KohaTest/Printer.pm | 26 ++++ 22 files changed, 393 insertions(+), 228 deletions(-) create mode 100644 C4/Printer.pm create mode 100644 t/db_dependent/lib/KohaTest/Printer.pm diff --git a/C4/Auth.pm b/C4/Auth.pm index 59c9955..71abfb3 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -28,6 +28,7 @@ require Exporter; use C4::Context; use C4::Templates; # to get the template use C4::Branch; # GetBranches +use C4::Printer qw(GetPrinterDetails); use C4::VirtualShelves; use POSIX qw/strftime/; use List::MoreUtils qw/ any /; @@ -46,7 +47,8 @@ BEGIN { $debug = $ENV{DEBUG}; @ISA = qw(Exporter); @EXPORT = qw(&checkauth &get_template_and_user &haspermission &get_user_subpermissions); - @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw &get_all_subpermissions &get_user_subpermissions); + @EXPORT_OK = qw(&check_api_auth &get_session &check_cookie_auth &checkpw + &get_all_subpermissions &get_user_subpermissions &get_user_printer); %EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] ); $ldap = C4::Context->config('useldapserver') || 0; $cas = C4::Context->preference('casAuthentication'); @@ -310,6 +312,9 @@ sub get_template_and_user { $template->param(dateformat_iso => 1); } + my $userenv = C4::Context->userenv; + my $userenv_branch = $userenv ? $userenv->{"branch"} : undef; + # these template parameters are set the same regardless of $in->{'type'} $template->param( "BiblioDefaultView".C4::Context->preference("BiblioDefaultView") => 1, @@ -317,9 +322,9 @@ sub get_template_and_user { GoogleJackets => C4::Context->preference("GoogleJackets"), OpenLibraryCovers => C4::Context->preference("OpenLibraryCovers"), KohaAdminEmailAddress => "" . C4::Context->preference("KohaAdminEmailAddress"), - LoginBranchcode => (C4::Context->userenv?C4::Context->userenv->{"branch"}:"insecure"), - LoginFirstname => (C4::Context->userenv?C4::Context->userenv->{"firstname"}:"Bel"), - LoginSurname => C4::Context->userenv?C4::Context->userenv->{"surname"}:"Inconnu", + LoginBranchcode => ($userenv?$userenv_branch:"insecure"), + LoginFirstname => ($userenv?$userenv->{"firstname"}:"Bel"), + LoginSurname => $userenv?$userenv->{"surname"}:"Inconnu", TagsEnabled => C4::Context->preference("TagsEnabled"), hide_marc => C4::Context->preference("hide_marc"), item_level_itypes => C4::Context->preference('item-level_itypes'), @@ -344,7 +349,7 @@ sub get_template_and_user { IntranetNav => C4::Context->preference("IntranetNav"), IntranetmainUserblock => C4::Context->preference("IntranetmainUserblock"), LibraryName => C4::Context->preference("LibraryName"), - LoginBranchname => (C4::Context->userenv?C4::Context->userenv->{"branchname"}:"insecure"), + LoginBranchname => ($userenv?$userenv->{"branchname"}:"insecure"), advancedMARCEditor => C4::Context->preference("advancedMARCEditor"), canreservefromotherbranches => C4::Context->preference('canreservefromotherbranches'), intranetcolorstylesheet => C4::Context->preference("intranetcolorstylesheet"), @@ -364,6 +369,14 @@ sub get_template_and_user { AllowMultipleCovers => C4::Context->preference('AllowMultipleCovers'), EnableBorrowerFiles => C4::Context->preference('EnableBorrowerFiles'), ); + if ( C4::Context->preference('UsePrintQueues') ) { + my $printer = get_user_printer(); + my $printer_rec = $printer ? GetPrinterDetails($printer) : {}; + $template->param( + UsePrintQueues => 1, + PrinterName => $printer_rec->{printername}, + ); + } } else { warn "template type should be OPAC, here it is=[" . $in->{'type'} . "]" unless ( $in->{'type'} eq 'opac' ); @@ -382,8 +395,8 @@ sub get_template_and_user { my $opac_name = ''; if (($opac_search_limit =~ /branch:(\w+)/ && $opac_limit_override) || $in->{'query'}->param('limit') =~ /branch:(\w+)/){ $opac_name = $1; # opac_search_limit is a branch, so we use it. - } elsif (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv && C4::Context->userenv->{'branch'}) { - $opac_name = C4::Context->userenv->{'branch'}; + } elsif (C4::Context->preference("SearchMyLibraryFirst") && $userenv_branch) { + $opac_name = $userenv_branch } $template->param( opaccolorstylesheet => C4::Context->preference("opaccolorstylesheet"), @@ -393,7 +406,7 @@ sub get_template_and_user { CalendarFirstDayOfWeek => (C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday")?0:1, LibraryName => "" . C4::Context->preference("LibraryName"), LibraryNameTitle => "" . $LibraryNameTitle, - LoginBranchname => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"", + LoginBranchname => $userenv?$userenv->{"branchname"}:"", OPACAmazonCoverImages => C4::Context->preference("OPACAmazonCoverImages"), OPACFRBRizeEditions => C4::Context->preference("OPACFRBRizeEditions"), OpacHighlightedWords => C4::Context->preference("OpacHighlightedWords"), @@ -424,7 +437,7 @@ sub get_template_and_user { RequestOnOpac => C4::Context->preference("RequestOnOpac"), 'Version' => C4::Context->preference('Version'), hidelostitems => C4::Context->preference("hidelostitems"), - mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && C4::Context->userenv) ? C4::Context->userenv->{'branch'} : '', + mylibraryfirst => (C4::Context->preference("SearchMyLibraryFirst") && $userenv) ? $userenv_branch : '', opaclayoutstylesheet => "" . C4::Context->preference("opaclayoutstylesheet"), opacbookbag => "" . C4::Context->preference("opacbookbag"), opaccredits => "" . C4::Context->preference("opaccredits"), @@ -1644,8 +1657,24 @@ sub getborrowernumber { return 0; } +=head2 get_user_printer + + $printer = get_user_printer(); + + Returns printer queue that is to be used for the logged in user + +=cut + +sub get_user_printer { + my $userenv = C4::Context->userenv or return; + if (my $printer = $userenv->{branchprinter}) { + return $printer; + } + my $branchname = $userenv->{branch} or return; + my $branch = GetBranchDetail($branchname) or return; + return $branch->{branchprinter}; +} -END { } # module clean-up code here (global destructor) 1; __END__ diff --git a/C4/Context.pm b/C4/Context.pm index 0a56aa8..7835497 100644 --- a/C4/Context.pm +++ b/C4/Context.pm @@ -978,7 +978,7 @@ sub userenv { =head2 set_userenv C4::Context->set_userenv($usernum, $userid, $usercnum, $userfirstname, - $usersurname, $userbranch, $userflags, $emailaddress); + $usersurname, $userbranch, $userflags, $emailaddress, $branchprinter); Establish a hash of user environment variables. diff --git a/C4/Koha.pm b/C4/Koha.pm index 26106cf..1e1a412 100644 --- a/C4/Koha.pm +++ b/C4/Koha.pm @@ -39,7 +39,6 @@ BEGIN { @EXPORT = qw( &slashifyDate &subfield_is_koha_internal_p - &GetPrinters &GetPrinter &GetItemTypes &getitemtypeinfo &GetCcodes &GetSupportName &GetSupportList @@ -600,45 +599,6 @@ sub getImageSets { return \@imagesets; } -=head2 GetPrinters - - $printers = &GetPrinters(); - @queues = keys %$printers; - -Returns information about existing printer queues. - -C<$printers> is a reference-to-hash whose keys are the print queues -defined in the printers table of the Koha database. The values are -references-to-hash, whose keys are the fields in the printers table. - -=cut - -sub GetPrinters { - my %printers; - my $dbh = C4::Context->dbh; - my $sth = $dbh->prepare("select * from printers"); - $sth->execute; - while ( my $printer = $sth->fetchrow_hashref ) { - $printers{ $printer->{'printqueue'} } = $printer; - } - return ( \%printers ); -} - -=head2 GetPrinter - - $printer = GetPrinter( $query, $printers ); - -=cut - -sub GetPrinter ($$) { - my ( $query, $printers ) = @_; # get printer for this query from printers - my $printer = $query->param('printer'); - my %cookie = $query->cookie('userenv'); - ($printer) || ( $printer = $cookie{'printer'} ) || ( $printer = '' ); - ( $printers->{$printer} ) || ( $printer = ( keys %$printers )[0] ); - return $printer; -} - =head2 getnbpages Returns the number of pages to display in a pagination bar, given the number diff --git a/C4/Printer.pm b/C4/Printer.pm new file mode 100644 index 0000000..0990bad --- /dev/null +++ b/C4/Printer.pm @@ -0,0 +1,148 @@ +#!/usr/bin/perl + +package C4::Printer; + +# Copyright 2012 Catalyst IT +# +# 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 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use strict; +use warnings; + +use C4::Context; + +use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); + +BEGIN { + $VERSION = 3.07.00.049; + require Exporter; + @ISA = qw(Exporter); + @EXPORT = qw( + ); + @EXPORT_OK = qw( + &GetPrinters &SearchPrinters &GetPrinterDetails + &AddPrinter &UpdatePrinter &DeletePrinter + ); +} + +=head1 NAME + +C4::Printer - functions that deal with printer selection + +=head1 SYNOPSIS + + use C4::Printer; + +=head1 DESCRIPTION + +This module provides functions to select printer for slips etc. + +TODO: Move SQL from admin/printers.pl to this module + +=head1 FUNCTIONS + +=head2 GetPrinters + + $printers = &GetPrinters(); + @queues = keys %$printers; + +Returns information about existing printer queues. + +C<$printers> is a reference-to-hash whose keys are the print queues +defined in the printers table of the Koha database. The values are +references-to-hash, whose keys are the fields in the printers table. + +=cut + +sub GetPrinters { + my %printers; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("select * from printers"); + $sth->execute; + while ( my $printer = $sth->fetchrow_hashref ) { + $printers{ $printer->{'printqueue'} } = $printer; + } + return ( \%printers ); +} + +=head2 SearchPrinters + + $printers = SearchPrinters( $searchstring ); + +=cut + +sub SearchPrinters { + my ($searchstring)=@_; + $searchstring .= '%'; + return C4::Context->dbh->selectall_arrayref(" + SELECT * FROM printers + WHERE printername like ? OR printqueue like ? ORDER BY printername + ", {Slice => {}}, $searchstring, $searchstring); +} + +=head2 GetPrinterDetails + + $printer_rec = GetPrinterDetails( $printqueue ); + +=cut + +sub GetPrinterDetails { + my ( $printer ) = @_; + my $dbh = C4::Context->dbh; + my $printername = $dbh->selectrow_hashref('SELECT * FROM printers WHERE printqueue = ?', undef, $printer); + return $printername; +} + +=head2 AddPrinter + + AddPrinter( $data ); + +=cut + +sub AddPrinter { + my ( $data ) = @_; + my $dbh = C4::Context->dbh; + $dbh->do("INSERT INTO printers (printername,printqueue,printtype) VALUES (?,?,?)", undef, + $data->{printername}, $data->{printqueue}, $data->{printtype}); +} + +=head2 UpdatePrinter + + UpdatePrinter( $printqueue, $data ); + +=cut + +sub UpdatePrinter { + my ( $printqueue, $data ) = @_; + my $dbh = C4::Context->dbh; + $dbh->do("UPDATE printers SET printqueue = ?, printername = ?, printtype = ? WHERE printqueue = ?", undef, + $data->{printqueue}, $data->{printername}, $data->{printtype}, $printqueue); +} + +=head2 DeletePrinter + + DeletePrinter( $printqueue ); + +=cut + +sub DeletePrinter { + my ( $printqueue ) = @_; + my $dbh = C4::Context->dbh; + $dbh->do("DELETE FROM printers WHERE printqueue = ?", undef, + $printqueue); +} + +1; diff --git a/admin/branches.pl b/admin/branches.pl index d172ea6..3671f43 100755 --- a/admin/branches.pl +++ b/admin/branches.pl @@ -45,6 +45,7 @@ use C4::Context; use C4::Output; use C4::Koha; use C4::Branch; +use C4::Printer qw(GetPrinters); # Fixed variables my $script_name = "/cgi-bin/koha/admin/branches.pl"; @@ -226,12 +227,9 @@ sub default { sub editbranchform { my ($branchcode,$innertemplate) = @_; - # initiate the scrolling-list to select the printers - my $printers = GetPrinters(); - my @printerloop; + my $data; my $oldprinter = ""; - if ($branchcode) { $data = GetBranchInfo($branchcode); $data = $data->[0]; @@ -241,15 +239,21 @@ sub editbranchform { _branch_to_template($data, $innertemplate); } - foreach my $thisprinter ( keys %$printers ) { - push @printerloop, { - value => $thisprinter, - selected => ( $oldprinter eq $printers->{$thisprinter} ), - branchprinter => $printers->{$thisprinter}->{'printqueue'}, - }; + if ( C4::Context->preference('UsePrintQueues') ) { + # initiate the scrolling-list to select the printers + my $printers = GetPrinters(); + my @printerloop; + foreach my $thisprinter ( keys %$printers ) { + push @printerloop, { + value => $thisprinter, + selected => ( $oldprinter eq $thisprinter ), + branchprinter => $printers->{$thisprinter}->{'printername'}, + }; + } + + $innertemplate->param( printerloop => \@printerloop ); } - $innertemplate->param( printerloop => \@printerloop ); # make the checkboxes..... # # We export a "categoryloop" array to the template, each element of which @@ -308,6 +312,7 @@ sub branchinfotable { my ($branchcode,$innertemplate) = @_; my $branchinfo = $branchcode ? GetBranchInfo($branchcode) : GetBranchInfo(); + my $printers = GetPrinters(); my @loop_data = (); foreach my $branch (@$branchinfo) { # @@ -367,6 +372,9 @@ sub branchinfotable { $row{'branch_name'} = $branch->{'branchname'}; $row{'branch_code'} = $branch->{'branchcode'}; $row{'value'} = $branch->{'branchcode'}; + if (my $printer = $branch->{'branchprinter'}) { + $row{'branchprintername'} = $printers->{$printer}->{'printername'}; + } push @loop_data, \%row; } diff --git a/admin/printers.pl b/admin/printers.pl index c7e7492..12e42d7 100755 --- a/admin/printers.pl +++ b/admin/printers.pl @@ -7,17 +7,17 @@ # ALGO : # this script use an $op to know what to do. # if $op is empty or none of the above values, -# - the default screen is build (with all records, or filtered datas). -# - the user can clic on add, modify or delete record. +# - the default screen is build (with all records, or filtered datas). +# - the user can clic on add, modify or delete record. # if $op=add_form -# - if primkey exists, this is a modification,so we read the $primkey record -# - builds the add/modify form +# - if primkey exists, this is a modification,so we read the $primkey record +# - builds the add/modify form # if $op=add_validate -# - the user has just send datas, so we create/modify the record +# - the user has just send datas, so we create/modify the record # if $op=delete_form -# - we show the record having primkey=$primkey and ask for deletion validation form +# - we show the record having primkey=$primkey and ask for deletion validation form # if $op=delete_confirm -# - we delete the record having primkey=$primkey +# - we delete the record having primkey=$primkey # Copyright 2000-2002 Katipo Communications @@ -43,19 +43,7 @@ use CGI; use C4::Context; use C4::Output; use C4::Auth; - -sub StringSearch { - my ($searchstring,$type)=@_; # why bother with $type if we don't use it?! - $searchstring=~ s/\'/\\\'/g; - my @data=split(' ',$searchstring); - my $sth = C4::Context->dbh->prepare(" - SELECT printername,printqueue,printtype from printers - WHERE (printername like ?) order by printername - "); - $sth->execute("$data[0]%"); - my $data=$sth->fetchall_arrayref({}); - return (scalar(@$data),$data); -} +use C4::Printer qw(GetPrinterDetails SearchPrinters AddPrinter UpdatePrinter DeletePrinter); my $input = new CGI; my $searchfield=$input->param('searchfield'); @@ -68,83 +56,95 @@ my $op = $input->param('op'); $searchfield=~ s/\,//g; my ($template, $loggedinuser, $cookie) = get_template_and_user({ - template_name => "admin/printers.tmpl", - query => $input, - type => "intranet", - authnotrequired => 0, + template_name => "admin/printers.tmpl", + query => $input, + type => "intranet", + authnotrequired => 0, flagsrequired => {parameters => 'parameters_remaining_permissions'}, - debug => 1, + debug => 1, }); -$template->param(searchfield => $searchfield, - script_name => $script_name); - #start the page and read in includes my $dbh = C4::Context->dbh; +my $list_printers = 1; ################## ADD_FORM ################################## # called by default. Used to create form to add or modify a record if ($op eq 'add_form') { - $template->param(add_form => 1); - #---- if primkey exists, it's a modify action, so read values to modify... - my $data; - if ($searchfield) { - my $sth=$dbh->prepare("SELECT printername,printqueue,printtype from printers where printername=?"); - $sth->execute($searchfield); - $data=$sth->fetchrow_hashref; - } - - $template->param(printqueue => $data->{'printqueue'}, - printtype => $data->{'printtype'}); - # END $OP eq ADD_FORM + $list_printers = 0; + $template->param(add_form => 1); + #---- if primkey exists, it's a modify action, so read values to modify... + my $data; + if ($searchfield) { + $data=GetPrinterDetails($searchfield); + } + + $template->param( + printqueue => $data->{'printqueue'}, + printername => $data->{'printername'}, + printtype => $data->{'printtype'} + ); +# END $OP eq ADD_FORM ################## ADD_VALIDATE ################################## # called by add_form, used to insert/modify data in DB } elsif ($op eq 'add_validate') { - $template->param(add_validate => 1); - if ($input->param('add')){ - my $sth=$dbh->prepare("INSERT INTO printers (printername,printqueue,printtype) VALUES (?,?,?)"); - $sth->execute($input->param('printername'),$input->param('printqueue'),$input->param('printtype')); - } else { - my $sth=$dbh->prepare("UPDATE printers SET printqueue=?,printtype=? WHERE printername=?"); - $sth->execute($input->param('printqueue'),$input->param('printtype'),$input->param('printername')); - } - # END $OP eq ADD_VALIDATE + my $params = $input->Vars; + if ($input->param('add')){ + AddPrinter($params); + } else { + UpdatePrinter($searchfield, $params); + } + $template->param(add_validate => 1); + $searchfield = ''; +# END $OP eq ADD_VALIDATE ################## DELETE_CONFIRM ################################## # called by default form, used to confirm deletion of data in DB } elsif ($op eq 'delete_confirm') { - $template->param(delete_confirm => 1); - my $sth=$dbh->prepare("select printername,printqueue,printtype from printers where printername=?"); - $sth->execute($searchfield); - my $data=$sth->fetchrow_hashref; - $template->param(printqueue => $data->{'printqueue'}, - printtype => $data->{'printtype'}); - # END $OP eq DELETE_CONFIRM + $list_printers = 0; + $template->param(delete_confirm => 1); + my $data=GetPrinterDetails($searchfield); + $template->param( + printqueue => $data->{'printqueue'}, + printtype => $data->{'printtype'}, + ); +# END $OP eq DELETE_CONFIRM ################## DELETE_CONFIRMED ################################## # called by delete_confirm, used to effectively confirm deletion of data in DB } elsif ($op eq 'delete_confirmed') { - $template->param(delete_confirmed => 1); - my $sth=$dbh->prepare("delete from printers where printername=?"); - $sth->execute($searchfield); - # END $OP eq DELETE_CONFIRMED + # XXX Delete can fail + DeletePrinter($searchfield); + $template->param(delete_confirmed => 1); + $template->param(list_printers => 1); + $searchfield = ''; +# END $OP eq DELETE_CONFIRMED ################## DEFAULT ########################################### } else { # DEFAULT - $template->param(else => 1); - my ($count,$results)=StringSearch($searchfield,'web'); - my $max = ($offset+$pagesize < $count) ? $offset+$pagesize : $count; - my @loop = (@$results)[$offset..$max]; - - $template->param(loop => \@loop); - - if ($offset>0) { - $template->param(offsetgtzero => 1, - prevpage => $offset-$pagesize); - } - if ($offset+$pagesize<$count) { - $template->param(ltcount => 1, - nextpage => $offset+$pagesize); - } - + $searchfield ||= $input->param('description') || ""; } #---- END $OP eq DEFAULT +if ($list_printers) { + $template->param(list_printers => 1); + my $results=SearchPrinters($searchfield); + my $count = $results ? scalar(@$results) : 0; + my $max = ($offset+$pagesize < $count) ? $offset+$pagesize : $count; + my @loop = (@$results)[$offset..$max-1]; + + $template->param(loop => \@loop); + + if ($offset>0) { + $template->param(offsetgtzero => 1, + prevpage => $offset-$pagesize); + } + if ($offset+$pagesize<$count) { + $template->param(ltcount => 1, + nextpage => $offset+$pagesize); + } +} + +$template->param( + searchfield => $searchfield, + script_name => $script_name +); + output_html_with_http_headers $input, $cookie, $template->output; diff --git a/circ/circulation.pl b/circ/circulation.pl index 78ac1a4..54138ef 100755 --- a/circ/circulation.pl +++ b/circ/circulation.pl @@ -29,7 +29,7 @@ use C4::Print; use C4::Auth qw/:DEFAULT get_session/; use C4::Dates qw/format_date/; use C4::Branch; # GetBranches -use C4::Koha; # GetPrinter +use C4::Koha; use C4::Circulation; use C4::Overdues qw/CheckBorrowerDebarred/; use C4::Members; @@ -67,12 +67,6 @@ if ($branch){ $session->param('branchname', GetBranchName($branch)); } -my $printer = $query->param('printer'); -if ($printer){ - # update our session so the userenv is updated - $session->param('branchprinter', $printer); -} - if (!C4::Context->userenv && !$branch){ if ($session->param('branch') eq 'NO_LIBRARY_SET'){ # no branch set we can't issue @@ -102,8 +96,6 @@ $findborrower =~ s|,| |g; my $borrowernumber = $query->param('borrowernumber'); $branch = C4::Context->userenv->{'branch'}; -$printer = C4::Context->userenv->{'branchprinter'}; - # If AutoLocation is not activated, we show the Circulation Parameters to chage settings of librarian if (C4::Context->preference("AutoLocation") != 1) { @@ -665,8 +657,6 @@ $template->param( borrowernumber => $borrowernumber, branch => $branch, branchname => GetBranchName($borrower->{'branchcode'}), - printer => $printer, - printername => $printer, firstname => $borrower->{'firstname'}, surname => $borrower->{'surname'}, showname => $borrower->{'showname'}, diff --git a/circ/returns.pl b/circ/returns.pl index 743bf18..0efa712 100755 --- a/circ/returns.pl +++ b/circ/returns.pl @@ -36,7 +36,6 @@ use C4::Context; use C4::Auth qw/:DEFAULT get_session/; use C4::Output; use C4::Circulation; -use C4::Print; use C4::Reserves; use C4::Biblio; use C4::Items; @@ -73,15 +72,10 @@ my ( $template, $librarian, $cookie ) = get_template_and_user( ##################### #Global vars my $branches = GetBranches(); -my $printers = GetPrinters(); -my $printer = C4::Context->userenv ? C4::Context->userenv->{'branchprinter'} : ""; my $overduecharges = (C4::Context->preference('finesMode') && C4::Context->preference('finesMode') ne 'off'); my $userenv_branch = C4::Context->userenv->{'branch'} || ''; -# -# Some code to handle the error if there is no branch or printer setting..... -# # Set up the item stack .... my %returneditems; @@ -601,9 +595,7 @@ foreach ( sort { $a <=> $b } keys %returneditems ) { $template->param( riloop => \@riloop, genbrname => $branches->{$userenv_branch}->{'branchname'}, - genprname => $printers->{$printer}->{'printername'}, branchname => $branches->{$userenv_branch}->{'branchname'}, - printer => $printer, errmsgloop => \@errmsgloop, exemptfine => $exemptfine, dropboxmode => $dropboxmode, diff --git a/circ/selectbranchprinter.pl b/circ/selectbranchprinter.pl index b5adcfc..67034b7 100755 --- a/circ/selectbranchprinter.pl +++ b/circ/selectbranchprinter.pl @@ -23,10 +23,10 @@ use CGI; use C4::Context; use C4::Output; -use C4::Auth qw/:DEFAULT get_session/; -use C4::Print; # GetPrinters +use C4::Auth qw/:DEFAULT get_session get_user_printer/; use C4::Koha; use C4::Branch; # GetBranches GetBranchesLoop +use C4::Printer qw(GetPrinters); # this will be the script that chooses branch and printer settings.... @@ -56,9 +56,10 @@ my $userenv_printer = C4::Context->userenv->{'branchprinter'} || ''; my @updated; # $session lddines here are doing the updating -if ($branch and $branches->{$branch}) { +my $branch_rec = $branch ? $branches->{$branch} : undef; +if ($branch_rec) { if (! $userenv_branch or $userenv_branch ne $branch ) { - my $branchname = GetBranchName($branch); + my $branchname = $branch_rec->{branchname}; $template->param(LoginBranchname => $branchname); # update template for new branch $template->param(LoginBranchcode => $branch); # update template for new branch $session->param('branchname', $branchname); # update sesssion in DB @@ -67,6 +68,8 @@ if ($branch and $branches->{$branch}) { updated_branch => 1, old_branch => $userenv_branch, }; + $printer ||= $branch_rec->{branchprinter}; + undef $userenv_printer; } # else branch the same, no update } else { $branch = $userenv_branch; # fallback value @@ -87,7 +90,7 @@ if ($printer) { }; } # else printer is the same, no update } else { - $printer = $userenv_printer; # fallback value + $printer = get_user_printer(); # fallback value } $template->param(updated => \@updated) if (scalar @updated); @@ -96,21 +99,6 @@ unless ($branches->{$branch}) { $branch = (keys %$branches)[0]; # if branch didn't really exist, then replace it w/ one that does } -my @printkeys = sort keys %$printers; -if (scalar(@printkeys) == 1 or not $printers->{$printer}) { - $printer = $printkeys[0]; # if printer didn't really exist, or there is only 1 anyway, then replace it w/ one that does -} - -my @printerloop; -foreach ( @printkeys ) { - next unless ($_); # skip printer if blank. - push @printerloop, { - selected => ( $_ eq $printer ), - name => $printers->{$_}->{'printername'}, - value => $_, - }; -} - my @recycle_loop; foreach ($query->param()) { $_ or next; # disclude blanks @@ -133,9 +121,28 @@ if (scalar @updated and not scalar @recycle_loop) { $template->param( referer => $referer, - printerloop => \@printerloop, branchloop => GetBranchesLoop($branch), recycle_loop=> \@recycle_loop, ); +if ( C4::Context->preference('UsePrintQueues') ) { + my @printkeys = keys %$printers; + if (scalar(@printkeys) == 1 or not $printers->{$printer}) { + $printer = $printkeys[0]; # if printer didn't really exist, or there is only 1 anyway, then replace it w/ one that does + } + + my @printerloop; + foreach ( @printkeys ) { + push @printerloop, { + selected => ( $_ eq $printer ), + name => $printers->{$_}->{'printername'}, + value => $_, + }; + } + + $template->param( + printerloop => \@printerloop, + ); +} + output_html_with_http_headers $query, $cookie, $template->output; diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index 91d42a2..048a7ea 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -364,10 +364,11 @@ CREATE TABLE `branches` ( -- information about your libraries or branches are st `branchurl` mediumtext, -- the URL for your library or branch's website `issuing` tinyint(4) default NULL, -- unused in Koha `branchip` varchar(15) default NULL, -- the IP address for your library or branch - `branchprinter` varchar(100) default NULL, -- unused in Koha + `branchprinter` varchar(20) default NULL, `branchnotes` mediumtext, -- notes related to your library or branch opac_info text, -- HTML that displays in OPAC PRIMARY KEY (`branchcode`) + FOREIGN KEY (branchprinter) REFERENCES printers (printqueue) ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- @@ -1570,12 +1571,13 @@ CREATE TABLE `pending_offline_operations` ( -- Table structure for table `printers` -- -DROP TABLE IF EXISTS `printers`; -CREATE TABLE `printers` ( - `printername` varchar(40) NOT NULL default '', - `printqueue` varchar(20) default NULL, - `printtype` varchar(20) default NULL, - PRIMARY KEY (`printername`) +DROP TABLE IF EXISTS printers; +CREATE TABLE printers ( + printername varchar(40) NOT NULL default '', + printqueue varchar(20) NOT NULL, + printtype varchar(20) default NULL, + PRIMARY KEY (printqueue), + UNIQUE (printername) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index 35f92cc..f21b071 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -366,6 +366,7 @@ INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ( INSERT INTO systempreferences (variable,value,explanation,type) VALUES('EnableBorrowerFiles','0','If enabled, allows librarians to upload and attach arbitrary files to a borrower record.','YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('UpdateTotalIssuesOnCirc','0','Whether to update the totalissues field in the biblio on each circ.',NULL,'YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('IntranetSlipPrinterJS','','Use this JavaScript for printing slips. Define at least function printThenClose(). For use e.g. with Firefox PlugIn jsPrintSetup, see http://jsprintsetup.mozdev.org/','','Free'); +INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('UsePrintQueues','0',NULL,NULL,'YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('OpacSuppressionByIPRange','','Restrict the suppression to IP adresses outside of the IP range','','free'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('PrefillItem','0','When a new item is added, should it be prefilled with last created item values?','','YesNo'); INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('SubfieldsToUseWhenPrefill','','Define a list of subfields to use when prefilling items (separated by space)','','Free'); diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index 25cd367..3649a78 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -5696,6 +5696,18 @@ if (C4::Context->preference("Version") < TransformToNum($DBversion)) { SetVersion($DBversion); } + + +$DBversion = "3.09.00.XXX"; +if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) { + $dbh->do("ALTER TABLE printers DROP PRIMARY KEY, MODIFY printqueue varchar(20) NOT NULL PRIMARY KEY, ADD UNIQUE (printername)"); + $dbh->do("ALTER TABLE branches MODIFY branchprinter varchar(20) NULL, ADD FOREIGN KEY (branchprinter) REFERENCES printers (printqueue) ON UPDATE CASCADE"); + $dbh->do("INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('UsePrintQueues','0',NULL,NULL,'YesNo')"); + + print "Upgrade to $DBversion done (Add borrowers.default_printqueue and 'UsePrintQueues' syspref)\n"; + SetVersion($DBversion); +} + =head1 FUNCTIONS =head2 TableExists($table) diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc index 72162d3..7da6f1f 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc @@ -59,6 +59,7 @@
    Additional parameters
      + [% IF UsePrintQueues %]
    • Network Printers
    • [% END %] [% IF ( NoZebra ) %]
    • Stop words
    • [% END %]
    • Z39.50 client targets
    • diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc index 3d71d45..7d3aa24 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc @@ -57,6 +57,9 @@ [% LoginBranchname %] [% END %] + [% IF UsePrintQueues && PrinterName %] + - [% PrinterName %] + [% END %] [% IF ( IndependantBranches ) %] [% IF ( CAN_user_management || CAN_user_editcatalogue_edit_catalogue ) %] ( Set library ) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt index 3906c46..d2e0fc5 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt @@ -101,8 +101,8 @@
      [% IF ( NoZebra ) %]
      Stop words
      Words ignored during search.
      [% END %] - + [% IF UsePrintQueues %]
      Network Printers
      +
      Printers (UNIX paths).
      [% END %]
      Z39.50 client targets
      Define which servers to query for MARC data in the integrated Z39.50 client.
      diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt index 15e8064..bcb918f 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/branches.tt @@ -143,10 +143,10 @@ tinyMCE.init({
    • Can be entered as a single IP, or a subnet such as 192.168.1.*
    • - +[% END %]
    • @@ -198,7 +198,9 @@ tinyMCE.init({ Address Properties IP - +[% IF UsePrintQueues %] + Printer +[% END %]   [% FOREACH branche IN branches %] @@ -250,9 +252,11 @@ tinyMCE.init({ [% branche.branchip %] - +[% IF UsePrintQueues %] + + [% branche.branchprintername %] + +[% END %] Edit diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref index a9ed6f7..23cd7ee 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -110,6 +110,12 @@ Circulation: yes: Do no: "Do not" - update a bibliographic record's total issues count whenever an item is issued (WARNING! This increases server load significantly; if performance is a concern, use the update_totalissues.pl cron job to update the total issues count). + - + - pref: UsePrintQueues + choices: + yes: "Use" + no: "Don't use" + - server print queues. Checkout Policy: - - pref: AllowNotForLoanOverride diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt index 7dfb80b..06fd229 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/printers.tt @@ -4,7 +4,7 @@ [% IF ( add_validate ) %] Printers › Printer added[% END %] [% IF ( delete_confirm ) %] Printers › Confirm deletion of printer '[% searchfield %]'[% END %] [% IF ( delete_confirmed ) %] Printers › Printer deleted[% END %] -[% IF ( else ) %]Printers[% END %] +[% IF ( list_printers ) %]Printers[% END %] [% INCLUDE 'doc-head-close.inc' %] [% IF ( add_form ) %] [% END %] diff --git a/koha-tmpl/opac-tmpl/prog/en/modules/search/results.tt b/koha-tmpl/opac-tmpl/prog/en/modules/search/results.tt index ab2ea12..39b5405 100644 --- a/koha-tmpl/opac-tmpl/prog/en/modules/search/results.tt +++ b/koha-tmpl/opac-tmpl/prog/en/modules/search/results.tt @@ -6,7 +6,7 @@ You did not specify any search criteria. [% END %] [% INCLUDE 'doc-head-close.inc' %] - + -- 1.7.9.5 From fridolyn.somers at biblibre.com Tue Sep 25 09:46:08 2012 From: fridolyn.somers at biblibre.com (Fridolyn SOMERS) Date: Tue, 25 Sep 2012 09:46:08 +0200 Subject: [Koha-patches] [PATCH] Bug 8443: Suggestions publication year and copyright date Message-ID: <1348559168-4685-1-git-send-email-fridolyn.somers@biblibre.com> --- C4/Acquisition.pm | 4 +- C4/Serials.pm | 2 +- C4/Suggestions.pm | 79 ++++++++++++-------- .../prog/en/modules/suggestion/suggestion.tt | 14 ++-- suggestion/suggestion.pl | 26 +++++-- 5 files changed, 77 insertions(+), 48 deletions(-) diff --git a/C4/Acquisition.pm b/C4/Acquisition.pm index 7d67e6f..a437616 100644 --- a/C4/Acquisition.pm +++ b/C4/Acquisition.pm @@ -1032,7 +1032,7 @@ Else, the upcoming July 1st is used. =item defaults entrydate to Now -The following keys are used: "biblionumber", "title", "basketno", "quantity", "notes", "biblioitemnumber", "rrp", "ecost", "gstrate", "unitprice", "subscription", "sort1", "sort2", "booksellerinvoicenumber", "listprice", "budgetdate", "purchaseordernumber", "branchcode", "booksellerinvoicenumber", "bookfundid". +The following keys are used: "biblionumber", "title", "basketno", "quantity", "notes", "biblioitemnumber", "rrp", "ecost", "gstrate", "unitprice", "subscription", "sort1", "sort2", "booksellerinvoicenumber", "listprice", "budgetdate", "purchaseordernumber", "branchcode", "booksellerinvoicenumber", "budget_id". =back @@ -1491,7 +1491,7 @@ C<@results> is an array of references-to-hash with the following keys: =item C -=item C +=item C =back diff --git a/C4/Serials.pm b/C4/Serials.pm index cedbcff..1596e8e 100644 --- a/C4/Serials.pm +++ b/C4/Serials.pm @@ -299,7 +299,7 @@ $subs = GetSubscription($subscriptionid) this function returns the subscription which has $subscriptionid as id. return : a hashref. This hash containts -subscription, subscriptionhistory, aqbudget.bookfundid, biblio.title +subscription, subscriptionhistory, aqbooksellers.name, biblio.title =cut diff --git a/C4/Suggestions.pm b/C4/Suggestions.pm index 0cb484a..610d161 100644 --- a/C4/Suggestions.pm +++ b/C4/Suggestions.pm @@ -93,7 +93,7 @@ sub SearchSuggestion { my $dbh = C4::Context->dbh; my @sql_params; my @query = ( - q{ SELECT suggestions.*, + q/SELECT suggestions.*, U1.branchcode AS branchcodesuggestedby, B1.branchname AS branchnamesuggestedby, U1.surname AS surnamesuggestedby, @@ -115,49 +115,61 @@ sub SearchSuggestion { LEFT JOIN borrowers AS U2 ON managedby=U2.borrowernumber LEFT JOIN branches AS B2 ON B2.branchcode=U2.branchcode LEFT JOIN categories AS C2 ON C2.categorycode = U2.categorycode - WHERE 1=1 - } , map { - if ( my $s = $suggestion->{$_} ) { - push @sql_params,'%'.$s.'%'; - " and suggestions.$_ like ? "; - } else { () } - } qw( title author isbn publishercode collectiontitle ) + WHERE 1=1/ ); + + # filter on biblio informations + foreach my $field ( + grep { + my $fieldname = $_; + any { $fieldname eq $_ } qw/title author isbn publishercode copyrightdate collectiontitle/ + } keys %$suggestion + ) + { + if ( $suggestion->{$field} ) { + push @sql_params, $suggestion->{$field}; + push @query, " AND suggestions.$field = ?"; + } + } + # filter on user branch my $userenv = C4::Context->userenv; if (C4::Context->preference('IndependantBranches')) { - if ($userenv) { - if (($userenv->{flags} % 2) != 1 && !$suggestion->{branchcode}){ - push @sql_params,$$userenv{branch}; - push @query,q{ and (suggestions.branchcode = ? or suggestions.branchcode ='')}; - } + if ($userenv) { + if (($userenv->{flags} % 2) != 1 && !$suggestion->{branchcode}){ + push @sql_params, $userenv->{branch}; + push @query, q/ AND (suggestions.branchcode = ? or suggestions.branchcode = '')/; } + } } - foreach my $field (grep { my $fieldname=$_; - any {$fieldname eq $_ } qw< - STATUS branchcode itemtype suggestedby managedby acceptedby - bookfundid biblionumber - >} keys %$suggestion - ) { - if ($$suggestion{$field}){ - push @sql_params,$suggestion->{$field}; - push @query, " and suggestions.$field=?"; - } + # filter on nillable fields + foreach my $field ( + grep { + my $fieldname = $_; + any { $fieldname eq $_ } qw/STATUS branchcode itemtype suggestedby managedby acceptedby budgetid biblionumber/ + } keys %$suggestion + ) + { + if ( $suggestion->{$field} ) { + push @sql_params, $suggestion->{$field}; + push @query, " AND suggestions.$field = ?"; + } else { - push @query, " and (suggestions.$field='' OR suggestions.$field IS NULL)"; + push @query, " AND (suggestions.$field = '' OR suggestions.$field IS NULL)"; } } + # filter on date fields my $today = C4::Dates->today('iso'); - - foreach ( qw( suggesteddate manageddate accepteddate ) ) { - my $from = $_ . "_from"; - my $to = $_ . "_to"; - if ($$suggestion{$from} || $$suggestion{$to}) { - push @query, " AND suggestions.suggesteddate BETWEEN '" - . (format_date_in_iso($$suggestion{$from}) || 0000-00-00) . "' AND '" . (format_date_in_iso($$suggestion{$to}) || $today) . "'"; - } + foreach my $field ( qw/suggesteddate manageddate accepteddate/ ) { + my $from = $field."_from"; + my $to = $field."_to"; + if ($suggestion->{$from} || $suggestion->{$to}) { + push @query, " AND suggestions.$field BETWEEN ? AND ?"; + push @sql_params, format_date_in_iso($suggestion->{$from}) || '0000-00-00'; + push @sql_params, format_date_in_iso($suggestion->{$to}) || $today; + } } $debug && warn "@query"; @@ -165,7 +177,8 @@ sub SearchSuggestion { $sth->execute(@sql_params); my @results; while ( my $data=$sth->fetchrow_hashref ){ - $$data{$$data{STATUS}} = 1; + # add status as field + $data->{$data->{STATUS}} = 1; push(@results,$data); } return (\@results); diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/suggestion/suggestion.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/suggestion/suggestion.tt index fb73fd1..554bc24 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/suggestion/suggestion.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/suggestion/suggestion.tt @@ -493,7 +493,7 @@ $(document).ready(function() { calcNewsuggTotal(); });
    • -
    • +
    • Suggestion information

      @@ -514,10 +514,10 @@ $(document).ready(function() { calcNewsuggTotal(); });
    • [% suggesteddate %] - + - +
    • @@ -527,10 +527,10 @@ $(document).ready(function() { calcNewsuggTotal(); });
    • [% manageddate %] - + - +
    • @@ -540,10 +540,10 @@ $(document).ready(function() { calcNewsuggTotal(); });
    • [% accepteddate %] - + - +
    • diff --git a/suggestion/suggestion.pl b/suggestion/suggestion.pl index 99f38eb..de85573 100755 --- a/suggestion/suggestion.pl +++ b/suggestion/suggestion.pl @@ -188,12 +188,24 @@ elsif ( $op eq 'show' ) { if ($op=~/else/) { $op='else'; - $displayby||="STATUS"; + $displayby||="STATUS"; # by default, organize by status delete $$suggestion_ref{'branchcode'} if($displayby eq "branchcode"); my $criteria_list=GetDistinctValues("suggestions.".$displayby); + my %criteria_dv; + foreach (@$criteria_list) { + my $criteria_key; + if ($_->{value}) { + $criteria_key = $_->{value}; + } else { + # agregate null and empty values under empty value + $criteria_key = ''; + } + my $criteria_cnt = $criteria_dv{$criteria_key} || 0; + $criteria_dv{$criteria_key} = $criteria_cnt + $_->{cnt}; + } my @allsuggestions; my $reasonsloop = GetAuthorisedValues("SUGGEST"); - foreach my $criteriumvalue ( map { $$_{'value'} } @$criteria_list ) { + foreach my $criteriumvalue (keys %criteria_dv) { # By default, display suggestions from current working branch if(not defined $branchfilter) { $$suggestion_ref{'branchcode'} = C4::Context->userenv->{'branch'}; @@ -356,16 +368,20 @@ $template->param( total => sprintf("%.2f", $$suggestion_ref{'total'}||0), ); +# lists of distinct values (without empty) for filters my %hashlists; foreach my $field ( qw(managedby acceptedby suggestedby budgetid) ) { my $values_list; - $values_list = GetDistinctValues( "suggestions." . $field ); - my @codes_list = map { + $values_list = GetDistinctValues( "suggestions.$field" ); + my @codes_list = + map { { 'code' => $$_{'value'}, 'desc' => GetCriteriumDesc( $$_{'value'}, $field ), 'selected' => ($$suggestion_ref{$field}) ? $$_{'value'} eq $$suggestion_ref{$field} : 0, } - } @$values_list; + } + grep { $$_{'value'} } + @$values_list; $hashlists{ lc($field) . "_loop" } = \@codes_list; } $template->param(%hashlists); -- 1.7.9.5 From nengard at bywatersolutions.com Tue Sep 25 17:54:41 2012 From: nengard at bywatersolutions.com (Nicole C. Engard) Date: Tue, 25 Sep 2012 11:54:41 -0400 Subject: [Koha-patches] [PATCH] Bug 7412: Follow up: Update preference to sentence Message-ID: <1348588481-2668-1-git-send-email-nengard@bywatersolutions.com> This patch updates the preference question to a sentence to follow the standard set with previous preferences. --- .../en/modules/admin/preferences/cataloguing.pref | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/cataloguing.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/cataloguing.pref index 404a04e..a6a3377 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/cataloguing.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/cataloguing.pref @@ -96,11 +96,11 @@ Cataloging: EAN13: incremental EAN-13 barcodes "OFF": not generated automatically. - - - When a new item is added, should it be prefilled with last created item values? + - When a new item is added, - pref: PrefillItem choices: - yes: the new item is prefilled with last created item values - no: the new item is not prefilled with last created item values + yes: the new item is prefilled with last created item values. + no: the new item is not prefilled with last created item values. - - Define a list of subfields to use when prefilling items (separated by space) - pref: SubfieldsToUseWhenPrefill -- 1.7.2.3 From oleonard at myacpl.org Tue Sep 25 20:33:24 2012 From: oleonard at myacpl.org (Owen Leonard) Date: Tue, 25 Sep 2012 14:33:24 -0400 Subject: [Koha-patches] [PATCH] Bug 8820 - Don't show orders table if there are no orders Message-ID: <1348598004-6284-1-git-send-email-oleonard@myacpl.org> This page removes the "basket empty" message in favor of hiding the orders table altogether when a basket has no orders. This patch also moves the "Show all details" checkbox under the "Orders" heading to associate it more closely with the area it affects. Other textual changes: - Correcting capitalization according to coding guidelines - Correcting terminology (basket group instead of basketgroup) - Replacing the "Order Details" heading with the more succinct "Orders" --- .../intranet-tmpl/prog/en/modules/acqui/basket.tt | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/basket.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/basket.tt index bd9d145..f271823 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/basket.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/basket.tt @@ -251,8 +251,8 @@

      - [% IF ( basketgroupdeliveryplace ) %]

      Basketgroup Delivery place: [% basketgroupdeliveryplace %]

      [% END %] - [% IF ( basketgroupbillingplace ) %]

      Basketgroup Billing place: [% basketgroupbillingplace %]

      [% END %] + [% IF ( basketgroupdeliveryplace ) %]

      Basket group delivery place: [% basketgroupdeliveryplace %]

      [% END %] + [% IF ( basketgroupbillingplace ) %]

      Basket group billing place: [% basketgroupbillingplace %]

      [% END %]
    [% END %]
    @@ -261,12 +261,12 @@ [% UNLESS ( delete_confirm ) %]
    + [% IF ( books_loop ) %] +

    Orders

    -

    Order Details

    - [% IF ( books_loop ) %] @@ -394,10 +394,6 @@ [% END %]
    - [% ELSE %] - - -
    Basket empty
    [% END %] [% IF ( listincgst ) %]** Vendor's listings already include tax. [% END %] @@ -478,8 +474,8 @@ - - + +
    [% END %] -- 1.7.9.5 From I.Brown at littleover.derby.sch.uk Tue Sep 25 17:14:43 2012 From: I.Brown at littleover.derby.sch.uk (Ivan Brown) Date: Tue, 25 Sep 2012 16:14:43 +0100 Subject: [Koha-patches] [PATCH] Bug 7643 - Can't upload and import zipped file of patron images Message-ID: <1348586083-4292-1-git-send-email-I.Brown@littleover.derby.sch.uk> Fixed problem with re-declaration of $filesuffix --- tools/picture-upload.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/picture-upload.pl b/tools/picture-upload.pl index bfb233e..4507fa1 100755 --- a/tools/picture-upload.pl +++ b/tools/picture-upload.pl @@ -80,7 +80,7 @@ if ( ($op eq 'Upload') && $uploadfile ) { # Case is important in these ope $debug and warn "dirname = $dirname"; my $filesuffix; if ( $uploadfilename =~ m/(\..+)$/i ) { - my $filesuffix = $1; + $filesuffix = $1; } ( $tfh, $tempfile ) = File::Temp::tempfile( SUFFIX => $filesuffix, UNLINK => 1 ); $debug and warn "tempfile = $tempfile"; -- 1.7.11.msysgit.1 [None] made the following annotations --------------------------------------------------------------------- The views expressed in this email are personal and may not necessarily reflect those of Derby City Council, unless explicitly stated otherwise This email, and any files transmitted with it, are confidential and intended solely for the use of the individual or entity to whom they are addressed. If you have received this email in error, please notify me immediately. If you are not the intended recipient of this email, you should not copy it for any purpose, or disclose its contents to any other person. Senders and recipients of e-mail should be aware that under the Data Protection Act 1998 and Freedom of Information Act 2000, the contents may have to be disclosed. --------------------------------------------------------------------- From oleonard at myacpl.org Tue Sep 25 21:45:52 2012 From: oleonard at myacpl.org (Owen Leonard) Date: Tue, 25 Sep 2012 15:45:52 -0400 Subject: [Koha-patches] [PATCH] Bug 8821 - Receive shipment page should hide inactive funds like new order form Message-ID: <1348602352-7042-1-git-send-email-oleonard@myacpl.org> This patch adapts the fund-handling code from neworderempty.pl in order to limit the display of funds by default to active ones, with the option to check a box to display all funds. To test, make sure you have both active and inactive funds. Start the process of receiving a shipment. The "fund" option in the receive shipment form should show only active funds. Checking the "show all" checkbox should allow you to choose from both active and inactive funds. --- acqui/parcels.pl | 23 +++++++++++++++----- .../intranet-tmpl/prog/en/modules/acqui/parcels.tt | 23 ++++++++++++++++---- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/acqui/parcels.pl b/acqui/parcels.pl index 4f2849d..8fbb883 100755 --- a/acqui/parcels.pl +++ b/acqui/parcels.pl @@ -161,13 +161,24 @@ if ($count_parcels) { $template->param( searchresults => $loopres, count => $count_parcels ); } -my $budgets = GetBudgets(); -my @budgets_loop; -foreach my $budget (@$budgets) { - next unless CanUserUseBudget($loggedinuser, $budget, $flags); - push @budgets_loop, $budget; +# build budget list +my $budget_loop = []; +my $budgets = GetBudgetHierarchy; +foreach my $r (@{$budgets}) { + next unless (CanUserUseBudget($loggedinuser, $r, $flags)); + if (!defined $r->{budget_amount} || $r->{budget_amount} == 0) { + next; + } + push @{$budget_loop}, { + b_id => $r->{budget_id}, + b_txt => $r->{budget_name}, + b_active => $r->{budget_period_active}, + }; } +@{$budget_loop} = + sort { uc( $a->{b_txt}) cmp uc( $b->{b_txt}) } @{$budget_loop}; + $template->param( orderby => $order, filter => $code, @@ -179,7 +190,7 @@ $template->param( shipmentdate_today => C4::Dates->new()->output(), booksellerid => $booksellerid, GST => C4::Context->preference('gist'), - budgets => \@budgets_loop, + budget_loop => $budget_loop, ); output_html_with_http_headers $input, $cookie, $template->output; diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcels.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcels.tt index 616139c..ba3fa37 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcels.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/parcels.tt @@ -12,6 +12,15 @@ var parcelst = $("#parcelst").dataTable($.extend(true, {}, dataTablesDefaults, { "sPaginationType": "four_button" } ) ); + $('#showallfunds').click(function() { + if ( $('#shipmentcost_budgetid .b_inactive').is(":visible") ) + { + $('#shipmentcost_budgetid .b_inactive').hide(); + } + else { + $('#shipmentcost_budgetid .b_inactive').show(); + } + }); }); //]]> @@ -136,13 +145,19 @@
  • - + + +
  • -- 1.7.9.5 From mtj at kohaaloha.com Wed Sep 26 09:54:32 2012 From: mtj at kohaaloha.com (Mason James) Date: Wed, 26 Sep 2012 19:54:32 +1200 Subject: [Koha-patches] [PATCH] git bz test Message-ID: <1348646072-20608-1-git-send-email-mtj@kohaaloha.com> http://bugs.koha-community.org/show_bug.cgi?id=6473 --- kohaversion.pl | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/kohaversion.pl b/kohaversion.pl index 1eefc92..4844d65 100644 --- a/kohaversion.pl +++ b/kohaversion.pl @@ -12,6 +12,7 @@ the kohaversion is divided in 4 parts : used by developers when the database changes. updatedatabase take care of the changes itself and is automatically called by Auth.pm when needed. =cut +xxxxxxxxxxx use strict; -- 1.7.2.5 From oleonard at myacpl.org Wed Sep 26 15:05:00 2012 From: oleonard at myacpl.org (Owen Leonard) Date: Wed, 26 Sep 2012 09:05:00 -0400 Subject: [Koha-patches] [PATCH] Bug 8827 - YUI CSS files no longer found on Yahoo servers Message-ID: <1348664700-7899-1-git-send-email-oleonard@myacpl.org> This patch removes the [% yuipath %] variable from the link to the YUI CSS files in the OPAC and adds a hard-coded path to the local files. To test, set your yuipath system preference to "from Yahoo's servers," clear your browser cache, and view the OPAC. The style and layout should look the same as if your yuipath preference were set to "local." --- .../opac-tmpl/prog/en/includes/doc-head-close.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/koha-tmpl/opac-tmpl/prog/en/includes/doc-head-close.inc b/koha-tmpl/opac-tmpl/prog/en/includes/doc-head-close.inc index f032b20..35d395f 100644 --- a/koha-tmpl/opac-tmpl/prog/en/includes/doc-head-close.inc +++ b/koha-tmpl/opac-tmpl/prog/en/includes/doc-head-close.inc @@ -3,8 +3,8 @@ - - + + [% SET opaclayoutstylesheet='opac.css' UNLESS opaclayoutstylesheet %] [% IF (opaclayoutstylesheet.match('^https?:|^\/')) %] -- 1.7.9.5 From nengard at bywatersolutions.com Wed Sep 26 03:32:10 2012 From: nengard at bywatersolutions.com (Nicole C. Engard) Date: Tue, 25 Sep 2012 21:32:10 -0400 Subject: [Koha-patches] [PATCH] [FOLLOW UP] Bug 7751: Decrease loan length with many holds Message-ID: <1348623130-4303-1-git-send-email-nengard@bywatersolutions.com> This patch merges the three preferences in to one sentence to make it clear that they are all linked together and to eliminate confusion that can come by having them as individual preferences. --- .../en/modules/admin/preferences/circulation.pref | 10 +++------- 1 files changed, 3 insertions(+), 7 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref index 531b878..20c37d8 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref @@ -412,17 +412,13 @@ Circulation: choices: yes: Enable no: "Don't enable" - - the reduction of loan period for items with number of holds above the threshold specified in decreaseLoanHighHoldsValue - - - - A loan should be reduced by + - the reduction of loan period by - pref: decreaseLoanHighHoldsDuration class: integer - - days, when decreaseLoanHighHoldsValue threshold is reached (if decreaseLoanHighHolds is enabled) - - - - A loan should be reduced by decreaseLoanHighHoldsDuration when + - days for items with more than - pref: decreaseLoanHighHoldsValue class: integer - - holds have been places (if decreaseLoanHighHolds is enables) + - holds. Fines Policy: - - Calculate fines based on days overdue -- 1.7.2.3 From fridolyn.somers at biblibre.com Wed Sep 26 16:47:37 2012 From: fridolyn.somers at biblibre.com (Fridolyn SOMERS) Date: Wed, 26 Sep 2012 16:47:37 +0200 Subject: [Koha-patches] [PATCH] Bug 8481: Items table is showing in opac-MARCdetail.pl but not in MARCdetail.pl Message-ID: <1348670857-11547-1-git-send-email-fridolyn.somers@biblibre.com> --- catalogue/MARCdetail.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalogue/MARCdetail.pl b/catalogue/MARCdetail.pl index 72e6709..2b967df 100755 --- a/catalogue/MARCdetail.pl +++ b/catalogue/MARCdetail.pl @@ -82,7 +82,7 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( } ); -my $record = GetMarcBiblio($biblionumber); +my $record = GetMarcBiblio($biblionumber, 1); $template->param( ocoins => GetCOinSBiblio($record) ); if ( not defined $record ) { -- 1.7.9.5 From nengard at bywatersolutions.com Wed Sep 26 18:45:40 2012 From: nengard at bywatersolutions.com (Nicole C. Engard) Date: Wed, 26 Sep 2012 12:45:40 -0400 Subject: [Koha-patches] [PATCH] Bug 8831: relabel gstrate with Tax rate Message-ID: <1348677940-2815-1-git-send-email-nengard@bywatersolutions.com> This patch changes gstrate to Tax rate to match the standard set on all other acq pages. --- .../prog/en/modules/acqui/neworderempty.tt | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/neworderempty.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/neworderempty.tt index ea044bb..6185e32 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/neworderempty.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/acqui/neworderempty.tt @@ -450,10 +450,10 @@ $(document).ready(function() [% END %]
  • [% IF ( close ) %] - gstrate: + Tax rate: [% gstrate %]% [% ELSE %] - +