View | Details | Raw Unified | Return to bug 7317
Collapse All | Expand All

(-)a/Koha/Illrequest.pm (+855 lines)
Line 0 Link Here
1
package Koha::Illrequest;
2
3
# Copyright PTFS Europe 2016
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15
# details.
16
#
17
# You should have received a copy of the GNU General Public License along with
18
# Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin
19
# Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21
# use Modern::Perl;
22
23
use Clone 'clone';
24
use File::Basename qw/basename/;
25
use Koha::Database;
26
use Koha::Email;
27
use Koha::Illrequestattributes;
28
use Koha::Patron;
29
use Mail::Sendmail;
30
31
use base qw(Koha::Object);
32
33
=head1 NAME
34
35
Koha::Illrequest - Koha Illrequest Object class
36
37
=head1 (Re)Design
38
39
An ILLRequest consists of two parts; the Illrequest Koha::Object, and a series
40
of related Illrequestattributes.
41
42
The former encapsulates the basic necessary information that any ILL requires
43
to be usable in Koha.  The latter is a set of additional properties used by
44
one of the backends.
45
46
The former subsumes the legacy "Status" object.  The latter remains
47
encapsulated in the "Record" object.
48
49
TODO:
50
51
- Anything invoking the ->status method; annotated with:
52
  + # Old use of ->status !
53
54
=head1 API
55
56
=head2 Backend API Response Principles
57
58
All methods should return a hashref in the following format:
59
60
=item * error
61
62
This should be set to 1 if an error was encountered.
63
64
=item * status
65
66
The status should be a string from the list of statuses detailed below.
67
68
=item * message
69
70
The message is a free text field that can be passed on to the end user.
71
72
=item * value
73
74
The value returned by the method.
75
76
=over
77
78
=head2 Interface Status Messages
79
80
=over
81
82
=item * branch_address_incomplete
83
84
An interface request has determined branch address details are incomplete.
85
86
=item * cancel_success
87
88
The interface's cancel_request method was successful in cancelling the
89
Illrequest using the API.
90
91
=item * cancel_fail
92
93
The interface's cancel_request method failed to cancel the Illrequest using
94
the API.
95
96
=item * unavailable
97
98
The interface's request method returned saying that the desired item is not
99
available for request.
100
101
=head2 Class Methods
102
103
=cut
104
105
=head3 type
106
107
=cut
108
109
sub _type {
110
    return 'Illrequest';
111
}
112
113
# XXX: Method to expose DBIx accessor for illrequestattributes relationship
114
sub illrequestattributes {
115
    my ( $self ) = @_;
116
    return Koha::Illrequestattributes->_new_from_dbic(
117
        scalar $self->_result->illrequestattributes
118
    );
119
}
120
121
# XXX: Method to expose DBIx accessor for borrower relationship
122
sub patron {
123
    my ( $self ) = @_;
124
    return Koha::Patron->_new_from_dbic(
125
        scalar $self->_result->borrowernumber
126
    );
127
}
128
129
sub load_backend {
130
    my ( $self, $backend_id ) = @_;
131
132
    my @raw = qw/Koha Illbackends/; # Base Path
133
134
    my $backend_name = $backend_id || $self->backend;
135
    $location = join "/", @raw, $backend_name, "Base.pm"; # File to load
136
    $backend_class = join "::", @raw, $backend_name, "Base"; # Package name
137
    require $location;
138
    $self->{_my_backend} = $backend_class->new({ config => $self->_config });
139
    return $self;
140
}
141
142
=head3 _backend
143
144
    my $backend = $abstract->_backend($new_backend);
145
    my $backend = $abstract->_backend;
146
147
Getter/Setter for our API object.
148
149
=cut
150
151
sub _backend {
152
    my ( $self, $backend ) = @_;
153
    $self->{_my_backend} = $backend if ( $backend );
154
    # Dynamically load our backend object, as late as possible.
155
    $self->load_backend unless ( $self->{_my_backend} );
156
    return $self->{_my_backend};
157
}
158
159
=head3 _config
160
161
    my $config = $abstract->_config($config);
162
    my $config = $abstract->_config;
163
164
Getter/Setter for our config object.
165
166
=cut
167
168
sub _config {
169
    my ( $self, $config ) = @_;
170
    $self->{_my_config} = $config if ( $config );
171
    # Load our config object, as late as possible.
172
    unless ( $self->{_my_config} ) {
173
        $self->{_my_config} = Koha::Illrequest::Config->new;
174
    }
175
    return $self->{_my_config};
176
}
177
178
=head3 metadata
179
180
=cut
181
182
sub metadata {
183
    my ( $self ) = @_;
184
    return $self->_backend->metadata($self);
185
}
186
187
=head3 _core_status_graph
188
189
=cut
190
191
sub _core_status_graph {
192
    my ( $self ) = @_;
193
    return {
194
        NEW => {
195
            prev_actions => [ ],                           # Actions containing buttons
196
                                                           # leading to this status
197
            id             => 'NEW',                       # ID of this status
198
            name           => 'New request',               # UI name of this status
199
            ui_method_name => 'New request',               # UI name of method leading
200
                                                           # to this status
201
            method         => 'create',                    # method to this status
202
            next_actions   => [ 'REQ', 'GENREQ', 'KILL' ], # buttons to add to all
203
                                                           # requests with this status
204
            ui_method_icon => 'fa-plus',                   # UI Style class
205
        },
206
        REQ => {
207
            prev_actions   => [ 'NEW', 'REQREV', 'QUEUED' ],
208
            id             => 'REQ',
209
            name           => 'Requested',
210
            ui_method_name => 'Confirm request',
211
            method         => 'confirm',
212
            next_actions   => [ 'REQREV', 'CANCREQ' ],
213
            ui_method_icon => 'fa-check',
214
        },
215
        GENREQ => {
216
            prev_actions   => [ 'NEW', 'REQREV' ],
217
            id             => 'GENREQ',
218
            name           => 'Requested from partners',
219
            ui_method_name => 'Place request with partners',
220
            method         => 'generic_confirm',
221
            next_actions   => [ 'COMP' ],
222
            ui_method_icon => 'fa-send-o',
223
        },
224
        REQREV => {
225
            prev_actions   => [ 'CANCREQ', 'REQ' ],
226
            id             => 'REQREV',
227
            name           => 'Request reverted',
228
            ui_method_name => 'Revert Request',
229
            method         => 'cancel',
230
            next_actions   => [ 'REQ', 'GENREQ', 'KILL' ],
231
            ui_method_icon => 'fa-times',
232
        },
233
        QUEUED => {
234
            prev_actions   => [ ],
235
            id             => 'QUEUED',
236
            name           => 'Queued request',
237
            ui_method_name => 0,
238
            method         => 0,
239
            next_actions   => [ 'REQ', 'KILL' ],
240
            ui_method_icon => 0,
241
        },
242
        CANCREQ => {
243
            prev_actions   => [ 'REQ' ],
244
            id             => 'CANCREQ',
245
            name           => 'Cancellation requested',
246
            ui_method_name => 0,
247
            method         => 0,
248
            next_actions   => [ 'REQREV' ],
249
            ui_method_icon => 0,
250
        },
251
        COMP => {
252
            prev_actions   => [ 'REQ' ],
253
            id             => 'COMP',
254
            name           => 'Completed',
255
            ui_method_name => 0,
256
            method         => 0,
257
            next_actions   => [ ],
258
            ui_method_icon => 0,
259
        },
260
        KILL => {
261
            prev_actions   => [ 'QUEUED', 'REQREV', 'NEW' ],
262
            id             => 'KILL',
263
            name           => 0,
264
            ui_method_name => 'Delete request',
265
            method         => 'delete',
266
            next_actions   => [ ],
267
            ui_method_icon => 'fa-trash',
268
        },
269
    };
270
}
271
272
sub _status_graph_union {
273
    my ( $self, $core_status_graph, $backend_status_graph ) = @_;
274
    # Create new status graph with:
275
    # - all core_status_graph
276
    # - for-each each backend_status_graph
277
    #   + add to new status graph
278
    #   + for each core prev_action:
279
    #     * locate core_status
280
    #     * update next_actions with additional next action.
281
    #   + for each core next_action:
282
    #     * locate core_status
283
    #     * update prev_actions with additional prev action
284
285
    my @core_status_ids = keys %{$core_status_graph};
286
    my $status_graph = clone($core_status_graph);
287
288
    foreach my $backend_status_key ( keys %{$backend_status_graph} ) {
289
        $backend_status = $backend_status_graph->{$backend_status_key};
290
        # Add to new status graph
291
        $status_graph->{$backend_status_key} = $backend_status;
292
        # Update all core methods' next_actions.
293
        foreach my $prev_action ( @{$backend_status->{prev_actions}} ) {
294
            if ( grep $prev_action, @core_status_ids ) {
295
                my @next_actions =
296
                     @{$status_graph->{$prev_action}->{next_actions}};
297
                push @next_actions, $backend_status_key;
298
                $status_graph->{$prev_action}->{next_actions}
299
                    = \@next_actions;
300
            }
301
        }
302
        # Update all core methods' prev_actions
303
        foreach my $next_action ( @{$backend_status->{next_actions}} ) {
304
            if ( grep $next_action, @core_status_ids ) {
305
                my @prev_actions =
306
                     @{$status_graph->{$next_action}->{prev_actions}};
307
                push @prev_actions, $backend_status_key;
308
                $status_graph->{$next_action}->{prev_actions}
309
                    = \@prev_actions;
310
            }
311
        }
312
    }
313
314
    return $status_graph;
315
}
316
317
### Core API methods
318
319
=head3 capabilities
320
321
    my $capabilities = $illrequest->capabilities;
322
323
Return a hashref mapping methods to operation names supported by the queried
324
backend.
325
326
Example return value:
327
328
    { create => "Create Request", confirm => "Progress Request" }
