Bugzilla – Attachment 59895 Details for
Bug 7317
Add an Interlibrary Loan Module to Circulation and OPAC
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
ILLModule
ILLModule.patch (text/plain), 145.93 KB, created by
Andrew Isherwood
on 2017-02-03 17:36:07 UTC
(
hide
)
Description:
ILLModule
Filename:
MIME Type:
Creator:
Andrew Isherwood
Created:
2017-02-03 17:36:07 UTC
Size:
145.93 KB
patch
obsolete
>From 5512aa211bbab94db56ee20b4776ad6832edb7eb Mon Sep 17 00:00:00 2001 >From: Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com> >Date: Fri, 3 Feb 2017 16:58:35 +0100 >Subject: [PATCH] ILLModule > >https://bugs.koha-community.org/show_bug.cgi?id=7317 >--- > Koha/Illrequest.pm | 1162 ++++++++++++++++++++ > Koha/Illrequest/Backend/Dummy/Base.pm | 576 ++++++++++ > Koha/Illrequest/Config.pm | 516 +++++++++ > Koha/Illrequestattribute.pm | 51 + > Koha/Illrequestattributes.pm | 55 + > Koha/Illrequests.pm | 99 ++ > Koha/REST/V1/Illrequests.pm | 80 ++ > api/v1/swagger/paths.json | 3 + > api/v1/swagger/paths/illrequests.json | 98 ++ > etc/koha-conf.xml | 14 + > ill/ill-requests.pl | 223 ++++ > .../intranet-tmpl/prog/en/includes/circ-menu.inc | 3 + > .../intranet-tmpl/prog/en/includes/ill-toolbar.inc | 31 + > .../prog/en/modules/ill/ill-requests.tt | 343 ++++++ > .../intranet-tmpl/prog/en/modules/intranet-main.tt | 5 + > t/db_dependent/Illrequest.t | 544 +++++++++ > t/db_dependent/Illrequest/Config.t | 344 ++++++ > t/db_dependent/Illrequest/Dummy.t | 378 +++++++ > t/db_dependent/api/v1/illrequests.t | 136 +++ > 19 files changed, 4661 insertions(+) > create mode 100644 Koha/Illrequest.pm > create mode 100644 Koha/Illrequest/Backend/Dummy/Base.pm > create mode 100644 Koha/Illrequest/Config.pm > create mode 100644 Koha/Illrequestattribute.pm > create mode 100644 Koha/Illrequestattributes.pm > create mode 100644 Koha/Illrequests.pm > create mode 100644 Koha/REST/V1/Illrequests.pm > create mode 100644 api/v1/swagger/paths/illrequests.json > create mode 100755 ill/ill-requests.pl > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/ill-toolbar.inc > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt > create mode 100644 t/db_dependent/Illrequest.t > create mode 100644 t/db_dependent/Illrequest/Config.t > create mode 100644 t/db_dependent/Illrequest/Dummy.t > create mode 100644 t/db_dependent/api/v1/illrequests.t > >diff --git a/Koha/Illrequest.pm b/Koha/Illrequest.pm >new file mode 100644 >index 0000000..2b5733e >--- /dev/null >+++ b/Koha/Illrequest.pm >@@ -0,0 +1,1162 @@ >+package Koha::Illrequest; >+ >+# Copyright PTFS Europe 2016 >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS >+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more >+# details. >+# >+# You should have received a copy of the GNU General Public License along with >+# Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin >+# Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+# use Modern::Perl; >+ >+use Clone 'clone'; >+use File::Basename qw/basename/; >+use Koha::Database; >+use Koha::Illrequestattributes; >+use Koha::Patron; >+ >+use base qw(Koha::Object); >+ >+=head1 NAME >+ >+Koha::Illrequest - Koha Illrequest Object class >+ >+=head1 (Re)Design >+ >+An ILLRequest consists of two parts; the Illrequest Koha::Object, and a series >+of related Illrequestattributes. >+ >+The former encapsulates the basic necessary information that any ILL requires >+to be usable in Koha. The latter is a set of additional properties used by >+one of the backends. >+ >+The former subsumes the legacy "Status" object. The latter remains >+encapsulated in the "Record" object. >+ >+TODO: >+ >+- Anything invoking the ->status method; annotated with: >+ + # Old use of ->status ! >+ >+=head1 API >+ >+=head2 Backend API Response Principles >+ >+All methods should return a hashref in the following format: >+ >+=item * error >+ >+This should be set to 1 if an error was encountered. >+ >+=item * status >+ >+The status should be a string from the list of statuses detailed below. >+ >+=item * message >+ >+The message is a free text field that can be passed on to the end user. >+ >+=item * value >+ >+The value returned by the method. >+ >+=over >+ >+=head2 Interface Status Messages >+ >+=over >+ >+=item * branch_address_incomplete >+ >+An interface request has determined branch address details are incomplete. >+ >+=item * cancel_success >+ >+The interface's cancel_request method was successful in cancelling the >+Illrequest using the API. >+ >+=item * cancel_fail >+ >+The interface's cancel_request method failed to cancel the Illrequest using >+the API. >+ >+=item * unavailable >+ >+The interface's request method returned saying that the desired item is not >+available for request. >+ >+=head2 Class Methods >+ >+=cut >+ >+=head3 type >+ >+=cut >+ >+sub _type { >+ return 'Illrequest'; >+} >+ >+# XXX: Method to expose DBIx accessor for illrequestattributes relationship >+sub illrequestattributes { >+ my ( $self ) = @_; >+ return Koha::Illrequestattributes->_new_from_dbic( >+ scalar $self->_result->illrequestattributes >+ ); >+} >+ >+# XXX: Method to expose DBIx accessor for borrower relationship >+sub patron { >+ my ( $self ) = @_; >+ return Koha::Patron->_new_from_dbic( >+ scalar $self->_result->borrowernumber >+ ); >+} >+ >+sub status { >+ my ( $self, $new ) = @_; >+ # Fetch old status... >+ my $old = $self->SUPER::status; >+ # If new... >+ if ( $new ) { >+ # Invoke hook, then update to $new >+ $rs = $self->backend_update_status({ >+ request => $self, >+ other => { >+ old_status => $old, >+ new_status => $new, >+ } >+ }); >+ die($rs->{message}) if $rs->{error}; >+ return $self->SUPER::status($new); >+ } >+ return $old; >+} >+ >+sub load_backend { >+ my ( $self, $backend_id ) = @_; >+ >+ my @raw = qw/Koha Illbackends/; # Base Path >+ >+ my $backend_name = $backend_id || $self->backend; >+ $location = join "/", @raw, $backend_name, "Base.pm"; # File to load >+ $backend_class = join "::", @raw, $backend_name, "Base"; # Package name >+ require $location; >+ $self->{_my_backend} = $backend_class->new({ config => $self->_config }); >+ return $self; >+} >+ >+=head3 _backend >+ >+ my $backend = $abstract->_backend($new_backend); >+ my $backend = $abstract->_backend; >+ >+Getter/Setter for our API object. >+ >+=cut >+ >+sub _backend { >+ my ( $self, $backend ) = @_; >+ $self->{_my_backend} = $backend if ( $backend ); >+ # Dynamically load our backend object, as late as possible. >+ $self->load_backend unless ( $self->{_my_backend} ); >+ return $self->{_my_backend}; >+} >+ >+=head3 _config >+ >+ my $config = $abstract->_config($config); >+ my $config = $abstract->_config; >+ >+Getter/Setter for our config object. >+ >+=cut >+ >+sub _config { >+ my ( $self, $config ) = @_; >+ $self->{_my_config} = $config if ( $config ); >+ # Load our config object, as late as possible. >+ unless ( $self->{_my_config} ) { >+ $self->{_my_config} = Koha::Illrequest::Config->new; >+ } >+ return $self->{_my_config}; >+} >+ >+=head3 metadata >+ >+=cut >+ >+sub metadata { >+ my ( $self ) = @_; >+ return {}; >+} >+ >+=head3 _core_status_graph >+ >+=cut >+ >+sub _core_status_graph { >+ my ( $self ) = @_; >+ return { >+ NEW => { >+ prev_actions => [ ], # Actions containing buttons >+ # leading to this status >+ id => 'NEW', # ID of this status >+ name => 'New request', # UI name of this status >+ ui_method_name => 'New request', # UI name of method leading >+ # to this status >+ method => 'create', # method to this status >+ next_actions => [ 'REQ', 'KILL' ], # buttons to add to all >+ # requests with this status >+ ui_method_icon => 'fa-plus', # UI Style class >+ }, >+ REQ => { >+ prev_actions => [ 'NEW', 'REQREV', 'QUEUED' ], >+ id => 'REQ', >+ name => 'Requested', >+ ui_method_name => 'Confirm request', >+ method => 'confirm', >+ next_actions => [ 'REQREV', 'CANCREQ' ], >+ ui_method_icon => 'fa-check', >+ }, >+ REQREV => { >+ prev_actions => [ 'CANCREQ', 'REQ' ], >+ id => 'REQREV', >+ name => 'Request reverted', >+ ui_method_name => 'Revert Request', >+ method => 'cancel', >+ next_actions => [ 'REQ', 'KILL' ], >+ ui_method_icon => 'fa-times', >+ }, >+ QUEUED => { >+ prev_actions => [ ], >+ id => 'QUEUED', >+ name => 'Queued request', >+ ui_method_name => 0, >+ method => 0, >+ next_actions => [ 'REQ', 'KILL' ], >+ ui_method_icon => 0, >+ }, >+ CANCREQ => { >+ prev_actions => [ 'REQ' ], >+ id => 'CANCREQ', >+ name => 'Cancellation requested', >+ ui_method_name => 0, >+ method => 0, >+ next_actions => [ 'REQREV' ], >+ ui_method_icon => 0, >+ }, >+ COMP => { >+ prev_actions => [ 'REQ' ], >+ id => 'COMP', >+ name => 'Completed', >+ ui_method_name => 0, >+ method => 0, >+ next_actions => [ ], >+ ui_method_icon => 0, >+ }, >+ KILL => { >+ prev_actions => [ 'QUEUED', 'REQREV', 'NEW' ], >+ id => 'KILL', >+ name => 0, >+ ui_method_name => 'Delete request', >+ method => 'delete', >+ next_actions => [ ], >+ ui_method_icon => 'fa-trash', >+ }, >+ }; >+} >+ >+sub _status_graph_union { >+ my ( $self, $core_status_graph, $backend_status_graph ) = @_; >+ # Create new status graph with: >+ # - all core_status_graph >+ # - for-each each backend_status_graph >+ # + add to new status graph >+ # + for each core prev_action: >+ # * locate core_status >+ # * update next_actions with additional next action. >+ # + for each core next_action: >+ # * locate core_status >+ # * update prev_actions with additional prev action >+ >+ my @core_status_ids = keys %{$core_status_graph}; >+ my $status_graph = clone($core_status_graph); >+ >+ foreach my $backend_status_key ( keys %{$backend_status_graph} ) { >+ $backend_status = $backend_status_graph->{$backend_status_key}; >+ # Add to new status graph >+ $status_graph->{$backend_status_key} = $backend_status; >+ # Update all core methods' next_actions. >+ foreach my $prev_action ( @{$backend_status->{prev_actions}} ) { >+ if ( grep $prev_action, @core_status_ids ) { >+ my @next_actions = >+ @{$status_graph->{$prev_action}->{next_actions}}; >+ push @next_actions, $backend_status_key; >+ $status_graph->{$prev_action}->{next_actions} >+ = \@next_actions; >+ } >+ } >+ # Update all core methods' prev_actions >+ foreach my $next_action ( @{$backend_status->{next_actions}} ) { >+ if ( grep $next_action, @core_status_ids ) { >+ my @prev_actions = >+ @{$status_graph->{$next_action}->{prev_actions}}; >+ push @prev_actions, $backend_status_key; >+ $status_graph->{$next_action}->{prev_actions} >+ = \@prev_actions; >+ } >+ } >+ } >+ >+ return $status_graph; >+} >+ >+### Core API methods >+ >+=head3 capabilities >+ >+ my $capabilities = $illrequest->capabilities; >+ >+Return a hashref mapping methods to operation names supported by the queried >+backend. >+ >+Example return value: >+ >+ { create => "Create Request", confirm => "Progress Request" } >+ >+=cut >+ >+sub capabilities { >+ my ( $self, $status ) = @_; >+ # Generate up to date status_graph >+ my $status_graph = $self->_status_graph_union( >+ $self->_core_status_graph, >+ $self->_backend->status_graph({ >+ request => $self, >+ other => {} >+ }) >+ ); >+ # Extract available actions from graph. >+ return $status_graph->{$status} if $status; >+ # Or return entire graph. >+ return $status_graph; >+} >+ >+sub available_backends { >+ my ( $self ) = @_; >+ my $backend_dir = $self->_config->backend_dir; >+ my @backends = (); >+ my @backends = <$backend_dir/*> if ( $backend_dir ); >+ my @backends = map { basename($_) } @backends; >+ return \@backends; >+} >+ >+sub available_actions { >+ my ( $self ) = @_; >+ my $current_action = $self->capabilities($self->status); >+ my @available_actions = map { $self->capabilities($_) } >+ @{$current_action->{next_actions}}; >+ return \@available_actions; >+} >+ >+sub backend_confirm { >+ my ( $self, $params ) = @_; >+ >+ # The backend handles setting of mandatory fields in the commit stage: >+ # - orderid >+ # - accessurl, cost (if available). >+ my $response = $self->_backend->confirm({ >+ request => $self, >+ other => $params, >+ }); >+ return $self->expandTemplate($response); >+} >+ >+sub backend_update_status { >+ my ( $self, $params ) = @_; >+ return $self->expandTemplate($self->_backend->update_status($params)); >+} >+ >+=head3 backend_cancel >+ >+ my $ILLResponse = $illRequest->backend_cancel; >+ >+The standard interface method allowing for request cancellation. >+ >+=cut >+ >+sub backend_cancel { >+ my ( $self ) = @_; >+ >+ my $result = $self->_backend->cancel({ request => $self }); >+ >+ # FIXME: API breakage: Untypical old return value (preserved below) may >+ # cause issues! >+ # return ( $self->expandTemplate($result), $self ); >+ >+ return $self->expandTemplate($result); >+} >+ >+=head3 backend_renew >+ >+ my $renew_response = $illRequest->backend_renew; >+ >+The standard interface method allowing for request renewal queries. >+ >+=cut >+ >+sub backend_renew { >+ my ( $self ) = @_; >+ return $self->expandTemplate( >+ $self->_backend->renew({ >+ request => $self, >+ }) >+ ); >+} >+ >+=head3 backend_status >+ >+ my $status_response = $illRequest->backend_status; >+ >+The standard interface method allowing for request status queries. >+ >+=cut >+ >+sub backend_status { >+ my ( $self, $params ) = @_; >+ return $self->expandTemplate( >+ $self->_backend->status({ >+ request => $self, >+ other => $params, >+ }) >+ ); >+} >+ >+=head3 backend_create >+ >+ my $create_response = $abstractILL->backend_create($params); >+ >+Return an array of Record objects created by querying our backend with >+a Search query. >+ >+In the context of the other ILL methods, this is a special method: we only >+pass it $params, as it does not yet have any other data associated with it. >+ >+=cut >+ >+sub backend_create { >+ my ( $self, $params ) = @_; >+ >+ # First perform API action, then... >+ my $args = { >+ request => $self, >+ other => $params, >+ }; >+ my $result = $self->_backend->create($args); >+ >+ # ... simple case: we're not at 'commit' stage. >+ my $stage = $result->{stage}; >+ return $self->expandTemplate($result) >+ unless ( 'commit' eq $stage || 'commit_manual' eq $stage ); >+ >+ # ... complex case: commit! >+ >+ # Do we still have space for an ILL or should we queue? >+ my $permitted = $self->check_limits( >+ { patron => $self->patron }, { librarycode => $self->branchcode } >+ ); >+ >+ # Now augment our committed request. >+ >+ # FIXME: WTF? may have to deal with manual creation here! >+ # if ( $params->{uin} ) { >+ # # API guarantees it will return UIN of backend if available. >+ # $result->{uin} = $params->{uin}; >+ # } else { >+ # # Else, considered manual creation >+ # $result->{primary_manual} = 1; >+ # } >+ >+ # FIXME: These two are for legacy purposes. Up to date branch and >+ # borrower can be found via >+ # $result->{request}->{borrower,branch}_id >+ $result->{branch} = "FIXME: Incorrect branch reference!"; >+ $result->{borrower} = "FIXME: Incorrect borrower reference!"; >+ >+ $result->{permitted} = $permitted; # Queue request? >+ >+ # FIXME: Here we should simply populate ourselves with our available data. >+ # >+ # This involves... >+ >+ # ...Updating status! >+ $self->status('QUEUED')->store unless ( $permitted ); >+ >+ # FIXME: Fix Unmediated ILLs! >+ # Handle Unmediated ILLs >+ # if ( C4::Context->preference("UnmediatedILL") && $permitted >+ # # XXX: Why && result manual? >+ # && $result->{manual} ) { >+ # # FIXME: Also carry out privilege checks >+ # my ( $response, $new_rq ) = $self->place_request; # WTF? >+ # if ( $response ) { >+ # $result->{value}->{request} = $new_rq; >+ # return $result; >+ # } else { >+ # die "Placing the request failed."; >+ # } >+ # } else { >+ # $result->{value}->{request} = $request; >+ # return $result; >+ # } >+ >+ return $self->expandTemplate($result); >+} >+ >+=head3 expandTemplate >+ >+ my $params = $abstract->expandTemplate($params); >+ >+Return a version of $PARAMS augmented with our required template path. >+ >+=cut >+ >+sub expandTemplate { >+ my ( $self, $params ) = @_; >+ my $backend = $self->_backend->name; >+ # Generate path to file to load >+ my $backend_dir = $self->_config->backend_dir; >+ my $backend_tmpl = join "/", $backend_dir, $backend; >+ my $intra_tmpl = join "/", $backend_tmpl, "intra-includes", >+ $params->{method} . ".inc"; >+ # Set file to load >+ $params->{template} = $intra_tmpl; >+ return $params; >+} >+ >+#### Abstract Imports >+ >+=head3 getCensorNotesStaff >+ >+ my $bool = $abstract->getCensorNotesStaff; >+ >+Return a boolean indicating whether we should be censoring staff notes or not, >+as determined by our configuration file. >+ >+=cut >+ >+sub getCensorNotesStaff { >+ my ( $self ) = @_; >+ my $censorship = $self->_config->censorship; >+ return $censorship->{censor_notes_staff}; >+} >+ >+=head3 getDisplayReplyDate >+ >+ my 0 = $abstract->getDisplayReplyDate; >+ >+Return a 0 if we want to hide it or 1 if not. >+ >+=cut >+ >+sub getDisplayReplyDate { >+ my ( $self ) = @_; >+ my $censorship = $self->_config->censorship; >+ # If censor is yes, don't display and vice versa. >+ return ( $censorship->{censor_reply_date} ) ? 0 : 1; >+} >+ >+=head3 getLimits >+ >+ my $limit_rules = $abstract->getLimits( { >+ type => 'brw_cat' | 'branch', >+ value => $value >+ } ); >+ >+Return the ILL limit rules for the supplied combination of type / value. >+ >+As the config may have no rules for this particular type / value combination, >+or for the default, we must define fall-back values here. >+ >+=cut >+ >+# FIXME: Needs unit tests! >+sub getLimits { >+ my ( $self, $params ) = @_; >+ my $limits = $self->_config->getLimitRules($params->{type}); >+ >+ return $limits->{$params->{value}} >+ || $limits->{default} >+ || { count => -1, method => 'active' }; >+} >+ >+=head3 getPrefix >+ >+ my $prefix = $abstract->getPrefix( { >+ brw_cat => $brw_cat, >+ branch => $branch_code, >+ } ); >+ >+Return the ILL prefix as defined by our $params: either per borrower category, >+per branch or the default. >+ >+=cut >+ >+sub getPrefix { >+ my ( $self, $params ) = @_; >+ my $brn_prefixes = $self->_config->getPrefixes('branch'); >+ my $brw_prefixes = $self->_config->getPrefixes('brw_cat'); >+ >+ return $brw_prefixes->{$params->{brw_cat}} >+ || $brn_prefixes->{$params->{branch}} >+ || $brw_prefixes->{default} >+ || ""; # "the empty prefix" >+} >+ >+#### Illrequests Imports >+ >+=head3 check_limits >+ >+ my $ok = $illRequests->check_limits( { >+ borrower => $borrower, >+ branchcode => 'branchcode' | undef, >+ } ); >+ >+Given $PARAMS, a hashref containing a $borrower object and a $branchcode, >+see whether we are still able to place ILLs. >+ >+LimitRules are derived from koha-conf.xml: >+ + default limit counts, and counting method >+ + branch specific limit counts & counting method >+ + borrower category specific limit counts & counting method >+ + err on the side of caution: a counting fail will cause fail, even if >+ the other counts passes. >+ >+=cut >+ >+# FIXME: Needs unit tests! >+sub check_limits { >+ my ( $self, $params ) = @_; >+ my $patron = $params->{patron}; >+ my $branchcode = $params->{librarycode} || $patron->branchcode; >+ >+ # Establish rules >+ my ( $branch_rules, $brw_rules ) = ( >+ $self->getLimits( { >+ type => 'branch', >+ value => $branchcode >+ } ), >+ $self->getLimits( { >+ type => 'brw_cat', >+ value => $patron->categorycode, >+ } ), >+ ); >+ # Almost there, but category code didn't quite work. >+ my ( $branch_limit, $brw_limit ) >+ = ( $branch_rules->{count}, $brw_rules->{count} ); >+ my ( $branch_count, $brw_count ) = ( >+ $self->_limit_counter( >+ $branch_rules->{method}, { branch_id => $branchcode } >+ ), >+ $self->_limit_counter( >+ $brw_rules->{method}, { borrower_id => $patron->borrowernumber } >+ ), >+ ); >+ >+ # Compare and return >+ # A limit of -1 means no limit exists. >+ # We return blocked if either branch limit or brw limit is reached. >+ if ( ( $branch_limit != -1 && $branch_limit <= $branch_count ) >+ || ( $brw_limit != -1 && $brw_limit <= $brw_count ) ) { >+ return 0; >+ } else { >+ return 1; >+ } >+} >+ >+# FIXME: Needs unit tests! >+sub _limit_counter { >+ my ( $self, $method, $target ) = @_; >+ >+ # Establish parameters of counts >+ my $where; >+ if ($method && $method eq 'annual') { >+ $where = \"YEAR(placement_date) = YEAR(NOW())"; >+ } else { # assume 'active' >+ # FIXME: This status list is ugly. There should be a method in config >+ # to return these. >+ $where = { status => { -not_in => [ 'QUEUED', 'COMP' ] } }; >+ } >+ >+ # Create resultset >+ my $resultset = Koha::Illrequests->search({ %{$target}, %{$where} }); >+ >+ # Fetch counts >+ return $resultset->count; >+} >+ >+=head3 summary >+ >+ my $summary = $illRequest->summary(); >+ >+Return a data-structure ready for JSON or other format based processing and >+display to the end-user. It returns a composit of $self's Record and Status >+`summary' methods. >+ >+=cut >+ >+# FIXME: To be handled in templates >+# FIXME: Needs Record handling sorting out >+# sub getSummary { >+# my ( $self, $params ) = @_; >+# $params->{id_prefix} = $self->id_prefix; >+# my $record = $self->record->getSummary($params); >+# my $status = $self->getStatusSummary($params); >+# my %summary = (%{$record}, %{$status}); >+# return \%summary; >+# } >+ >+# FIXME: To be handled in templates >+# sub getStatusSummary { >+# my ( $self, $params ) = @_; >+# my $return = { >+# id => [ "Request Number", $self->illrequest_id ], >+# biblionumber => [ "Biblio Number", $self->biblio_id ], >+# status => [ "Status", $self->status ], >+# reqtype => [ "Request Type", $self->medium ], >+# }; >+# # Add borrower or borrowernumber. >+# if ( $params->{brw} ) { >+# $return->{borrower} = [ "Borrower", $self->patron ] >+# } else { >+# $return->{borrowernumber} >+# = [ "Borrower Number", $self->borrower_id ]; >+# } >+# # Add ID with prefix >+# $return->{prefix_id} = [ >+# "Request Number", $params->{id_prefix} . $self->illrequest_id >+# ]; >+# return $return; >+# } >+ >+# FIXME: To be handled in templates >+# # FIXME: Missing new canonical fields! >+# sub getFullStatus { >+# my ( $self, $params ) = @_; >+ >+# my $return = { >+# id => [ "Request Number", $self->illrequest_id ], >+# biblionumber => [ "Biblio Number", $self->biblio_id ], >+# status => [ "Status", $self->status ], >+# placement_date => [ "Placement Date", $self->placed ], >+# ts => [ "Timestamp", $self->updated ], >+# completion_date => [ "Completion Date", $self->completed ], >+# reqtype => [ "Request Type", $self->medium ], >+# branch => [ "Branch", $self->branch_id ], >+# }; >+# # Add borrower or borrowernumber. >+# if ( $params->{brw} ) { >+# $return->{borrower} = [ "Borrower", $self->patron ] >+# } else { >+# $return->{borrowernumber} >+# = [ "Borrower Number", $self->borrower_id ]; >+# } >+# # Add ID with prefix >+# $return->{prefix_id} = [ >+# "Request Number", $params->{id_prefix} . $self->illrequest_id >+# ]; >+# # Add Reply Date if it's used >+# $return->{reply_date} = [ >+# $params->{display_reply_date}, $self->replied >+# ] if ( $params->{display_reply_date} ); >+# return $return; >+# } >+ >+# =head3 getFullDetails >+ >+# my $details = $illRequest->getFullDetails; >+# my $details = $illRequest->getFullDetails( { brw => 1 } ); >+ >+# Return a data-structure ready for JSON or other format based processing and >+# display to the end-user. It returns a composit of $self's Record and Status >+# `fullDetails' methods. >+ >+# =cut >+ >+# FIXME: To be handled in templates >+# # FIXME: Needs Record handling sorting out >+# sub getFullDetails { >+# my ( $self, $params ) = @_; >+# $params->{id_prefix} = $self->id_prefix; >+# $params = $self->_censor($params); >+# my $record = $self->record->getFullDetails($params); >+# my $status = $self->getFullStatus($params); >+# my %summary = (%{$record}, %{$status}); >+ >+# return \%summary; >+# } >+ >+# =head3 getForEditing >+ >+# my $partialRequest = $illRequest->getForEditing(); >+ >+# Return a data-structure ready-for-JSON-or-other-format conversion and >+# display. The data-structure will be a hashref of 2, with the first entry >+# consisting of a summary of the Record, and the second entry consisting of the >+# full Status details. >+ >+# The former is for display and should not be edited by hand. The latter can be edited. >+ >+# =cut >+ >+# FIXME: To be handled in templates >+# # FIXME: Needs Record handling sorting out >+# sub getForEditing { >+# my ( $self, $params ) = @_; >+# $params->{id_prefix} = $self->id_prefix; >+# $params = $self->_censor($params); >+# my $record = $self->record->getFullDetails($params); >+# my $status = $self->getFullStatus($params); >+ >+# return [ $record, $status ]; >+# } >+ >+# =head3 _seed_from_manual_entry >+ >+# my $request = $illRequest->_seed_from_manual_entry($params); >+ >+# When an API does not have any valid items for a customer, they may want to >+# manually enter item details. This procedure provides a way for us to create >+# an Illrequest in Koha using fields populated via Abstract's >+# `manual_entry_fields` method. >+ >+# =cut >+ >+# sub _seed_from_manual_entry { >+# my ( $self, $opts ) = @_; >+# $self->record($opts->{value}); >+ >+# # Our fields >+# $self->reqtype($opts->{"m./type"}); >+# $self->borrowernumber($opts->{borrower}); >+# $self->branch($opts->{branch}); >+# $self->ts(DateTime->now); >+# $self->placement_date(DateTime->now); >+# if ( $opts->{permitted} ) { >+# $self->status('NEW'); >+# } else { >+# $self->status('QUEUED'); >+# } >+ >+# # FIXME: No longer exists >+# # $self->save; # save to DB. >+ >+# return $self; >+# } >+ >+# =head3 _seed_from_api >+ >+# my $request = $illRequest->_seed_from_api($params); >+ >+# This seeding procedure is designed to populate an Illrequest using a search >+# result from the API in use by Abstract. >+ >+# =cut >+ >+# sub _seed_from_api { >+# my ( $self, $opts ) = @_; >+ >+# # FIXME: illcomm: We are currently creating the Record using >+# # spec.yaml, at the Record level. I think it might be better to >+# # let the API define what values to store in Record. Perhaps we >+# # still use a Spec.yaml, but make that backend defined: BLDSS >+# # might do, but NNCIPP might not. >+# # >+# # All that Record should care about is that we have key-value >+# # pairs that can dessicate and reconstitute data stored in the db. >+# $self->record($opts->{value}); >+ >+# # Our fields >+# $self->reqtype($self->record->getProperty('type')); >+# $self->borrowernumber($opts->{borrower}); >+# $self->branch($opts->{branch}); >+# $self->ts(DateTime->now); >+# $self->reply_date(DateTime->now); >+# $self->placement_date(DateTime->now); >+# if ( $opts->{permitted} ) { >+# $self->status('NEW'); >+# } else { >+# $self->status('QUEUED'); >+# } >+ >+# # FIXME: No longer exists >+# # $self->save; # save to DB. >+ >+# return $self; >+# } >+ >+# =head3 _seed_from_store >+ >+# my $request = $illRequest->_seed_from_store($params); >+ >+# Read a Record from the Koha Database. Here, we simply do a db attribute / >+# Illrequest dump and feed that dump into Record structure: column_names => >+# column values. >+ >+# =cut >+ >+# # FIXME: Unclear what we do here now: We already load our own values, using >+# # ->search or ->find. Maybe here we just need to create record? >+ >+# sub _seed_from_store { >+# my ( $self, $opts ) = @_; >+ >+# my $result_set = Koha::Database->new->schema->resultset('Illrequest'); >+# my $result = $result_set->find( $opts->{id} ); >+ >+# if ( $result ) { >+# my $linked = $result_set->search_related( >+# 'ill_request_attributes', { req_id => $opts->{id} } >+# ); >+# my $attributes = { $result->get_columns }; >+# while ( my $attribute = $linked->next ) { >+# $attributes->{ $attribute->get_column('type') } >+# = $attribute->get_column('value'); >+# } >+# $attributes->{borrower} = _borrower_from_number({ >+# number => $attributes->{borrowernumber}, strategy => 'brw' >+# }); >+# # XXX: A bit Kludgy. >+# my $tmp = $self->_abstract->build($attributes); >+# $self->record($tmp->{record}); >+# $self->status($tmp->{status}); >+# return $self; >+# } >+ >+# return 0; >+# } >+ >+=head3 requires_moderation >+ >+ my $status = $illRequest->requires_moderation; >+ >+Return the name of the status if moderation by staff is required; or 0 >+otherwise. >+ >+=cut >+ >+sub requires_moderation { >+ my ( $self ) = @_; >+ my $require_moderation = { >+ 'CANCREQ' => 'CANCREQ', >+ }; >+ return $require_moderation->{$self->status}; >+} >+ >+=head3 is_manual_request >+ >+ my $bool = $illRequest->is_manual_request; >+ >+Return 1 if this request is a manually created request, 0 if it was created >+using the API search method. >+ >+=cut >+ >+# FIXME: Needs Record handling sorting out. Update 18/01/17 I believe manual >+# should be a separate backend, and thus it can be handled just like any other >+# backend. This should be removed once confirmed working. >+sub is_manual_request { >+ my ( $self ) = @_; >+ return 1 if ( $self->record->property('manual') ); >+ return 0 >+} >+ >+=head3 place_generic_request >+ >+ my ( $result, $email ) = $illRequest->place_generic_request($params); >+ >+Create an email from $PARAMS and submit it. If we are successful, return 1 >+and the email summary. If not, then return 0 and the email summary. >+ >+=cut >+ >+sub place_generic_request { >+ my ( $self, $params ) = @_; >+ >+ my $message = Koha::Email->new; >+ $params->{to} = join("; ", @{$params->{to}}); >+ if ( !$params->{from} || $params->{from} eq '' ) { >+ die "No originator for email: ", $params->{from}; >+ } >+ if ( !$params->{replyto} || $params->{replyto} eq '') { >+ $params->{replyto} = $params->{from}; >+ } >+ if ( !$params->{sender} || $params->{sender} eq '' ) { >+ $params->{sender} = $params->{from}; >+ } >+ my %mail = $message->create_message_headers( >+ { >+ to => $params->{to}, >+ from => $params->{from}, >+ replyto => $params->{replyto}, >+ sender => $params->{sender}, >+ subject => Encode::encode( "utf8", $params->{subject} ), >+ message => Encode::encode( "utf8", $params->{message} ), >+ contenttype => 'text/plain; charset="utf8"', >+ } >+ ); >+ >+ my $result = sendmail(%mail); >+ if ( $result ) { >+ # XXX: Needs store? >+ $self->status("GENREQ"); >+ return (1, $params); >+ } else { >+ carp($Mail::Sendmail::error); >+ return (0, $params); >+ } >+ >+} >+ >+=head3 prepare_generic_request >+ >+ my $emailTemplate = $illRequest->prepare_generic_request; >+ >+Return a hashref containing 'subject'and 'body' for an email. >+ >+=cut >+ >+# FIXME: Needs Record handling sorting out >+sub prepare_generic_request { >+ my ( $self ) = @_; >+ >+ >+ my $draft->{subject} = "ILL Request"; >+ $draft->{body} = <<EOF; >+Dear Sir/Madam, >+ >+ We would like to request an interlibrary loan for title matching the >+following description: >+ >+EOF >+ >+ my $details = $self->record->getFullDetails; >+ while (my ($key, $values) = each %{$details}) { >+ if (${$values}[1]) { >+ $draft->{body} .= " - " . ${$values}[0] >+ . ": " . ${$values}[1]. "\n"; >+ } >+ } >+ >+ $draft->{body} .= <<EOF; >+ >+Please let us know if you are able to supply this to us. >+ >+Kind Regards >+EOF >+ >+ return $draft; >+} >+ >+=head3 _borrower_from_number >+ >+ my $_borrower_from_number = $illRequest->_borrower_from_number(); >+ >+Return a borrower from the given card or borrower $NUMBER. The strategy for >+resolution depends on $strategy: >+ - 'crd' means try only cardnumber, error otherwise. >+ - 'brw' means try only borrowernumber, error otherwise. >+ - else: try both and return the first match. >+ >+=cut >+ >+sub _borrower_from_number { >+ my ( $params ) = @_; >+ my $number = $params->{number}; >+ my $strategy = $params->{strategy}; >+ >+ my $patrons = Koha::Patrons->new; >+ my $brws; >+ if ( $strategy && 'crd' eq $strategy ) { >+ $brws = $patrons->search( { cardnumber => $number } ); >+ } elsif ( $strategy && 'brw' eq $strategy ) { >+ $brws = $patrons->search( { borrowernumber => $number } ); >+ } else { >+ $brws = $patrons->search( { borrowernumber => $number } ); >+ $brws = $patrons->search( { cardnumber => $number } ) >+ unless ( $brws->count == 1 ); >+ } >+ >+ # we should have a unique brw. >+ die "Invalid borrower" if ( $brws->count > 1 ); >+ >+ # Check if borrower still exists / has not been deleted. >+ if ( $brws->count == 0 ) { >+ die "Invalid borrower" if ( $params->{required} ); >+ # We allow "deleted" borrowers. >+ return { type => "borrower", deleted => 1 }; >+ } >+ return $brws->next; >+} >+ >+=head3 id_prefix >+ >+ my $prefix = $record->id_prefix; >+ >+Return the prefix appropriate for the current Illrequest as derived from the >+borrower and branch associated with this request's Status, and the config >+file. >+ >+=cut >+ >+sub id_prefix { >+ my ( $self ) = @_; >+ # FIXME: can we automatically use borrowernumber as borrower? >+ my $brw = $self->patron; >+ my $brw_cat = "dummy"; >+ $brw_cat = $brw->categorycode >+ unless ( 'HASH' eq ref($brw) && $brw->{deleted} ); >+ my $prefix = $self->getPrefix( { >+ brw_cat => $brw_cat, >+ branch => $self->branchcode, >+ } ); >+ $prefix .= "-" if ( $prefix ); >+ return $prefix; >+} >+ >+=head3 _censor >+ >+ my $params = $illRequest->_censor($params); >+ >+Return $params, modified to reflect our censorship requirements. >+ >+=cut >+ >+sub _censor { >+ my ( $self, $params ) = @_; >+ $params->{censor_notes_staff} = $self->getCensorNotesStaff >+ if ( $params->{opac} ); >+ $params->{display_reply_date} = $self->getDisplayReplyDate; >+ >+ return $params; >+} >+ >+=head1 AUTHOR >+ >+Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com> >+ >+=cut >+ >+1; >diff --git a/Koha/Illrequest/Backend/Dummy/Base.pm b/Koha/Illrequest/Backend/Dummy/Base.pm >new file mode 100644 >index 0000000..b832bf8 >--- /dev/null >+++ b/Koha/Illrequest/Backend/Dummy/Base.pm >@@ -0,0 +1,576 @@ >+package Koha::Illrequest::Backend::Dummy::Base; >+ >+# Copyright PTFS Europe 2014 >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+use DateTime; >+use Koha::Illrequestattribute; >+ >+=head1 NAME >+ >+Koha::Illrequest::Backend::Dummy - Koha ILL Backend: Dummy >+ >+=head1 SYNOPSIS >+ >+Koha ILL implementation for the "Dummy" backend. >+ >+=head1 DESCRIPTION >+ >+=head2 Overview >+ >+We will be providing the Abstract interface which requires we implement the >+following methods: >+- create -> initial placement of the request for an ILL order >+- confirm -> confirm placement of the ILL order >+- list -> list all ILL Requests currently placed with the backend >+- renew -> request a currently borrowed ILL be renewed in the backend >+- update_status -> ILL module update hook: custom actions on status update >+- cancel -> request an already 'confirm'ed ILL order be cancelled >+- status -> request the current status of a confirmed ILL order >+ >+Each of the above methods will receive the following parameter from >+Illrequest.pm: >+ >+ { >+ request => $request, >+ other => $other, >+ } >+ >+where: >+ >+- $REQUEST is the Illrequest object in Koha. It's associated >+ Illrequestattributes can be accessed through the `illrequestattributes` >+ method. >+- $OTHER is any further data, generally provided through templates .INCs >+ >+Each of the above methods should return a hashref of the following format: >+ >+ return { >+ error => 0, >+ # ^------- 0|1 to indicate an error >+ status => 'result_code', >+ # ^------- Summary of the result of the operation >+ message => 'Human readable message.', >+ # ^------- Message, possibly to be displayed >+ # Normally messages are derived from status in INCLUDE. >+ # But can be used to pass API messages to the INCLUDE. >+ method => 'list', >+ # ^------- Name of the current method invoked. >+ # Used to load the appropriate INCLUDE. >+ stage => 'commit', >+ # ^------- The current stage of this method >+ # Used by INCLUDE to determine HTML to generate. >+ # 'commit' will result in final processing by Illrequest.pm. >+ next => 'illview'|'illlist', >+ # ^------- When stage is 'commit', should we move on to ILLVIEW the >+ # current request or ILLLIST all requests. >+ value => {}, >+ # ^------- A hashref containing an arbitrary return value that this >+ # backend wants to supply to its INCLUDE. >+ }; >+ >+=head2 On the Dummy backend >+ >+The Dummy backend is rather simple, but provides correctly formatted response >+values, that other backends can model themselves after. >+ >+The code is not DRY -- primarily so that each method can be looked at in >+isolation rather than having to familiarise oneself with helper procedures. >+ >+=head1 API >+ >+=head2 Class Methods >+ >+=cut >+ >+=head3 new >+ >+ my $backend = Koha::Illrequest::Backend::Dummy->new; >+ >+=cut >+ >+sub new { >+ # -> instantiate the backend >+ my ( $class ) = @_; >+ my $self = {}; >+ bless( $self, $class ); >+ return $self; >+} >+ >+=head3 _data_store >+ >+ my $request = $self->_data_store($id); >+ my $requests = $self->_data_store; >+ >+A mock of a data store. When passed no parameters it returns all entries. >+When passed one it will return the entry matched by its id. >+ >+=cut >+ >+sub _data_store { >+ my $data = { >+ 1234 => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ }, >+ 5678 => { >+ id => 5678, >+ title => "Interlibrary loans in Koha", >+ author => "A.N. Other", >+ }, >+ }; >+ # ID search >+ my ( $self, $id ) = @_; >+ return $data->{$id} if $id; >+ >+ # Full search >+ my @entries; >+ while ( my ( $k, $v ) = each %{$data} ) { >+ push @entries, $v; >+ } >+ return \@entries; >+} >+ >+=head3 create >+ >+ my $response = $backend->create({ >+ request => $requestdetails, >+ other => $other, >+ }); >+ >+This is the initial creation of the request. Generally this stage will be >+some form of search with the backend. >+ >+By and large we will not have useful $requestdetails (borrowernumber, >+branchcode, status, etc.). >+ >+$params is simply an additional slot for any further arbitrary values to pass >+to the backend. >+ >+This is an example of a multi-stage method. >+ >+=cut >+ >+sub create { >+ # -> initial placement of the request for an ILL order >+ my ( $self, $params ) = @_; >+ my $stage = $params->{other}->{stage}; >+ if ( !$stage || $stage eq 'init' ) { >+ # We simply need our template .INC to produce a search form. >+ return { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'create', >+ stage => 'search_form', >+ value => {}, >+ }; >+ } elsif ( $stage eq 'search_form' ) { >+ # Received search query in 'other'; perform search... >+ >+ # No-op on Dummy >+ >+ # and return results. >+ return { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'create', >+ stage => 'search_results', >+ value => { >+ borrowernumber => $params->{other}->{borrowernumber}, >+ branchcode => $params->{other}->{branchcode}, >+ medium => $params->{other}->{medium}, >+ candidates => $self->_data_store, >+ } >+ }; >+ } elsif ( $stage eq 'search_results' ) { >+ # We have a selection >+ my $id = $params->{other}->{id}; >+ >+ # -> select from backend... >+ my $request_details = $self->_data_store($id); >+ >+ # ...Populate Illrequest >+ my $request = $params->{request}; >+ $request->borrower_id($params->{other}->{borrowernumber}); >+ $request->branch_id($params->{other}->{branchcode}); >+ $request->medium($params->{other}->{medium}); >+ $request->status('NEW'); >+ $request->placed(DateTime->now); >+ $request->updated(DateTime->now); >+ $request->store; >+ # ...Populate Illrequestattributes >+ while ( my ( $type, $value ) = each %{$request_details} ) { >+ Koha::Illrequestattribute->new({ >+ illrequest_id => $request->illrequest_id, >+ type => $type, >+ value => $value, >+ })->store; >+ } >+ >+ # -> create response. >+ return { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'create', >+ stage => 'commit', >+ next => 'illview', >+ value => $request_details, >+ }; >+ } else { >+ # Invalid stage, return error. >+ return { >+ error => 1, >+ status => 'unknown_stage', >+ message => '', >+ method => 'create', >+ stage => $params->{stage}, >+ value => {}, >+ }; >+ } >+} >+ >+=head3 confirm >+ >+ my $response = $backend->confirm({ >+ request => $requestdetails, >+ other => $other, >+ }); >+ >+Confirm the placement of the previously "selected" request (by using the >+'create' method). >+ >+In this case we will generally use $request. >+This will be supplied at all times through Illrequest. $other may be supplied >+using templates. >+ >+=cut >+ >+sub confirm { >+ # -> confirm placement of the ILL order >+ my ( $self, $params ) = @_; >+ # Turn Illrequestattributes into a plain hashref >+ my $value = {}; >+ my $attributes = $params->{request}->illrequestattributes; >+ foreach my $attr (@{$attributes->as_list}) { >+ $value->{$attr->type} = $attr->value; >+ }; >+ # Submit request to backend... >+ >+ # No-op for Dummy >+ >+ # ...parse response... >+ $attributes->find_or_create({ type => "status", value => "On order" }); >+ my $request = $params->{request}; >+ $request->cost("30 GBP"); >+ $request->orderid($value->{id}); >+ $request->status("REQ"); >+ $request->accessurl("URL") if $value->{url}; >+ $request->store; >+ $value->{status} = "On order"; >+ $value->{cost} = "30 GBP"; >+ # ...then return our result: >+ return { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'confirm', >+ stage => 'commit', >+ next => 'illview', >+ value => $value, >+ }; >+} >+ >+=head3 list >+ >+ my $response = $backend->list({ >+ request => $requestdetails, >+ other => $other, >+ }; >+ >+Attempt to get a list of the currently registered requests with the backend. >+ >+Parameters are optional for this request. A backend may be supplied with >+details of a specific request (or a group of requests in $other), but equally >+no parameters might be provided at all. >+ >+Normally no parameters will be provided in the 'create' stage. After this, >+parameters may be provided using templates. >+ >+=cut >+ >+sub list { >+ # -> list all ILL Requests currently placed with the backend >+ # (we ignore all params provided) >+ my ( $self, $params ) = @_; >+ my $stage = $params->{other}->{stage}; >+ if ( !$stage || $stage eq 'init' ) { >+ return { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'list', >+ stage => 'list', >+ value => { >+ 1 => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ status => "On order", >+ cost => "30 GBP", >+ }, >+ }, >+ }; >+ } elsif ( $stage eq 'list' ) { >+ return { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'list', >+ stage => 'commit', >+ value => {}, >+ }; >+ } else { >+ # Invalid stage, return error. >+ return { >+ error => 1, >+ status => 'unknown_stage', >+ message => '', >+ method => 'create', >+ stage => $params->{stage}, >+ value => {}, >+ }; >+ } >+} >+ >+=head3 renew >+ >+ my $response = $backend->renew({ >+ request => $requestdetails, >+ other => $other, >+ }); >+ >+Attempt to renew a request that was supplied through backend and is currently >+in use by us. >+ >+We will generally use $request. This will be supplied at all times through >+Illrequest. $other may be supplied using templates. >+ >+=cut >+ >+sub renew { >+ # -> request a currently borrowed ILL be renewed in the backend >+ my ( $self, $params ) = @_; >+ # Turn Illrequestattributes into a plain hashref >+ my $value = {}; >+ my $attributes = $params->{request}->illrequestattributes; >+ foreach my $attr (@{$attributes->as_list}) { >+ $value->{$attr->type} = $attr->value; >+ }; >+ # Submit request to backend, parse response... >+ my ( $error, $status, $message ) = ( 0, '', '' ); >+ if ( !$value->{status} || $value->{status} eq 'On order' ) { >+ $error = 1; >+ $status = 'not_renewed'; >+ $message = 'Order not yet delivered.'; >+ } else { >+ $value->{status} = "Renewed"; >+ } >+ # ...then return our result: >+ return { >+ error => $error, >+ status => $status, >+ message => $message, >+ method => 'renew', >+ stage => 'commit', >+ value => $value, >+ }; >+} >+ >+=head3 update_status >+ >+ my $response = $backend->update_status({ >+ request => $requestdetails, >+ other => $other, >+ }); >+ >+Our Illmodule is handling a request to update the status of an Illrequest. As >+part of this we give the backend an opportunity to perform arbitrary actions >+on update to a new status. >+ >+We will provide $request. This will be supplied at all times through >+Illrequest. $other will contain entries for the old status and the new >+status, as well as other information provided from templates. >+ >+$old_status, $new_status. >+ >+=cut >+ >+sub update_status { >+ # -> ILL module update hook: custom actions on status update >+ my ( $self, $params ) = @_; >+ # Turn Illrequestattributes into a plain hashref >+ my $value = {}; >+ my $attributes = $params->{request}->illrequestattributes; >+ foreach my $attr (@{$attributes->as_list}) { >+ $value->{$attr->type} = $attr->value; >+ }; >+ # Submit request to backend, parse response... >+ my ( $error, $status, $message ) = (0, '', ''); >+ my $old = $params->{other}->{old_status}; >+ my $new = $params->{other}->{new_status}; >+ if ( !$new || $new eq 'ERR' ) { >+ ( $error, $status, $message ) = ( >+ 1, 'failed_update_hook', >+ 'Fake reason for failing to perform update operation.' >+ ); >+ } >+ return { >+ error => $error, >+ status => $status, >+ message => $message, >+ method => 'update_status', >+ stage => 'commit', >+ value => $value, >+ }; >+} >+ >+=head3 cancel >+ >+ my $response = $backend->cancel({ >+ request => $requestdetails, >+ other => $other, >+ }); >+ >+We will attempt to cancel a request that was confirmed. >+ >+We will generally use $request. This will be supplied at all times through >+Illrequest. $other may be supplied using templates. >+ >+=cut >+ >+sub cancel { >+ # -> request an already 'confirm'ed ILL order be cancelled >+ my ( $self, $params ) = @_; >+ # Turn Illrequestattributes into a plain hashref >+ my $value = {}; >+ my $attributes = $params->{request}->illrequestattributes; >+ foreach my $attr (@{$attributes->as_list}) { >+ $value->{$attr->type} = $attr->value; >+ }; >+ # Submit request to backend, parse response... >+ my ( $error, $status, $message ) = (0, '', ''); >+ if ( !$value->{status} ) { >+ ( $error, $status, $message ) = ( >+ 1, 'unknown_request', 'Cannot cancel an unknown request.' >+ ); >+ } else { >+ $attributes->find({ type => "status" })->delete; >+ $params->{request}->status("REQREV"); >+ $params->{request}->cost(undef); >+ $params->{request}->orderid(undef); >+ $params->{request}->store; >+ } >+ return { >+ error => $error, >+ status => $status, >+ message => $message, >+ method => 'cancel', >+ stage => 'commit', >+ value => $value, >+ }; >+} >+ >+=head3 status >+ >+ my $response = $backend->create({ >+ request => $requestdetails, >+ other => $other, >+ }); >+ >+We will try to retrieve the status of a specific request. >+ >+We will generally use $request. This will be supplied at all times through >+Illrequest. $other may be supplied using templates. >+ >+=cut >+ >+sub status { >+ # -> request the current status of a confirmed ILL order >+ my ( $self, $params ) = @_; >+ my $value = {}; >+ my $stage = $params->{other}->{stage}; >+ my ( $error, $status, $message ) = (0, '', ''); >+ if ( !$stage || $stage eq 'init' ) { >+ # Generate status result >+ # Turn Illrequestattributes into a plain hashref >+ my $attributes = $params->{request}->illrequestattributes; >+ foreach my $attr (@{$attributes->as_list}) { >+ $value->{$attr->type} = $attr->value; >+ } >+ ; >+ # Submit request to backend, parse response... >+ if ( !$value->{status} ) { >+ ( $error, $status, $message ) = ( >+ 1, 'unknown_request', 'Cannot query status of an unknown request.' >+ ); >+ } >+ return { >+ error => $error, >+ status => $status, >+ message => $message, >+ method => 'status', >+ stage => 'status', >+ value => $value, >+ }; >+ >+ } elsif ( $stage eq 'status') { >+ # No more to do for method. Return to illlist. >+ return { >+ error => $error, >+ status => $status, >+ message => $message, >+ method => 'status', >+ stage => 'commit', >+ next => 'illlist', >+ value => {}, >+ }; >+ >+ } else { >+ # Invalid stage, return error. >+ return { >+ error => 1, >+ status => 'unknown_stage', >+ message => '', >+ method => 'create', >+ stage => $params->{stage}, >+ value => {}, >+ }; >+ } >+} >+ >+=head1 AUTHOR >+ >+Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com> >+ >+=cut >+ >+1; >diff --git a/Koha/Illrequest/Config.pm b/Koha/Illrequest/Config.pm >new file mode 100644 >index 0000000..87264b8 >--- /dev/null >+++ b/Koha/Illrequest/Config.pm >@@ -0,0 +1,516 @@ >+package Koha::Illrequest::Config; >+ >+# Copyright 2013,2014 PTFS Europe Ltd >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+use C4::Context; >+ >+=head1 NAME >+ >+Koha::Illrequest::Config - Koha ILL Configuration Object >+ >+=head1 SYNOPSIS >+ >+Object-oriented class that giving access to the illconfig data derived >+from ill/config.yaml. >+ >+=head1 DESCRIPTION >+ >+Config object providing abstract representation of the expected XML >+returned by ILL API. >+ >+In particular the config object uses a YAML file, whose path is >+defined by <illconfig> in koha-conf.xml. That YAML file provides the >+data structure exposed in this object. >+ >+By default the configured data structure complies with fields used by >+the British Library Interlibrary Loan DSS API. >+ >+The config file also provides mappings for Record Object accessors. >+ >+FIXME: illcomm: In general Config should be split into two parts: >+those that are high-level and those that are backend specific. The >+latter should not get specific accessors, but rather a generic >+accessor, that takes a setting name as string parameter. >+ >+=head1 API >+ >+=head2 Class Methods >+ >+=head3 new >+ >+ my $config = Koha::Illrequest::Config->new(); >+ >+Create a new Koha::Illrequest::Config object, with mapping data loaded from the >+ILL configuration file. >+ >+=cut >+ >+sub new { >+ my ( $class, $test ) = @_; >+ my $self = {}; >+ >+ $self->{configuration} = _load_configuration( >+ C4::Context->config("interlibrary_loans"), >+ C4::Context->preference("UnmediatedILL") >+ ) unless ( $test ); >+ >+ bless $self, $class; >+ >+ return $self; >+} >+ >+=head3 backend >+ >+ $backend = $config->backend($name); >+ $backend = $config->backend; >+ >+Standard setter/accessor for our backend. >+ >+=cut >+ >+sub backend { >+ my ( $self, $new ) = @_; >+ $self->{configuration}->{backend} = $new if $new; >+ return $self->{configuration}->{backend}; >+} >+ >+=head3 backend_dir >+ >+ $backend_dir = $config->backend_dir($new_path); >+ $backend_dir = $config->backend_dir; >+ >+Standard setter/accessor for our backend_directory. >+ >+=cut >+ >+sub backend_dir { >+ my ( $self, $new ) = @_; >+ $self->{configuration}->{backend_directory} = $new if $new; >+ return $self->{configuration}->{backend_directory}; >+} >+ >+=head3 limits >+ >+ $limits = $config->limits($limitshash); >+ $limits = $config->limits; >+ >+Standard setter/accessor for our limits. No parsing is performed on >+$LIMITSHASH, so caution should be exercised when using this setter. >+ >+=cut >+ >+sub limits { >+ my ( $self, $new ) = @_; >+ $self->{configuration}->{limits} = $new if $new; >+ return $self->{configuration}->{limits}; >+} >+ >+=head3 getPrefixes >+ >+ my $prefixes = $config->getPrefixes('brw_cat' | 'branch'); >+ >+Return the prefix for ILLs defined by our config. >+ >+=cut >+ >+sub getPrefixes { >+ my ( $self, $type ) = @_; >+ die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' ); >+ my $values = $self->{configuration}->{prefixes}->{$type}; >+ $values->{default} = $self->{configuration}->{prefixes}->{default}; >+ return $values; >+} >+ >+=head3 getLibraryPrivileges >+ >+ my $privileges= $config->getLibraryPrivileges; >+ >+Return the LibraryPrivilege definitions defined by our config. >+ >+=cut >+ >+sub getLibraryPrivileges { >+ my ( $self ) = @_; >+ my $values= $self->{configuration}->{library_privileges}->{branch} || {}; >+ $values->{default} = >+ $self->{configuration}->{library_privileges}->{default}; >+ return $values; >+} >+ >+=head3 getLimitRules >+ >+ my $rules = $config->getLimitRules('brw_cat' | 'branch') >+ >+Return the hash of ILL limit rules defined by our config. >+ >+=cut >+ >+sub getLimitRules { >+ my ( $self, $type ) = @_; >+ die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' ); >+ my $values = $self->{configuration}->{limits}->{$type}; >+ $values->{default} = $self->{configuration}->{limits}->{default}; >+ return $values; >+} >+ >+=head3 getDigitalRecipients >+ >+ my $recipient_rules= $config->getDigitalRecipients('brw_cat' | 'branch'); >+ >+Return the hash of digital_recipient settings defined by our config. >+ >+=cut >+ >+sub getDigitalRecipients { >+ my ( $self, $type ) = @_; >+ die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' ); >+ my $values = $self->{configuration}->{digital_recipients}->{$type}; >+ $values->{default} = >+ $self->{configuration}->{digital_recipients}->{default}; >+ return $values; >+} >+ >+=head3 getDefaultFormats >+ >+ my $defaultFormat = $config->getLimitRules('brw_cat' | 'branch') >+ >+Return the hash of ILL default formats defined by our config. >+ >+=cut >+ >+sub getDefaultFormats { >+ my ( $self, $type ) = @_; >+ die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' ); >+ my $values = $self->{configuration}->{default_formats}->{$type}; >+ $values->{default} = $self->{configuration}->{default_formats}->{default}; >+ return $values; >+} >+ >+=head3 getCredentials >+ >+ my $credentials = $config->getCredentials($branchCode); >+ >+Fetch the best-fit credentials: if we have credentials for $branchCode, use >+those; otherwise fall back on default credentials. If neither can be found, >+simply populate application details, and populate key details with 0. >+ >+=cut >+ >+sub getCredentials { >+ my ( $self, $branchCode ) = @_; >+ my $creds = $self->{configuration}->{credentials} >+ || die "We have no credentials defined. Please check koha-conf.xml."; >+ >+ my $exact = { api_key => 0, api_auth => 0 }; >+ if ( $branchCode && $creds->{api_keys}->{$branchCode} ) { >+ $exact = $creds->{api_keys}->{$branchCode} >+ } elsif ( $creds->{api_keys}->{default} ) { >+ $exact = $creds->{api_keys}->{default}; >+ } >+ >+ return { >+ api_key => $exact->{api_key}, >+ api_key_auth => $exact->{api_auth}, >+ api_application => $creds->{api_application}->{key}, >+ api_application_auth => $creds->{api_application}->{auth}, >+ }; >+} >+ >+=head3 censorship >+ >+ my $censoredValues = $config->censorship($hash); >+ my $censoredValues = $config->censorship; >+ >+Standard setter/accessor for our limits. No parsing is performed on $HASH, so >+caution should be exercised when using this setter. >+ >+Return our censorship values for the OPAC as loaded from the koha-conf.xml, or >+the fallback value (no censorship). >+ >+=cut >+ >+sub censorship { >+ my ( $self, $new ) = @_; >+ $self->{configuration}->{censorship} = $new if $new; >+ return $self->{configuration}->{censorship}; >+} >+ >+=head3 getApiUrl >+ >+ my $api_url = $config->getApiUrl; >+ >+Return the url for the api configured by koha-conf.xml, or the fall-back url. >+ >+=cut >+ >+sub getApiUrl { >+ my ( $self ) = @_; >+ return $self->{configuration}->{api_url}; >+} >+ >+=head3 getApiSpecFile >+ >+ my $api_spec_file = $config->getApiSpecFile; >+ >+Return the path pointing to the API specfile, if it indeed exists, from by >+koha-conf.xml, or 0. >+ >+=cut >+ >+sub getApiSpecFile { >+ my ( $self ) = @_; >+ return $self->{configuration}->{spec_file} || 0; >+} >+ >+=head3 _load_configuration >+ >+ my $configuration = $config->_load_configuration($config_from_xml); >+ >+Read the configuration values passed as the parameter, and populate a hashref >+suitable for use with these. >+ >+A key task performed here is the parsing of the input in the configuration >+file to ensure we have only valid input there. >+ >+=cut >+ >+sub _load_configuration { >+ my ( $from_xml, $unmediated ) = @_; >+ my $xml_config = $from_xml->{configuration}; >+ my $xml_api_url = $from_xml->{api_url}; >+ my $xml_backend_dir = $from_xml->{backend_directory}; >+ >+ # Input validation >+ die "CONFIGURATION has not been defined in koha-conf.xml." >+ unless ( ref($xml_config) eq "HASH" ); >+ die "APPLICATION has not been defined in koha-conf.xml." >+ unless ( ref($from_xml->{application}) eq "HASH" ); >+ >+ # Default data structure to be returned >+ my $configuration = { >+ backend_directory => $xml_backend_dir, >+ api_url => $xml_api_url || 'http://apitest.bldss.bl.uk', >+ spec_file => $from_xml->{api_specification}, >+ censorship => { >+ censor_notes_staff => 0, >+ censor_reply_date => 0, >+ }, >+ credentials => { >+ api_application => {}, >+ api_keys => {}, >+ }, >+ library_privileges => {}, >+ limits => {}, >+ default_formats => {}, >+ digital_recipients => {}, >+ prefixes => {}, >+ }; >+ >+ # Per Branch Configuration >+ my $branches = $xml_config->{branch}; >+ if ( ref($branches) eq "ARRAY" ) { >+ # Multiple branch overrides defined >+ map { >+ _load_unit_config({ >+ unit => $_, >+ id => $_->{code}, >+ config => $configuration, >+ type => 'branch' >+ }) >+ } @{$branches}; >+ } elsif ( ref($branches) eq "HASH" ) { >+ # Single branch override defined >+ _load_unit_config({ >+ unit => $branches, >+ id => $branches->{code}, >+ config => $configuration, >+ type => 'branch' >+ }); >+ } >+ >+ # Per Borrower Category Configuration >+ my $brw_cats = $xml_config->{borrower_category}; >+ if ( ref($brw_cats) eq "ARRAY" ) { >+ # Multiple borrower category overrides defined >+ map { >+ _load_unit_config({ >+ unit => $_, >+ id => $_->{code}, >+ config => $configuration, >+ type => 'brw_cat' >+ }) >+ } @{$brw_cats}; >+ } elsif ( ref($brw_cats) eq "HASH" ) { >+ # Single branch override defined >+ _load_unit_config({ >+ unit => $brw_cats, >+ id => $brw_cats->{code}, >+ config => $configuration, >+ type => 'brw_cat' >+ }); >+ } >+ >+ # Default Configuration >+ _load_unit_config({ >+ unit => $xml_config, >+ id => 'default', >+ config => $configuration >+ }); >+ >+ # Application key & auth >+ $configuration->{credentials}->{api_application} = { >+ key => $from_xml->{application}->{key}, >+ auth => $from_xml->{application}->{auth}, >+ }; >+ >+ # Censorship >+ my $staff_comments = $from_xml->{staff_request_comments}; >+ $configuration->{censorship}->{censor_notes_staff} = 1 >+ if ( $staff_comments && 'hide' eq $staff_comments ); >+ my $reply_date = $from_xml->{reply_date}; >+ if ( 'hide' eq $reply_date ) { >+ $configuration->{censorship}->{censor_reply_date} = 1; >+ } else { >+ $configuration->{censorship}->{censor_reply_date} = $reply_date; >+ } >+ >+ die "No DEFAULT_FORMATS has been defined in koha-conf.xml, but UNMEDIATEDILL is active." >+ if ( $unmediated && !$configuration->{default_formats}->{default} ); >+ >+ return $configuration; >+} >+ >+=head3 _load_unit_config >+ >+ my $configuration->{part} = _load_unit_config($params); >+ >+$PARAMS is a hashref with the following elements: >+- unit: the part of the configuration we are parsing. >+- id: the name within which we will store the parsed unit in config. >+- config: the configuration we are augmenting. >+- type: the type config unit we are parsing. Assumed to be 'default'. >+ >+Read `unit', and augment `config' with these under `id'. >+ >+This is a helper for _load_configuration. >+ >+A key task performed here is the parsing of the input in the configuration >+file to ensure we have only valid input there. >+ >+=cut >+ >+sub _load_unit_config { >+ my ( $params ) = @_; >+ my $unit = $params->{unit}; >+ my $id = $params->{id}; >+ my $config = $params->{config}; >+ my $type = $params->{type}; >+ return $config unless $id; >+ >+ if ( $unit->{api_key} && $unit->{api_auth} ) { >+ $config->{credentials}->{api_keys}->{$id} = { >+ api_key => $unit->{api_key}, >+ api_auth => $unit->{api_auth}, >+ }; >+ } >+ # Add request_limit rules. >+ # METHOD := 'annual' || 'active' >+ # COUNT := x >= -1 >+ if ( ref $unit->{request_limit} eq 'HASH' ) { >+ my $method = $unit->{request_limit}->{method}; >+ my $count = $unit->{request_limit}->{count}; >+ if ( 'default' eq $id ) { >+ $config->{limits}->{$id}->{method} = $method >+ if ( $method && ( 'annual' eq $method || 'active' eq $method ) ); >+ $config->{limits}->{$id}->{count} = $count >+ if ( $count && ( -1 <= $count ) ); >+ } else { >+ $config->{limits}->{$type}->{$id}->{method} = $method >+ if ( $method && ( 'annual' eq $method || 'active' eq $method ) ); >+ $config->{limits}->{$type}->{$id}->{count} = $count >+ if ( $count && ( -1 <= $count ) ); >+ } >+ } >+ >+ # Add library_privilege rules (only per branch). >+ # LIBRARY_PRIVILEGE := 1 || 0 >+ unless ( $type && 'brw_cat' eq $type ) { >+ if ( $unit->{library_privilege} ) { >+ if ( 'default' eq $id ) { >+ $config->{library_privileges}->{$id} = >+ $unit->{library_privilege}; >+ } else { >+ $config->{library_privileges}->{branch}->{$id} = >+ $unit->{library_privilege}; >+ } >+ } >+ } >+ >+ # Add prefix rules. >+ # PREFIX := string >+ if ( $unit->{prefix} ) { >+ if ( 'default' eq $id ) { >+ $config->{prefixes}->{$id} = $unit->{prefix}; >+ } else { >+ $config->{prefixes}->{$type}->{$id} = $unit->{prefix}; >+ } >+ } >+ >+ # Add digital_recipient rules. >+ # DIGITAL_RECIPIENT := borrower || branch (defaults to borrower) >+ if ( $unit->{digital_recipient} ) { >+ if ( 'default' eq $id ) { >+ $config->{digital_recipients}->{$id} = $unit->{digital_recipient}; >+ } else { >+ $config->{digital_recipients}->{$type}->{$id} = >+ $unit->{digital_recipient}; >+ } >+ } >+ >+ # Add default_formats types. >+ # FORMAT && QUALITY && QUANTITY && SERVICE && SPEED := x >= 0 >+ if ( ref $unit->{default_formats} eq 'HASH' ) { >+ my @fields = qw(format quality quantity service speed); >+ if ( 'default' eq $id ) { >+ for ( @fields ) { >+ my $val = $unit->{default_formats}->{$_}; >+ die "Invalid default_formats: '$_' missing" >+ unless $val; >+ $config->{default_formats}->{$id}->{$_} = $val; >+ } >+ } else { >+ for ( @fields ) { >+ my $val = $unit->{default_formats}->{$_}; >+ die "Invalid default_formats: '$_' missing" >+ unless $val; >+ $config->{default_formats}->{$type}->{$id}->{$_} = $val; >+ } >+ } >+ } >+ >+ return $config; >+} >+ >+=head1 AUTHOR >+ >+Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com> >+ >+=cut >+ >+1; >diff --git a/Koha/Illrequestattribute.pm b/Koha/Illrequestattribute.pm >new file mode 100644 >index 0000000..16bc086 >--- /dev/null >+++ b/Koha/Illrequestattribute.pm >@@ -0,0 +1,51 @@ >+package Koha::Illrequestattribute; >+ >+# Copyright PTFS Europe 2016 >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS >+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more >+# details. >+# >+# You should have received a copy of the GNU General Public License along with >+# Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin >+# Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Koha::Database; >+ >+use base qw(Koha::Object); >+ >+=head1 NAME >+ >+Koha::Illrequestattribute - Koha Illrequestattribute Object class >+ >+=head1 API >+ >+=head2 Class Methods >+ >+=cut >+ >+=head3 type >+ >+=cut >+ >+sub _type { >+ return 'Illrequestattribute'; >+} >+ >+=head1 AUTHOR >+ >+Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com> >+ >+=cut >+ >+1; >diff --git a/Koha/Illrequestattributes.pm b/Koha/Illrequestattributes.pm >new file mode 100644 >index 0000000..e05fa6e >--- /dev/null >+++ b/Koha/Illrequestattributes.pm >@@ -0,0 +1,55 @@ >+package Koha::Illrequestattributes; >+ >+# Copyright PTFS Europe 2016 >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Koha::Database; >+use Koha::Illrequestattribute; >+ >+use base qw(Koha::Objects); >+ >+=head1 NAME >+ >+Koha::Illrequestattributes - Koha Illrequestattributes Object class >+ >+=head1 API >+ >+=head2 Class Methods >+ >+=cut >+ >+=head3 type >+ >+=cut >+ >+sub _type { >+ return 'Illrequestattribute'; >+} >+ >+sub object_class { >+ return 'Koha::Illrequestattribute'; >+} >+ >+=head1 AUTHOR >+ >+Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com> >+ >+=cut >+ >+1; >diff --git a/Koha/Illrequests.pm b/Koha/Illrequests.pm >new file mode 100644 >index 0000000..4bfd865 >--- /dev/null >+++ b/Koha/Illrequests.pm >@@ -0,0 +1,99 @@ >+package Koha::Illrequests; >+ >+# Copyright PTFS Europe 2016 >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Koha::Database; >+use Koha::Illrequest; >+use Koha::Illrequest::Config; >+ >+use base qw(Koha::Objects); >+ >+=head1 NAME >+ >+Koha::Illrequests - Koha Illrequests Object class >+ >+=head1 API >+ >+=head2 Class Methods >+ >+=cut >+ >+=head3 type >+ >+=cut >+ >+sub _type { >+ return 'Illrequest'; >+} >+ >+sub object_class { >+ return 'Koha::Illrequest'; >+} >+ >+##### To be implemented Facade >+ >+=head3 new >+ >+ my $illRequests = Koha::Illrequests->new(); >+ >+Create an ILLREQUESTS object, a singleton through which we can interact with >+ILLREQUEST objects stored in the database or search for ILL candidates at API >+backends. >+ >+=cut >+ >+sub new { >+ my ( $class, $attributes ) = @_; >+ >+ my $self = $class->SUPER::new($class, $attributes); >+ >+ # FIXME: Unless we can inject config into Illrequest objects, strip this >+ # out. >+ my $config = Koha::Illrequest::Config->new; # <- Necessary >+ $self->{_config} = $config; # <- Necessary >+ >+ return $self; >+} >+ >+=head3 search_incomplete >+ >+ my $requests = $illRequests->search_incomplete; >+ >+A specialised version of `search`, returning all requests currently >+not considered completed. >+ >+=cut >+ >+sub search_incomplete { >+ my ( $self ) = @_; >+ $self->search( { >+ status => [ >+ -and => { '!=', 'COMP' }, { '!=', 'GENCOMP' } >+ ] >+ } ); >+} >+ >+=head1 AUTHOR >+ >+Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com> >+ >+=cut >+ >+1; >diff --git a/Koha/REST/V1/Illrequests.pm b/Koha/REST/V1/Illrequests.pm >new file mode 100644 >index 0000000..564d648 >--- /dev/null >+++ b/Koha/REST/V1/Illrequests.pm >@@ -0,0 +1,80 @@ >+package Koha::REST::V1::Illrequests; >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+ >+use Koha::Illrequests; >+use Koha::Library; >+ >+sub list { >+ my ($c, $args, $cb) = @_; >+ >+ my $filter; >+ $args //= {}; >+ my $output = []; >+ >+ # Create a hash where all keys are embedded values >+ # Enables easy checking >+ my %embed; >+ if (defined $args->{embed}) { >+ %embed = map { $_ => 1 } @{$args->{embed}}; >+ delete $args->{embed}; >+ } >+ >+ for my $filter_param ( keys %$args ) { >+ my @values = split(/,/, $args->{$filter_param}); >+ $filter->{$filter_param} = \@values; >+ } >+ >+ my $requests = Koha::Illrequests->search($filter); >+ >+ while (my $request = $requests->next) { >+ my $unblessed = $request->unblessed; >+ # Add the request's id_prefix >+ $unblessed->{id_prefix} = $request->id_prefix; >+ # Augment the request response with patron details >+ # if appropriate >+ if (defined $embed{patron}) { >+ my $patron = $request->patron; >+ $unblessed->{patron} = { >+ firstname => $patron->firstname, >+ surname => $patron->surname, >+ cardnumber => $patron->cardnumber >+ }; >+ } >+ # Augment the request response with status details >+ # if appropriate >+ if (defined $embed{capabilities}) { >+ $unblessed->{capabilities} = $request->capabilities; >+ } >+ # Augment the request response with branch details >+ # if appropriate >+ if (defined $embed{branch}) { >+ $unblessed->{branch} = Koha::Libraries->find( >+ $request->branchcode >+ )->unblessed; >+ } >+ push @{$output}, $unblessed >+ } >+ >+ return $c->$cb( $output, 200 ); >+ >+} >+ >+1; >diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json >index b1fcf40..608d717 100644 >--- a/api/v1/swagger/paths.json >+++ b/api/v1/swagger/paths.json >@@ -16,5 +16,8 @@ > }, > "/patrons/{borrowernumber}": { > "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}" >+ }, >+ "/illrequests": { >+ "$ref": "paths/illrequests.json#/~1illrequests" > } > } >diff --git a/api/v1/swagger/paths/illrequests.json b/api/v1/swagger/paths/illrequests.json >new file mode 100644 >index 0000000..ddafd80 >--- /dev/null >+++ b/api/v1/swagger/paths/illrequests.json >@@ -0,0 +1,98 @@ >+{ >+ "/illrequests": { >+ "get": { >+ "x-mojo-controller": "Koha::REST::V1::Illrequests", >+ "operationId": "list", >+ "tags": ["illrequests"], >+ "parameters": [{ >+ "name": "embed", >+ "in": "query", >+ "description": "Additional objects that should be embedded in the response", >+ "required": false, >+ "type": "array", >+ "collectionFormat": "csv", >+ "items": { >+ "type": "string", >+ "enum": [ >+ "patron", >+ "branch", >+ "capabilities" >+ ] >+ } >+ }, { >+ "name": "backend", >+ "in": "query", >+ "description": "The name of a ILL backend", >+ "required": false, >+ "type": "string" >+ }, { >+ "name": "orderid", >+ "in": "query", >+ "description": "The order ID of a request", >+ "required": false, >+ "type": "string" >+ }, { >+ "name": "biblio_id", >+ "in": "query", >+ "description": "The biblio ID associated with a request", >+ "required": false, >+ "type": "integer" >+ }, { >+ "name": "borrower_id", >+ "in": "query", >+ "description": "The borrower ID associated with a request", >+ "required": false, >+ "type": "integer" >+ }, { >+ "name": "completed", >+ "in": "query", >+ "description": "The date the request was considered completed", >+ "required": false, >+ "type": "string" >+ }, { >+ "name": "status", >+ "in": "query", >+ "description": "A full status string e.g. REQREV", >+ "required": false, >+ "type": "string" >+ }, { >+ "name": "medium", >+ "in": "query", >+ "description": "The medium of the requested item", >+ "required": false, >+ "type": "string" >+ }, { >+ "name": "updated", >+ "in": "query", >+ "description": "The last updated date of the request", >+ "required": false, >+ "type": "string" >+ }, { >+ "name": "placed", >+ "in": "query", >+ "description": "The date the request was placed", >+ "required": false, >+ "type": "string" >+ }, { >+ "name": "branch_id", >+ "in": "query", >+ "description": "The ID of the pickup branch", >+ "required": false, >+ "type": "string" >+ }], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "OK" >+ } >+ }, >+ "x-koha-authorization": { >+ "permissions": { >+ "borrowers": "1" >+ } >+ } >+ } >+ } >+} >diff --git a/etc/koha-conf.xml b/etc/koha-conf.xml >index 1e5c799..6dd08b4 100644 >--- a/etc/koha-conf.xml >+++ b/etc/koha-conf.xml >@@ -149,5 +149,19 @@ __PAZPAR2_TOGGLE_XML_POST__ > <plack_max_requests>50</plack_max_requests> > <plack_workers>2</plack_workers> > >+ <interlibrary_loans> >+ <!-- Path to where Illbackends are located on the system >+ - This setting should normally not be touched --> >+ <backend_directory>__PERL_MODULE_DIR__/Koha/Illbackends</backend_directory> >+ <!-- How should we treat staff comments? >+ - hide: don't show in OPAC >+ - show: show in OPAC --> >+ <staff_request_comments>hide</staff_request_comments> >+ <!-- How should we treat the reply_date field? >+ - hide: don't show this field in the UI >+ - any other string: show, with this label --> >+ <reply_date>hide</reply_date> >+ </interlibrary_loans> >+ > </config> > </yazgfs> >diff --git a/ill/ill-requests.pl b/ill/ill-requests.pl >new file mode 100755 >index 0000000..840ec78 >--- /dev/null >+++ b/ill/ill-requests.pl >@@ -0,0 +1,223 @@ >+#!/usr/bin/perl >+ >+# Copyright 2013 PTFS-Europe Ltd and Mark Gavillet >+# Copyright 2014 PTFS-Europe Ltd >+# >+# This file is part of Koha. >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use CGI; >+ >+use C4::Auth; >+use C4::Output; >+use Koha::AuthorisedValues; >+use Koha::Illrequests; >+use Koha::Libraries; >+ >+my $cgi = CGI->new; >+my $illRequests = Koha::Illrequests->new; >+ >+# Grab all passed data >+# 'our' since Plack changes the scoping >+# of 'my' >+our $params = $cgi->Vars(); >+ >+my $op = $params->{method}; >+ >+my ( $template, $patronnumber, $cookie ) = get_template_and_user( { >+ template_name => 'ill/ill-requests.tt', >+ query => $cgi, >+ type => 'intranet', >+ flagsrequired => { ill => '*' }, >+} ); >+ >+if ( $op eq 'illview' ) { >+ # View the details of an ILL >+ my $request = Koha::Illrequests->find($params->{illrequest_id}); >+ >+ $template->param( >+ request => $request >+ ); >+ >+} elsif ( $op eq 'create' ) { >+ # We're in the process of creating a request >+ my $request = Koha::Illrequest->new >+ ->load_backend($params->{backend}); >+ my $backend_result = $request->backend_create($params); >+ $template->param( >+ whole => $backend_result, >+ request => $request >+ ); >+ handle_commit_maybe($backend_result, $request); >+ >+} elsif ( $op eq 'confirm' ) { >+ # Backend 'confirm' method >+ # confirm requires a specific request, so first, find it. >+ my $request = Koha::Illrequests->find($params->{illrequest_id}); >+ my $backend_result = $request->backend_confirm($params); >+ $template->param( whole => $backend_result ); >+ >+ # handle special commit rules & update type >+ handle_commit_maybe($backend_result, $request); >+ >+} elsif ( $op eq 'status' ) { >+ # Backend 'status' method >+ # status requires a specific request, so first, find it. >+ my $request = Koha::Illrequests->find($params->{illrequest_id}); >+ my $backend_result = $request->backend_status($params); >+ $template->param( >+ whole => $backend_result, >+ request => $request, >+ ); >+ >+ # handle special commit rules & update type >+ handle_commit_maybe($backend_result, $request); >+ >+} elsif ( $op eq 'cancel' ) { >+ # Backend 'cancel' method >+ # cancel requires a specific request, so first, find it. >+ my $request = Koha::Illrequests->find($params->{illrequest_id}); >+ my $backend_result = $request->backend_cancel($params); >+ $template->param( >+ whole => $backend_result, >+ request => $request, >+ ); >+ >+ # handle special commit rules & update type >+ handle_commit_maybe($backend_result, $request); >+ >+} elsif ( $op eq 'edit_action' ) { >+ # Handle edits to the Illrequest object. >+ # (not the Illrequestattributes) >+ # We simulate the API for backend requests for uniformity. >+ # So, init: >+ my $request = Koha::Illrequests->find($params->{illrequest_id}); >+ if ( !$params->{stage} ) { >+ my $backend_result = { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'edit_action', >+ stage => 'init', >+ next => '', >+ value => {} >+ }; >+ $template->param( >+ whole => $backend_result, >+ request => $request >+ ); >+ } else { >+ # Commit: >+ # Save the changes >+ $request->borrowernumber($params->{borrowernumber}); >+ $request->biblio_id($params->{biblio_id}); >+ $request->branchcode($params->{branchcode}); >+ $request->notesopac($params->{notesopac}); >+ $request->notesstaff($params->{notesstaff}); >+ $request->store; >+ my $backend_result = { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'edit_action', >+ stage => 'commit', >+ next => 'illlist', >+ value => {} >+ }; >+ handle_commit_maybe($backend_result, $request); >+ } >+ >+} elsif ( $op eq 'moderate_action' ) { >+ # Moderate action is required for an ILL submodule / syspref. >+ # Currently still needs to be implemented. >+ redirect_to_list(); >+ >+} elsif ( $op eq 'delete_action' ) { >+ # We simply delete the request... >+ my $request = Koha::Illrequests->find($params->{illrequest_id})->delete; >+ # ... then return to list view. >+ redirect_to_list(); >+ >+} elsif ( $op eq 'manual_action' ) { >+ my %flds = $cgi->Vars; >+ my $flds = {}; >+ while ( my ( $k, $v ) = each %flds ) { >+ $flds->{$k} = $v if ( 'query_type' ne $k or 'query_value' ); >+ } >+ # Rename borrower key >+ $flds->{borrower} = $flds->{brw}; >+ my $request = $illRequests->request($flds); >+ # push @{$requests}, $request if $request; # Obsolete >+ $op = 'request'; >+ >+} else { >+ # type eq 'illlist' || default action >+ # Display all current ILLs >+ my $requests = $illRequests->search(); >+ >+ $template->param( >+ requests => $requests >+ ); >+ >+ # If we receive a pre-filter, make it available to the template >+ my $possible_filters = ['borrowernumber']; >+ my $active_filters = []; >+ foreach my $filter(@{$possible_filters}) { >+ if ($params->{$filter}) { >+ push @{$active_filters}, >+ { name => $filter, value => $params->{$filter}}; >+ } >+ } >+ if (scalar @{$active_filters} > 0) { >+ $template->param( >+ prefilters => $active_filters >+ ); >+ } >+} >+ >+# Get a list of backends >+my $ir = Koha::Illrequest->new; >+ >+$template->param( >+ backends => $ir->available_backends, >+ media => [ "Book", "Article", "Journal" ], >+ query_type => $op, >+ branches => Koha::Libraries->search->unblessed, >+ here_link => "/cgi-bin/koha/ill/ill-requests.pl" >+); >+ >+output_html_with_http_headers( $cgi, $cookie, $template->output ); >+ >+sub handle_commit_maybe { >+ my ( $backend_result, $request ) = @_; >+ # We need to special case 'commit' >+ if ( $backend_result->{stage} eq 'commit' ) { >+ if ( $backend_result->{next} eq 'illview' ) { >+ # Redirect to a view of the newly created request >+ print $cgi->redirect( >+ '/cgi-bin/koha/ill/ill-requests.pl?method=illview&illrequest_id='. >+ $request->id >+ ); >+ } else { >+ # Redirect to a requests list view >+ redirect_to_list(); >+ } >+ } >+} >+ >+sub redirect_to_list { >+ print $cgi->redirect('/cgi-bin/koha/ill/ill-requests.pl'); >+} >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc >index 083a88c..9169675 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc >@@ -108,5 +108,8 @@ > [% IF Koha.Preference('HouseboundModule') %] > [% IF houseboundview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% borrowernumber %]">Housebound</a></li> > [% END %] >+ [% IF Koha.Preference('ILLModule') %] >+ <li><a href="/cgi-bin/koha/ill/ill-requests.pl?borrowernumber=[% borrowernumber %]">Interlibrary loans</a></li> >+ [% END %] > </ul></div> > [% END %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/ill-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/ill-toolbar.inc >new file mode 100644 >index 0000000..9d91aac >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/ill-toolbar.inc >@@ -0,0 +1,31 @@ >+[% USE Koha %] >+<script type="text/javascript"> >+//<![CDATA[ >+$(document).ready(function(){ >+}); >+//]]> >+</script> >+ >+[% IF Koha.Preference('ILLModule ') %] >+<div id="toolbar" class="btn-toolbar"> >+ [% IF backends.size > 1 %] >+ <div class="dropdown btn-group"> >+ <button class="btn btn-small dropdown-toggle" type="button" id="ill-backend-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> >+ <i class="fa fa-plus"></i> New ILL request <span class="caret"></span> >+ </button> >+ <ul class="dropdown-menu" aria-labelledby="ill-backend-dropdown"> >+ [% FOREACH backend IN backends %] >+ <li><a href="/cgi-bin/koha/ill/ill-requests.pl?method=create&backend=[% backend %]">[% backend %]</a></li> >+ [% END %] >+ </ul> >+ </div> >+ [% ELSE %] >+ <a id="ill-new" class="btn btn-small" href="/cgi-bin/koha/ill/ill-requests.pl?method=create&backend=[% backends.0 %]"> >+ <i class="fa fa-plus"></i> New ILL request >+ </a> >+ [% END %] >+ <a id="ill-list" class="btn btn-small btn-group" href="/cgi-bin/koha/ill/ill-requests.pl"> >+ <i class="fa fa-list"></i> List requests >+ </a> >+</div> >+[% END %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt >new file mode 100644 >index 0000000..881d2bc >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt >@@ -0,0 +1,343 @@ >+[% USE AuthorisedValues %] >+[% USE Branches %] >+ >+[% INCLUDE 'doc-head-open.inc' %] >+<title>Koha › ILL requests ›</title> >+[% INCLUDE 'doc-head-close.inc' %] >+<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.tablesorter.min.js"></script> >+<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script> >+<link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/datatables.css" /> >+[% INCLUDE 'datatables.inc' %] >+[% INCLUDE 'calendar.inc' %] >+<script type="text/javascript"> >+//<![CDATA[ >+ $(document).ready(function() { >+ var myTable = $("#ill-requests").DataTable($.extend(true, {}, dataTablesDefaults, { >+ "aoColumnDefs": [ // Last column shouldn't be sortable or searchable >+ { "aTargets": [ -1 ], "bSortable": false, "bSearchable": false }, >+ ], >+ "aaSorting": [[ 4, "desc" ]], // Default sort, updated descending >+ "iDisplayLength": 10, // 10 results per page >+ "sPaginationType": "full_numbers", // Pagination display >+ "sAjaxDataProp": "", // Data is in the root object of the response >+ "deferRender": true, // Improve performance on big datasets >+ "ajax": { >+ url: "/api/v1/illrequests?embed=patron,capabilities,branch", >+ cache: true >+ }, >+ "columns": [ >+ { >+ render: function(data, type, row) { >+ return '<a title="View borrower details" ' + >+ 'href="/cgi-bin/koha/members/moremember.pl?borrowernumber='+row.borrowernumber+'">' + >+ row.patron.firstname + ' ' + row.patron.surname + >+ '</a>'; >+ }, >+ className: 'borrower_name' >+ }, >+ { >+ data: 'borrowernumber', >+ className: 'borrowernumber' >+ }, >+ { >+ data: 'biblio_id', >+ className: 'biblio_id' >+ }, >+ { >+ data: 'branch.branchname', >+ className: 'branch_name' >+ }, >+ { >+ render: function(data, type, row) { >+ return row.capabilities[row.status].name; >+ }, >+ className: 'status' >+ }, >+ { >+ data: 'updated', >+ className: 'updated' >+ }, >+ { >+ data: 'medium', >+ className: 'medium' >+ }, >+ { >+ data: 'cost', >+ className: 'cost' >+ }, >+ { >+ render: function(data, type, row) { >+ return row.id_prefix + row.illrequest_id; >+ }, >+ className: 'request_id' >+ }, >+ { >+ data: null, >+ render: function(data, type, row) { >+ return '<a class="btn btn-mini" ' + >+ 'href="/cgi-bin/koha/ill/ill-requests.pl?method=illview&illrequest_id=' + >+ row.illrequest_id + >+ '">Manage request</a>' + >+ '</div>' >+ } >+ } >+ ] >+ })); >+ var filters = $('#ill-requests').data(); >+ if (typeof filters !== 'undefined') { >+ var filterNames = Object.keys(filters).filter( >+ function(thisData) { >+ return thisData.match(/^filter/); >+ } >+ ); >+ filterNames.forEach(function(thisFilter) { >+ var filterClass = "." + toColumnName(thisFilter); >+ var regex = '^'+filters[thisFilter]+'$'; >+ myTable.columns(filterClass).search(regex, true, false).draw(); >+ }); >+ myTable.draw(); >+ } >+ >+ function toColumnName(myVal) { >+ return myVal >+ .replace(/^filter/, '') >+ .replace(/([A-Z])/g, "_$1") >+ .replace(/^_/,'').toLowerCase(); >+ }; >+ >+ }); >+//]]> >+</script> >+</head> >+ >+<body id="acq_suggestion" class="acq"> >+[% INCLUDE 'header.inc' %] >+[% INCLUDE 'cat-search.inc' %] >+ >+<div id="breadcrumbs"> >+ <a href="/cgi-bin/koha/mainpage.pl">Home</a> › >+ [% IF query_type == 'create' %] >+ <a href=[% parent %]>ILL requests</a> › New request >+ [% ELSIF query_type == 'status' %] >+ <a href=[% parent %]>ILL requests</a> › Status >+ [% ELSE %] >+ ILL requests >+ [% END %] >+</div> >+ >+<div id="doc3" class="yui-t2"> >+<div id="bd"> >+ <div id="yui-main"> >+ <div class="yui-b"> >+ [% INCLUDE 'ill-toolbar.inc' %] >+ >+ [% IF whole.error %] >+ <h1>Error performing operation</h1> >+ <!-- Dispatch on Status --> >+ <p>We encountered an error:</p> >+ <ol> >+ <li>[% whole.status %]</li> >+ <li>[% whole.message %]</li> >+ </ol> >+ [% END %] >+ >+ [% IF query_type == 'create' %] >+ <h1>New ILL request</h1> >+ [% INCLUDE $whole.template %] >+ >+ [% ELSIF query_type == 'confirm' %] >+ <h1>Confirm ILL request</h1> >+ [% INCLUDE $whole.template %] >+ >+ [% ELSIF query_type == 'status' %] >+ <h1>View current status of an ILL request</h1> >+ [% INCLUDE $whole.template %] >+ >+ [% ELSIF query_type == 'edit_action' %] >+ <form method="POST" action=[% here_link %]> >+ <fieldset class="rows"> >+ <legend>Request details</legend> >+ <ol> >+ <li class="borrowernumber"> >+ <label for="borrowernumber">Borrower number:</label> >+ <input name="borrowernumber" id="borrowernumber" type="text" value="[% request.borrowernumber %]"> >+ </li> >+ <li class="biblio_id"> >+ <label for="biblio_id" class="biblio_id">Biblio number:</label> >+ <input name="biblio_id" id="biblio_id" type="text" value="[% request.biblio_id %]" /> >+ </li> >+ <li class="branchcode"> >+ <label class="branchcode">Branch:</label> >+ <select name="branchcode" id="branch"> >+ <option value=""></option> >+ [% FOREACH branch IN branches %] >+ [% IF ( branch.branchcode == request.branchcode ) %] >+ <option value="[% branch.branchcode %]" selected="selected"> >+ [% branch.branchname %] >+ </option> >+ [% ELSE %] >+ <option value="[% branch.branchcode %]"> >+ [% branch.branchname %] >+ </option> >+ [% END %] >+ [% END %] >+ </select> >+ </li> >+ <li class="status"> >+ <label class="status">Status:</label> >+ [% stat = request.status %] >+ [% request.capabilities.$stat.name %] >+ </li> >+ <li class="updated"> >+ <label class="updated">Last updated:</label> >+ [% request.updated %] >+ </li> >+ <li class="medium"> >+ <label class="medium">Request type:</label> >+ [% request.medium %] >+ </li> >+ <li class="cost"> >+ <label class="cost">Cost:</label> >+ [% request.cost %] >+ </li> >+ <li class="req_id"> >+ <label class="req_id">Request number:</label> >+ [% request.id_prefix _ request.illrequest_id %] >+ </li> >+ <li class="notesstaff"> >+ <label class="notesstaff">Staff notes:</label> >+ <textarea name="notesstaff" id="notesstaff" rows="5">[% request.notesstaff %]</textarea> >+ </li> >+ <li class="notesopac"> >+ <label class="notesopac">Opac notes:</label> >+ <textarea name="notesopac" id="notesopac" rows="5">[% request.notesopac %]</textarea> >+ </li> >+ <ol> >+ [% FOREACH attr IN request.illrequestattributes %] >+ <li class="[% attr.type %]"> >+ <label class="[% attr.type %]">[% attr.type %]</label> >+ [% attr.value %] >+ </li> >+ [% END %] >+ </ol> >+ </ol> >+ </fieldset> >+ <input type="hidden" value="edit_action" name="method"/> >+ <input type="hidden" value="form" name="stage"/> >+ <input type="hidden" value="[% request.illrequest_id %]" name="illrequest_id"/> >+ <input type="submit" value="submit"/> >+ </form> >+ >+ [% ELSIF query_type == 'illview' %] >+ [% actions = request.available_actions %] >+ [% capabilities = request.capabilities %] >+ [% req_status = request.status %] >+ <h1>Manage ILL request</h1> >+ <div id="toolbar" class="btn-toolbar"> >+ <a title="Edit request" id="ill-toolbar-btn-edit-action" class="btn btn-small" href="/cgi-bin/koha/ill/ill-requests.pl?method=edit_action&illrequest_id=[% request.illrequest_id %]"> >+ Edit request >+ </a> >+ [% FOREACH action IN actions %] >+ [% IF action.method != 0 %] >+ <a title="[% action.ui_method_name %]" id="ill-toolbar-btn-[% action.id | lower %]" class="btn btn-small" href="/cgi-bin/koha/ill/ill-requests.pl?method=[% action.method %]&illrequest_id=[% request.illrequest_id %]"> >+ <span class="fa [% action.ui_method_icon %]"></span> >+ [% action.ui_method_name %] >+ </a> >+ [% END %] >+ [% END %] >+ </div> >+ <fieldset class="rows" style="float: none;"> >+ <ol> >+ <li class="borrowernumber"> >+ <label class="borrowernumber">Borrower:</label> >+ [% borrowerlink = "/cgi-bin/koha/members/moremember.pl" >+ _ "?borrowernumber=" _ brw.borrowernumber %] >+ <a href="[% borrowerlink %]" title="View borrower details"> >+ [% request.patron.firstname _ " " >+ _ request.patron.surname _ " [" >+ _ request.patron.cardnumber >+ _ "]" %] >+ </a> >+ </li> >+ <li class="biblio_id"> >+ <label class="biblio_id">Biblio number:</label> >+ [% request.biblio_id %] >+ </li> >+ <li class="branchcode"> >+ <label class="branchcode">Branch:</label> >+ [% Branches.GetName(request.branchcode) %] >+ </li> >+ <li class="status"> >+ <label class="status">Status:</label> >+ [% capabilities.$req_status.name %] >+ </li> >+ <li class="updated"> >+ <label class="updated">Last updated:</label> >+ [% request.updated %] >+ </li> >+ <li class="medium"> >+ <label class="medium">Request type:</label> >+ [% request.medium %] >+ </li> >+ <li class="cost"> >+ <label class="cost">Cost:</label> >+ [% request.cost %] >+ </li> >+ <li class="req_id"> >+ <label class="req_id">Request number:</label> >+ [% request.id_prefix _ request.illrequest_id %] >+ </li> >+ <li class="notesstaff"> >+ <label class="notes_staff">Staff notes:</label> >+ <pre>[% request.notesstaff %]</pre> >+ </li> >+ <li class="notesopac"> >+ <label class="notes_opac">Notes:</label> >+ <pre>[% request.notesopac %]</pre> >+ </li> >+ <ol> >+ [% FOREACH attr IN request.illrequestattributes %] >+ <li class="[% attr.type %]"> >+ <label class="[% attr.type %]">[% attr.type %]</label> >+ [% attr.value %] >+ </li> >+ [% END %] >+ </ol> >+ </ol> >+ </fieldset> >+ </div> >+ >+ [% ELSE %] >+ <!-- illlist --> >+ <h1>View ILL requests</h1> >+ <div id="results"> >+ <h2>Details for all requests</h2> >+ <table >+ [% FOREACH filter IN prefilters %] >+ data-filter-[% filter.name %]="[% filter.value %]" >+ [% END %] >+ id="ill-requests"> >+ <thead> >+ <tr> >+ <th id="borrower_name">Borrower:</th> >+ <th id="borrowernumber">Borrower number:</th> >+ <th id="biblio_id">Biblio number:</th> >+ <th id="branchcode">Branch:</th> >+ <th id="status">Status:</th> >+ <th id="updated">Last updated:</th> >+ <th id="medium">Request type:</th> >+ <th id="cost">Cost:</th> >+ <th id="req_id">Request number:</th> >+ <th id="link">Manage request:</th> >+ </tr> >+ </thead> >+ <tbody> >+ </tbody> >+ </table> >+ </div> >+ [% END %] >+ >+ </div> >+</div> >+ >+[% INCLUDE 'intranet-bottom.inc' %] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt >index c34c9ce..8b6c557 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt >@@ -63,6 +63,11 @@ > <li> > <a class="icon_general icon_authorities" href="/cgi-bin/koha/authorities/authorities-home.pl">Authorities</a> > </li> >+ [% IF Koha.Preference('ILLModule') %] >+ <li> >+ <a class="icon_general icon_ill" href="/cgi-bin/koha/ill/ill-requests.pl">ILL requests</a> >+ </li> >+ [% END %] > </ul> > </div><!-- /area-list-left --> > </div><!-- /yui-u first --> >diff --git a/t/db_dependent/Illrequest.t b/t/db_dependent/Illrequest.t >new file mode 100644 >index 0000000..5fd5f0b >--- /dev/null >+++ b/t/db_dependent/Illrequest.t >@@ -0,0 +1,544 @@ >+#!/usr/bin/perl >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 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 Modern::Perl; >+ >+use File::Basename qw/basename/; >+use Koha::Database; >+use Koha::Illrequestattributes; >+use Koha::Patrons; >+use t::lib::TestBuilder; >+ >+use Test::More tests => 44; >+ >+# We want to test the Koha IllRequest object. At its core it's a simple >+# Koha::Object, mapping to the ill_request table. >+# >+# This object will supersede the Status object in ILLModule. >+# >+# We must ensure perfect backward compatibility between the current model and >+# the Status less model. >+ >+use_ok('Koha::Illrequest'); >+use_ok('Koha::Illrequests'); >+ >+my $schema = Koha::Database->new->schema; >+$schema->storage->txn_begin; >+ >+my $builder = t::lib::TestBuilder->new; >+ >+my $patron = $builder->build({ source => 'Borrower' }); >+my $branch = $builder->build({ source => 'Branch' }); >+ >+my $illRequest = $builder->build({ >+ source => 'Illrequest', >+ value => { >+ borrowernumber => $patron->{borrowernumber}, >+ branch => $branch->{branchcode}, >+ biblionumber => 0, >+ status => 'NEW', >+ completion_date => 0, >+ reqtype => 'book', >+ } >+}); >+ >+my $illObject = Koha::Illrequests->find($illRequest->{illrequest_id}); >+ >+isa_ok($illObject, "Koha::Illrequest"); >+ >+# Test delete works correctly. >+my $illRequestDelete = $builder->build({ >+ source => 'Illrequest', >+ value => { >+ borrowernumber => $patron->{borrowernumber}, >+ branch => $branch->{branchcode}, >+ biblionumber => 0, >+ status => 'NEW', >+ completion_date => 0, >+ reqtype => 'book', >+ } >+}); >+sub ill_req_search { >+ return Koha::Illrequestattributes->search({ >+ illrequest_id => $illRequestDelete->{illrequest_id} >+ })->count; >+} >+ >+is(ill_req_search, 0, "Correctly not found matching Illrequestattributes."); >+# XXX: For some reason test builder can't build Illrequestattributes. >+my $illReqAttr = Koha::Illrequestattribute->new({ >+ illrequest_id => $illRequestDelete->{illrequest_id}, >+ type => "test", >+ value => "Hello World" >+})->store; >+is(ill_req_search, 1, "We have found a matching Illrequestattribute."); >+ >+Koha::Illrequests->find($illRequestDelete->{illrequest_id})->delete; >+is( >+ Koha::Illrequests->find($illRequestDelete->{illrequest_id}), >+ undef, >+ "Correctly deleted Illrequest." >+); >+is(ill_req_search, 0, "Correctly deleted Illrequestattributes."); >+ >+# Test Accessing of related records. >+ >+# # TODO the conclusion from being able to use one_to_many? we no longer need >+# # the Record object: simply pass the `ill_request_attributes` resultset >+# # whenever one would pass a Record. >+ >+my $illRequest2 = $builder->build({ >+ source => 'Illrequest', >+ value => { >+ borrower_id => $patron->{borrowernumber}, >+ branch_id => $branch->{branchcode}, >+ biblio_id => 0, >+ status => 'NEW', >+ completed => 0, >+ medium => 'book', >+ } >+}); >+my $illReqAttr2 = Koha::Illrequestattribute->new({ >+ illrequest_id => $illRequest2->{illrequest_id}, >+ type => "test2", >+ value => "Hello World" >+})->store; >+my $illReqAttr3 = Koha::Illrequestattribute->new({ >+ illrequest_id => $illRequest2->{illrequest_id}, >+ type => "test3", >+ value => "Hello Space" >+})->store; >+ >+my $illRequestAttributes = Koha::Illrequests >+ ->find($illRequest2->{illrequest_id})->illrequestattributes; >+ >+isa_ok($illRequestAttributes, "Koha::Illrequestattributes"); >+ >+is($illRequestAttributes->count, 2, "Able to search related."); >+ >+# Test loading of 'Config'. >+ >+my $rqConfigTest = Koha::Illrequest->new({ >+ borrower_id => $patron->{borrowernumber}, >+ branch_id => $branch->{branchcode}, >+}); >+ >+isa_ok($rqConfigTest->_config, "Koha::Illrequest::Config"); >+ >+# Test loading of 'Dummy' backend. >+ >+my $rqBackendTest = Koha::Illrequest->new({ >+ borrower_id => $patron->{borrowernumber}, >+ branch_id => $branch->{branchcode}, >+})->store; >+ >+$rqBackendTest->_config->backend("Dummy"); >+$rqBackendTest->_config->limits({ default => { count => -1 } }); >+isa_ok($rqBackendTest->_backend, "Koha::Illbackends::Dummy::Base"); >+ >+# Test use of 'Dummy' Backend. >+ >+## Test backend_update_status >+ >+# FIXME: This breaks transparancy of ->status method! >+eval { $rqBackendTest->status("ERR") }; >+ok($@, "status: Test for status error on hook fail."); >+ >+# FIXME: Will need to test this on new illRequest to not pollute rest of >+# tests. >+ >+# is($rqBackendTest->status("NEW")->status, "NEW", "status: Setter works >+# OK."); >+# is($rqBackendTest->status(null), null, "status: Unsetter works OK."); >+ >+## Test backend_create >+ >+is( >+ $rqBackendTest->status, >+ undef, >+ "backend_create: Test our status initiates correctly." >+); >+ >+# Request a search form >+my $created_rq = $rqBackendTest->backend_create({ >+ stage => "search_form", >+ method => "create", >+}); >+ >+is( $created_rq->{stage}, 'search_results', >+ "backend_create: search_results stage." ); >+ >+# Request search results >+# FIXME: fails because of missing patron / branch info. >+# $created_rq = $rqBackendTest->backend_create({ >+# stage => "search_results", >+# method => "create", >+# other => { search => "interlibrary loans" }, >+# }); >+ >+# is_deeply( >+# $created_rq, >+# { >+# error => 0, >+# status => '', >+# message => '', >+# method => 'create', >+# stage => 'search_results', >+# template => 'ill/Dummy/create.inc', >+# value => [ >+# { >+# id => 1234, >+# title => "Ordering ILLs using Koha", >+# author => "A.N. Other", >+# }, >+# { >+# id => 5678, >+# title => "Interlibrary loans in Koha", >+# author => "A.N. Other", >+# }, >+# ], >+# } >+# , >+# "backend_create: search_results stage." >+# ); >+ >+# # Create the request >+# $created_rq = $rqBackendTest->backend_create({ >+# stage => "commit", >+# method => "create", >+# other => { >+# id => 1234, >+# title => "Ordering ILLs using Koha", >+# author => "A.N. Other", >+# }, >+# }); >+ >+# while ( my ( $field, $value ) = each %{$created_rq} ) { >+# isnt($value, undef, "backend_create: key '$field' exists"); >+# }; >+ >+# is( >+# $rqBackendTest->status, >+# "NEW", >+# "backend_create: Test our status was updated." >+# ); >+ >+# cmp_ok( >+# $rqBackendTest->illrequestattributes->count, >+# "==", >+# 3, >+# "backend_create: Ensure we have correctly stored our new attributes." >+# ); >+ >+# ## Test backend_list >+ >+# is_deeply( >+# $rqBackendTest->backend_list->{value}, >+# { >+# 1 => { >+# id => 1234, >+# title => "Ordering ILLs using Koha", >+# author => "A.N. Other", >+# status => "On order", >+# cost => "30 GBP", >+# } >+# }, >+# "backend_list: Retrieve our list of requested requests." >+# ); >+ >+# ## Test backend_renew >+ >+# ok( >+# $rqBackendTest->backend_renew->{error}, >+# "backend_renew: Error for invalid request." >+# ); >+# is_deeply( >+# $rqBackendTest->backend_renew->{value}, >+# { >+# id => 1234, >+# title => "Ordering ILLs using Koha", >+# author => "A.N. Other", >+# }, >+# "backend_renew: Renew request." >+# ); >+ >+# ## Test backend_confirm >+ >+# my $rqBackendTestConfirmed = $rqBackendTest->backend_confirm; >+# is( >+# $rqBackendTest->status, >+# "REQ", >+# "backend_commit: Confirm status update correct." >+# ); >+# is( >+# $rqBackendTest->orderid, >+# 1234, >+# "backend_commit: Confirm orderid populated correctly." >+# ); >+ >+# ## Test backend_status >+ >+# is( >+# $rqBackendTest->backend_status->{error}, >+# 0, >+# "backend_status: error for invalid request." >+# ); >+# is_deeply( >+# $rqBackendTest->backend_status->{value}, >+# { >+# id => 1234, >+# status => "On order", >+# title => "Ordering ILLs using Koha", >+# author => "A.N. Other", >+# }, >+# "backend_status: Retrieve the status of request." >+# ); >+ >+# # Now test trying to get status on non-confirmed request. >+my $rqBackendTestUnconfirmed = Koha::Illrequest->new({ >+ borrower_id => $patron->{borrowernumber}, >+ branch_id => $branch->{branchcode}, >+})->store; >+$rqBackendTestUnconfirmed->_config->backend("Dummy"); >+$rqBackendTestUnconfirmed->_config->limits({ default => { count => -1 } }); >+ >+$rqBackendTestUnconfirmed->backend_create({ >+ stage => "commit", >+ method => "create", >+ other => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ }, >+}); >+is( >+ $rqBackendTestUnconfirmed->backend_status->{error}, >+ 1, >+ "backend_status: error for invalid request." >+); >+ >+## Test backend_cancel >+ >+# is( >+# $rqBackendTest->backend_cancel->{error}, >+# 0, >+# "backend_cancel: Successfully cancelling request." >+# ); >+# is_deeply( >+# $rqBackendTest->backend_cancel->{value}, >+# { >+# id => 1234, >+# title => "Ordering ILLs using Koha", >+# author => "A.N. Other", >+# }, >+# "backend_cancel: Cancel request." >+# ); >+ >+# Now test trying to cancel non-confirmed request. >+is( >+ $rqBackendTestUnconfirmed->backend_cancel->{error}, >+ 1, >+ "backend_cancel: error for invalid request." >+); >+is_deeply( >+ $rqBackendTestUnconfirmed->backend_cancel->{value}, >+ {}, >+ "backend_cancel: Cancel request." >+); >+ >+# Test Helpers >+ >+## Test getCensorNotesStaff >+ >+is($rqBackendTest->getCensorNotesStaff, 1, "getCensorNotesStaff: Public."); >+$rqBackendTest->_config->censorship({ >+ censor_notes_staff => 0, >+ censor_reply_date => 0, >+}); >+is($rqBackendTest->getCensorNotesStaff, 0, "getCensorNotesStaff: Censored."); >+ >+## Test getCensorNotesStaff >+ >+is($rqBackendTest->getDisplayReplyDate, 1, "getDisplayReplyDate: Yes."); >+$rqBackendTest->_config->censorship({ >+ censor_notes_staff => 0, >+ censor_reply_date => 1, >+}); >+is($rqBackendTest->getDisplayReplyDate, 0, "getDisplayReplyDate: No."); >+ >+# FIXME: These should be handled by the templates. >+# # Test Output Helpers >+ >+# ## Test getStatusSummary >+ >+# $rqBackendTest->medium("Book")->store; >+# is_deeply( >+# $rqBackendTest->getStatusSummary({brw => 0}), >+# { >+# biblionumber => ["Biblio Number", undef], >+# borrowernumber => ["Borrower Number", $patron->{borrowernumber}], >+# id => ["Request Number", $rqBackendTest->illrequest_id], >+# prefix_id => ["Request Number", $rqBackendTest->illrequest_id], >+# reqtype => ["Request Type", "Book"], >+# status => ["Status", "REQREV"], >+# }, >+# "getStatusSummary: Without Borrower." >+# ); >+ >+# is_deeply( >+# $rqBackendTest->getStatusSummary({brw => 1}), >+# { >+# biblionumber => ["Biblio Number", undef], >+# borrower => ["Borrower", Koha::Patrons->find($patron->{borrowernumber})], >+# id => ["Request Number", $rqBackendTest->illrequest_id], >+# prefix_id => ["Request Number", $rqBackendTest->illrequest_id], >+# reqtype => ["Request Type", "Book"], >+# status => ["Status", "REQREV"], >+# }, >+# "getStatusSummary: With Borrower." >+# ); >+ >+# ## Test getFullStatus >+ >+# is_deeply( >+# $rqBackendTest->getFullStatus({brw => 0}), >+# { >+# biblionumber => ["Biblio Number", undef], >+# borrowernumber => ["Borrower Number", $patron->{borrowernumber}], >+# id => ["Request Number", $rqBackendTest->illrequest_id], >+# prefix_id => ["Request Number", $rqBackendTest->illrequest_id], >+# reqtype => ["Request Type", "Book"], >+# status => ["Status", "REQREV"], >+# placement_date => ["Placement Date", $rqBackendTest->placed], >+# completion_date => ["Completion Date", $rqBackendTest->completed], >+# ts => ["Timestamp", $rqBackendTest->updated], >+# branch => ["Branch", $rqBackendTest->branch_id], >+# }, >+# "getFullStatus: Without Borrower." >+# ); >+ >+# is_deeply( >+# $rqBackendTest->getFullStatus({brw => 1}), >+# { >+# biblionumber => ["Biblio Number", undef], >+# borrower => ["Borrower", Koha::Patrons->find($patron->{borrowernumber})], >+# id => ["Request Number", $rqBackendTest->illrequest_id], >+# prefix_id => ["Request Number", $rqBackendTest->illrequest_id], >+# reqtype => ["Request Type", "Book"], >+# status => ["Status", "REQREV"], >+# placement_date => ["Placement Date", $rqBackendTest->placed], >+# completion_date => ["Completion Date", $rqBackendTest->completed], >+# ts => ["Timestamp", $rqBackendTest->updated], >+# branch => ["Branch", $rqBackendTest->branch_id], >+# }, >+# "getFullStatus: With Borrower." >+# ); >+ >+## Test available_backends >+subtest 'available_backends' => sub { >+ plan tests => 1; >+ >+ my $rq = Koha::Illrequest->new({ >+ borrower_id => $patron->{borrowernumber}, >+ branch_id => $branch->{branchcode}, >+ })->store; >+ >+ my @backends = (); >+ my $backenddir = $rq->_config->backend_dir; >+ @backends = <$backenddir/*> if ( $backenddir ); >+ @backends = map { basename($_) } @backends; >+ is_deeply(\@backends, $rq->available_backends, >+ "Correctly identify available backends."); >+ >+}; >+ >+## Test capabilities >+ >+my $rqCapTest = Koha::Illrequest->new({ >+ borrower_id => $patron->{borrowernumber}, >+ branch_id => $branch->{branchcode}, >+})->store; >+ >+is( keys %{$rqCapTest->_core_status_graph}, >+ @{[ 'NEW', 'REQ', 'REVREQ', 'QUEUED', 'CANCREQ', 'COMP', 'KILL' ]}, >+ "Complete list of core statuses." ); >+ >+my $union = $rqCapTest->_status_graph_union( >+ $rqCapTest->_core_status_graph, >+ { >+ TEST => { >+ prev_actions => [ 'COMP' ], >+ id => 'TEST', >+ name => "Test", >+ ui_method_name => "Perform test", >+ method => 'test', >+ next_actions => [ 'NEW' ] >+ }, >+ BLAH => { >+ prev_actions => [ 'COMP' ], >+ id => 'BLAH', >+ name => "BLAH", >+ ui_method_name => "Perform test", >+ method => 'test', >+ next_actions => [ 'NEW' ] >+ }, >+ } >+); >+ok( ( grep 'BLAH', @{$union->{COMP}->{next_actions}} and >+ grep 'TEST', @{$union->{COMP}->{next_actions}} ), >+ "next_actions: updated." ); >+ok( ( grep 'BLAH', @{$union->{NEW}->{prev_actions}} and >+ grep 'TEST', @{$union->{NEW}->{prev_actions}} ), >+ "next_actions: updated." ); >+ >+## Test available_backends >+subtest 'available_actions' => sub { >+ plan tests => 1; >+ >+ my $rq = Koha::Illrequest->new({ >+ borrower_id => $patron->{borrowernumber}, >+ branch_id => $branch->{branchcode}, >+ status => 'NEW', >+ })->store; >+ >+ is_deeply( >+ $rq->available_actions, >+ [ >+ { >+ prev_actions => [ 'NEW', 'REQREV', 'QUEUED' ], >+ id => 'REQ', >+ name => 'Requested', >+ ui_method_name => 'Create request', >+ method => 'confirm', >+ next_actions => [ 'REQREV' ], >+ }, >+ { >+ prev_actions => [ 'CANCREQ', 'QUEUED', 'REQREV', 'NEW' ], >+ id => 'KILL', >+ name => 0, >+ ui_method_name => 'Delete request', >+ method => 'delete', >+ next_actions => [ ], >+ } >+ ] >+ ); >+}; >+ >+$schema->storage->txn_rollback; >+ >+1; >diff --git a/t/db_dependent/Illrequest/Config.t b/t/db_dependent/Illrequest/Config.t >new file mode 100644 >index 0000000..df2bcac >--- /dev/null >+++ b/t/db_dependent/Illrequest/Config.t >@@ -0,0 +1,344 @@ >+#!/usr/bin/perl >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Test::Exception; >+use Test::More; >+use Test::Warn; >+ >+# Some data structures that will be repeatedly referenced >+my $defaults = { >+ api_key => "564euie", >+ api_auth => "unidaenudvnled", >+}; >+my $application = { >+ key => "6546uedrun", >+ auth => "edutrineadue", >+}; >+# Simulate $from_xml >+my $params = { >+ application => $application, >+ backend => "Dummy", >+ configuration => $defaults, >+ reply_date => "hide", >+ staff_request_comments => "hide", >+}; >+my $first_branch = { >+ code => "test", api_key => "dphügnpgüffq", api_auth => "udrend" >+}; >+my $second_branch = { >+ code => "second", >+ api_key => "eduirn", >+ api_auth => "eudtireand", >+ request_limit => { count => "5" }, >+}; >+ >+BEGIN { >+ use_ok('Koha::Illrequest::Config'); >+} >+ >+my $config = Koha::Illrequest::Config->new(1); # with test_mode enabled. >+isa_ok($config, 'Koha::Illrequest::Config'); >+ >+# _load_configuration >+is_deeply( >+ Koha::Illrequest::Config::_load_configuration($params), >+ { >+ api_url => 'http://apitest.bldss.bl.uk', >+ backend => 'Dummy', >+ censorship => { >+ censor_notes_staff => 1, >+ censor_reply_date => 1, >+ }, >+ credentials => { >+ api_application => $application, >+ api_keys => { default => $defaults }, >+ }, >+ default_formats => {}, >+ digital_recipients => {}, >+ library_privileges => {}, >+ limits => {}, >+ prefixes => {}, >+ spec_file => undef, >+ }, >+ "Basic _load_configuration" >+); >+ >+$params->{configuration}->{request_limit}->{count} = 10; >+is_deeply( >+ Koha::Illrequest::Config::_load_configuration($params), >+ { >+ api_url => 'http://apitest.bldss.bl.uk', >+ backend => 'Dummy', >+ censorship => { >+ censor_notes_staff => 1, >+ censor_reply_date => 1, >+ }, >+ credentials => { >+ api_application => $application, >+ api_keys => { >+ default => { >+ api_auth => $defaults->{api_auth}, >+ api_key => $defaults->{api_key}, >+ } >+ }, >+ }, >+ default_formats => {}, >+ digital_recipients => {}, >+ library_privileges => {}, >+ limits => { default => { count => 10 } }, >+ prefixes => {}, >+ spec_file => undef, >+ }, >+ "Basic _load_configuration, with limit" >+); >+ >+$params->{configuration}->{branch} = $first_branch; >+is_deeply( >+ Koha::Illrequest::Config::_load_configuration($params), >+ { >+ api_url => 'http://apitest.bldss.bl.uk', >+ backend => 'Dummy', >+ censorship => { >+ censor_notes_staff => 1, >+ censor_reply_date => 1, >+ }, >+ credentials => { >+ api_keys => { >+ default => { >+ api_key => $defaults->{api_key}, >+ api_auth => $defaults->{api_auth}, >+ }, >+ $first_branch->{code} => { >+ api_key => $first_branch->{api_key}, >+ api_auth => $first_branch->{api_auth}, >+ }, >+ }, >+ api_application => $application, >+ }, >+ default_formats => {}, >+ digital_recipients => {}, >+ library_privileges => {}, >+ limits => { default => { count => 10 } }, >+ prefixes => {}, >+ spec_file => undef, >+ }, >+ "Single Branch _load_configuration" >+); >+ >+$params->{configuration}->{branch} = [ $first_branch, $second_branch ]; >+is_deeply( >+ Koha::Illrequest::Config::_load_configuration($params), >+ { >+ api_url => 'http://apitest.bldss.bl.uk', >+ credentials => { >+ api_keys => { >+ default => { >+ api_key => $defaults->{api_key}, >+ api_auth => $defaults->{api_auth}, >+ }, >+ $first_branch->{code} => { >+ api_key => $first_branch->{api_key}, >+ api_auth => $first_branch->{api_auth}, >+ }, >+ $second_branch->{code} => { >+ api_key => $second_branch->{api_key}, >+ api_auth => $second_branch->{api_auth}, >+ }, >+ }, >+ api_application => $application, >+ }, >+ backend => 'Dummy', >+ censorship => { >+ censor_notes_staff => 1, >+ censor_reply_date => 1, >+ }, >+ default_formats => {}, >+ digital_recipients => {}, >+ library_privileges => {}, >+ limits => { >+ default => {count => 10 }, >+ branch => { >+ $second_branch->{code} => {count => 5 }, >+ }, >+ }, >+ prefixes => {}, >+ spec_file => undef, >+ }, >+ "Multi Branch _load_configuration" >+); >+ >+dies_ok { Koha::Illrequest::Config::_load_configuration($params, 1) } >+ "Unmediated, missing config _load_configuration"; >+ >+$params->{configuration}->{default_formats} = { >+ format => 1, quality => 1, quantity => 1, service => 1, speed => 1 >+}; >+is_deeply( >+ Koha::Illrequest::Config::_load_configuration($params, 1), >+ { >+ api_url => 'http://apitest.bldss.bl.uk', >+ backend => 'Dummy', >+ censorship => { >+ censor_notes_staff => 1, >+ censor_reply_date => 1, >+ }, >+ credentials => { >+ api_keys => { >+ default => { >+ api_key => $defaults->{api_key}, >+ api_auth => $defaults->{api_auth}, >+ }, >+ $first_branch->{code} => { >+ api_key => $first_branch->{api_key}, >+ api_auth => $first_branch->{api_auth}, >+ }, >+ $second_branch->{code} => { >+ api_key => $second_branch->{api_key}, >+ api_auth => $second_branch->{api_auth}, >+ }, >+ }, >+ api_application => $application, >+ }, >+ default_formats => { >+ default => { >+ format => 1, >+ quality => 1, >+ quantity => 1, >+ service => 1, >+ speed => 1 >+ } >+ }, >+ digital_recipients => {}, >+ library_privileges => {}, >+ limits => { >+ default => {count => 10 }, >+ branch => { >+ $second_branch->{code} => {count => 5 }, >+ }, >+ }, >+ prefixes => {}, >+ spec_file => undef, >+ }, >+ "default_formats, default only _load_configuration" >+); >+ >+# getDefaultFormats >+dies_ok { $config->getLimitRules('wrongType') } >+ "Faulty getDefaultFormats"; >+ >+$config->{configuration} = >+ Koha::Illrequest::Config::_load_configuration($params); >+is_deeply( >+ $config->getDefaultFormats('brw_cat'), >+ { >+ default => { >+ format => 1, >+ quality => 1, >+ quantity => 1, >+ service => 1, >+ speed => 1 >+ } >+ }, >+ "Default getDefaultFormats" >+); >+ >+# getLimitRules >+dies_ok { $config->getLimitRules('wrongType') } >+ "Faulty getLimitRules"; >+ >+$config->{configuration} = >+ Koha::Illrequest::Config::_load_configuration($params); >+is_deeply( >+ $config->getLimitRules('branch'), >+ { >+ default => { count => 10 }, >+ second => { count => 5 }, >+ }, >+ "second branch getLimitRules" >+); >+ >+is_deeply( >+ $config->getLimitRules('brw_cat'), >+ { >+ default => { count => 10 }, >+ }, >+ "empty brw_cat getLimitRules" >+); >+ >+# getCredentials >+$params = { >+ application => $application, >+ backend => 'Dummy', >+ configuration => {}, >+ reply_date => "hide", >+ staff_request_comments => "hide", >+}; >+$config->{configuration} = >+ Koha::Illrequest::Config::_load_configuration($params); >+is_deeply( >+ $config->getCredentials, >+ { >+ api_key => 0, >+ api_key_auth => 0, >+ api_application => $application->{key}, >+ api_application_auth => $application->{auth}, >+ }, >+ "getCredentials, no creds, just App." >+); >+ >+$params->{configuration} = $defaults; >+$config->{configuration} = >+ Koha::Illrequest::Config::_load_configuration($params), >+is_deeply( >+ $config->getCredentials, >+ { >+ api_key => $defaults->{api_key}, >+ api_key_auth => $defaults->{api_auth}, >+ api_application => $application->{key}, >+ api_application_auth => $application->{auth}, >+ }, >+ "getCredentials, default creds & App." >+); >+ >+$params->{configuration}->{branch} = $first_branch; >+$config->{configuration} = >+ Koha::Illrequest::Config::_load_configuration($params), >+is_deeply( >+ $config->getCredentials($first_branch->{code}), >+ { >+ api_key => $first_branch->{api_key}, >+ api_key_auth => $first_branch->{api_auth}, >+ api_application => $application->{key}, >+ api_application_auth => $application->{auth}, >+ }, >+ "getCredentials, $first_branch->{code} creds & App." >+); >+ >+is_deeply( >+ $config->getCredentials("random"), >+ { >+ api_key => $defaults->{api_key}, >+ api_key_auth => $defaults->{api_auth}, >+ api_application => $application->{key}, >+ api_application_auth => $application->{auth}, >+ }, >+ "getCredentials, fallback creds & app." >+); >+ >+done_testing; >diff --git a/t/db_dependent/Illrequest/Dummy.t b/t/db_dependent/Illrequest/Dummy.t >new file mode 100644 >index 0000000..38b5fac >--- /dev/null >+++ b/t/db_dependent/Illrequest/Dummy.t >@@ -0,0 +1,378 @@ >+#!/usr/bin/perl >+# >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 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 Modern::Perl; >+ >+use Koha::Database; >+use Koha::Illrequests; >+use t::lib::TestBuilder; >+ >+use Test::More tests => 15; >+ >+# This is a set of basic tests for the Dummy backend, largely to provide >+# sanity checks for testing at the higher level Illrequest.pm level. >+# >+# The Dummy backend is rather simple, but provides correctly formatted >+# response values, that other backends can model themselves after. >+ >+use_ok('Koha::Illrequest::Backend::Dummy'); >+ >+my $backend = Koha::Illrequest::Backend::Dummy->new; >+ >+isa_ok($backend, 'Koha::Illrequest::Backend::Dummy'); >+ >+ >+my $schema = Koha::Database->new->schema; >+$schema->storage->txn_begin; >+ >+my $builder = t::lib::TestBuilder->new; >+ >+my $patron = $builder->build({ source => 'Borrower' }); >+my $branch = $builder->build({ source => 'Branch' }); >+ >+my $illRequest = $builder->build({ >+ source => 'Illrequest', >+ value => { >+ borrowernumber => $patron->{borrowernumber}, >+ branch => $branch->{branchcode}, >+ } >+}); >+my $mock_request = Koha::Illrequests->find($illRequest->{illrequest_id}); >+$mock_request->_config->backend("Dummy"); >+$mock_request->_config->limits({ default => { count => -1 } }); >+ >+# Test Create >+my $rq = $backend->create({ >+ request => $mock_request, >+ method => 'create', >+ stage => 'search_form', >+ other => undef, >+}); >+ >+is_deeply( >+ $rq, >+ { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'create', >+ stage => 'search_form', >+ value => {}, >+ }, >+ "Search_Form stage of create method." >+); >+ >+$rq = $backend->create({ >+ request => $mock_request, >+ method => 'create', >+ stage => 'search_results', >+ other => { search => "interlibrary loans" }, >+}); >+ >+is_deeply( >+ $rq, >+ { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'create', >+ stage => 'search_results', >+ value => [ >+ { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ }, >+ { >+ id => 5678, >+ title => "Interlibrary loans in Koha", >+ author => "A.N. Other", >+ }, >+ ], >+ }, >+ "Search_Results stage of create method." >+); >+ >+$rq = $backend->create({ >+ request => $mock_request, >+ method => 'create', >+ stage => 'commit', >+ other => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ }, >+}); >+ >+is_deeply( >+ $rq, >+ { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'create', >+ stage => 'commit', >+ value => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other" >+ }, >+ }, >+ "Commit stage of create method." >+); >+ >+$rq = $backend->create({ >+ request => $mock_request, >+ method => 'create', >+ stage => 'unknown_stage', >+ other => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ }, >+}); >+ >+is_deeply( >+ $rq, >+ { >+ error => 1, >+ status => 'unknown_stage', >+ message => '', >+ method => 'create', >+ stage => 'unknown_stage', >+ value => {}, >+ }, >+ "Commit stage of create method." >+); >+ >+# Test Confirm >+ >+$rq = $backend->confirm({ >+ request => $mock_request, >+ other => undef, >+}); >+ >+is_deeply( >+ $rq, >+ { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'confirm', >+ stage => 'commit', >+ value => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ status => "On order", >+ cost => "30 GBP", >+ }, >+ }, >+ "Basic confirm method." >+); >+ >+# Test List >+ >+is_deeply( >+ $backend->list, >+ { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'list', >+ stage => 'commit', >+ value => { >+ 1 => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ status => "On order", >+ cost => "30 GBP", >+ }, >+ }, >+ }, >+ "Basic list method." >+); >+ >+# Test Renew >+ >+is_deeply( >+ $backend->renew({ >+ request => $mock_request, >+ other => undef, >+ }), >+ { >+ error => 1, >+ status => 'not_renewed', >+ message => 'Order not yet delivered.', >+ method => 'renew', >+ stage => 'commit', >+ value => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ status => "On order", >+ }, >+ }, >+ "Basic renew method." >+); >+ >+Koha::Illrequestattributes->find({ >+ illrequest_id => $mock_request->illrequest_id, >+ type => "status" >+})->set({ value => "Delivered" })->store; >+ >+is_deeply( >+ $backend->renew({ >+ request => $mock_request, >+ other => undef, >+ }), >+ { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'renew', >+ stage => 'commit', >+ value => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ status => "Renewed", >+ }, >+ }, >+ "Modified renew method." >+); >+ >+# Test Update_Status >+ >+is_deeply( >+ $backend->update_status({ >+ request => $mock_request, >+ other => undef, >+ }), >+ { >+ error => 1, >+ status => 'failed_update_hook', >+ message => 'Fake reason for failing to perform update operation.', >+ method => 'update_status', >+ stage => 'commit', >+ value => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ status => "Delivered", >+ }, >+ }, >+ "Basic update_status method." >+); >+ >+# FIXME: Perhaps we should add a test checking for specific status code >+# transitions. >+ >+# Test Cancel >+ >+is_deeply( >+ $backend->cancel({ >+ request => $mock_request, >+ other => undef, >+ }), >+ { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'cancel', >+ stage => 'commit', >+ value => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ status => "Delivered", >+ }, >+ }, >+ "Basic cancel method." >+); >+ >+is_deeply( >+ $backend->cancel({ >+ request => $mock_request, >+ other => undef, >+ }), >+ { >+ error => 1, >+ status => 'unknown_request', >+ message => 'Cannot cancel an unknown request.', >+ method => 'cancel', >+ stage => 'commit', >+ value => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ }, >+ }, >+ "Attempt to cancel an unconfirmed request." >+); >+ >+# Test Status >+ >+is_deeply( >+ $backend->status({ >+ request => $mock_request, >+ other => undef, >+ }), >+ { >+ error => 1, >+ status => 'unknown_request', >+ message => 'Cannot query status of an unknown request.', >+ method => 'status', >+ stage => 'commit', >+ value => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ }, >+ }, >+ "Attempt to get status of an unconfirmed request." >+); >+ >+$rq = $backend->confirm({ >+ request => $mock_request, >+ other => undef, >+}); >+ >+is_deeply( >+ $backend->status({ >+ request => $mock_request, >+ other => undef, >+ }), >+ { >+ error => 0, >+ status => '', >+ message => '', >+ method => 'status', >+ stage => 'commit', >+ value => { >+ id => 1234, >+ title => "Ordering ILLs using Koha", >+ author => "A.N. Other", >+ status => "On order", >+ }, >+ }, >+ "Basic status method." >+); >+ >+1; >diff --git a/t/db_dependent/api/v1/illrequests.t b/t/db_dependent/api/v1/illrequests.t >new file mode 100644 >index 0000000..fb306ba >--- /dev/null >+++ b/t/db_dependent/api/v1/illrequests.t >@@ -0,0 +1,136 @@ >+#!/usr/bin/env perl >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it under the >+# terms of the GNU General Public License as published by the Free Software >+# Foundation; either version 3 of the License, or (at your option) any later >+# version. >+# >+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY >+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR >+# A PARTICULAR PURPOSE. See the GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License along >+# with Koha; if not, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Test::More tests => 1; >+use Test::Mojo; >+use Test::Warn; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+use C4::Auth; >+use Koha::Illrequests; >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling >+# this affects the other REST api tests >+t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' ); >+ >+my $remote_address = '127.0.0.1'; >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+subtest 'list() tests' => sub { >+ >+ plan tests => 6; >+ >+ $schema->storage->txn_begin; >+ >+ Koha::Illrequests->search->delete; >+ my ( $borrowernumber, $session_id ) = >+ create_user_and_session( { authorized => 1 } ); >+ >+ ## Authorized user tests >+ # No requests, so empty array should be returned >+ my $tx = $t->ua->build_tx( GET => '/api/v1/illrequests' ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(200)->json_is( [] ); >+ >+# my $city_country = 'France'; >+# my $city = $builder->build( >+# { source => 'City', value => { city_country => $city_country } } ); >+# >+# # One city created, should get returned >+# $tx = $t->ua->build_tx( GET => '/api/v1/cities' ); >+# $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+# $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+# $t->request_ok($tx)->status_is(200)->json_is( [$city] ); >+# >+# my $another_city = $builder->build( >+# { source => 'City', value => { city_country => $city_country } } ); >+# my $city_with_another_country = $builder->build( { source => 'City' } ); >+# >+# # Two cities created, they should both be returned >+# $tx = $t->ua->build_tx( GET => '/api/v1/cities' ); >+# $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+# $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+# $t->request_ok($tx)->status_is(200) >+# ->json_is( [ $city, $another_city, $city_with_another_country ] ); >+# >+# # Filtering works, two cities sharing city_country >+# $tx = >+# $t->ua->build_tx( GET => "/api/v1/cities?city_country=" . $city_country ); >+# $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+# $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+# my $result = >+# $t->request_ok($tx)->status_is(200)->json_is( [ $city, $another_city ] ); >+# >+# $tx = $t->ua->build_tx( >+# GET => "/api/v1/cities?city_name=" . $city->{city_name} ); >+# $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+# $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+# $t->request_ok($tx)->status_is(200)->json_is( [$city] ); >+ >+ # Warn on unsupported query parameter >+ $tx = $t->ua->build_tx( GET => '/api/v1/illrequests?request_blah=blah' ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(400)->json_is( >+ [{ path => '/query/request_blah', message => 'Malformed query string'}] >+ ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+sub create_user_and_session { >+ >+ my $args = shift; >+ my $flags = ( $args->{authorized} ) ? $args->{authorized} : 0; >+ my $dbh = C4::Context->dbh; >+ >+ my $user = $builder->build( >+ { >+ source => 'Borrower', >+ value => { >+ flags => $flags >+ } >+ } >+ ); >+ >+ # Create a session for the authorized user >+ my $session = C4::Auth::get_session(''); >+ $session->param( 'number', $user->{borrowernumber} ); >+ $session->param( 'id', $user->{userid} ); >+ $session->param( 'ip', '127.0.0.1' ); >+ $session->param( 'lasttime', time() ); >+ $session->flush; >+ >+ if ( $args->{authorized} ) { >+ $dbh->do( " >+ INSERT INTO user_permissions (borrowernumber,module_bit,code) >+ VALUES (?,3,'parameters_remaining_permissions')", undef, >+ $user->{borrowernumber} ); >+ } >+ >+ return ( $user->{borrowernumber}, $session->id ); >+} >+ >+1; >-- >2.1.4
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 7317
:
59893
|
59894
|
59895
|
59920
|
60485
|
60486
|
60487
|
60488
|
63081
|
63082
|
63083
|
63084
|
63085
|
63086
|
63087
|
63088
|
63089
|
63090
|
63113
|
63175
|
63176
|
63354
|
63421
|
63472
|
63475
|
65469
|
65470
|
65471
|
65472
|
65473
|
65474
|
65475
|
65476
|
66266
|
67242
|
67243
|
67244
|
67245
|
67246
|
67247
|
67248
|
67249
|
67250
|
67251
|
67252
|
67253
|
67254
|
67255
|
67256
|
67257
|
67258
|
67259
|
67260
|
67261
|
67262
|
67263
|
67264
|
67281
|
67282
|
67283
|
67284
|
67285
|
67286
|
67287
|
67288
|
67289
|
67290
|
67291
|
67292
|
67293
|
67294
|
67295
|
67296
|
67297
|
67298
|
67299
|
67300
|
67301
|
67302
|
67303
|
68185
|
68186
|
68328
|
68391
|
68406
|
68407
|
68408
|
68409
|
68418
|
68444
|
68445
|
68483
|
68484
|
68485
|
68487
|
68516
|
68517
|
68518
|
68519
|
68520
|
68521
|
68522
|
68523
|
68524
|
68525
|
68542
|
68543
|
68544
|
68545
|
68546
|
68547
|
68548
|
68549
|
68550
|
68551
|
68552
|
68558
|
68559
|
68560
|
68561
|
68562
|
68563
|
68564
|
68565
|
68566
|
68567
|
68858
|
68874
|
68877
|
68878
|
68885
|
68886
|
68921
|
68922
|
68926
|
68927
|
68928
|
68929
|
68931
|
68932
|
68933
|
68988
|
68989
|
68999
|
69000
|
69006
|
69008
|
69009
|
69012
|
69014
|
69044
|
69045
|
69046
|
69056
|
69059
|
69060
|
69061
|
69063
|
69064
|
69065
|
69066
|
69069
|
69070
|
69071
|
69074