329
330
=cut
331
332
sub capabilities {
333
    my ( $self, $status ) = @_;
334
    # Generate up to date status_graph
335
    my $status_graph = $self->_status_graph_union(
336
        $self->_core_status_graph,
337
        $self->_backend->status_graph({
338
            request => $self,
339
            other   => {}
340
        })
341
    );
342
    # Extract available actions from graph.
343
    return $status_graph->{$status} if $status;
344
    # Or return entire graph.
345
    return $status_graph;
346
}
347
348
=head3 custom_capability
349
350
Return the result of invoking $CANDIDATE on this request's backend with
351
$PARAMS, or 0 if $CANDIDATE is an unknown method on backend.
352
353
=cut
354
355
sub custom_capability {
356
    my ( $self, $candidate, $params ) = @_;
357
    foreach my $capability ( values $self->capabilities ) {
358
        if ( $candidate eq $capability->{method} ) {
359
            my $response =
360
                $self->_backend->$candidate({
361
                    request    => $self,
362
                    other      => $params,
363
                });
364
            return $self->expandTemplate($response);
365
        }
366
    }
367
    return 0;
368
}
369
370
sub available_backends {
371
    my ( $self ) = @_;
372
    my $backend_dir = $self->_config->backend_dir;
373
    my @backends = ();
374
    my @backends = <$backend_dir/*> if ( $backend_dir );
375
    my @backends = map { basename($_) } @backends;
376
    return \@backends;
377
}
378
379
sub available_actions {
380
    my ( $self ) = @_;
381
    my $current_action = $self->capabilities($self->status);
382
    my @available_actions = map { $self->capabilities($_) }
383
        @{$current_action->{next_actions}};
384
    return \@available_actions;
385
}
386
387
sub backend_confirm {
388
    my ( $self, $params ) = @_;
389
390
    # The backend handles setting of mandatory fields in the commit stage:
391
    # - orderid
392
    # - accessurl, cost (if available).
393
    my $response = $self->_backend->confirm({
394
            request    => $self,
395
            other      => $params,
396
        });
397
    return $self->expandTemplate($response);
398
}
399
400
sub backend_update_status {
401
    my ( $self, $params ) = @_;
402
    return $self->expandTemplate($self->_backend->update_status($params));
403
}
404
405
=head3 backend_cancel
406
407
    my $ILLResponse = $illRequest->backend_cancel;
408
409
The standard interface method allowing for request cancellation.
410
411
=cut
412
413
sub backend_cancel {
414
    my ( $self, $params ) = @_;
415
416
    my $result = $self->_backend->cancel({
417
        request => $self,
418
        other => $params
419
    });
420
421
    return $self->expandTemplate($result);
422
}
423
424
=head3 backend_renew
425
426
    my $renew_response = $illRequest->backend_renew;
427
428
The standard interface method allowing for request renewal queries.
429
430
=cut
431
432
sub backend_renew {
433
    my ( $self ) = @_;
434
    return $self->expandTemplate(
435
        $self->_backend->renew({
436
            request    => $self,
437
        })
438
    );
439
}
440
441
=head3 backend_create
442
443
    my $create_response = $abstractILL->backend_create($params);
444
445
Return an array of Record objects created by querying our backend with
446
a Search query.
447
448
In the context of the other ILL methods, this is a special method: we only
449
pass it $params, as it does not yet have any other data associated with it.
450
451
=cut
452
453
sub backend_create {
454
    my ( $self, $params ) = @_;
455
456
    # First perform API action, then...
457
    my $args = {
458
        request => $self,
459
        other   => $params,
460
    };
461
    my $result = $self->_backend->create($args);
462
463
    # ... simple case: we're not at 'commit' stage.
464
    my $stage = $result->{stage};
465
    return $self->expandTemplate($result)
466
        unless ( 'commit' eq $stage );
467
468
    # ... complex case: commit!
469
470
    # Do we still have space for an ILL or should we queue?
471
    my $permitted = $self->check_limits(
472
        { patron => $self->patron }, { librarycode => $self->branchcode }
473
    );
474
475
    # Now augment our committed request.
476
477
    $result->{permitted} = $permitted;             # Queue request?
478
479
    # This involves...
480
481
    # ...Updating status!
482
    $self->status('QUEUED')->store unless ( $permitted );
483
484
    # FIXME: Fix Unmediated ILLs!
485
    # Handle Unmediated ILLs
486
    # if ( C4::Context->preference("UnmediatedILL") && $permitted
487
    #       # XXX: Why && result manual?
488
    #       && $result->{manual} ) {
489
    #     # FIXME: Also carry out privilege checks
490
    #     my ( $response, $new_rq ) = $self->place_request; # WTF?
491
    #     if ( $response ) {
492
    #         $result->{value}->{request} = $new_rq;
493
    #         return $result;
494
    #     } else {
495
    #         die "Placing the request failed.";
496
    #     }
497
    # } else {
498
    #     $result->{value}->{request} = $request;
499
    #     return $result;
500
    # }
501
502
    return $self->expandTemplate($result);
503
}
504
505
=head3 expandTemplate
506
507
    my $params = $abstract->expandTemplate($params);
508
509
Return a version of $PARAMS augmented with our required template path.
510
511
=cut
512
513
sub expandTemplate {
514
    my ( $self, $params ) = @_;
515
    my $backend = $self->_backend->name;
516
    # Generate path to file to load
517
    my $backend_dir = $self->_config->backend_dir;
518
    my $backend_tmpl = join "/", $backend_dir, $backend;
519
    my $intra_tmpl =  join "/", $backend_tmpl, "intra-includes",
520
        $params->{method} . ".inc";
521
    my $opac_tmpl =  join "/", $backend_tmpl, "opac-includes",
522
        $params->{method} . ".inc";
523
    # Set files to load
524
    $params->{template} = $intra_tmpl;
525
    $params->{opac_template} = $opac_tmpl;
526
    return $params;
527
}
528
529
#### Abstract Imports
530
531
=head3 getCensorNotesStaff
532
533
    my $bool = $abstract->getCensorNotesStaff;
534
535
Return a boolean indicating whether we should be censoring staff notes or not,
536
as determined by our configuration file.
537
538
=cut
539
540
sub getCensorNotesStaff {
541
    my ( $self ) = @_;
542
    my $censorship = $self->_config->censorship;
543
    return $censorship->{censor_notes_staff};
544
}
545
546
=head3 getDisplayReplyDate
547
548
    my 0 = $abstract->getDisplayReplyDate;
549
550
Return a 0 if we want to hide it or 1 if not.
551
552
=cut
553
554
sub getDisplayReplyDate {
555
    my ( $self ) = @_;
556
    my $censorship = $self->_config->censorship;
557
    # If censor is yes, don't display and vice versa.
558
    return ( $censorship->{censor_reply_date} ) ? 0 : 1;
559
}
560
561
=head3 getLimits
562
563
    my $limit_rules = $abstract->getLimits( {
564
        type  => 'brw_cat' | 'branch',
565
        value => $value
566
    } );
567
568
Return the ILL limit rules for the supplied combination of type / value.
569
570
As the config may have no rules for this particular type / value combination,
571
or for the default, we must define fall-back values here.
572
573
=cut
574
575
# FIXME: Needs unit tests!
576
sub getLimits {
577
    my ( $self, $params ) = @_;
578
    my $limits = $self->_config->getLimitRules($params->{type});
579
580
    return $limits->{$params->{value}}
581
        || $limits->{default}
582
        || { count => -1, method => 'active' };
583
}
584
585
=head3 getPrefix
586
587
    my $prefix = $abstract->getPrefix( {
588
        brw_cat => $brw_cat,
589
        branch  => $branch_code,
590
    } );
591
592
Return the ILL prefix as defined by our $params: either per borrower category,
593
per branch or the default.
594
595
=cut
596
597
sub getPrefix {
598
    my ( $self, $params ) = @_;
599
    my $brn_prefixes = $self->_config->getPrefixes('branch');
600
    my $brw_prefixes = $self->_config->getPrefixes('brw_cat');
601
602
    return $brw_prefixes->{$params->{brw_cat}}
603
        || $brn_prefixes->{$params->{branch}}
604
        || $brw_prefixes->{default}
605
        || "";                  # "the empty prefix"
606
}
607
608
#### Illrequests Imports
609
610
=head3 check_limits
611
612
    my $ok = $illRequests->check_limits( {
613
        borrower   => $borrower,
614
        branchcode => 'branchcode' | undef,
615
    } );
616
617
Given $PARAMS, a hashref containing a $borrower object and a $branchcode,
618
see whether we are still able to place ILLs.
619
620
LimitRules are derived from koha-conf.xml:
621
 + default limit counts, and counting method
622
 + branch specific limit counts & counting method
623
 + borrower category specific limit counts & counting method
624
 + err on the side of caution: a counting fail will cause fail, even if
625
   the other counts passes.
626
627
=cut
628
629
# FIXME: Needs unit tests!
630
sub check_limits {
631
    my ( $self, $params ) = @_;
632
    my $patron     = $params->{patron};
633
    my $branchcode = $params->{librarycode} || $patron->branchcode;
634
635
    # Establish rules
636
    my ( $branch_rules, $brw_rules ) = (
637
        $self->getLimits( {
638
            type => 'branch',
639
            value => $branchcode
640
        } ),
641
        $self->getLimits( {
642
            type => 'brw_cat',
643
            value => $patron->categorycode,
644
        } ),
645
    );
646
    # Almost there, but category code didn't quite work.
647
    my ( $branch_limit, $brw_limit )
648
        = ( $branch_rules->{count}, $brw_rules->{count} );
649
    my ( $branch_count, $brw_count ) = (
650
        $self->_limit_counter(
651
            $branch_rules->{method}, { branch_id => $branchcode }
652
        ),
653
        $self->_limit_counter(
654
            $brw_rules->{method}, { borrower_id => $patron->borrowernumber }
655
        ),
656
    );
657
658
    # Compare and return
659
    # A limit of -1 means no limit exists.
660
    # We return blocked if either branch limit or brw limit is reached.
661
    if ( ( $branch_limit != -1 && $branch_limit <= $branch_count )
662
             || ( $brw_limit != -1 && $brw_limit <= $brw_count ) ) {
663
        return 0;
664
    } else {
665
        return 1;
666
    }
667
}
668
669
# FIXME: Needs unit tests!
670
sub _limit_counter {
671
    my ( $self, $method, $target ) = @_;
672
673
    # Establish parameters of counts
674
    my $where;
675
    if ($method && $method eq 'annual') {
676
        $where = \"YEAR(placement_date) = YEAR(NOW())";
677
    } else {                    # assume 'active'
678
        # FIXME: This status list is ugly. There should be a method in config
679
        # to return these.
680
        $where = { status => { -not_in => [ 'QUEUED', 'COMP' ] } };
681
    }
682
683
    # Create resultset
684
    my $resultset = Koha::Illrequests->search({ %{$target}, %{$where} });
685
686
    # Fetch counts
687
    return $resultset->count;
688
}
689
690
=head3 requires_moderation
691
692
    my $status = $illRequest->requires_moderation;
693
694
Return the name of the status if moderation by staff is required; or 0
695
otherwise.
696
697
=cut
698
699
sub requires_moderation {
700
    my ( $self ) = @_;
701
    my $require_moderation = {
702
        'CANCREQ' => 'CANCREQ',
703
    };
704
    return $require_moderation->{$self->status};
705
}
706
707
=head3 generic_confirm
708
709
    my $stage_summary = $illRequest->generic_confirm;
710
711
Handle the generic_confirm extended method.  The first stage involves creating
712
a template email for the end user to edit in the browser.  The second stage
713
attempts to submit the email.
714
715
=cut
716
717
sub generic_confirm {
718
    my ( $self, $params ) = @_;
719
    if ( !$params->{stage}|| $params->{stage} eq 'init' ) {
720
        my $draft->{subject} = "ILL Request";
721
        $draft->{body} = <<EOF;
722
Dear Sir/Madam,
723
724
    We would like to request an interlibrary loan for a title matching the
725
following description:
726
727
EOF
728
729
        my $details = $self->metadata;
730
        while (my ($title, $value) = each %{$details}) {
731
            $draft->{body} .= "  - " . $title . ": " . $value . "\n"
732
                if $value;
733
        }
734
        $draft->{body} .= <<EOF;
735
736
Please let us know if you are able to supply this to us.
737
738
Kind Regards
739
EOF
740
741
        my $partners = Koha::Patrons->search({
742
            categorycode => $self->_config->partner_code
743
        });
744
        return {
745
            error   => 0,
746
            status  => '',
747
            message => '',
748
            method  => 'generic_confirm',
749
            stage   => 'draft',
750
            value   => {
751
                draft    => $draft,
752
                partners => $partners,
753
            }
754
        };
755
756
    } elsif ( 'draft' eq $params->{stage} ) {
757
        # Create the to header
758
        my $to = $params->{partners};
759
        $to =~ s/^\x00//;       # Strip leading NULLs
760
        $to =~ s/\x00/; /;      # Replace others with '; '
761
        die "No target email addresses found. Either select at least one partner or check your ILL partner library records." if ( !$to );
762
        # Create the from, replyto and sender headers
763
        $branch = Koha::Libraries->find($params->{current_branchcode})
764
            || die "Invalid current branchcode. Are you logged in as the database user?";
765
        my $from = $branch->branchemail;
766
        my $replyto = $branch->branchreplyto || $from;
767
        die "Your branch has no email address. Please set it."
768
            if ( !$from );
769
        # Create the email
770
        my $message = Koha::Email->new;
771
        my %mail = $message->create_message_headers(
772
            {
773
                to          => $to,
774
                from        => $from,
775
                replyto     => $replyto,
776
                subject     => Encode::encode( "utf8", $params->{subject} ),
777
                message     => Encode::encode( "utf8", $params->{body} ),
778
                contenttype => 'text/plain',
779
            }
780
        );
781
        # Send it
782
        my $result = sendmail(%mail);
783
        if ( $result ) {
784
            $self->status("GENREQ")->store;
785
            return {
786
                error   => 0,
787
                status  => '',
788
                message => '',
789
                method  => 'generic_confirm',
790
                stage   => 'commit',
791
                next    => 'illview',
792
            };
793
        } else {
794
            return {
795
                error   => 1,
796
                status  => 'email_failed',
797
                message => $Mail::Sendmail::error,
798
                method  => 'generic_confirm',
799
                stage   => 'draft',
800
            };
801
        }
802
    } else {
803
        die "Unknown stage, should not have happened."
804
    }
805
}
806
807
=head3 id_prefix
808
809
    my $prefix = $record->id_prefix;
810
811
Return the prefix appropriate for the current Illrequest as derived from the
812
borrower and branch associated with this request's Status, and the config
813
file.
814
815
=cut
816
817
sub id_prefix {
818
    my ( $self ) = @_;
819
    # FIXME: can we automatically use borrowernumber as borrower?
820
    my $brw = $self->patron;
821
    my $brw_cat = "dummy";
822
    $brw_cat = $brw->categorycode
823
        unless ( 'HASH' eq ref($brw) && $brw->{deleted} );
824
    my $prefix = $self->getPrefix( {
825
        brw_cat => $brw_cat,
826
        branch  => $self->branchcode,
827
    } );
828
    $prefix .= "-" if ( $prefix );
829
    return $prefix;
830
}
831
832
=head3 _censor
833
834
    my $params = $illRequest->_censor($params);
835
836
Return $params, modified to reflect our censorship requirements.
837
838
=cut
839
840
sub _censor {
841
    my ( $self, $params ) = @_;
842
    $params->{censor_notes_staff} = $self->getCensorNotesStaff
843
        if ( $params->{opac} );
844
    $params->{display_reply_date} = $self->getDisplayReplyDate;
845
846
    return $params;
847
}
848
849
=head1 AUTHOR
850
851
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
852
853
=cut
854
855
1;
(-)a/Koha/Illrequest/Backend/Dummy/Base.pm (+576 lines)
Line 0 Link Here
1
package Koha::Illrequest::Backend::Dummy::Base;
2
3
# Copyright PTFS Europe 2014
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
use DateTime;
22
use Koha::Illrequestattribute;
23
24
=head1 NAME
25
26
Koha::Illrequest::Backend::Dummy - Koha ILL Backend: Dummy
27
28
=head1 SYNOPSIS
29
30
Koha ILL implementation for the "Dummy" backend.
31
32
=head1 DESCRIPTION
33
34
=head2 Overview
35
36
We will be providing the Abstract interface which requires we implement the
37
following methods:
38
- create        -> initial placement of the request for an ILL order
39
- confirm       -> confirm placement of the ILL order
40
- list          -> list all ILL Requests currently placed with the backend
41
- renew         -> request a currently borrowed ILL be renewed in the backend
42
- update_status -> ILL module update hook: custom actions on status update
43
- cancel        -> request an already 'confirm'ed ILL order be cancelled
44
- status        -> request the current status of a confirmed ILL order
45
46
Each of the above methods will receive the following parameter from
47
Illrequest.pm:
48
49
  {
50
      request    => $request,
51
      other      => $other,
52
  }
53
54
where:
55
56
- $REQUEST is the Illrequest object in Koha.  It's associated
57
  Illrequestattributes can be accessed through the `illrequestattributes`
58
  method.
59
- $OTHER is any further data, generally provided through templates .INCs
60
61
Each of the above methods should return a hashref of the following format:
62
63
    return {
64
        error   => 0,
65
        # ^------- 0|1 to indicate an error
66
        status  => 'result_code',
67
        # ^------- Summary of the result of the operation
68
        message => 'Human readable message.',
69
        # ^------- Message, possibly to be displayed
70
        #          Normally messages are derived from status in INCLUDE.
71
        #          But can be used to pass API messages to the INCLUDE.
72
        method  => 'list',
73
        # ^------- Name of the current method invoked.
74
        #          Used to load the appropriate INCLUDE.
75
        stage   => 'commit',
76
        # ^------- The current stage of this method
77
        #          Used by INCLUDE to determine HTML to generate.
78
        #          'commit' will result in final processing by Illrequest.pm.
79
        next    => 'illview'|'illlist',
80
        # ^------- When stage is 'commit', should we move on to ILLVIEW the
81
        #          current request or ILLLIST all requests.
82
        value   => {},
83
        # ^------- A hashref containing an arbitrary return value that this
84
        #          backend wants to supply to its INCLUDE.
85
    };
86
87
=head2 On the Dummy backend
88
89
The Dummy backend is rather simple, but provides correctly formatted response
90
values, that other backends can model themselves after.
91
92
The code is not DRY -- primarily so that each method can be looked at in
93
isolation rather than having to familiarise oneself with helper procedures.
94
95
=head1 API
96
97
=head2 Class Methods
98
99
=cut
100
101
=head3 new
102
103
  my $backend = Koha::Illrequest::Backend::Dummy->new;
104
105
=cut
106
107
sub new {
108
    # -> instantiate the backend
109
    my ( $class ) = @_;
110
    my $self = {};
111
    bless( $self, $class );
112
    return $self;
113
}
114
115
=head3 _data_store
116
117
  my $request = $self->_data_store($id);
118
  my $requests = $self->_data_store;
119
120
A mock of a data store.  When passed no parameters it returns all entries.
121
When passed one it will return the entry matched by its id.
122
123
=cut
124
125
sub _data_store {
126
    my $data = {
127
        1234 => {
128
            id     => 1234,
129
            title  => "Ordering ILLs using Koha",
130
            author => "A.N. Other",
131
        },
132
        5678 => {
133
            id     => 5678,
134
            title  => "Interlibrary loans in Koha",
135
            author => "A.N. Other",
136
        },
137
    };
138
    # ID search
139
    my ( $self, $id ) = @_;
140
    return $data->{$id} if $id;
141
142
    # Full search
143
    my @entries;
144
    while ( my ( $k, $v ) = each %{$data} ) {
145
        push @entries, $v;
146
    }
147
    return \@entries;
148
}
149
150
=head3 create
151
152
  my $response = $backend->create({
153
      request    => $requestdetails,
154
      other      => $other,
155
  });
156
157
This is the initial creation of the request.  Generally this stage will be
158
some form of search with the backend.
159
160
By and large we will not have useful $requestdetails (borrowernumber,
161
branchcode, status, etc.).
162
163
$params is simply an additional slot for any further arbitrary values to pass
164
to the backend.
165
166
This is an example of a multi-stage method.
167
168
=cut
169
170
sub create {
171
    # -> initial placement of the request for an ILL order
172
    my ( $self, $params ) = @_;
173
    my $stage = $params->{other}->{stage};
174
    if ( !$stage || $stage eq 'init' ) {
175
        # We simply need our template .INC to produce a search form.
176
        return {
177
            error   => 0,
178
            status  => '',
179
            message => '',
180
            method  => 'create',
181
            stage   => 'search_form',
182
            value   => {},
183
        };
184
    } elsif ( $stage eq 'search_form' ) {
185
	# Received search query in 'other'; perform search...
186
187
        # No-op on Dummy
188
189
        # and return results.
190
        return {
191
            error   => 0,
192
            status  => '',
193
            message => '',
194
            method  => 'create',
195
            stage   => 'search_results',
196
            value   => {
197
                borrowernumber => $params->{other}->{borrowernumber},
198
                branchcode     => $params->{other}->{branchcode},
199
                medium         => $params->{other}->{medium},
200
                candidates     => $self->_data_store,
201
            }
202
        };
203
    } elsif ( $stage eq 'search_results' ) {
204
        # We have a selection
205
        my $id = $params->{other}->{id};
206
207
        # -> select from backend...
208
        my $request_details = $self->_data_store($id);
209
210
        # ...Populate Illrequest
211
        my $request = $params->{request};
212
        $request->borrower_id($params->{other}->{borrowernumber});
213
        $request->branch_id($params->{other}->{branchcode});
214
        $request->medium($params->{other}->{medium});
215
        $request->status('NEW');
216
        $request->placed(DateTime->now);
217
        $request->updated(DateTime->now);
218
        $request->store;
219
        # ...Populate Illrequestattributes
220
        while ( my ( $type, $value ) = each %{$request_details} ) {
221
            Koha::Illrequestattribute->new({
222
                illrequest_id => $request->illrequest_id,
223
                type          => $type,
224
                value         => $value,
225
            })->store;
226
        }
227
228
        # -> create response.
229
        return {
230
            error   => 0,
231
            status  => '',
232
            message => '',
233
            method  => 'create',
234
            stage   => 'commit',
235
            next    => 'illview',
236
            value   => $request_details,
237
        };
238
    } else {
239
	# Invalid stage, return error.
240
        return {
241
            error   => 1,
242
            status  => 'unknown_stage',
243
            message => '',
244
            method  => 'create',
245
            stage   => $params->{stage},
246
            value   => {},
247
        };
248
    }
249
}
250
251
=head3 confirm
252
253
  my $response = $backend->confirm({
254
      request    => $requestdetails,
255
      other      => $other,
256
  });
257
258
Confirm the placement of the previously "selected" request (by using the
259
'create' method).
260
261
In this case we will generally use $request.
262
This will be supplied at all times through Illrequest.  $other may be supplied
263
using templates.
264
265
=cut
266
267
sub confirm {
268
    # -> confirm placement of the ILL order
269
    my ( $self, $params ) = @_;
270
    # Turn Illrequestattributes into a plain hashref
271
    my $value = {};
272
    my $attributes = $params->{request}->illrequestattributes;
273
    foreach my $attr (@{$attributes->as_list}) {
274
        $value->{$attr->type} = $attr->value;
275
    };
276
    # Submit request to backend...
277
278
    # No-op for Dummy
279
280
    # ...parse response...
281
    $attributes->find_or_create({ type => "status", value => "On order" });
282
    my $request = $params->{request};
283
    $request->cost("30 GBP");
284
    $request->orderid($value->{id});
285
    $request->status("REQ");
286
    $request->accessurl("URL") if $value->{url};
287
    $request->store;
288
    $value->{status} = "On order";
289
    $value->{cost} = "30 GBP";
290
    # ...then return our result:
291
    return {
292
        error    => 0,
293
        status   => '',
294
        message  => '',
295
        method   => 'confirm',
296
        stage    => 'commit',
297
        next     => 'illview',
298
        value    => $value,
299
    };
300
}
301
302
=head3 list
303
304
  my $response = $backend->list({
305
      request    => $requestdetails,
306
      other      => $other,
307
  };
308
309
Attempt to get a list of the currently registered requests with the backend.
310
311
Parameters are optional for this request.  A backend may be supplied with
312
details of a specific request (or a group of requests in $other), but equally
313
no parameters might be provided at all.
314
315
Normally no parameters will be provided in the 'create' stage.  After this,
316
parameters may be provided using templates.
317
318
=cut
319
320
sub list {
321
    # -> list all ILL Requests currently placed with the backend
322
    #    (we ignore all params provided)
323
    my ( $self, $params ) = @_;
324
    my $stage = $params->{other}->{stage};
325
    if ( !$stage || $stage eq 'init' ) {
326
        return {
327
            error   => 0,
328
            status  => '',
329
            message => '',
330
            method  => 'list',
331
            stage   => 'list',
332
            value   => {
333
                1 => {
334
                    id     => 1234,
335
                    title  => "Ordering ILLs using Koha",
336
                    author => "A.N. Other",
337
                    status => "On order",
338
                    cost   => "30 GBP",
339
                },
340
            },
341
        };
342
    } elsif ( $stage eq 'list' ) {
343
        return {
344
            error   => 0,
345
            status  => '',
346
            message => '',
347
            method  => 'list',
348
            stage   => 'commit',
349
            value   => {},
350
        };
351
    } else {
352
        # Invalid stage, return error.
353
        return {
354
            error   => 1,
355
            status  => 'unknown_stage',
356
            message => '',
357
            method  => 'create',
358
            stage   => $params->{stage},
359
            value   => {},
360
        };
361
    }
362
}
363
364
=head3 renew
365
366
  my $response = $backend->renew({
367
      request    => $requestdetails,
368
      other      => $other,
369
  });
370
371
Attempt to renew a request that was supplied through backend and is currently
372
in use by us.
373
374
We will generally use $request.  This will be supplied at all times through
375
Illrequest.  $other may be supplied using templates.
376
377
=cut
378
379
sub renew {
380
    # -> request a currently borrowed ILL be renewed in the backend
381
    my ( $self, $params ) = @_;
382
    # Turn Illrequestattributes into a plain hashref
383
    my $value = {};
384
    my $attributes = $params->{request}->illrequestattributes;
385
    foreach my $attr (@{$attributes->as_list}) {
386
        $value->{$attr->type} = $attr->value;
387
    };
388
    # Submit request to backend, parse response...
389
    my ( $error, $status, $message ) = ( 0, '', '' );
390
    if ( !$value->{status} || $value->{status} eq 'On order' ) {
391
        $error = 1;
392
        $status = 'not_renewed';
393
        $message = 'Order not yet delivered.';
394
    } else {
395
        $value->{status} = "Renewed";
396
    }
397
    # ...then return our result:
398
    return {
399
        error   => $error,
400
        status  => $status,
401
        message => $message,
402
        method  => 'renew',
403
        stage   => 'commit',
404
        value   => $value,
405
    };
406
}
407
408
=head3 update_status
409
410
  my $response = $backend->update_status({
411
      request    => $requestdetails,
412
      other      => $other,
413
  });
414
415
Our Illmodule is handling a request to update the status of an Illrequest.  As
416
part of this we give the backend an opportunity to perform arbitrary actions
417
on update to a new status.
418
419
We will provide $request.  This will be supplied at all times through
420
Illrequest.  $other will contain entries for the old status and the new
421
status, as well as other information provided from templates.
422
423
$old_status, $new_status.
424
425
=cut
426
427
sub update_status {
428
    # -> ILL module update hook: custom actions on status update
429
    my ( $self, $params ) = @_;
430
    # Turn Illrequestattributes into a plain hashref
431
    my $value = {};
432
    my $attributes = $params->{request}->illrequestattributes;
433
    foreach my $attr (@{$attributes->as_list}) {
434
        $value->{$attr->type} = $attr->value;
435
    };
436
    # Submit request to backend, parse response...
437
    my ( $error, $status, $message ) = (0, '', '');
438
    my $old = $params->{other}->{old_status};
439
    my $new = $params->{other}->{new_status};
440
    if ( !$new || $new eq 'ERR' ) {
441
        ( $error, $status, $message ) = (
442
            1, 'failed_update_hook',
443
            'Fake reason for failing to perform update operation.'
444
        );
445
    }
446
    return {
447
        error   => $error,
448
        status  => $status,
449
        message => $message,
450
        method  => 'update_status',
451
        stage   => 'commit',
452
        value   => $value,
453
    };
454
}
455
456
=head3 cancel
457
458
  my $response = $backend->cancel({
459
      request    => $requestdetails,
460
      other      => $other,
461
  });
462
463
We will attempt to cancel a request that was confirmed.
464
465
We will generally use $request.  This will be supplied at all times through
466
Illrequest.  $other may be supplied using templates.
467
468
=cut
469
470
sub cancel {
471
    # -> request an already 'confirm'ed ILL order be cancelled
472
    my ( $self, $params ) = @_;
473
    # Turn Illrequestattributes into a plain hashref
474
    my $value = {};
475
    my $attributes = $params->{request}->illrequestattributes;
476
    foreach my $attr (@{$attributes->as_list}) {
477
        $value->{$attr->type} = $attr->value;
478
    };
479
    # Submit request to backend, parse response...
480
    my ( $error, $status, $message ) = (0, '', '');
481
    if ( !$value->{status} ) {
482
        ( $error, $status, $message ) = (
483
            1, 'unknown_request', 'Cannot cancel an unknown request.'
484
        );
485
    } else {
486
        $attributes->find({ type => "status" })->delete;
487
        $params->{request}->status("REQREV");
488
        $params->{request}->cost(undef);
489
        $params->{request}->orderid(undef);
490
        $params->{request}->store;
491
    }
492
    return {
493
        error   => $error,
494
        status  => $status,
495
        message => $message,
496
        method  => 'cancel',
497
        stage   => 'commit',
498
        value   => $value,
499
    };
500
}
501
502
=head3 status
503
504
  my $response = $backend->create({
505
      request    => $requestdetails,
506
      other      => $other,
507
  });
508
509
We will try to retrieve the status of a specific request.
510
511
We will generally use $request.  This will be supplied at all times through
512
Illrequest.  $other may be supplied using templates.
513
514
=cut
515
516
sub status {
517
    # -> request the current status of a confirmed ILL order
518
    my ( $self, $params ) = @_;
519
    my $value = {};
520
    my $stage = $params->{other}->{stage};
521
    my ( $error, $status, $message ) = (0, '', '');
522
    if ( !$stage || $stage eq 'init' ) {
523
        # Generate status result
524
        # Turn Illrequestattributes into a plain hashref
525
        my $attributes = $params->{request}->illrequestattributes;
526
        foreach my $attr (@{$attributes->as_list}) {
527
            $value->{$attr->type} = $attr->value;
528
        }
529
        ;
530
        # Submit request to backend, parse response...
531
        if ( !$value->{status} ) {
532
            ( $error, $status, $message ) = (
533
                1, 'unknown_request', 'Cannot query status of an unknown request.'
534
            );
535
        }
536
        return {
537
            error   => $error,
538
            status  => $status,
539
            message => $message,
540
            method  => 'status',
541
            stage   => 'status',
542
            value   => $value,
543
        };
544
545
    } elsif ( $stage eq 'status') {
546
        # No more to do for method.  Return to illlist.
547
        return {
548
            error   => $error,
549
            status  => $status,
550
            message => $message,
551
            method  => 'status',
552
            stage   => 'commit',
553
            next    => 'illlist',
554
            value   => {},
555
        };
556
557
    } else {
558
        # Invalid stage, return error.
559
        return {
560
            error   => 1,
561
            status  => 'unknown_stage',
562
            message => '',
563
            method  => 'create',
564
            stage   => $params->{stage},
565
            value   => {},
566
        };
567
    }
568
}
569
570
=head1 AUTHOR
571
572
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
573
574
=cut
575
576
1;
(-)a/Koha/Illrequest/Config.pm (+389 lines)
Line 0 Link Here
1
package Koha::Illrequest::Config;
2
3
# Copyright 2013,2014 PTFS Europe Ltd
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
use C4::Context;
22
23
=head1 NAME
24
25
Koha::Illrequest::Config - Koha ILL Configuration Object
26
27
=head1 SYNOPSIS
28
29
Object-oriented class that giving access to the illconfig data derived
30
from ill/config.yaml.
31
32
=head1 DESCRIPTION
33
34
Config object providing abstract representation of the expected XML
35
returned by ILL API.
36
37
In particular the config object uses a YAML file, whose path is
38
defined by <illconfig> in koha-conf.xml. That YAML file provides the
39
data structure exposed in this object.
40
41
By default the configured data structure complies with fields used by
42
the British Library Interlibrary Loan DSS API.
43
44
The config file also provides mappings for Record Object accessors.
45
46
FIXME: illcomm: In general Config should be split into two parts:
47
those that are high-level and those that are backend specific.  The
48
latter should not get specific accessors, but rather a generic
49
accessor, that takes a setting name as string parameter.
50
51
=head1 API
52
53
=head2 Class Methods
54
55
=head3 new
56
57
    my $config = Koha::Illrequest::Config->new();
58
59
Create a new Koha::Illrequest::Config object, with mapping data loaded from the
60
ILL configuration file.
61
62
=cut
63
64
sub new {
65
    my ( $class, $test ) = @_;
66
    my $self  = {};
67
68
    $self->{configuration} = _load_configuration(
69
        C4::Context->config("interlibrary_loans"),
70
        C4::Context->preference("UnmediatedILL")
71
      ) unless ( $test );
72
73
    bless $self, $class;
74
75
    return $self;
76
}
77
78
=head3 backend
79
80
    $backend = $config->backend($name);
81
    $backend = $config->backend;
82
83
Standard setter/accessor for our backend.
84
85
=cut
86
87
sub backend {
88
    my ( $self, $new ) = @_;
89
    $self->{configuration}->{backend} = $new if $new;
90
    return $self->{configuration}->{backend};
91
}
92
93
=head3 backend_dir
94
95
    $backend_dir = $config->backend_dir($new_path);
96
    $backend_dir = $config->backend_dir;
97
98
Standard setter/accessor for our backend_directory.
99
100
=cut
101
102
sub backend_dir {
103
    my ( $self, $new ) = @_;
104
    $self->{configuration}->{backend_directory} = $new if $new;
105
    return $self->{configuration}->{backend_directory};
106
}
107
108
=head3 partner_code
109
110
    $partner_code = $config->partner_code($new_path);
111
    $partner_code = $config->partner_code;
112
113
Standard setter/accessor for our partner_code.
114
115
=cut
116
117
sub partner_code {
118
    my ( $self, $new ) = @_;
119
    $self->{configuration}->{partner_code} = $new if $new;
120
    return $self->{configuration}->{partner_code};
121
}
122
123
=head3 limits
124
125
    $limits = $config->limits($limitshash);
126
    $limits = $config->limits;
127
128
Standard setter/accessor for our limits.  No parsing is performed on
129
$LIMITSHASH, so caution should be exercised when using this setter.
130
131
=cut
132
133
sub limits {
134
    my ( $self, $new ) = @_;
135
    $self->{configuration}->{limits} = $new if $new;
136
    return $self->{configuration}->{limits};
137
}
138
139
=head3 getPrefixes
140
141
    my $prefixes = $config->getPrefixes('brw_cat' | 'branch');
142
143
Return the prefix for ILLs defined by our config.
144
145
=cut
146
147
sub getPrefixes {
148
    my ( $self, $type ) = @_;
149
    die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' );
150
    my $values = $self->{configuration}->{prefixes}->{$type};
151
    $values->{default} = $self->{configuration}->{prefixes}->{default};
152
    return $values;
153
}
154
155
=head3 getLimitRules
156
157
    my $rules = $config->getLimitRules('brw_cat' | 'branch')
158
159
Return the hash of ILL limit rules defined by our config.
160
161
=cut
162
163
sub getLimitRules {
164
    my ( $self, $type ) = @_;
165
    die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' );
166
    my $values = $self->{configuration}->{limits}->{$type};
167
    $values->{default} = $self->{configuration}->{limits}->{default};
168
    return $values;
169
}
170
171
=head3 getDigitalRecipients
172
173
    my $recipient_rules= $config->getDigitalRecipients('brw_cat' | 'branch');
174
175
Return the hash of digital_recipient settings defined by our config.
176
177
=cut
178
179
sub getDigitalRecipients {
180
    my ( $self, $type ) = @_;
181
    die "Unexpected type." unless ( $type eq 'brw_cat' || $type eq 'branch' );
182
    my $values = $self->{configuration}->{digital_recipients}->{$type};
183
    $values->{default} =
184
        $self->{configuration}->{digital_recipients}->{default};
185
    return $values;
186
}
187
188
=head3 censorship
189
190
    my $censoredValues = $config->censorship($hash);
191
    my $censoredValues = $config->censorship;
192
193
Standard setter/accessor for our limits.  No parsing is performed on $HASH, so
194
caution should be exercised when using this setter.
195
196
Return our censorship values for the OPAC as loaded from the koha-conf.xml, or
197
the fallback value (no censorship).
198
199
=cut
200
201
sub censorship {
202
    my ( $self, $new ) = @_;
203
    $self->{configuration}->{censorship} = $new if $new;
204
    return $self->{configuration}->{censorship};
205
}
206
207
=head3 _load_configuration
208
209
    my $configuration = $config->_load_configuration($config_from_xml);
210
211
Read the configuration values passed as the parameter, and populate a hashref
212
suitable for use with these.
213
214
A key task performed here is the parsing of the input in the configuration
215
file to ensure we have only valid input there.
216
217
=cut
218
219
sub _load_configuration {
220
    my ( $xml_config, $unmediated ) = @_;
221
    my $xml_backend_dir = $xml_config->{backend_directory};
222
223
    # Default data structure to be returned
224
    my $configuration = {
225
        backend_directory  => $xml_backend_dir,
226
        censorship         => {
227
            censor_notes_staff => 0,
228
            censor_reply_date => 0,
229
        },
230
        limits             => {},
231
        digital_recipients => {},
232
        prefixes           => {},
233
        partner_code       => 'ILLLIBS',
234
    };
235
236
    # Per Branch Configuration
237
    my $branches = $xml_config->{branch};
238
    if ( ref($branches) eq "ARRAY" ) {
239
        # Multiple branch overrides defined
240
        map {
241
            _load_unit_config({
242
                unit   => $_,
243
                id     => $_->{code},
244
                config => $configuration,
245
                type   => 'branch'
246
            })
247
        } @{$branches};
248
    } elsif ( ref($branches) eq "HASH" ) {
249
        # Single branch override defined
250
        _load_unit_config({
251
            unit   => $branches,
252
            id     => $branches->{code},
253
            config => $configuration,
254
            type   => 'branch'
255
        });
256
    }
257
258
    # Per Borrower Category Configuration
259
    my $brw_cats = $xml_config->{borrower_category};
260
    if ( ref($brw_cats) eq "ARRAY" ) {
261
        # Multiple borrower category overrides defined
262
        map {
263
            _load_unit_config({
264
                unit   => $_,
265
                id     => $_->{code},
266
                config => $configuration,
267
                type   => 'brw_cat'
268
            })
269
        } @{$brw_cats};
270
    } elsif ( ref($brw_cats) eq "HASH" ) {
271
        # Single branch override defined
272
        _load_unit_config({
273
            unit   => $brw_cats,
274
            id     => $brw_cats->{code},
275
            config => $configuration,
276
            type   => 'brw_cat'
277
        });
278
    }
279
280
    # Default Configuration
281
    _load_unit_config({
282
        unit   => $xml_config,
283
        id     => 'default',
284
        config => $configuration
285
    });
286
287
    # Censorship
288
    my $staff_comments = $xml_config->{staff_request_comments};
289
    $configuration->{censorship}->{censor_notes_staff} = 1
290
        if ( $staff_comments && 'hide' eq $staff_comments );
291
    my $reply_date = $xml_config->{reply_date};
292
    if ( 'hide' eq $reply_date ) {
293
        $configuration->{censorship}->{censor_reply_date} = 1;
294
    } else {
295
        $configuration->{censorship}->{censor_reply_date} = $reply_date;
296
    }
297
298
    # ILL Partners
299
    $configuration->{partner_code} = $xml_config->{partner_code} || 'ILLLIBS';
300
301
    die "No DEFAULT_FORMATS has been defined in koha-conf.xml, but UNMEDIATEDILL is active."
302
        if ( $unmediated && !$configuration->{default_formats}->{default} );
303
304
    return $configuration;
305
}
306
307
=head3 _load_unit_config
308
309
    my $configuration->{part} = _load_unit_config($params);
310
311
$PARAMS is a hashref with the following elements:
312
- unit: the part of the configuration we are parsing.
313
- id: the name within which we will store the parsed unit in config.
314
- config: the configuration we are augmenting.
315
- type: the type config unit we are parsing.  Assumed to be 'default'.
316
317
Read `unit', and augment `config' with these under `id'.
318
319
This is a helper for _load_configuration.
320
321
A key task performed here is the parsing of the input in the configuration
322
file to ensure we have only valid input there.
323
324
=cut
325
326
sub _load_unit_config {
327
    my ( $params ) = @_;
328
    my $unit = $params->{unit};
329
    my $id = $params->{id};
330
    my $config = $params->{config};
331
    my $type = $params->{type};
332
    return $config unless $id;
333
334
    if ( $unit->{api_key} && $unit->{api_auth} ) {
335
        $config->{credentials}->{api_keys}->{$id} = {
336
            api_key  => $unit->{api_key},
337
            api_auth => $unit->{api_auth},
338
        };
339
    }
340
    # Add request_limit rules.
341
    # METHOD := 'annual' || 'active'
342
    # COUNT  := x >= -1
343
    if ( ref $unit->{request_limit} eq 'HASH' ) {
344
        my $method  = $unit->{request_limit}->{method};
345
        my $count = $unit->{request_limit}->{count};
346
        if ( 'default' eq $id ) {
347
            $config->{limits}->{$id}->{method}  = $method
348
                if ( $method && ( 'annual' eq $method || 'active' eq $method ) );
349
            $config->{limits}->{$id}->{count} = $count
350
                if ( $count && ( -1 <= $count ) );
351
        } else {
352
            $config->{limits}->{$type}->{$id}->{method}  = $method
353
                if ( $method && ( 'annual' eq $method || 'active' eq $method ) );
354
            $config->{limits}->{$type}->{$id}->{count} = $count
355
                if ( $count && ( -1 <= $count ) );
356
        }
357
    }
358
359
    # Add prefix rules.
360
    # PREFIX := string
361
    if ( $unit->{prefix} ) {
362
        if ( 'default' eq $id ) {
363
            $config->{prefixes}->{$id} = $unit->{prefix};
364
        } else {
365
            $config->{prefixes}->{$type}->{$id} = $unit->{prefix};
366
        }
367
    }
368
369
    # Add digital_recipient rules.
370
    # DIGITAL_RECIPIENT := borrower || branch (defaults to borrower)
371
    if ( $unit->{digital_recipient} ) {
372
        if ( 'default' eq $id ) {
373
            $config->{digital_recipients}->{$id} = $unit->{digital_recipient};
374
        } else {
375
            $config->{digital_recipients}->{$type}->{$id} =
376
                $unit->{digital_recipient};
377
        }
378
    }
379
380
    return $config;
381
}
382
383
=head1 AUTHOR
384
385
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
386
387
=cut
388
389
1;
(-)a/Koha/Illrequestattribute.pm (+51 lines)
Line 0 Link Here
1
package Koha::Illrequestattribute;
2
3
# Copyright PTFS Europe 2016
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15
# details.
16
#
17
# You should have received a copy of the GNU General Public License along with
18
# Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin
19
# Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21
use Modern::Perl;
22
23
use Koha::Database;
24
25
use base qw(Koha::Object);
26
27
=head1 NAME
28
29
Koha::Illrequestattribute - Koha Illrequestattribute Object class
30
31
=head1 API
32
33
=head2 Class Methods
34
35
=cut
36
37
=head3 type
38
39
=cut
40
41
sub _type {
42
    return 'Illrequestattribute';
43
}
44
45
=head1 AUTHOR
46
47
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
48
49
=cut
50
51
1;
(-)a/Koha/Illrequestattributes.pm (+55 lines)
Line 0 Link Here
1
package Koha::Illrequestattributes;
2
3
# Copyright PTFS Europe 2016
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
22
use Koha::Database;
23
use Koha::Illrequestattribute;
24
25
use base qw(Koha::Objects);
26
27
=head1 NAME
28
29
Koha::Illrequestattributes - Koha Illrequestattributes Object class
30
31
=head1 API
32
33
=head2 Class Methods
34
35
=cut
36
37
=head3 type
38
39
=cut
40
41
sub _type {
42
    return 'Illrequestattribute';
43
}
44
45
sub object_class {
46
    return 'Koha::Illrequestattribute';
47
}
48
49
=head1 AUTHOR
50
51
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
52
53
=cut
54
55
1;
(-)a/Koha/Illrequests.pm (+99 lines)
Line 0 Link Here
1
package Koha::Illrequests;
2
3
# Copyright PTFS Europe 2016
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
22
use Koha::Database;
23
use Koha::Illrequest;
24
use Koha::Illrequest::Config;
25
26
use base qw(Koha::Objects);
27
28
=head1 NAME
29
30
Koha::Illrequests - Koha Illrequests Object class
31
32
=head1 API
33
34
=head2 Class Methods
35
36
=cut
37
38
=head3 type
39
40
=cut
41
42
sub _type {
43
    return 'Illrequest';
44
}
45
46
sub object_class {
47
    return 'Koha::Illrequest';
48
}
49
50
##### To be implemented Facade
51
52
=head3 new
53
54
    my $illRequests = Koha::Illrequests->new();
55
56
Create an ILLREQUESTS object, a singleton through which we can interact with
57
ILLREQUEST objects stored in the database or search for ILL candidates at API
58
backends.
59
60
=cut
61
62
sub new {
63
    my ( $class, $attributes ) = @_;
64
65
    my $self = $class->SUPER::new($class, $attributes);
66
67
    # FIXME: Unless we can inject config into Illrequest objects, strip this
68
    # out.
69
    my $config = Koha::Illrequest::Config->new; # <- Necessary
70
    $self->{_config} = $config;                 # <- Necessary
71
72
    return $self;
73
}
74
75
=head3 search_incomplete
76
77
    my $requests = $illRequests->search_incomplete;
78
79
A specialised version of `search`, returning all requests currently
80
not considered completed.
81
82
=cut
83
84
sub search_incomplete {
85
    my ( $self ) = @_;
86
    $self->search( {
87
        status => [
88
            -and => { '!=', 'COMP' }, { '!=', 'GENCOMP' }
89
        ]
90
    } );
91
}
92
93
=head1 AUTHOR
94
95
Alex Sassmannshausen <alex.sassmannshausen@ptfs-europe.com>
96
97
=cut
98
99
1;
(-)a/Koha/REST/V1/Illrequests.pm (+85 lines)
Line 0 Link Here
1
package Koha::REST::V1::Illrequests;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 3 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18
use Modern::Perl;
19
20
use Mojo::Base 'Mojolicious::Controller';
21
22
use Koha::Illrequests;
23
use Koha::Library;
24
25
sub list {
26
    my ($c, $args, $cb) = @_;
27
28
    my $filter;
29
    $args //= {};
30
    my $output = [];
31
32
    # Create a hash where all keys are embedded values
33
    # Enables easy checking
34
    my %embed;
35
    if (defined $args->{embed}) {
36
        %embed = map { $_ => 1 }  @{$args->{embed}};
37
        delete $args->{embed};
38
    }
39
40
    for my $filter_param ( keys %$args ) {
41
        my @values = split(/,/, $args->{$filter_param});
42
        $filter->{$filter_param} = \@values;
43
    }
44
45
    my $requests = Koha::Illrequests->search($filter);
46
47
    while (my $request = $requests->next) {
48
        my $unblessed = $request->unblessed;
49
        # Add the request's id_prefix
50
        $unblessed->{id_prefix} = $request->id_prefix;
51
        # Augment the request response with patron details
52
        # if appropriate
53
        if (defined $embed{patron}) {
54
            my $patron = $request->patron;
55
            $unblessed->{patron} = {
56
                firstname  => $patron->firstname,
57
                surname    => $patron->surname,
58
                cardnumber => $patron->cardnumber
59
            };
60
        }
61
        # Augment the request response with metadata details
62
        # if appropriate
63
        if (defined $embed{metadata}) {
64
            $unblessed->{metadata} = $request->metadata;
65
        }
66
        # Augment the request response with status details
67
        # if appropriate
68
        if (defined $embed{capabilities}) {
69
            $unblessed->{capabilities} = $request->capabilities;
70
        }
71
        # Augment the request response with branch details
72
        # if appropriate
73
        if (defined $embed{branch}) {
74
            $unblessed->{branch} = Koha::Libraries->find(
75
                $request->branchcode
76
            )->unblessed;
77
        }
78
        push @{$output}, $unblessed
79
    }
80
81
    return $c->$cb( $output, 200 );
82
83
}
84
85
1;
(-)a/api/v1/swagger/paths.json (+3 lines)
Lines 16-20 Link Here
16
  },
16
  },
17
  "/patrons/{borrowernumber}": {
17
  "/patrons/{borrowernumber}": {
18
    "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}"
18
    "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}"
19
  },
20
  "/illrequests": {
21
    "$ref": "paths/illrequests.json#/~1illrequests"
19
  }
22
  }
20
}
23
}
(-)a/api/v1/swagger/paths/illrequests.json (+98 lines)
Line 0 Link Here
1
{
2
    "/illrequests": {
3
        "get": {
4
            "x-mojo-controller": "Koha::REST::V1::Illrequests",
5
            "operationId": "list",
6
            "tags": ["illrequests"],
7
            "parameters": [{
8
                "name": "embed",
9
                "in": "query",
10
                "description": "Additional objects that should be embedded in the response",
11
                "required": false,
12
                "type": "array",
13
                "collectionFormat": "csv",
14
                "items": {
15
                    "type": "string",
16
                    "enum": [
17
                        "patron",
18
                        "branch",
19
                        "capabilities"
20
                    ]
21
                }
22
            }, {
23
                "name": "backend",
24
                "in": "query",
25
                "description": "The name of a ILL backend",
26
                "required": false,
27
                "type": "string"
28
            }, {
29
                "name": "orderid",
30
                "in": "query",
31
                "description": "The order ID of a request",
32
                "required": false,
33
                "type": "string"
34
            }, {
35
                "name": "biblio_id",
36
                "in": "query",
37
                "description": "The biblio ID associated with a request",
38
                "required": false,
39
                "type": "integer"
40
            }, {
41
                "name": "borrower_id",
42
                "in": "query",
43
                "description": "The borrower ID associated with a request",
44
                "required": false,
45
                "type": "integer"
46
            }, {
47
                "name": "completed",
48
                "in": "query",
49
                "description": "The date the request was considered completed",
50
                "required": false,
51
                "type": "string"
52
            }, {
53
                "name": "status",
54
                "in": "query",
55
                "description": "A full status string e.g. REQREV",
56
                "required": false,
57
                "type": "string"
58
            }, {
59
                "name": "medium",
60
                "in": "query",
61
                "description": "The medium of the requested item",
62
                "required": false,
63
                "type": "string"
64
            }, {
65
                "name": "updated",
66
                "in": "query",
67
                "description": "The last updated date of the request",
68
                "required": false,
69
                "type": "string"
70
            }, {
71
                "name": "placed",
72
                "in": "query",
73
                "description": "The date the request was placed",
74
                "required": false,
75
                "type": "string"
76
            }, {
77
                "name": "branch_id",
78
                "in": "query",
79
                "description": "The ID of the pickup branch",
80
                "required": false,
81
                "type": "string"
82
            }],
83
            "produces": [
84
                "application/json"
85
            ],
86
            "responses": {
87
                "200": {
88
                    "description": "OK"
89
                }
90
            },
91
            "x-koha-authorization": {
92
                "permissions": {
93
                    "borrowers": "1"
94
                }
95
            }
96
        }
97
    }
98
}
(-)a/etc/koha-conf.xml (+21 lines)
Lines 153-157 __PAZPAR2_TOGGLE_XML_POST__ Link Here
153
 <plack_max_requests>50</plack_max_requests>
153
 <plack_max_requests>50</plack_max_requests>
154
 <plack_workers>2</plack_workers>
154
 <plack_workers>2</plack_workers>
155
155
156
 <interlibrary_loans>
157
     <!-- Path to where Illbackends are located on the system
158
          - This setting should normally not be touched -->
159
     <backend_directory>__PERL_MODULE_DIR__/Koha/Illbackends</backend_directory>
160
     <!-- How should we treat staff comments?
161
          - hide: don't show in OPAC
162
          - show: show in OPAC -->
163
     <staff_request_comments>hide</staff_request_comments>
164
     <!-- How should we treat the reply_date field?
165
          - hide: don't show this field in the UI
166
          - any other string: show, with this label -->
167
     <reply_date>hide</reply_date>
168
     <!-- Where should digital ILLs be sent?
169
          - borrower: send it straight to the borrower email
170
          - branch: send the ILL to the branch email -->
171
     <digital_recipient>branch</digital_recipient>
172
     <!-- What patron category should we use for p2p ILL requests?
173
          - By default this is set to 'ILLLIBS' -->
174
     <partner_code>ILLLIBS</partner_code>
175
 </interlibrary_loans>
176
156
</config>
177
</config>
157
</yazgfs>
178
</yazgfs>
(-)a/ill/ill-requests.pl (+238 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2013 PTFS-Europe Ltd and Mark Gavillet
4
# Copyright 2014 PTFS-Europe Ltd
5
#
6
# This file is part of Koha.
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
22
use CGI;
23
24
use C4::Auth;
25
use C4::Output;
26
use Koha::AuthorisedValues;
27
use Koha::Illrequests;
28
use Koha::Libraries;
29
30
my $cgi = CGI->new;
31
my $illRequests = Koha::Illrequests->new;
32
33
# Grab all passed data
34
# 'our' since Plack changes the scoping
35
# of 'my'
36
our $params = $cgi->Vars();
37
38
my $op = $params->{method} || 'illlist';
39
40
my ( $template, $patronnumber, $cookie ) = get_template_and_user( {
41
    template_name => 'ill/ill-requests.tt',
42
    query         => $cgi,
43
    type          => 'intranet',
44
    flagsrequired => { ill => '*' },
45
} );
46
47
if ( $op eq 'illview' ) {
48
    # View the details of an ILL
49
    my $request = Koha::Illrequests->find($params->{illrequest_id});
50
51
    $template->param(
52
        request => $request
53
    );
54
55
} elsif ( $op eq 'create' ) {
56
    # We're in the process of creating a request
57
    my $request = Koha::Illrequest->new
58
        ->load_backend($params->{backend});
59
    my $backend_result = $request->backend_create($params);
60
    $template->param(
61
        whole   => $backend_result,
62
        request => $request
63
    );
64
    handle_commit_maybe($backend_result, $request);
65
66
} elsif ( $op eq 'confirm' ) {
67
    # Backend 'confirm' method
68
    # confirm requires a specific request, so first, find it.
69
    my $request = Koha::Illrequests->find($params->{illrequest_id});
70
    my $backend_result = $request->backend_confirm($params);
71
    $template->param(
72
        whole   => $backend_result,
73
        request => $request,
74
    );
75
76
    # handle special commit rules & update type
77
    handle_commit_maybe($backend_result, $request);
78
79
} elsif ( $op eq 'cancel' ) {
80
    # Backend 'cancel' method
81
    # cancel requires a specific request, so first, find it.
82
    my $request = Koha::Illrequests->find($params->{illrequest_id});
83
    my $backend_result = $request->backend_cancel($params);
84
    $template->param(
85
        whole   => $backend_result,
86
        request => $request,
87
    );
88
89
    # handle special commit rules & update type
90
    handle_commit_maybe($backend_result, $request);
91
92
} elsif ( $op eq 'edit_action' ) {
93
    # Handle edits to the Illrequest object.
94
    # (not the Illrequestattributes)
95
    # We simulate the API for backend requests for uniformity.
96
    # So, init:
97
    my $request = Koha::Illrequests->find($params->{illrequest_id});
98
    if ( !$params->{stage} ) {
99
        my $backend_result = {
100
            error   => 0,
101
            status  => '',
102
            message => '',
103
            method  => 'edit_action',
104
            stage   => 'init',
105
            next    => '',
106
            value   => {}
107
        };
108
        $template->param(
109
            whole   => $backend_result,
110
            request => $request
111
        );
112
    } else {
113
        # Commit:
114
        # Save the changes
115
        $request->borrowernumber($params->{borrowernumber});
116
        $request->biblio_id($params->{biblio_id});
117
        $request->branchcode($params->{branchcode});
118
        $request->notesopac($params->{notesopac});
119
        $request->notesstaff($params->{notesstaff});
120
        $request->store;
121
        my $backend_result = {
122
            error   => 0,
123
            status  => '',
124
            message => '',
125
            method  => 'edit_action',
126
            stage   => 'commit',
127
            next    => 'illlist',
128
            value   => {}
129
        };
130
        handle_commit_maybe($backend_result, $request);
131
    }
132
133
} elsif ( $op eq 'moderate_action' ) {
134
    # Moderate action is required for an ILL submodule / syspref.
135
    # Currently still needs to be implemented.
136
    redirect_to_list();
137
138
} elsif ( $op eq 'delete_confirm') {
139
    my $request = Koha::Illrequests->find($params->{illrequest_id});
140
141
    $template->param(
142
        request => $request
143
    );
144
145
} elsif ( $op eq 'delete' ) {
146
147
    # Check if the request is confirmed, if not, redirect
148
    # to the confirmation view
149
    if ($params->{confirmed} == 1) {
150
        # We simply delete the request...
151
        my $request = Koha::Illrequests->find(
152
            $params->{illrequest_id}
153
        )->delete;
154
        # ... then return to list view.
155
        redirect_to_list();
156
    } else {
157
        print $cgi->redirect(
158
            "/cgi-bin/koha/ill/ill-requests.pl?" .
159
            "method=delete_confirm&illrequest_id=" .
160
            $params->{illrequest_id});
161
    }
162
163
} elsif ( $op eq 'generic_confirm' ) {
164
    my $request = Koha::Illrequests->find($params->{illrequest_id});
165
    $params->{current_branchcode} = C4::Context->mybranch;
166
    my $backend_result = $request->generic_confirm($params);
167
    $template->param(
168
        whole => $backend_result,
169
        request => $request,
170
    );
171
172
    # handle special commit rules & update type
173
    handle_commit_maybe($backend_result, $request);
174
175
} elsif ( $op eq 'illlist') {
176
    # Display all current ILLs
177
    my $requests = $illRequests->search();
178
179
    $template->param(
180
        requests => $requests
181
    );
182
183
    # If we receive a pre-filter, make it available to the template
184
    my $possible_filters = ['borrowernumber'];
185
    my $active_filters = [];
186
    foreach my $filter(@{$possible_filters}) {
187
        if ($params->{$filter}) {
188
            push @{$active_filters},
189
                { name => $filter, value => $params->{$filter}};
190
        }
191
    }
192
    if (scalar @{$active_filters} > 0) {
193
        $template->param(
194
            prefilters => $active_filters
195
        );
196
    }
197
} else {
198
    my $request = Koha::Illrequests->find($params->{illrequest_id});
199
    my $backend_result = $request->custom_capability($op, $params);
200
    $template->param(
201
        whole => $backend_result,
202
        request => $request,
203
    );
204
}
205
206
# Get a list of backends
207
my $ir = Koha::Illrequest->new;
208
209
$template->param(
210
    backends    => $ir->available_backends,
211
    media       => [ "Book", "Article", "Journal" ],
212
    query_type  => $op,
213
    branches    => Koha::Libraries->search->unblessed,
214
    here_link   => "/cgi-bin/koha/ill/ill-requests.pl"
215
);
216
217
output_html_with_http_headers( $cgi, $cookie, $template->output );
218
219
sub handle_commit_maybe {
220
    my ( $backend_result, $request ) = @_;
221
    # We need to special case 'commit'
222
    if ( $backend_result->{stage} eq 'commit' ) {
223
        if ( $backend_result->{next} eq 'illview' ) {
224
            # Redirect to a view of the newly created request
225
            print $cgi->redirect(
226
                '/cgi-bin/koha/ill/ill-requests.pl?method=illview&illrequest_id='.
227
                $request->id
228
            );
229
        } else {
230
            # Redirect to a requests list view
231
            redirect_to_list();
232
        }
233
    }
234
}
235
236
sub redirect_to_list {
237
    print $cgi->redirect('/cgi-bin/koha/ill/ill-requests.pl');
238
}
(-)a/koha-tmpl/intranet-tmpl/prog/css/staff-global.css (+77 lines)
Lines 2995-2997 fieldset.rows + fieldset.action { Link Here
2995
.yui-u .rows li p label.widelabel {
2995
.yui-u .rows li p label.widelabel {
2996
    width: auto;
2996
    width: auto;
2997
}
2997
}
2998
2999
#interlibraryloans h1 {
3000
    margin: 1em 0;
3001
}
3002
3003
#interlibraryloans h2 {
3004
    margin-bottom: 20px;
3005
}
3006
3007
#interlibraryloans h3 {
3008
    margin-top: 20px;
3009
}
3010
3011
#interlibraryloans .bg-info {
3012
    overflow: auto;
3013
    position: relative;
3014
}
3015
3016
#interlibraryloans #search-summary {
3017
    -webkit-transform: translateY(-50%);
3018
    -ms-transform: translateY(-50%);
3019
    -o-transform: translateY(-50%);
3020
    transform: translateY(-50%);
3021
    position: absolute;
3022
    top: 50%;
3023
}
3024
3025
#interlibraryloans .format h5 {
3026
    margin-top: 20px;
3027
}
3028
3029
#interlibraryloans .format li {
3030
    list-style: none;
3031
}
3032
3033
#interlibraryloans .format h4 {
3034
    margin-bottom: 20px;
3035
}
3036
3037
#interlibraryloans .format input {
3038
    margin: 10px 0;
3039
}
3040
3041
#interlibraryloans #freeform-fields .custom-name {
3042
    width: 9em;
3043
    margin-right: 1em;
3044
    text-align: right;
3045
}
3046
3047
#interlibraryloans #freeform-fields .delete-new-field {
3048
    margin-left: 1em;
3049
}
3050
3051
#interlibraryloans #add-new-fields {
3052
    margin: 1em;
3053
}
3054
3055
#ill-view-panel {
3056
    margin-top: 15px;
3057
}
3058
3059
#ill-view-panel h3 {
3060
    margin-bottom: 10px;
3061
}
3062
3063
#ill-view-panel h4 {
3064
    margin-bottom: 20px;
3065
}
3066
3067
#ill-view-panel .rows div {
3068
    height: 1em;
3069
    margin-bottom: 1em;
3070
}
3071
3072
#ill-view-panel #requestattributes .label {
3073
    width: auto;
3074
}
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc (+3 lines)
Lines 112-116 Link Here
112
    [% IF Koha.Preference('HouseboundModule') %]
112
    [% IF Koha.Preference('HouseboundModule') %]
113
        [% IF houseboundview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% borrowernumber %]">Housebound</a></li>
113
        [% IF houseboundview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% borrowernumber %]">Housebound</a></li>
114
    [% END %]
114
    [% END %]
115
    [% IF Koha.Preference('ILLModule') %]
116
        <li><a href="/cgi-bin/koha/ill/ill-requests.pl?borrowernumber=[% borrowernumber %]">Interlibrary loans</a></li>
117
    [% END %]
115
</ul></div>
118
</ul></div>
116
[% END %]
119
[% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/ill-toolbar.inc (+24 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% IF Koha.Preference('ILLModule ') %]
3
    <div id="toolbar" class="btn-toolbar">
4
        [% IF backends.size > 1 %]
5
            <div class="dropdown btn-group">
6
                <button class="btn btn-sm btn-default dropdown-toggle" type="button" id="ill-backend-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
7
                    <i class="fa fa-plus"></i> New ILL request <span class="caret"></span>
8
                </button>
9
                <ul class="dropdown-menu" aria-labelledby="ill-backend-dropdown">
10
                    [% FOREACH backend IN backends %]
11
                        <li><a href="/cgi-bin/koha/ill/ill-requests.pl?method=create&amp;backend=[% backend %]">[% backend %]</a></li>
12
                    [% END %]
13
                </ul>
14
            </div>
15
        [% ELSE %]
16
            <a id="ill-new" class="btn btn-sm btn-default" href="/cgi-bin/koha/ill/ill-requests.pl?method=create&amp;backend=[% backends.0 %]">
17
                <i class="fa fa-plus"></i> New ILL request
18
            </a>
19
        [% END %]
20
        <a id="ill-list" class="btn btn-sm btn-default btn-group" href="/cgi-bin/koha/ill/ill-requests.pl">
21
            <i class="fa fa-list"></i> List requests
22
        </a>
23
    </div>
24
[% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc (+1 lines)
Lines 20-25 Link Here
20
    [%- CASE 'plugins' -%]<span>Koha plugins</span>
20
    [%- CASE 'plugins' -%]<span>Koha plugins</span>
21
    [%- CASE 'lists' -%]<span>Lists</span>
21
    [%- CASE 'lists' -%]<span>Lists</span>
22
    [%- CASE 'clubs' -%]<span>Patron clubs</span>
22
    [%- CASE 'clubs' -%]<span>Patron clubs</span>
23
    [%- CASE 'ill' -%]<span>Create and modify Interlibrary loan requests</span>
23
    [%- END -%]
24
    [%- END -%]
24
[%- END -%]
25
[%- END -%]
25
26
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt (+487 lines)
Line 0 Link Here
1
[% USE Branches %]
2
3
[% INCLUDE 'doc-head-open.inc' %]
4
<title>Koha &rsaquo; ILL requests  &rsaquo;</title>
5
[% INCLUDE 'doc-head-close.inc' %]
6
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.tablesorter.min.js"></script>
7
<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
8
<link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/datatables.css">
9
[% INCLUDE 'datatables.inc' %]
10
<script type="text/javascript">
11
    //<![CDATA[
12
    $(document).ready(function() {
13
        var myTable = $("#ill-requests").DataTable($.extend(true, {}, dataTablesDefaults, {
14
            "aoColumnDefs": [  // Last column shouldn't be sortable or searchable
15
                { "aTargets": [ -1 ], "bSortable": false, "bSearchable": false },
16
            ],
17
            "aaSorting": [[ 8, "desc" ]], // Default sort, updated descending
18
            "processing": true, // Display a message when manipulating
19
            "language": {
20
                "loadingRecords": "Please wait - loading requests...",
21
                "zeroRecords": "No requests were found"
22
            },
23
            "iDisplayLength": 10, // 10 results per page
24
            "sPaginationType": "full_numbers", // Pagination display
25
            "sAjaxDataProp": "", // Data is in the root object of the response
26
            "deferRender": true, // Improve performance on big datasets
27
            "ajax": {
28
                url: "/api/v1/illrequests?embed=metadata,patron,capabilities,branch",
29
                cache: true
30
            },
31
            "columns": [
32
                {
33
                    data: 'orderid',
34
                    className: 'orderid'
35
                },
36
                {
37
                    render: function(data, type, row) {
38
                        return (
39
                            row.metadata.hasOwnProperty('Author') &&
40
                            row.metadata.Author.length > 0
41
                        ) ? row.metadata.Author : 'N/A';
42
                    },
43
                    className: 'title'
44
                },
45
                {
46
                    render: function(data, type, row) {
47
                        return (
48
                            row.metadata.hasOwnProperty('Title') &&
49
                            row.metadata.Title.length > 0
50
                        ) ? row.metadata.Title : 'N/A';
51
                    },
52
                    className: 'title'
53
                },
54
                {
55
                    render: function(data, type, row) {
56
                        return '<a title="View borrower details" ' +
57
                        'href="/cgi-bin/koha/members/moremember.pl?' +
58
                        'borrowernumber='+row.borrowernumber+'">' +
59
                        row.patron.firstname + ' ' + row.patron.surname +
60
                        '</a>';
61
                    },
62
                    className: 'borrower_name'
63
                },
64
                {
65
                    data: 'borrowernumber',
66
                    className: 'borrowernumber'
67
                },
68
                {
69
                    data: 'biblio_id',
70
                    className: 'biblio_id'
71
                },
72
                {
73
                    data: 'branch.branchname',
74
                    className: 'branch_name'
75
                },
76
                {
77
                    render: function(data, type, row) {
78
                        return row.capabilities[row.status].name;
79
                    },
80
                    className: 'status'
81
                },
82
                {
83
                    data: 'updated',
84
                    className: 'updated'
85
                },
86
                {
87
                    data: 'medium',
88
                    className: 'medium'
89
                },
90
                {
91
                    data: 'cost',
92
                    className: 'cost'
93
                },
94
                {
95
                    render: function(data, type, row) {
96
                        return row.id_prefix + row.illrequest_id;
97
                    },
98
                    className: 'request_id'
99
                },
100
                {
101
                    data: null,
102
                    render: function(data, type, row) {
103
                        return '<a class="btn btn-default btn-sm" ' +
104
                        'href="/cgi-bin/koha/ill/ill-requests.pl?' +
105
                        'method=illview&amp;illrequest_id=' +
106
                        row.illrequest_id +
107
                        '">Manage request</a>' +
108
                        '</div>'
109
                    }
110
                }
111
            ]
112
        }));
113
        var filters = $('#ill-requests').data();
114
        if (typeof filters !== 'undefined') {
115
            var filterNames = Object.keys(filters).filter(
116
                function(thisData) {
117
                    return thisData.match(/^filter/);
118
                }
119
            );
120
            filterNames.forEach(function(thisFilter) {
121
                var filterClass = "." + toColumnName(thisFilter);
122
                var regex = '^'+filters[thisFilter]+'$';
123
                myTable.columns(filterClass).search(regex, true, false);
124
            });
125
            myTable.draw();
126
        }
127
128
        function toColumnName(myVal) {
129
            return myVal
130
                .replace(/^filter/, '')
131
                .replace(/([A-Z])/g, "_$1")
132
                .replace(/^_/,'').toLowerCase();
133
        };
134
135
        $('#toggle_requestattributes').click(function() {
136
            $('#requestattributes').toggleClass('content_hidden');
137
        });
138
139
        $('#partner_filter').keyup(function() {
140
            var needle = $('#partner_filter').val();
141
            $('#partners > option').each(function() {
142
                var regex = new RegExp(needle, 'i');
143
                if (
144
                    needle.length == 0 ||
145
                    $(this).is(':selected') ||
146
                    $(this).text().match(regex)
147
                ) {
148
                    $(this).show();
149
                } else {
150
                    $(this).hide();
151
                }
152
            });
153
        });
154
155
    });
156
    //]]>
157
</script>
158
</head>
159
160
<body id="acq_suggestion" class="acq">
161
[% INCLUDE 'header.inc' %]
162
[% INCLUDE 'cat-search.inc' %]
163
164
<div id="breadcrumbs">
165
    <a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo;
166
    [% IF query_type == 'create' %]
167
        <a href=[% parent %]>ILL requests</a> &rsaquo; New request
168
    [% ELSIF query_type == 'status' %]
169
        <a href=[% parent %]>ILL requests</a> &rsaquo; Status
170
    [% ELSE %]
171
        ILL requests
172
    [% END %]
173
</div>
174
175
<div id="doc3" class="yui-t2">
176
    <div id="bd">
177
        <div id="yui-main">
178
            <div id="interlibraryloans" class="yui-b">
179
                [% INCLUDE 'ill-toolbar.inc' %]
180
181
                [% IF whole.error %]
182
                    <h1>Error performing operation</h1>
183
                    <!-- Dispatch on Status -->
184
                    <p>We encountered an error:</p>
185
                    <ol>
186
                        <li>[% whole.status %]</li>
187
                        <li>[% whole.message %]</li>
188
                    </ol>
189
                [% END %]
190
191
                [% IF query_type == 'create' %]
192
                    <h1>New ILL request</h1>
193
                    [% PROCESS $whole.template %]
194
195
                [% ELSIF query_type == 'confirm' %]
196
                    <h1>Confirm ILL request</h1>
197
                    [% PROCESS $whole.template %]
198
199
                [% ELSIF query_type == 'cancel' %]
200
                    <h1>Cancel an confirmed request</h1>
201
                    [% PROCESS $whole.template %]
202
203
                [% ELSIF query_type == 'generic_confirm' %]
204
                    <h1>Place request with partner libraries</h1>
205
                    <!-- Start of GENERIC_EMAIL case -->
206
                    [% IF whole.value.partners %]
207
                       [% ill_url = here_link _ "?method=illview&illrequest_id=" _ request.illrequest_id %]
208
                        <form method="POST" action=[% here_link %]>
209
                            <fieldset class="rows">
210
                                <legend>Interlibrary loan request details</legend>
211
                                <ol>
212
                                    <li>
213
                                        <label for="partner_filter">Filter partner libraries:</label>
214
                                        <input type="text" id="partner_filter">
215
                                    </li>
216
                                    <li>
217
                                        <label for="partners">Select partner libraries:</label>
218
                                        <select size="5" multiple="true" id="partners"
219
                                                name="partners">
220
                                            [% FOREACH partner IN whole.value.partners %]
221
                                                <option value=[% partner.email %]>
222
                                                    [% partner.branchcode _ " - " _ partner.surname %]
223
                                                </option>
224
                                            [% END %]
225
                                        </select>
226
227
                                    </li>
228
                                    <li>
229
                                        <label for="subject">Subject Line</label>
230
                                        <input type="text" name="subject"
231
                                               id="subject" type="text"
232
                                               value="[% whole.value.draft.subject %]"/>
233
                                    </li>
234
                                    <li>
235
                                        <label for="body">Email text:</label>
236
                                        <textarea name="body" id="body" rows="20" cols="80">[% whole.value.draft.body %]</textarea>
237
                                    </li>
238
                                </ol>
239
                                <input type="hidden" value="generic_confirm" name="method">
240
                                <input type="hidden" value="draft" name="stage">
241
                                <input type="hidden" value="[% request.illrequest_id %]" name="illrequest_id">
242
                            </fieldset>
243
                            <fieldset class="action">
244
                                <input type="submit" class="btn btn-default" value="Send email"/>
245
                                <span><a href="[% ill_url %]" title="Return to request details">Cancel</a></span>
246
                            </fieldset>
247
                        </form>
248
                    [% ELSE %]
249
                        <fieldset class="rows">
250
                            <legend>Interlibrary loan request details</legend>
251
                            <p>No partners have been defined yet. Please create appropriate patron records (by default ILLLIBS category).</p>
252
                            <p>Be sure to provide email addresses for these patrons.</p>
253
                            <p><span><a href="[% ill_url %]" title="Return to request details">Cancel</a></span></p>
254
                        </fieldset>
255
                    [% END %]
256
                <!-- generic_confirm ends here -->
257
258
                [% ELSIF query_type == 'edit_action' %]
259
                    <form method="POST" action=[% here_link %]>
260
                        <fieldset class="rows">
261
                            <legend>Request details</legend>
262
                            <ol>
263
                                <li class="borrowernumber">
264
                                    <label for="borrowernumber">Borrower number:</label>
265
                                    <input name="borrowernumber" id="borrowernumber" type="text" value="[% request.borrowernumber %]">
266
                                </li>
267
                                <li class="biblio_id">
268
                                    <label for="biblio_id" class="biblio_id">Biblio number:</label>
269
                                    <input name="biblio_id" id="biblio_id" type="text" value="[% request.biblio_id %]">
270
                                </li>
271
                                <li class="branchcode">
272
                                    <label for="branchcode" class="branchcode">Branch:</label>
273
                                    <select name="branchcode" id="branch">
274
                                        [% FOREACH branch IN branches %]
275
                                            [% IF ( branch.branchcode == request.branchcode ) %]
276
                                                <option value="[% branch.branchcode %]" selected="selected">
277
                                                    [% branch.branchname %]
278
                                                </option>
279
                                            [% ELSE %]
280
                                                <option value="[% branch.branchcode %]">
281
                                                    [% branch.branchname %]
282
                                                </option>
283
                                            [% END %]
284
                                        [% END %]
285
                                    </select>
286
                                </li>
287
                                <li class="status">
288
                                    <label class="status">Status:</label>
289
                                    [% stat = request.status %]
290
                                    [% request.capabilities.$stat.name %]
291
                                </li>
292
                                <li class="updated">
293
                                    <label class="updated">Last updated:</label>
294
                                    [% request.updated %]
295
                                </li>
296
                                <li class="medium">
297
                                    <label class="medium">Request type:</label>
298
                                    [% request.medium %]
299
                                </li>
300
                                <li class="cost">
301
                                    <label class="cost">Cost:</label>
302
                                    [% request.cost %]
303
                                </li>
304
                                <li class="req_id">
305
                                    <label class="req_id">Request number:</label>
306
                                    [% request.id_prefix _ request.illrequest_id %]
307
                                </li>
308
                                <li class="notesstaff">
309
                                    <label for="notesstaff" class="notesstaff">Staff notes:</label>
310
                                    <textarea name="notesstaff" id="notesstaff" rows="5">[% request.notesstaff %]</textarea>
311
                                </li>
312
                                <li class="notesopac">
313
                                    <label for="notesopac" class="notesopac">Opac notes:</label>
314
                                    <textarea name="notesopac" id="notesopac" rows="5">[% request.notesopac %]</textarea>
315
                                </li>
316
                            </ol>
317
                        </fieldset>
318
                        <fieldset class="action">
319
                            <input type="hidden" value="edit_action" name="method">
320
                            <input type="hidden" value="form" name="stage">
321
                            <input type="hidden" value="[% request.illrequest_id %]" name="illrequest_id">
322
                            <input type="submit" value="Submit">
323
                            <a class="cancel" href="/cgi-bin/koha/ill/ill-requests.pl?method=illview&amp;illrequest_id=[% request.id %]">Cancel</a>
324
                        </fieldset>
325
                    </form>
326
327
                [% ELSIF query_type == 'delete_confirm' %]
328
329
                    <div class="dialog alert">
330
                        <h3>Are you sure you wish to delete this request?</h3>
331
                        <p>
332
                            <a class="btn btn-default btn-sm approve" href="?method=delete&amp;illrequest_id=[% request.id %]&amp;confirmed=1"><i class="fa fa-fw fa-check"></i>Yes</a>
333
                            <a class="btn btn-default btn-sm deny" href="?method=illview&amp;illrequest_id=[% request.id %]"><i class="fa fa-fw fa-remove"></i>No</a>
334
                        </p>
335
                    </div>
336
337
338
                [% ELSIF query_type == 'illview' %]
339
                    [% actions = request.available_actions %]
340
                    [% capabilities = request.capabilities %]
341
                    [% req_status = request.status %]
342
                    <h1>Manage ILL request</h1>
343
                    <div id="toolbar" class="btn-toolbar">
344
                        <a title="Edit request" id="ill-toolbar-btn-edit-action" class="btn btn-sm btn-default" href="/cgi-bin/koha/ill/ill-requests.pl?method=edit_action&amp;illrequest_id=[% request.illrequest_id %]">
345
                        <span class="fa fa-pencil"></span>
346
                        Edit request
347
                        </a>
348
                        [% FOREACH action IN actions %]
349
                            [% IF action.method != 0 %]
350
                                <a title="[% action.ui_method_name %]" id="ill-toolbar-btn-[% action.id | lower %]" class="btn btn-sm btn-default" href="/cgi-bin/koha/ill/ill-requests.pl?method=[% action.method %]&amp;illrequest_id=[% request.illrequest_id %]">
351
                                <span class="fa [% action.ui_method_icon %]"></span>
352
                                [% action.ui_method_name %]
353
                                </a>
354
                            [% END %]
355
                        [% END %]
356
                    </div>
357
                    <div id="ill-view-panel" class="panel panel-default">
358
                        <div class="panel-heading">
359
                            <h3>Request details</h3>
360
                        </div>
361
                        <div class="panel-body">
362
                            <h4>Details from library</h4>
363
                            <div class="rows">
364
                                <div class="orderid">
365
                                    <span class="label orderid">Order ID:</span>
366
                                    [% request.orderid || "N/A" %]
367
                                </div>
368
                                <div class="borrowernumber">
369
                                    <span class="label borrowernumber">Borrower:</span>
370
                                    [% borrowerlink = "/cgi-bin/koha/members/moremember.pl"
371
                                    _ "?borrowernumber=" _ request.patron.borrowernumber %]
372
                                    <a href="[% borrowerlink %]" title="View borrower details">
373
                                    [% request.patron.firstname _ " "
374
                                    _ request.patron.surname _ " ["
375
                                    _ request.patron.cardnumber
376
                                    _ "]" %]
377
                                    </a>
378
                                </div>
379
380
                                <div class="biblio_id">
381
                                    <span class="label biblio_id">Biblio number:</span>
382
                                    [% request.biblio_id || "N/A" %]
383
                                </div>
384
                                <div class="branchcode">
385
                                    <span class="label branchcode">Branch:</span>
386
                                    [% Branches.GetName(request.branchcode) %]
387
                                </div>
388
                                <div class="status">
389
                                    <span class="label status">Status:</span>
390
                                    [% capabilities.$req_status.name %]
391
                                </div>
392
                                <div class="updated">
393
                                    <span class="label updated">Last updated:</span>
394
                                    [% request.updated %]
395
                                </div>
396
                                <div class="medium">
397
                                    <span class="label medium">Request type:</span>
398
                                    [% request.medium %]
399
                                </div>
400
                                <div class="cost">
401
                                    <span class="label cost">Cost:</span>
402
                                    [% request.cost || "N/A" %]
403
                                </div>
404
                                <div class="req_id">
405
                                    <span class="label req_id">Request number:</span>
406
                                    [% request.id_prefix _ request.illrequest_id %]
407
                                </div>
408
                                <div class="notesstaff">
409
                                    <span class="label notes_staff">Staff notes:</span>
410
                                    <pre>[% request.notesstaff %]</pre>
411
                                </div>
412
                                <div class="notesopac">
413
                                    <span class="label notes_opac">Notes:</span>
414
                                    <pre>[% request.notesopac %]</pre>
415
                                </div>
416
                            </div>
417
                            <div class="rows">
418
                                <h4>Details from supplier ([% request.backend %])</h4>
419
                                [% FOREACH meta IN request.metadata %]
420
                                    <div class="requestmeta-[% meta.key %]">
421
                                        <span class="label">[% meta.key %]:</span>
422
                                        [% meta.value %]
423
                                    </div>
424
                                [% END %]
425
                            </div>
426
                            <div class="rows">
427
                                <h3><a id="toggle_requestattributes" href="#">Toggle full supplier metadata</a></h3>
428
                                <div id="requestattributes" class="content_hidden">
429
                                    [% FOREACH attr IN request.illrequestattributes %]
430
                                        <div class="requestattr-[% attr.type %]">
431
                                            <span class="label">[% attr.type %]:</span>
432
                                            [% attr.value %]
433
                                        </div>
434
                                    [% END %]
435
                                </div>
436
437
                            </div>
438
                        </div>
439
                    </div>
440
441
                [% ELSIF query_type == 'illlist' %]
442
                    <!-- illlist -->
443
                    <h1>View ILL requests</h1>
444
                    <div id="results">
445
                        <h3>Details for all requests</h3>
446
                        <table
447
                            [% FOREACH filter IN prefilters %]
448
                            data-filter-[% filter.name %]="[% filter.value %]"
449
                            [% END %]
450
                            id="ill-requests">
451
                            <thead>
452
                                <tr>
453
                                    <th id="orderid">Order ID:</th>
454
                                    <th id="author">Author:</th>
455
                                    <th id="title">Title:</th>
456
                                    <th id="borrower_name">Borrower:</th>
457
                                    <th id="borrowernumber">Borrower number:</th>
458
                                    <th id="biblio_id">Biblio number:</th>
459
                                    <th id="branchcode">Branch:</th>
460
                                    <th id="status">Status:</th>
461
                                    <th id="updated">Last updated:</th>
462
                                    <th id="medium">Request type:</th>
463
                                    <th id="cost">Cost:</th>
464
                                    <th id="req_id">Request number:</th>
465
                                    <th id="link">Manage request:</th>
466
                                </tr>
467
                            </thead>
468
                            <tbody>
469
                            </tbody>
470
                        </table>
471
                    </div>
472
                [% ELSE %]
473
                <!-- Custom Backend Action -->
474
                [% INCLUDE $whole.template %]
475
476
                [% END %]
477
            </div>
478
        </div>
479
    </div>
480
</div>
481
482
[% TRY %]
483
[% PROCESS backend_jsinclude %]
484
[% CATCH %]
485
[% END %]
486
487
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt (+5 lines)
Lines 61-66 Link Here
61
                    <li>
61
                    <li>
62
                        <a class="icon_general icon_authorities" href="/cgi-bin/koha/authorities/authorities-home.pl">Authorities</a>
62
                        <a class="icon_general icon_authorities" href="/cgi-bin/koha/authorities/authorities-home.pl">Authorities</a>
63
                    </li>
63
                    </li>
64
                    [% IF Koha.Preference('ILLModule') %]
65
                    <li>
66
                        <a class="icon_general icon_ill" href="/cgi-bin/koha/ill/ill-requests.pl">ILL requests</a>
67
                    </li>
68
                    [% END %]
64
                </ul>
69
                </ul>
65
            </div><!-- /area-list-left -->
70
            </div><!-- /area-list-left -->
66
        </div><!-- /yui-u first -->
71
        </div><!-- /yui-u first -->
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc (+9 lines)
Lines 104-109 Link Here
104
                [% END %]
104
                [% END %]
105
                <a href="/cgi-bin/koha/opac-discharge.pl">ask for a discharge</a></li>
105
                <a href="/cgi-bin/koha/opac-discharge.pl">ask for a discharge</a></li>
106
            [% END %]
106
            [% END %]
107
108
            [% IF Koha.Preference( 'ILLModule' ) == 1 %]
109
                [% IF ( illrequestsview ) %]
110
                    <li class="active">
111
                [% ELSE %]
112
                    <li>
113
                [% END %]
114
                <a href="/cgi-bin/koha/opac-illrequests.pl">your interlibrary loan requests</a></li>
115
            [% END %]
107
        </ul>
116
        </ul>
108
    </div>
117
    </div>
109
[% END %]
118
[% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-illrequests.tt (+219 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% USE Branches %]
3
[% INCLUDE 'doc-head-open.inc' %]
4
<title>[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog &rsaquo;   Your Interlibrary loan requests</title>[% INCLUDE 'doc-head-close.inc' %]
5
[% BLOCK cssinclude %][% END %]
6
</head>
7
[% INCLUDE 'bodytag.inc' bodyid='opac-illrequests' bodyclass='scrollto' %]
8
[% BLOCK messages %]
9
    [% IF message == "1" %]
10
        <div class="alert alert-success" role="alert">Request updated</div>
11
    [% ELSIF message == "2" %]
12
        <div class="alert alert-success" role="alert">Request placed</div>
13
    [% END %]
14
[% END %]
15
[% INCLUDE 'masthead.inc' %]
16
<div class="main">
17
    <ul class="breadcrumb noprint">
18
        <li><a href="/cgi-bin/koha/opac-main.pl">Home</a> <span class="divider">&rsaquo;</span></li>
19
        [% IF ( loggedinusername ) %]
20
            <li><a href="/cgi-bin/koha/opac-user.pl">[% USER_INFO.title %] [% USER_INFO.firstname %] [% USER_INFO.surname %]</a> <span class="divider">&rsaquo;</span></li>
21
        [% END %]
22
23
        [% IF method != 'list' %]
24
            <li><a href="/cgi-bin/koha/opac-illrequests.pl">Interlibrary loan requests</a> <span class="divider">&rsaquo;</span></li>
25
            [% IF method == 'create' %]
26
                <li>New Interlibrary loan request</li>
27
            [% ELSIF method == 'view' %]
28
                <li>View Interlibrary loan request</li>
29
            [% END %]
30
        [% ELSE %]
31
            <li>Interlibrary loan requests</li>
32
        [% END %]
33
34
    </ul> <!-- / .breadcrumb -->
35
36
<div class="container-fluid">
37
    <div class="row-fluid">
38
        [% IF ( OpacNav||loggedinusername ) && !print %]
39
            <div class="span2">
40
                <div id="navigation">
41
                    [% INCLUDE 'navigation.inc' IsPatronPage=1 %]
42
                </div>
43
            </div>
44
        [% END %]
45
46
        [% IF ( OpacNav||loggedinusername ) %]
47
            <div class="span10">
48
        [% ELSE %]
49
            <div class="span12">
50
        [% END %]
51
            <div id="illrequests" class="maincontent">
52
                [% IF method == 'create' %]
53
                    <h2>New Interlibrary loan request</h2>
54
                    [% INCLUDE messages %]
55
                    [% IF backends %]
56
                        <form method="post" id="illrequestcreate-form" novalidate="novalidate">
57
                            <fieldset class="rows">
58
                                <label for="backend">Provider:</label>
59
                                <select name="backend">
60
                                    [% FOREACH backend IN backends %]
61
                                        <option value="[% backend %]">[% backend %]</option>
62
                                    [% END %]
63
                                </select>
64
                            </fieldset>
65
                            <fieldset class="action">
66
                                <input type="hidden" name="method" value="create">
67
                                <input type="submit" name="create_select_backend" value="Next &raquo;">
68
                            </fieldset>
69
                        </form>
70
                    [% ELSE %]
71
                        [% PROCESS $whole.opac_template %]
72
                    [% END %]
73
                [% ELSIF method == 'list' %]
74
                    <h2>Interlibrary loan requests</h2>
75
                    [% INCLUDE messages %]
76
77
                    <div id="illrequests-create-button" class="dropdown btn-group">
78
                        [% IF backends.size > 1 %]
79
                                <button class="btn btn-default dropdown-toggle" type="button" id="ill-backend-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
80
                                    <i class="fa fa-plus"></i> Create a new request <span class="caret"></span>
81
                                </button>
82
                                <ul id="backend-dropdown-options" class="dropdown-menu nojs" aria-labelledby="ill-backend-dropdown">
83
                                    [% FOREACH backend IN backends %]
84
                                        <li><a href="/cgi-bin/koha/opac-illrequests.pl?method=create&amp;backend=[% backend %]">[% backend %]</a></li>
85
                                    [% END %]
86
                                </ul>
87
                        [% ELSE %]
88
                            <a id="ill-new" class="btn btn-default" href="/cgi-bin/koha/opac-illrequests.pl?method=create&amp;backend=[% backends.0 %]">
89
                                <i class="fa fa-plus"></i> Create a new request
90
                            </a>
91
                        [% END %]
92
                    </div>
93
94
                    <table id="illrequestlist" class="table table-bordered table-striped">
95
                        <thead>
96
                            <tr>
97
                                <th>Author</th>
98
                                <th>Title</th>
99
                                <th>Requested from</th>
100
                                <th>Request type</th>
101
                                <th>Status</th>
102
                                <th>Last updated</th>
103
                                <th></th>
104
                            </tr>
105
                        </thead>
106
                        <tbody>
107
                            [% FOREACH request IN requests %]
108
                                [% status = request.status %]
109
                                <tr>
110
                                    <td>[% request.metadata.Author || 'N/A' %]</td>
111
                                    <td>[% request.metadata.Title || 'N/A' %]</td>
112
                                    <td>[% request.backend %]</td>
113
                                    <td>[% request.medium %]</td>
114
                                    <td>[% request.capabilities.$status.name %]</td>
115
                                    <td>[% request.updated %]</td>
116
                                    <td>
117
                                        <a href="/cgi-bin/koha/opac-illrequests.pl?method=view&amp;illrequest_id=[% request.id %]" class="btn btn-default btn-small pull-right">View</a>
118
                                    </td>
119
                                </tr>
120
                            [% END %]
121
                        </tbody>
122
                    </table>
123
                [% ELSIF method == 'view' %]
124
                    <h2>View Interlibrary loan request</h2>
125
                    [% INCLUDE messages %]
126
                    [% status = request.status %]
127
                    <form method="post" action="?method=update" id="illrequestupdate-form" novalidate="novalidate">
128
                            <fieldset class="rows">
129
                                <legend id="library_legend">Details from library</legend>
130
                                <ol>
131
                                    <li>
132
                                        <label for="backend">Requested from:</label>
133
                                        [% request.backend %]
134
                                    </li>
135
                                    [% IF request.biblio_id %]
136
                                        <li>
137
                                            <label for="biblio">Requested item:</label>
138
                                            <a href="/cgi-bin/koha/opac-detail.pl?biblionumber=[% request.biblio_id %]">Click here to view</a>
139
                                        </li>
140
                                    [% END %]
141
                                    <li>
142
                                        <label for="branchcode">Collection library:</label>
143
                                        [% Branches.GetName(request.branchcode) %]
144
                                    </li>
145
                                    <li>
146
                                        <label for="status">Status:</label>
147
                                        [% request.capabilities.$status.name %]
148
                                    </li>
149
                                    <li>
150
                                        <label for="medium">Request type:</label>
151
                                        [% request.medium %]
152
                                    </li>
153
                                    <li>
154
                                        <label for="placed">Request placed:</label>
155
                                        [% request.placed %]
156
                                    </li>
157
                                    <li>
158
                                        <label for="updated">Last updated:</label>
159
                                        [% request.updated %]
160
                                    </li>
161
                                    <li>
162
                                        <label for="notesopac">Notes:</label>
163
                                        [% IF !request.completed %]
164
                                            <textarea name="notesopac" rows="5" cols="50">[% request.notesopac %]</textarea>
165
                                        [% ELSE %]
166
                                            [% request.notesopac %]
167
                                        [% END %]
168
                                    </li>
169
                                </ol>
170
                            </fieldset>
171
                            <div class="rows">
172
                                <legend id="backend_legend">Details from [% request.backend %]</legend>
173
                                [% FOREACH meta IN request.metadata %]
174
                                    <div class="requestattr-[% meta.key %]">
175
                                        <span class="label">[% meta.key %]:</span>
176
                                        [% meta.value || 'N/A' %]
177
                                    </div>
178
                                [% END %]
179
                            </div>
180
                            <fieldset class="action illrequest-actions">
181
                                <input type="hidden" name="illrequest_id" value="[% request.illrequest_id %]">
182
                                <input type="hidden" name="method" value="update">
183
                                [% IF !request.completed %]
184
                                    [% IF request.status != "CANCREQ" %]
185
                                        <a class="cancel-illrequest btn btn-danger" href="/cgi-bin/koha/opac-illrequests.pl?method=cancreq&amp;illrequest_id=[% request.illrequest_id %]">Request cancellation</a>
186
                                    [% END %]
187
                                    <input type="submit" class="update-illrequest btn btn-default" value="Submit modifications">
188
                                [% END %]
189
                                <span class="cancel"><a href="/cgi-bin/koha/opac-illrequests.pl">Cancel</a></span>
190
                            </fieldset>
191
                        </form>
192
                    [% END %]
193
                </div> <!-- / .maincontent -->
194
            </div> <!-- / .span10/12 -->
195
        </div> <!-- / .row-fluid -->
196
    </div> <!-- / .container-fluid -->
197
</div> <!-- / .main -->
198
199
[% INCLUDE 'opac-bottom.inc' %]
200
201
[% BLOCK jsinclude %]
202
[% INCLUDE 'datatables.inc' %]
203
<script type="text/javascript">
204
    //<![CDATA[
205
        $("#illrequestlist").dataTable($.extend(true, {}, dataTablesDefaults, {
206
            "aoColumnDefs": [
207
                { "aTargets": [ -1 ], "bSortable": false, "bSearchable": false }
208
            ],
209
            "aaSorting": [[ 3, "desc" ]],
210
            "deferRender": true
211
        }));
212
        $("#backend-dropdown-options").removeClass("nojs");
213
    //]]>
214
</script>
215
[% TRY %]
216
[% PROCESS backend_jsinclude %]
217
[% CATCH %]
218
[% END %]
219
[% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results-grouped.tt (-7 / +23 lines)
Lines 253-266 href="/cgi-bin/koha/opac-rss.pl?[% query_cgi %][% limit_cgi |html %]" /> Link Here
253
                            [% INCLUDE 'page-numbers.inc' %]
253
                            [% INCLUDE 'page-numbers.inc' %]
254
                        [% END # / IF total %]
254
                        [% END # / IF total %]
255
255
256
                        [% IF Koha.Preference( 'suggestion' ) == 1 %]
256
                        [% IF
257
                            [% IF Koha.Preference( 'AnonSuggestions' ) == 1 %]
257
                            Koha.Preference( 'suggestion' ) == 1 &&
258
                                <div class="suggestion">Not finding what you're looking for?<br />  Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></div>
258
                            (
259
                            [% ELSE %]
259
                                Koha.Preference( 'AnonSuggestions' ) == 1 ||
260
                                [% IF ( loggedinusername ) %]<div class="suggestion">Not finding what you're looking for?<br />  Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></div>[% END %]
260
                                loggedinusername ||
261
                            [% END %]
261
                                Koha.Preference( 'ILLModule' ) == 1
262
                            )
263
                        %]
264
                            <div class="suggestion">
265
                                Not finding what you're looking for?
266
                                <ul>
267
                                    [% IF Koha.Preference( 'AnonSuggestions' ) == 1 %]
268
                                        <li>Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></li>
269
                                    [% ELSE %]
270
                                        [% IF ( loggedinusername ) %]
271
                                            <li>Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></li>
272
                                        [% END %]
273
                                    [% END %]
274
                                    [% IF Koha.Preference( 'ILLModule' ) == 1 && loggedinusername %]
275
                                        <li>Make an <a href="/cgi-bin/koha/opac-illrequests.pl?op=create">Interlibrary loan request</a></li>
276
                                    [% END %]
277
                                </ul>
278
                            </div>
262
                        [% END %]
279
                        [% END %]
263
264
                    </div> <!-- / #grouped-results -->
280
                    </div> <!-- / #grouped-results -->
265
                </div> <!-- /.span10/12 -->
281
                </div> <!-- /.span10/12 -->
266
            </div> <!-- / .row-fluid -->
282
            </div> <!-- / .row-fluid -->
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-results.tt (-10 / +23 lines)
Lines 562-577 Link Here
562
562
563
                    [% END # / IF total %]
563
                    [% END # / IF total %]
564
564
565
                    [% IF Koha.Preference( 'suggestion' ) == 1 %]
565
                    [% IF
566
                        [% IF Koha.Preference( 'AnonSuggestions' ) == 1 %]
566
                        Koha.Preference( 'suggestion' ) == 1 &&
567
                            <div class="suggestion">Not finding what you're looking for?<br />  Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></div>
567
                        (
568
                        [% ELSE %]
568
                            Koha.Preference( 'AnonSuggestions' ) == 1 ||
569
                            [% IF ( loggedinusername ) %]
569
                            loggedinusername ||
570
                                <div class="suggestion">
570
                            Koha.Preference( 'ILLModule' ) == 1
571
                                    Not finding what you're looking for?<br />  Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a>
571
                        )
572
                                </div>
572
                    %]
573
                            [% END %]
573
                        <div class="suggestion">
574
                        [% END %]
574
                            Not finding what you're looking for?
575
                            <ul>
576
                                [% IF Koha.Preference( 'AnonSuggestions' ) == 1 %]
577
                                    <li>Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></li>
578
                                [% ELSE %]
579
                                    [% IF ( loggedinusername ) %]
580
                                        <li>Make a <a href="/cgi-bin/koha/opac-suggestions.pl?op=add">purchase suggestion</a></li>
581
                                    [% END %]
582
                                [% END %]
583
                                [% IF Koha.Preference( 'ILLModule' ) == 1 && loggedinusername %]
584
                                    <li>Make an <a href="/cgi-bin/koha/opac-illrequests.pl?op=create">Interlibrary loan request</a></li>
585
                                [% END %]
586
                            </ul>
587
                        </div>
575
                    [% END %]
588
                    [% END %]
576
                    </div> <!-- / #userresults -->
589
                    </div> <!-- / #userresults -->
577
                </div> <!-- /.span10/12 -->
590
                </div> <!-- /.span10/12 -->
(-)a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less (+38 lines)
Lines 2503-2508 a.reviewlink:visited { Link Here
2503
    font-size: 90%;
2503
    font-size: 90%;
2504
}
2504
}
2505
2505
2506
#illrequests {
2507
    .illrequest-actions {
2508
        .btn,
2509
        .cancel {
2510
            margin-right: 5px;
2511
        }
2512
        padding-top: 20px;
2513
        margin-bottom: 20px;
2514
    }
2515
    #illrequests-create-button {
2516
        margin-bottom: 20px;
2517
    }
2518
    .bg-info {
2519
        overflow: auto;
2520
        position: relative;
2521
    }
2522
    .bg-info {
2523
        #search-summary {
2524
            -webkit-transform: translateY(-50%);
2525
            -ms-transform: translateY(-50%);
2526
            -o-transform: translateY(-50%);
2527
            transform: translateY(-50%);
2528
            position: absolute;
2529
            top: 50%;
2530
        }
2531
2532
    }
2533
    #freeform-fields .custom-name {
2534
        float: left;
2535
        width: 8em;
2536
        margin-right: 1em;
2537
        text-align: right;
2538
    }
2539
    .dropdown:hover .dropdown-menu.nojs {
2540
        display: block;
2541
    }
2542
}
2543
2506
#dc_fieldset {
2544
#dc_fieldset {
2507
    border: 1px solid #dddddd;
2545
    border: 1px solid #dddddd;
2508
    border-width: 1px;
2546
    border-width: 1px;
(-)a/opac/opac-illrequests.pl (+129 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2017 PTFS-Europe Ltd
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use CGI qw ( -utf8 );
23
use C4::Auth;
24
use C4::Koha;
25
use C4::Output;
26
27
use Koha::Illrequests;
28
use Koha::Libraries;
29
use Koha::Patrons;
30
31
my $query = new CGI;
32
33
# Grab all passed data
34
# 'our' since Plack changes the scoping
35
# of 'my'
36
our $params = $query->Vars();
37
38
# if illrequests is disabled, leave immediately
39
if ( ! C4::Context->preference('ILLModule') ) {
40
    print $query->redirect("/cgi-bin/koha/errors/404.pl");
41
    exit;
42
}
43
44
my ( $template, $loggedinuser, $cookie ) = get_template_and_user({
45
    template_name   => "opac-illrequests.tt",
46
    query           => $query,
47
    type            => "opac",
48
    authnotrequired => ( C4::Context->preference("OpacPublic") ? 1 : 0 ),
49
});
50
51
my $op = $params->{'method'} || 'list';
52
53
if ( $op eq 'list' ) {
54
55
    my $requests = Koha::Illrequests->search(
56
        { borrowernumber => $loggedinuser }
57
    );
58
    my $req = Koha::Illrequest->new;
59
    $template->param(
60
        requests => $requests,
61
        backends    => $req->available_backends
62
    );
63
64
} elsif ( $op eq 'view') {
65
    my $request = Koha::Illrequests->find({
66
        borrowernumber => $loggedinuser,
67
        illrequest_id  => $params->{illrequest_id}
68
    });
69
    $template->param(
70
        request => $request
71
    );
72
73
} elsif ( $op eq 'update') {
74
    my $request = Koha::Illrequests->find({
75
        borrowernumber => $loggedinuser,
76
        illrequest_id  => $params->{illrequest_id}
77
    });
78
    $request->notesopac($params->{notesopac})->store;
79
    print $query->redirect(
80
        '/cgi-bin/koha/opac-illrequests.pl?method=view&illrequest_id=' .
81
        $params->{illrequest_id} .
82
        '&message=1'
83
    );
84
} elsif ( $op eq 'cancreq') {
85
    my $request = Koha::Illrequests->find({
86
        borrowernumber => $loggedinuser,
87
        illrequest_id  => $params->{illrequest_id}
88
    });
89
    $request->status('CANCREQ')->store;
90
    print $query->redirect(
91
        '/cgi-bin/koha/opac-illrequests.pl?method=view&illrequest_id=' .
92
        $params->{illrequest_id} .
93
        '&message=1'
94
    );
95
96
} elsif ( $op eq 'create' ) {
97
    if (!$params->{backend}) {
98
        my $req = Koha::Illrequest->new;
99
        $template->param(
100
            backends    => $req->available_backends
101
        );
102
    } else {
103
        my $request = Koha::Illrequest->new
104
            ->load_backend($params->{backend});
105
        $params->{cardnumber} = Koha::Patrons->find({
106
            borrowernumber => $loggedinuser
107
        })->cardnumber;
108
        my $backend_result = $request->backend_create($params);
109
        $template->param(
110
            media       => [ "Book", "Article", "Journal" ],
111
            branches    => Koha::Libraries->search->unblessed,
112
            whole       => $backend_result,
113
            request     => $request
114
        );
115
        if ($backend_result->{stage} eq 'commit') {
116
            print $query->redirect('/cgi-bin/koha/opac-illrequests.pl?message=2');
117
        }
118
    }
119
120
121
}
122
123
$template->param(
124
    message         => $params->{message},
125
    illrequestsview => 1,
126
    method              => $op
127
);
128
129
output_html_with_http_headers $query, $cookie, $template->output;
(-)a/t/db_dependent/Illrequest.t (+544 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
#
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 2 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
#
18
19
use Modern::Perl;
20
21
use File::Basename qw/basename/;
22
use Koha::Database;
23
use Koha::Illrequestattributes;
24
use Koha::Patrons;
25
use t::lib::TestBuilder;
26
27
use Test::More tests => 44;
28
29
# We want to test the Koha IllRequest object.  At its core it's a simple
30
# Koha::Object, mapping to the ill_request table.
31
#
32
# This object will supersede the Status object in ILLModule.
33
#
34
# We must ensure perfect backward compatibility between the current model and
35
# the Status less model.
36
37
use_ok('Koha::Illrequest');
38
use_ok('Koha::Illrequests');
39
40
my $schema = Koha::Database->new->schema;
41
$schema->storage->txn_begin;
42
43
my $builder = t::lib::TestBuilder->new;
44
45
my $patron = $builder->build({ source => 'Borrower' });
46
my $branch = $builder->build({ source => 'Branch' });
47
48
my $illRequest = $builder->build({
49
    source => 'Illrequest',
50
    value => {
51
        borrowernumber  => $patron->{borrowernumber},
52
        branch          => $branch->{branchcode},
53
        biblionumber    => 0,
54
        status          => 'NEW',
55
        completion_date => 0,
56
        reqtype         => 'book',
57
    }
58
});
59
60
my $illObject = Koha::Illrequests->find($illRequest->{illrequest_id});
61
62
isa_ok($illObject, "Koha::Illrequest");
63
64
# Test delete works correctly.
65
my $illRequestDelete = $builder->build({
66
    source => 'Illrequest',
67
    value => {
68
        borrowernumber  => $patron->{borrowernumber},
69
        branch          => $branch->{branchcode},
70
        biblionumber    => 0,
71
        status          => 'NEW',
72
        completion_date => 0,
73
        reqtype         => 'book',
74
    }
75
});
76
sub ill_req_search {
77
    return Koha::Illrequestattributes->search({
78
        illrequest_id => $illRequestDelete->{illrequest_id}
79
    })->count;
80
}
81
82
is(ill_req_search, 0, "Correctly not found matching Illrequestattributes.");
83
# XXX: For some reason test builder can't build Illrequestattributes.
84
my $illReqAttr = Koha::Illrequestattribute->new({
85
    illrequest_id => $illRequestDelete->{illrequest_id},
86
    type => "test",
87
    value => "Hello World"
88
})->store;
89
is(ill_req_search, 1, "We have found a matching Illrequestattribute.");
90
91
Koha::Illrequests->find($illRequestDelete->{illrequest_id})->delete;
92
is(
93
    Koha::Illrequests->find($illRequestDelete->{illrequest_id}),
94
    undef,
95
    "Correctly deleted Illrequest."
96
);
97
is(ill_req_search, 0, "Correctly deleted Illrequestattributes.");
98
99
# Test Accessing of related records.
100
101
# # TODO the conclusion from being able to use one_to_many? we no longer need
102
# # the Record object: simply pass the `ill_request_attributes` resultset
103
# # whenever one would pass a Record.
104
105
my $illRequest2 = $builder->build({
106
    source => 'Illrequest',
107
    value  => {
108
        borrower_id => $patron->{borrowernumber},
109
        branch_id   => $branch->{branchcode},
110
        biblio_id   => 0,
111
        status      => 'NEW',
112
        completed   => 0,
113
        medium      => 'book',
114
    }
115
});
116
my $illReqAttr2 = Koha::Illrequestattribute->new({
117
    illrequest_id => $illRequest2->{illrequest_id},
118
    type          => "test2",
119
    value         => "Hello World"
120
})->store;
121
my $illReqAttr3 = Koha::Illrequestattribute->new({
122
    illrequest_id => $illRequest2->{illrequest_id},
123
    type          => "test3",
124
    value         => "Hello Space"
125
})->store;
126
127
my $illRequestAttributes = Koha::Illrequests
128
    ->find($illRequest2->{illrequest_id})->illrequestattributes;
129
130
isa_ok($illRequestAttributes, "Koha::Illrequestattributes");
131
132
is($illRequestAttributes->count, 2, "Able to search related.");
133
134
# Test loading of 'Config'.
135
136
my $rqConfigTest = Koha::Illrequest->new({
137
    borrower_id => $patron->{borrowernumber},
138
    branch_id   => $branch->{branchcode},
139
});
140
141
isa_ok($rqConfigTest->_config, "Koha::Illrequest::Config");
142
143
# Test loading of 'Dummy' backend.
144
145
my $rqBackendTest = Koha::Illrequest->new({
146
    borrower_id => $patron->{borrowernumber},
147
    branch_id   => $branch->{branchcode},
148
})->store;
149
150
$rqBackendTest->_config->backend("Dummy");
151
$rqBackendTest->_config->limits({ default => { count => -1 } });
152
isa_ok($rqBackendTest->_backend, "Koha::Illbackends::Dummy::Base");
153
154
# Test use of 'Dummy' Backend.
155
156
## Test backend_update_status
157
158
# FIXME: This breaks transparancy of ->status method!
159
eval { $rqBackendTest->status("ERR") };
160
ok($@, "status: Test for status error on hook fail.");
161
162
# FIXME: Will need to test this on new illRequest to not pollute rest of
163
# tests.
164
165
# is($rqBackendTest->status("NEW")->status, "NEW", "status: Setter works
166
# OK.");
167
# is($rqBackendTest->status(null), null, "status: Unsetter works OK.");
168
169
## Test backend_create
170
171
is(
172
    $rqBackendTest->status,
173
    undef,
174
    "backend_create: Test our status initiates correctly."
175
);
176
177
# Request a search form
178
my $created_rq = $rqBackendTest->backend_create({
179
    stage  => "search_form",
180
    method => "create",
181
});
182
183
is( $created_rq->{stage}, 'search_results',
184
    "backend_create: search_results stage." );
185
186
# Request search results
187
# FIXME: fails because of missing patron / branch info.
188
# $created_rq = $rqBackendTest->backend_create({
189
#     stage  => "search_results",
190
#     method => "create",
191
#     other  => { search => "interlibrary loans" },
192
# });
193
194
# is_deeply(
195
#     $created_rq,
196
#     {
197
#         error    => 0,
198
#         status   => '',
199
#         message  => '',
200
#         method   => 'create',
201
#         stage    => 'search_results',
202
#         template => 'ill/Dummy/create.inc',
203
#         value    => [
204
#             {
205
#                 id     => 1234,
206
#                 title  => "Ordering ILLs using Koha",
207
#                 author => "A.N. Other",
208
#             },
209
#             {
210
#                 id     => 5678,
211
#                 title  => "Interlibrary loans in Koha",
212
#                 author => "A.N. Other",
213
#             },
214
#         ],
215
#     }
216
#     ,
217
#     "backend_create: search_results stage."
218
# );
219
220
# # Create the request
221
# $created_rq = $rqBackendTest->backend_create({
222
#     stage  => "commit",
223
#     method => "create",
224
#     other  => {
225
#         id     => 1234,
226
#         title  => "Ordering ILLs using Koha",
227
#         author => "A.N. Other",
228
#     },
229
# });
230
231
# while ( my ( $field, $value ) = each %{$created_rq} ) {
232
#     isnt($value, undef, "backend_create: key '$field' exists");
233
# };
234
235
# is(
236
#     $rqBackendTest->status,
237
#     "NEW",
238
#     "backend_create: Test our status was updated."
239
# );
240
241
# cmp_ok(
242
#     $rqBackendTest->illrequestattributes->count,
243
#     "==",
244
#     3,
245
#     "backend_create: Ensure we have correctly stored our new attributes."
246
# );
247
248
# ## Test backend_list
249
250
# is_deeply(
251
#     $rqBackendTest->backend_list->{value},
252
#     {
253
#         1 => {
254
#             id     => 1234,
255
#             title  => "Ordering ILLs using Koha",
256
#             author => "A.N. Other",
257
#             status => "On order",
258
#             cost   => "30 GBP",
259
#         }
260
#     },
261
#     "backend_list: Retrieve our list of requested requests."
262
# );
263
264
# ## Test backend_renew
265
266
# ok(
267
#     $rqBackendTest->backend_renew->{error},
268
#     "backend_renew: Error for invalid request."
269
# );
270
# is_deeply(
271
#     $rqBackendTest->backend_renew->{value},
272
#     {
273
#         id     => 1234,
274
#         title  => "Ordering ILLs using Koha",
275
#         author => "A.N. Other",
276
#     },
277
#     "backend_renew: Renew request."
278
# );
279
280
# ## Test backend_confirm
281
282
# my $rqBackendTestConfirmed = $rqBackendTest->backend_confirm;
283
# is(
284
#     $rqBackendTest->status,
285
#     "REQ",
286
#     "backend_commit: Confirm status update correct."
287
# );
288
# is(
289
#     $rqBackendTest->orderid,
290
#     1234,
291
#     "backend_commit: Confirm orderid populated correctly."
292
# );
293
294
# ## Test backend_status
295
296
# is(
297
#     $rqBackendTest->backend_status->{error},
298
#     0,
299
#     "backend_status: error for invalid request."
300
# );
301
# is_deeply(
302
#     $rqBackendTest->backend_status->{value},
303
#     {
304
#         id     => 1234,
305
#         status => "On order",
306
#         title  => "Ordering ILLs using Koha",
307
#         author => "A.N. Other",
308
#     },
309
#     "backend_status: Retrieve the status of request."
310
# );
311
312
# # Now test trying to get status on non-confirmed request.
313
my $rqBackendTestUnconfirmed = Koha::Illrequest->new({
314
    borrower_id => $patron->{borrowernumber},
315
    branch_id   => $branch->{branchcode},
316
})->store;
317
$rqBackendTestUnconfirmed->_config->backend("Dummy");
318
$rqBackendTestUnconfirmed->_config->limits({ default => { count => -1 } });
319
320
$rqBackendTestUnconfirmed->backend_create({
321
    stage  => "commit",
322
    method => "create",
323
    other  => {
324
        id     => 1234,
325
        title  => "Ordering ILLs using Koha",
326
        author => "A.N. Other",
327
    },
328
});
329
is(
330
    $rqBackendTestUnconfirmed->backend_status->{error},
331
    1,
332
    "backend_status: error for invalid request."
333
);
334
335
## Test backend_cancel
336
337
# is(
338
#     $rqBackendTest->backend_cancel->{error},
339
#     0,
340
#     "backend_cancel: Successfully cancelling request."
341
# );
342
# is_deeply(
343
#     $rqBackendTest->backend_cancel->{value},
344
#     {
345
#         id     => 1234,
346
#         title  => "Ordering ILLs using Koha",
347
#         author => "A.N. Other",
348
#     },
349
#     "backend_cancel: Cancel request."
350
# );
351
352
# Now test trying to cancel non-confirmed request.
353
is(
354
    $rqBackendTestUnconfirmed->backend_cancel->{error},
355
    1,
356
    "backend_cancel: error for invalid request."
357
);
358
is_deeply(
359
    $rqBackendTestUnconfirmed->backend_cancel->{value},
360
    {},
361
    "backend_cancel: Cancel request."
362
);
363
364
# Test Helpers
365
366
## Test getCensorNotesStaff
367
368
is($rqBackendTest->getCensorNotesStaff, 1, "getCensorNotesStaff: Public.");
369
$rqBackendTest->_config->censorship({
370
    censor_notes_staff => 0,
371
    censor_reply_date  => 0,
372
});
373
is($rqBackendTest->getCensorNotesStaff, 0, "getCensorNotesStaff: Censored.");
374
375
## Test getCensorNotesStaff
376
377
is($rqBackendTest->getDisplayReplyDate, 1, "getDisplayReplyDate: Yes.");
378
$rqBackendTest->_config->censorship({
379
    censor_notes_staff => 0,
380
    censor_reply_date  => 1,
381
});
382
is($rqBackendTest->getDisplayReplyDate, 0, "getDisplayReplyDate: No.");
383
384
# FIXME: These should be handled by the templates.
385
# # Test Output Helpers
386
387
# ## Test getStatusSummary
388
389
# $rqBackendTest->medium("Book")->store;
390
# is_deeply(
391
#     $rqBackendTest->getStatusSummary({brw => 0}),
392
#     {
393
#         biblionumber => ["Biblio Number", undef],
394
#         borrowernumber => ["Borrower Number", $patron->{borrowernumber}],
395
#         id => ["Request Number", $rqBackendTest->illrequest_id],
396
#         prefix_id => ["Request Number", $rqBackendTest->illrequest_id],
397
#         reqtype => ["Request Type", "Book"],
398
#         status => ["Status", "REQREV"],
399
#     },
400
#     "getStatusSummary: Without Borrower."
401
# );
402
403
# is_deeply(
404
#     $rqBackendTest->getStatusSummary({brw => 1}),
405
#     {
406
#         biblionumber => ["Biblio Number", undef],
407
#         borrower => ["Borrower", Koha::Patrons->find($patron->{borrowernumber})],
408
#         id => ["Request Number", $rqBackendTest->illrequest_id],
409
#         prefix_id => ["Request Number", $rqBackendTest->illrequest_id],
410
#         reqtype => ["Request Type", "Book"],
411
#         status => ["Status", "REQREV"],
412
#     },
413
#     "getStatusSummary: With Borrower."
414
# );
415
416
# ## Test getFullStatus
417
418
# is_deeply(
419
#     $rqBackendTest->getFullStatus({brw => 0}),
420
#     {
421
#         biblionumber => ["Biblio Number", undef],
422
#         borrowernumber => ["Borrower Number", $patron->{borrowernumber}],
423
#         id => ["Request Number", $rqBackendTest->illrequest_id],
424
#         prefix_id => ["Request Number", $rqBackendTest->illrequest_id],
425
#         reqtype => ["Request Type", "Book"],
426
#         status => ["Status", "REQREV"],
427
#         placement_date => ["Placement Date", $rqBackendTest->placed],
428
#         completion_date => ["Completion Date", $rqBackendTest->completed],
429
#         ts => ["Timestamp", $rqBackendTest->updated],
430
#         branch => ["Branch", $rqBackendTest->branch_id],
431
#     },
432
#     "getFullStatus: Without Borrower."
433
# );
434
435
# is_deeply(
436
#     $rqBackendTest->getFullStatus({brw => 1}),
437
#     {
438
#         biblionumber => ["Biblio Number", undef],
439
#         borrower => ["Borrower", Koha::Patrons->find($patron->{borrowernumber})],
440
#         id => ["Request Number", $rqBackendTest->illrequest_id],
441
#         prefix_id => ["Request Number", $rqBackendTest->illrequest_id],
442
#         reqtype => ["Request Type", "Book"],
443
#         status => ["Status", "REQREV"],
444
#         placement_date => ["Placement Date", $rqBackendTest->placed],
445
#         completion_date => ["Completion Date", $rqBackendTest->completed],
446
#         ts => ["Timestamp", $rqBackendTest->updated],
447
#         branch => ["Branch", $rqBackendTest->branch_id],
448
#     },
449
#     "getFullStatus: With Borrower."
450
# );
451
452
## Test available_backends
453
subtest 'available_backends' => sub {
454
    plan tests => 1;
455
456
    my $rq = Koha::Illrequest->new({
457
        borrower_id => $patron->{borrowernumber},
458
        branch_id   => $branch->{branchcode},
459
    })->store;
460
461
    my @backends = ();
462
    my $backenddir = $rq->_config->backend_dir;
463
    @backends = <$backenddir/*> if ( $backenddir );
464
    @backends = map { basename($_) } @backends;
465
    is_deeply(\@backends, $rq->available_backends,
466
              "Correctly identify available backends.");
467
468
};
469
470
## Test capabilities
471
472
my $rqCapTest = Koha::Illrequest->new({
473
    borrower_id => $patron->{borrowernumber},
474
    branch_id   => $branch->{branchcode},
475
})->store;
476
477
is( keys %{$rqCapTest->_core_status_graph},
478
    @{[ 'NEW', 'REQ', 'REVREQ', 'QUEUED', 'CANCREQ', 'COMP', 'KILL' ]},
479
    "Complete list of core statuses." );
480
481
my $union = $rqCapTest->_status_graph_union(
482
    $rqCapTest->_core_status_graph,
483
    {
484
        TEST => {
485
            prev_actions => [ 'COMP' ],
486
            id           => 'TEST',
487
            name         => "Test",
488
            ui_method_name => "Perform test",
489
            method         => 'test',
490
            next_actions   => [ 'NEW' ]
491
        },
492
        BLAH => {
493
            prev_actions => [ 'COMP' ],
494
            id           => 'BLAH',
495
            name         => "BLAH",
496
            ui_method_name => "Perform test",
497
            method         => 'test',
498
            next_actions   => [ 'NEW' ]
499
        },
500
    }
501
);
502
ok( ( grep 'BLAH', @{$union->{COMP}->{next_actions}} and
503
          grep 'TEST', @{$union->{COMP}->{next_actions}} ),
504
    "next_actions: updated." );
505
ok( ( grep 'BLAH', @{$union->{NEW}->{prev_actions}} and
506
          grep 'TEST', @{$union->{NEW}->{prev_actions}} ),
507
    "next_actions: updated." );
508
509
## Test available_backends
510
subtest 'available_actions' => sub {
511
    plan tests => 1;
512
513
    my $rq = Koha::Illrequest->new({
514
        borrower_id => $patron->{borrowernumber},
515
        branch_id   => $branch->{branchcode},
516
        status      => 'NEW',
517
    })->store;
518
519
    is_deeply(
520
        $rq->available_actions,
521
        [
522
            {
523
                prev_actions   => [ 'NEW', 'REQREV', 'QUEUED' ],
524
                id             => 'REQ',
525
                name           => 'Requested',
526
                ui_method_name => 'Create request',
527
                method         => 'confirm',
528
                next_actions   => [ 'REQREV' ],
529
            },
530
            {
531
                prev_actions   => [ 'CANCREQ', 'QUEUED', 'REQREV', 'NEW' ],
532
                id             => 'KILL',
533
                name           => 0,
534
                ui_method_name => 'Delete request',
535
                method         => 'delete',
536
                next_actions   => [ ],
537
            }
538
        ]
539
    );
540
};
541
542
$schema->storage->txn_rollback;
543
544
1;
(-)a/t/db_dependent/Illrequest/Config.t (+344 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Test::Exception;
21
use Test::More;
22
use Test::Warn;
23
24
# Some data structures that will be repeatedly referenced
25
my $defaults  = {
26
    api_key  => "564euie",
27
    api_auth => "unidaenudvnled",
28
};
29
my $application = {
30
    key  => "6546uedrun",
31
    auth => "edutrineadue",
32
};
33
# Simulate $from_xml
34
my $params = {
35
    application            => $application,
36
    backend                => "Dummy",
37
    configuration          => $defaults,
38
    reply_date             => "hide",
39
    staff_request_comments => "hide",
40
};
41
my $first_branch = {
42
    code => "test", api_key => "dphügnpgüffq", api_auth => "udrend"
43
};
44
my $second_branch = {
45
    code          => "second",
46
    api_key       => "eduirn",
47
    api_auth      => "eudtireand",
48
    request_limit => { count => "5" },
49
};
50
51
BEGIN {
52
    use_ok('Koha::Illrequest::Config');
53
}
54
55
my $config = Koha::Illrequest::Config->new(1); # with test_mode enabled.
56
isa_ok($config, 'Koha::Illrequest::Config');
57
58
# _load_configuration
59
is_deeply(
60
    Koha::Illrequest::Config::_load_configuration($params),
61
    {
62
        api_url         => 'http://apitest.bldss.bl.uk',
63
        backend         => 'Dummy',
64
        censorship      => {
65
            censor_notes_staff => 1,
66
            censor_reply_date => 1,
67
        },
68
        credentials     => {
69
            api_application => $application,
70
            api_keys        => { default => $defaults },
71
        },
72
        default_formats => {},
73
        digital_recipients => {},
74
        library_privileges => {},
75
        limits          => {},
76
        prefixes           => {},
77
        spec_file       => undef,
78
    },
79
    "Basic _load_configuration"
80
);
81
82
$params->{configuration}->{request_limit}->{count} = 10;
83
is_deeply(
84
    Koha::Illrequest::Config::_load_configuration($params),
85
    {
86
        api_url            => 'http://apitest.bldss.bl.uk',
87
        backend            => 'Dummy',
88
        censorship         => {
89
            censor_notes_staff => 1,
90
            censor_reply_date => 1,
91
        },
92
        credentials        => {
93
            api_application => $application,
94
            api_keys        => {
95
                default => {
96
                    api_auth => $defaults->{api_auth},
97
                    api_key  => $defaults->{api_key},
98
                }
99
            },
100
        },
101
        default_formats    => {},
102
        digital_recipients => {},
103
        library_privileges => {},
104
        limits             => { default => { count => 10 } },
105
        prefixes           => {},
106
        spec_file          => undef,
107
    },
108
    "Basic _load_configuration, with limit"
109
);
110
111
$params->{configuration}->{branch} = $first_branch;
112
is_deeply(
113
    Koha::Illrequest::Config::_load_configuration($params),
114
    {
115
        api_url         => 'http://apitest.bldss.bl.uk',
116
        backend         => 'Dummy',
117
        censorship      => {
118
            censor_notes_staff => 1,
119
            censor_reply_date => 1,
120
        },
121
        credentials     => {
122
            api_keys        => {
123
                default => {
124
                    api_key  => $defaults->{api_key},
125
                    api_auth => $defaults->{api_auth},
126
                },
127
                $first_branch->{code} => {
128
                    api_key  => $first_branch->{api_key},
129
                    api_auth => $first_branch->{api_auth},
130
                },
131
            },
132
            api_application => $application,
133
        },
134
        default_formats => {},
135
        digital_recipients => {},
136
        library_privileges => {},
137
        limits          => { default => { count => 10 } },
138
        prefixes           => {},
139
        spec_file       => undef,
140
    },
141
    "Single Branch _load_configuration"
142
);
143
144
$params->{configuration}->{branch} = [ $first_branch, $second_branch ];
145
is_deeply(
146
    Koha::Illrequest::Config::_load_configuration($params),
147
    {
148
        api_url         => 'http://apitest.bldss.bl.uk',
149
        credentials     => {
150
            api_keys        => {
151
                default => {
152
                    api_key  => $defaults->{api_key},
153
                    api_auth => $defaults->{api_auth},
154
                },
155
                $first_branch->{code} => {
156
                    api_key  => $first_branch->{api_key},
157
                    api_auth => $first_branch->{api_auth},
158
                },
159
                $second_branch->{code} => {
160
                    api_key  => $second_branch->{api_key},
161
                    api_auth => $second_branch->{api_auth},
162
                },
163
            },
164
            api_application => $application,
165
        },
166
        backend         => 'Dummy',
167
        censorship      => {
168
            censor_notes_staff => 1,
169
            censor_reply_date => 1,
170
        },
171
        default_formats => {},
172
        digital_recipients => {},
173
        library_privileges => {},
174
        limits          => {
175
            default => {count => 10 },
176
            branch  => {
177
                $second_branch->{code} => {count => 5 },
178
            },
179
        },
180
        prefixes           => {},
181
        spec_file       => undef,
182
    },
183
    "Multi Branch _load_configuration"
184
);
185
186
dies_ok { Koha::Illrequest::Config::_load_configuration($params, 1) }
187
    "Unmediated, missing config _load_configuration";
188
189
$params->{configuration}->{default_formats} = {
190
    format => 1, quality => 1, quantity => 1, service => 1, speed => 1
191
};
192
is_deeply(
193
    Koha::Illrequest::Config::_load_configuration($params, 1),
194
    {
195
        api_url         => 'http://apitest.bldss.bl.uk',
196
        backend         => 'Dummy',
197
        censorship      => {
198
            censor_notes_staff => 1,
199
            censor_reply_date => 1,
200
        },
201
        credentials     => {
202
            api_keys        => {
203
                default => {
204
                    api_key  => $defaults->{api_key},
205
                    api_auth => $defaults->{api_auth},
206
                },
207
                $first_branch->{code} => {
208
                    api_key  => $first_branch->{api_key},
209
                    api_auth => $first_branch->{api_auth},
210
                },
211
                $second_branch->{code} => {
212
                    api_key  => $second_branch->{api_key},
213
                    api_auth => $second_branch->{api_auth},
214
                },
215
            },
216
            api_application => $application,
217
        },
218
        default_formats => {
219
            default => {
220
                format => 1,
221
                quality => 1,
222
                quantity => 1,
223
                service => 1,
224
                speed => 1
225
            }
226
        },
227
        digital_recipients => {},
228
        library_privileges => {},
229
        limits          => {
230
            default => {count => 10 },
231
            branch  => {
232
                $second_branch->{code} => {count => 5 },
233
            },
234
        },
235
        prefixes           => {},
236
        spec_file       => undef,
237
    },
238
    "default_formats, default only _load_configuration"
239
);
240
241
# getDefaultFormats
242
dies_ok { $config->getLimitRules('wrongType') }
243
    "Faulty getDefaultFormats";
244
245
$config->{configuration} =
246
    Koha::Illrequest::Config::_load_configuration($params);
247
is_deeply(
248
    $config->getDefaultFormats('brw_cat'),
249
    {
250
        default => {
251
            format => 1,
252
            quality => 1,
253
            quantity => 1,
254
            service => 1,
255
            speed => 1
256
        }
257
    },
258
    "Default getDefaultFormats"
259
);
260
261
# getLimitRules
262
dies_ok { $config->getLimitRules('wrongType') }
263
    "Faulty getLimitRules";
264
265
$config->{configuration} =
266
    Koha::Illrequest::Config::_load_configuration($params);
267
is_deeply(
268
    $config->getLimitRules('branch'),
269
    {
270
        default => { count => 10 },
271
        second  => { count => 5 },
272
    },
273
    "second branch getLimitRules"
274
);
275
276
is_deeply(
277
    $config->getLimitRules('brw_cat'),
278
    {
279
        default => { count => 10 },
280
    },
281
    "empty brw_cat getLimitRules"
282
);
283
284
# getCredentials
285
$params = {
286
    application            => $application,
287
    backend                => 'Dummy',
288
    configuration          => {},
289
    reply_date             => "hide",
290
    staff_request_comments => "hide",
291
};
292
$config->{configuration} =
293
    Koha::Illrequest::Config::_load_configuration($params);
294
is_deeply(
295
    $config->getCredentials,
296
    {
297
        api_key              => 0,
298
        api_key_auth         => 0,
299
        api_application      => $application->{key},
300
        api_application_auth => $application->{auth},
301
    },
302
    "getCredentials, no creds, just App."
303
);
304
305
$params->{configuration} = $defaults;
306
$config->{configuration} =
307
    Koha::Illrequest::Config::_load_configuration($params),
308
is_deeply(
309
    $config->getCredentials,
310
    {
311
        api_key              => $defaults->{api_key},
312
        api_key_auth         => $defaults->{api_auth},
313
        api_application      => $application->{key},
314
        api_application_auth => $application->{auth},
315
    },
316
    "getCredentials, default creds & App."
317
);
318
319
$params->{configuration}->{branch} = $first_branch;
320
$config->{configuration} =
321
    Koha::Illrequest::Config::_load_configuration($params),
322
is_deeply(
323
    $config->getCredentials($first_branch->{code}),
324
    {
325
        api_key              => $first_branch->{api_key},
326
        api_key_auth         => $first_branch->{api_auth},
327
        api_application      => $application->{key},
328
        api_application_auth => $application->{auth},
329
    },
330
    "getCredentials, $first_branch->{code} creds & App."
331
);
332
333
is_deeply(
334
    $config->getCredentials("random"),
335
    {
336
        api_key              => $defaults->{api_key},
337
        api_key_auth         => $defaults->{api_auth},
338
        api_application      => $application->{key},
339
        api_application_auth => $application->{auth},
340
    },
341
    "getCredentials, fallback creds & app."
342
);
343
344
done_testing;
(-)a/t/db_dependent/Illrequest/Dummy.t (+378 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
#
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 2 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
#
18
19
use Modern::Perl;
20
21
use Koha::Database;
22
use Koha::Illrequests;
23
use t::lib::TestBuilder;
24
25
use Test::More tests => 15;
26
27
# This is a set of basic tests for the Dummy backend, largely to provide
28
# sanity checks for testing at the higher level Illrequest.pm level.
29
#
30
# The Dummy backend is rather simple, but provides correctly formatted
31
# response values, that other backends can model themselves after.
32
33
use_ok('Koha::Illrequest::Backend::Dummy');
34
35
my $backend = Koha::Illrequest::Backend::Dummy->new;
36
37
isa_ok($backend, 'Koha::Illrequest::Backend::Dummy');
38
39
40
my $schema = Koha::Database->new->schema;
41
$schema->storage->txn_begin;
42
43
my $builder = t::lib::TestBuilder->new;
44
45
my $patron = $builder->build({ source => 'Borrower' });
46
my $branch = $builder->build({ source => 'Branch' });
47
48
my $illRequest = $builder->build({
49
    source => 'Illrequest',
50
    value => {
51
        borrowernumber  => $patron->{borrowernumber},
52
        branch          => $branch->{branchcode},
53
    }
54
});
55
my $mock_request = Koha::Illrequests->find($illRequest->{illrequest_id});
56
$mock_request->_config->backend("Dummy");
57
$mock_request->_config->limits({ default => { count => -1 } });
58
59
# Test Create
60
my $rq = $backend->create({
61
    request    => $mock_request,
62
    method     => 'create',
63
    stage      => 'search_form',
64
    other      => undef,
65
});
66
67
is_deeply(
68
    $rq,
69
    {
70
        error   => 0,
71
        status  => '',
72
        message => '',
73
        method  => 'create',
74
        stage   => 'search_form',
75
        value   => {},
76
    },
77
    "Search_Form stage of create method."
78
);
79
80
$rq = $backend->create({
81
    request    => $mock_request,
82
    method     => 'create',
83
    stage      => 'search_results',
84
    other      => { search => "interlibrary loans" },
85
});
86
87
is_deeply(
88
    $rq,
89
    {
90
        error   => 0,
91
        status  => '',
92
        message => '',
93
        method  => 'create',
94
        stage   => 'search_results',
95
        value   => [
96
            {
97
                id     => 1234,
98
                title  => "Ordering ILLs using Koha",
99
                author => "A.N. Other",
100
            },
101
            {
102
                id     => 5678,
103
                title  => "Interlibrary loans in Koha",
104
                author => "A.N. Other",
105
            },
106
        ],
107
    },
108
    "Search_Results stage of create method."
109
);
110
111
$rq = $backend->create({
112
    request    => $mock_request,
113
    method     => 'create',
114
    stage      => 'commit',
115
    other      => {
116
        id     => 1234,
117
        title  => "Ordering ILLs using Koha",
118
        author => "A.N. Other",
119
    },
120
});
121
122
is_deeply(
123
    $rq,
124
    {
125
        error   => 0,
126
        status  => '',
127
        message => '',
128
        method  => 'create',
129
        stage   => 'commit',
130
        value   => {
131
            id     => 1234,
132
            title  => "Ordering ILLs using Koha",
133
            author => "A.N. Other"
134
        },
135
    },
136
    "Commit stage of create method."
137
);
138
139
$rq = $backend->create({
140
    request    => $mock_request,
141
    method     => 'create',
142
    stage      => 'unknown_stage',
143
    other      => {
144
        id     => 1234,
145
        title  => "Ordering ILLs using Koha",
146
        author => "A.N. Other",
147
    },
148
});
149
150
is_deeply(
151
    $rq,
152
    {
153
        error   => 1,
154
        status  => 'unknown_stage',
155
        message => '',
156
        method  => 'create',
157
        stage   => 'unknown_stage',
158
        value   => {},
159
    },
160
    "Commit stage of create method."
161
);
162
163
# Test Confirm
164
165
$rq = $backend->confirm({
166
    request    => $mock_request,
167
    other      => undef,
168
});
169
170
is_deeply(
171
    $rq,
172
    {
173
        error   => 0,
174
        status  => '',
175
        message => '',
176
        method  => 'confirm',
177
        stage   => 'commit',
178
        value   => {
179
            id     => 1234,
180
            title  => "Ordering ILLs using Koha",
181
            author => "A.N. Other",
182
            status => "On order",
183
            cost   => "30 GBP",
184
        },
185
    },
186
    "Basic confirm method."
187
);
188
189
# Test List
190
191
is_deeply(
192
    $backend->list,
193
    {
194
        error   => 0,
195
        status  => '',
196
        message => '',
197
        method  => 'list',
198
        stage   => 'commit',
199
        value   => {
200
            1 => {
201
                id     => 1234,
202
                title  => "Ordering ILLs using Koha",
203
                author => "A.N. Other",
204
                status => "On order",
205
                cost   => "30 GBP",
206
            },
207
        },
208
    },
209
    "Basic list method."
210
);
211
212
# Test Renew
213
214
is_deeply(
215
    $backend->renew({
216
        request    => $mock_request,
217
        other      => undef,
218
    }),
219
    {
220
        error   => 1,
221
        status  => 'not_renewed',
222
        message => 'Order not yet delivered.',
223
        method  => 'renew',
224
        stage   => 'commit',
225
        value   => {
226
            id     => 1234,
227
            title  => "Ordering ILLs using Koha",
228
            author => "A.N. Other",
229
            status => "On order",
230
        },
231
    },
232
    "Basic renew method."
233
);
234
235
Koha::Illrequestattributes->find({
236
    illrequest_id => $mock_request->illrequest_id,
237
    type          => "status"
238
})->set({ value => "Delivered" })->store;
239
240
is_deeply(
241
    $backend->renew({
242
        request    => $mock_request,
243
        other      => undef,
244
    }),
245
    {
246
        error   => 0,
247
        status  => '',
248
        message => '',
249
        method  => 'renew',
250
        stage   => 'commit',
251
        value   => {
252
            id     => 1234,
253
            title  => "Ordering ILLs using Koha",
254
            author => "A.N. Other",
255
            status => "Renewed",
256
        },
257
    },
258
    "Modified renew method."
259
);
260
261
# Test Update_Status
262
263
is_deeply(
264
    $backend->update_status({
265
        request    => $mock_request,
266
        other      => undef,
267
    }),
268
    {
269
        error   => 1,
270
        status  => 'failed_update_hook',
271
        message => 'Fake reason for failing to perform update operation.',
272
        method  => 'update_status',
273
        stage   => 'commit',
274
        value   => {
275
            id     => 1234,
276
            title  => "Ordering ILLs using Koha",
277
            author => "A.N. Other",
278
            status => "Delivered",
279
        },
280
    },
281
    "Basic update_status method."
282
);
283
284
# FIXME: Perhaps we should add a test checking for specific status code
285
# transitions.
286
287
# Test Cancel
288
289
is_deeply(
290
    $backend->cancel({
291
        request    => $mock_request,
292
        other      => undef,
293
    }),
294
    {
295
        error   => 0,
296
        status  => '',
297
        message => '',
298
        method  => 'cancel',
299
        stage   => 'commit',
300
        value   => {
301
            id     => 1234,
302
            title  => "Ordering ILLs using Koha",
303
            author => "A.N. Other",
304
            status => "Delivered",
305
        },
306
    },
307
    "Basic cancel method."
308
);
309
310
is_deeply(
311
    $backend->cancel({
312
        request    => $mock_request,
313
        other      => undef,
314
    }),
315
    {
316
        error   => 1,
317
        status  => 'unknown_request',
318
        message => 'Cannot cancel an unknown request.',
319
        method  => 'cancel',
320
        stage   => 'commit',
321
        value   => {
322
            id     => 1234,
323
            title  => "Ordering ILLs using Koha",
324
            author => "A.N. Other",
325
        },
326
    },
327
    "Attempt to cancel an unconfirmed request."
328
);
329
330
# Test Status
331
332
is_deeply(
333
    $backend->status({
334
        request    => $mock_request,
335
        other      => undef,
336
    }),
337
    {
338
        error   => 1,
339
        status  => 'unknown_request',
340
        message => 'Cannot query status of an unknown request.',
341
        method  => 'status',
342
        stage   => 'commit',
343
        value   => {
344
            id     => 1234,
345
            title  => "Ordering ILLs using Koha",
346
            author => "A.N. Other",
347
        },
348
    },
349
    "Attempt to get status of an unconfirmed request."
350
);
351
352
$rq = $backend->confirm({
353
    request    => $mock_request,
354
    other      => undef,
355
});
356
357
is_deeply(
358
    $backend->status({
359
        request    => $mock_request,
360
        other      => undef,
361
    }),
362
    {
363
        error   => 0,
364
        status  => '',
365
        message => '',
366
        method  => 'status',
367
        stage   => 'commit',
368
        value   => {
369
            id     => 1234,
370
            title  => "Ordering ILLs using Koha",
371
            author => "A.N. Other",
372
            status => "On order",
373
        },
374
    },
375
    "Basic status method."
376
);
377
378
1;
(-)a/t/db_dependent/api/v1/illrequests.t (-1 / +136 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/env perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 3 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18
use Modern::Perl;
19
20
use Test::More tests => 1;
21
use Test::Mojo;
22
use Test::Warn;
23
24
use t::lib::TestBuilder;
25
use t::lib::Mocks;
26
27
use C4::Auth;
28
use Koha::Illrequests;
29
30
my $schema  = Koha::Database->new->schema;
31
my $builder = t::lib::TestBuilder->new;
32
33
# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling
34
# this affects the other REST api tests
35
t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
36
37
my $remote_address = '127.0.0.1';
38
my $t              = Test::Mojo->new('Koha::REST::V1');
39
40
subtest 'list() tests' => sub {
41
42
    plan tests => 6;
43
44
    $schema->storage->txn_begin;
45
46
    Koha::Illrequests->search->delete;
47
    my ( $borrowernumber, $session_id ) =
48
      create_user_and_session( { authorized => 1 } );
49
50
    ## Authorized user tests
51
    # No requests, so empty array should be returned
52
    my $tx = $t->ua->build_tx( GET => '/api/v1/illrequests' );
53
    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
54
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
55
    $t->request_ok($tx)->status_is(200)->json_is( [] );
56
57
#    my $city_country = 'France';
58
#    my $city         = $builder->build(
59
#        { source => 'City', value => { city_country => $city_country } } );
60
#
61
#    # One city created, should get returned
62
#    $tx = $t->ua->build_tx( GET => '/api/v1/cities' );
63
#    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
64
#    $tx->req->env( { REMOTE_ADDR => $remote_address } );
65
#    $t->request_ok($tx)->status_is(200)->json_is( [$city] );
66
#
67
#    my $another_city = $builder->build(
68
#        { source => 'City', value => { city_country => $city_country } } );
69
#    my $city_with_another_country = $builder->build( { source => 'City' } );
70
#
71
#    # Two cities created, they should both be returned
72
#    $tx = $t->ua->build_tx( GET => '/api/v1/cities' );
73
#    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
74
#    $tx->req->env( { REMOTE_ADDR => $remote_address } );
75
#    $t->request_ok($tx)->status_is(200)
76
#      ->json_is( [ $city, $another_city, $city_with_another_country ] );
77
#
78
#    # Filtering works, two cities sharing city_country
79
#    $tx =
80
#      $t->ua->build_tx( GET => "/api/v1/cities?city_country=" . $city_country );
81
#    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
82
#    $tx->req->env( { REMOTE_ADDR => $remote_address } );
83
#    my $result =
84
#      $t->request_ok($tx)->status_is(200)->json_is( [ $city, $another_city ] );
85
#
86
#    $tx = $t->ua->build_tx(
87
#        GET => "/api/v1/cities?city_name=" . $city->{city_name} );
88
#    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
89
#    $tx->req->env( { REMOTE_ADDR => $remote_address } );
90
#    $t->request_ok($tx)->status_is(200)->json_is( [$city] );
91
92
    # Warn on unsupported query parameter
93
    $tx = $t->ua->build_tx( GET => '/api/v1/illrequests?request_blah=blah' );
94
    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
95
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
96
    $t->request_ok($tx)->status_is(400)->json_is(
97
        [{ path => '/query/request_blah', message => 'Malformed query string'}]
98
    );
99
100
    $schema->storage->txn_rollback;
101
};
102
103
sub create_user_and_session {
104
105
    my $args  = shift;
106
    my $flags = ( $args->{authorized} ) ? $args->{authorized} : 0;
107
    my $dbh   = C4::Context->dbh;
108
109
    my $user = $builder->build(
110
        {
111
            source => 'Borrower',
112
            value  => {
113
                flags => $flags
114
            }
115
        }
116
    );
117
118
    # Create a session for the authorized user
119
    my $session = C4::Auth::get_session('');
120
    $session->param( 'number',   $user->{borrowernumber} );
121
    $session->param( 'id',       $user->{userid} );
122
    $session->param( 'ip',       '127.0.0.1' );
123
    $session->param( 'lasttime', time() );
124
    $session->flush;
125
126
    if ( $args->{authorized} ) {
127
        $dbh->do( "
128
            INSERT INTO user_permissions (borrowernumber,module_bit,code)
129
            VALUES (?,3,'parameters_remaining_permissions')", undef,
130
            $user->{borrowernumber} );
131
    }
132
133
    return ( $user->{borrowernumber}, $session->id );
134
}
135
136
1;

Return to bug 7317