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

(-)a/Koha/REST/V1/Illrequests.pm (-1 / +9 lines)
Lines 55-61 sub list { Link Here
55
    }
55
    }
56
56
57
    # Get all requests
57
    # Get all requests
58
    my @requests = Koha::Illrequests->as_list;
58
    # If necessary, only get those from a specified patron
59
    my @requests;
60
    if ($args->{borrowernumber}) {
61
        @requests = Koha::Illrequests->search(
62
            { borrowernumber => $args->{borrowernumber} }
63
        );
64
    } else {
65
        @requests = Koha::Illrequests->as_list;
66
    }
59
67
60
    # Identify patrons & branches that
68
    # Identify patrons & branches that
61
    # we're going to need and get them
69
    # we're going to need and get them
(-)a/ill/ill-requests.pl (-3 / +2 lines)
Lines 273-285 if ( $backends_available ) { Link Here
273
        my $active_filters = [];
273
        my $active_filters = [];
274
        foreach my $filter(@{$possible_filters}) {
274
        foreach my $filter(@{$possible_filters}) {
275
            if ($params->{$filter}) {
275
            if ($params->{$filter}) {
276
                push @{$active_filters},
276
                push @{$active_filters}, "$filter=$params->{$filter}";
277
                    { name => $filter, value => $params->{$filter}};
278
            }
277
            }
279
        }
278
        }
280
        if (scalar @{$active_filters} > 0) {
279
        if (scalar @{$active_filters} > 0) {
281
            $template->param(
280
            $template->param(
282
                prefilters => $active_filters
281
                prefilters => join(",", @{$active_filters})
283
            );
282
            );
284
        }
283
        }
285
284
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc (-1 / +1 lines)
Lines 132-138 Link Here
132
        [% IF houseboundview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% patron.borrowernumber | uri %]">Housebound</a></li>
132
        [% IF houseboundview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/housebound.pl?borrowernumber=[% patron.borrowernumber | uri %]">Housebound</a></li>
133
    [% END %]
133
    [% END %]
134
    [% IF Koha.Preference('ILLModule') && CAN_user_ill %]
134
    [% IF Koha.Preference('ILLModule') && CAN_user_ill %]
135
        <li><a href="/cgi-bin/koha/ill/ill-requests.pl?borrowernumber=[% patron.borrowernumber | uri %]">Interlibrary loans</a></li>
135
        [% IF illview %]<li class="active">[% ELSE %]<li>[% END %]<a href="/cgi-bin/koha/members/ill-requests.pl?borrowernumber=[% patron.borrowernumber | uri %]">Interlibrary loans</a></li>
136
    [% END %]
136
    [% END %]
137
</ul></div>
137
</ul></div>
138
138
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/ill-list-table-strings.inc (+19 lines)
Line 0 Link Here
1
<script>
2
    var ill_borrower_details = _('View borrower details');
3
    var ill_biblio_details = _('View biblio details');
4
    var ill_statuses = {
5
        new: _('New request'),
6
        req: _('Requested'),
7
        genreq: _('Requested from partners'),
8
        rev: _('Reverted'),
9
        que: _('Queued request'),
10
        canc: _('Cancellation requested'),
11
        comp: _('Completed'),
12
        del: _('Delete request')
13
    };
14
    var ill_manage = _('Manage request');
15
    var ill_columns = {
16
        biblio_id: _('Bibliographic record ID'),
17
        updated: _('Updated on')
18
    };
19
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/ill-list-table.inc (+42 lines)
Line 0 Link Here
1
<div>
2
	[% IF prefilters.length > 0 %]
3
	<table id="ill-requests" data-prefilters="[% prefilters | html %]">
4
	[% ELSE %]
5
	<table id="ill-requests">
6
	[% END %]
7
		<thead>
8
			<tr id="illview-header">
9
				<th scope="col">Request ID</th>
10
				<th scope="col">Author</th>
11
				<th scope="col">Title</th>
12
				<th scope="col">Article title</th>
13
				<th scope="col">Issue</th>
14
				<th scope="col">Volume</th>
15
				<th scope="col">Year</th>
16
				<th scope="col">Pages</th>
17
				<th scope="col">Type</th>
18
				<th scope="col">Order ID</th>
19
				<th scope="col">Patron</th>
20
				<th scope="col">Bibliographic record</th>
21
				<th scope="col">Branch</th>
22
				<th scope="col">Status</th>
23
				<th scope="col" class="placed">&nbsp;</th>
24
				<th scope="col" class="placed_formatted">Placed on</th>
25
				<th scope="col" class="updated">&nbsp;</th>
26
				<th scope="col" class="updated_formatted">Updated on</th>
27
				<th scope="col">Replied</th>
28
				<th scope="col" class="completed">&nbsp;</th>
29
				<th scope="col" class="completed_formatted">Completed on</th>
30
				<th scope="col">Access URL</th>
31
				<th scope="col">Cost</th>
32
				<th scope="col">Comments</th>
33
				<th scope="col">OPAC notes</th>
34
				<th scope="col">Staff notes</th>
35
				<th scope="col">Backend</th>
36
				<th scope="col" class="actions"></th>
37
			</tr>
38
		</thead>
39
		<tbody id="illview-body">
40
		</tbody>
41
	</table>
42
</div>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/ill/ill-requests.tt (-571 / +19 lines)
Lines 507-549 Link Here
507
                    <h1>View ILL requests</h1>
507
                    <h1>View ILL requests</h1>
508
                    <div id="results">
508
                    <div id="results">
509
                        <h3>Details for all requests</h3>
509
                        <h3>Details for all requests</h3>
510
                        [% INCLUDE 'ill-list-table.inc' %]
510
511
511
                        <table id="ill-requests">
512
                            <thead>
513
                                <tr id="illview-header">
514
                                    <th scope="col">Request ID</th>
515
                                    <th scope="col">Author</th>
516
                                    <th scope="col">Title</th>
517
                                    <th scope="col">Article title</th>
518
                                    <th scope="col">Issue</th>
519
                                    <th scope="col">Volume</th>
520
                                    <th scope="col">Year</th>
521
                                    <th scope="col">Pages</th>
522
                                    <th scope="col">Type</th>
523
                                    <th scope="col">Order ID</th>
524
                                    <th scope="col">Patron</th>
525
                                    <th scope="col">Bibliographic record</th>
526
                                    <th scope="col">Branch</th>
527
                                    <th scope="col">Status</th>
528
                                    <th scope="col" class="placed">&nbsp;</th>
529
                                    <th scope="col" class="placed_formatted">Placed on</th>
530
                                    <th scope="col" class="updated">&nbsp;</th>
531
                                    <th scope="col" class="updated_formatted">Updated on</th>
532
                                    <th scope="col">Replied</th>
533
                                    <th scope="col" class="completed">&nbsp;</th>
534
                                    <th scope="col" class="completed_formatted">Completed on</th>
535
                                    <th scope="col">Access URL</th>
536
                                    <th scope="col">Cost</th>
537
                                    <th scope="col">Comments</th>
538
                                    <th scope="col">OPAC notes</th>
539
                                    <th scope="col">Staff notes</th>
540
                                    <th scope="col">Backend</th>
541
                                    <th scope="col" class="actions"></th>
542
                                </tr>
543
                            </thead>
544
                            <tbody id="illview-body">
545
                            </tbody>
546
                        </table>
547
                    </div>
512
                    </div>
548
                [% ELSE %]
513
                [% ELSE %]
549
                <!-- Custom Backend Action -->
514
                <!-- Custom Backend Action -->
Lines 555-1102 Link Here
555
        </div>
520
        </div>
556
    </div>
521
    </div>
557
522
558
[% TRY %]
559
[% PROCESS backend_jsinclude %]
560
[% CATCH %]
561
[% END %]
562
563
[% MACRO jsinclude BLOCK %]
523
[% MACRO jsinclude BLOCK %]
564
    [% INCLUDE 'datatables.inc' %]
524
    [% INCLUDE 'datatables.inc' %]
565
    [% INCLUDE 'columns_settings.inc' %]
525
    [% INCLUDE 'columns_settings.inc' %]
566
    [% INCLUDE 'calendar.inc' %]
526
    [% INCLUDE 'calendar.inc' %]
567
    [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
527
    [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
568
    <script>
528
    <script>
569
        $(document).ready(function() {
529
        // Date format for datepickers
570
530
        var dateMap = {
571
            // Illview Datatable setup
531
            dmydot: 'dd.mm.yy',
572
532
            iso: 'yy-mm-dd',
573
            var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %];
533
            metric: 'dd/mm/yy',
574
534
            us: 'mm/dd/yy'
575
            var table;
535
        };
576
536
        var dateFormat = dateMap['[% Koha.Preference('dateformat') | html %]'];
577
            // Filters that are active
537
        var prefilters = '[% prefilters | html %]';
578
            var activeFilters = {};
538
        // Set column settings
579
539
        var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %];
580
            // Fields we need to expand (flatten)
581
            var expand = [
582
                'metadata',
583
                'patron',
584
                'library'
585
            ];
586
587
            // Expanded fields
588
            // This is auto populated
589
            var expanded = {};
590
591
            // Filterable columns
592
            var filterable = {
593
                status: {
594
                    prep: function(tableData, oData) {
595
                        var uniques = {};
596
                        tableData.forEach(function(row) {
597
                            var resolvedName;
598
                            if (row.status_alias) {
599
                                resolvedName = row.status_alias.lib;
600
                            } else {
601
                                resolvedName = getStatusName(
602
                                    oData[0].capabilities[row.status].name
603
                                );
604
                            }
605
                            uniques[resolvedName] = 1
606
                        });
607
                        Object.keys(uniques).sort().forEach(function(unique) {
608
                            $('#illfilter_status').append(
609
                                '<option value="' + unique  +
610
                                '">' + unique +  '</option>'
611
                            );
612
                        });
613
                    },
614
                    listener: function() {
615
                        var me = 'status';
616
                        $('#illfilter_status').change(function() {
617
                            var sel = $('#illfilter_status option:selected').val();
618
                            if (sel && sel.length > 0) {
619
                                activeFilters[me] = function() {
620
                                    table.api().column(13).search(sel);
621
                                }
622
                            } else {
623
                                if (activeFilters.hasOwnProperty(me)) {
624
                                    delete activeFilters[me];
625
                                }
626
                            }
627
                        });
628
                    },
629
                    clear: function() {
630
                        $('#illfilter_status').val('');
631
                    }
632
                },
633
                pickupBranch: {
634
                    prep: function(tableData, oData) {
635
                        var uniques = {};
636
                        tableData.forEach(function(row) {
637
                            uniques[row.library_branchname] = 1
638
                        });
639
                        Object.keys(uniques).sort().forEach(function(unique) {
640
                            $('#illfilter_branchname').append(
641
                                '<option value="' + unique  +
642
                                '">' + unique +  '</option>'
643
                            );
644
                        });
645
                    },
646
                    listener: function() {
647
                        var me = 'pickupBranch';
648
                        $('#illfilter_branchname').change(function() {
649
                            var sel = $('#illfilter_branchname option:selected').val();
650
                            if (sel && sel.length > 0) {
651
                                activeFilters[me] = function() {
652
                                    table.api().column(12).search(sel);
653
                                }
654
                            } else {
655
                                if (activeFilters.hasOwnProperty(me)) {
656
                                    delete activeFilters[me];
657
                                }
658
                            }
659
                        });
660
                    },
661
                    clear: function() {
662
                        $('#illfilter_branchname').val('');
663
                    }
664
                },
665
                patron: {
666
                    listener: function() {
667
                        var me = 'patron';
668
                        $('#illfilter_patron').change(function() {
669
                            var val = $('#illfilter_patron').val();
670
                            if (val && val.length > 0) {
671
                                activeFilters[me] = function() {
672
                                    table.api().column(10).search(val);
673
                                }
674
                            } else {
675
                                if (activeFilters.hasOwnProperty(me)) {
676
                                    delete activeFilters[me];
677
                                }
678
                            }
679
                        });
680
                    },
681
                    clear: function() {
682
                        $('#illfilter_patron').val('');
683
                    }
684
                },
685
                dateModified: {
686
                    clear: function() {
687
                        $('#illfilter_datemodified_start, #illfilter_datemodified_end').val('');
688
                    }
689
                },
690
                datePlaced: {
691
                    clear: function() {
692
                        $('#illfilter_dateplaced_start, #illfilter_dateplaced_end').val('');
693
                    }
694
                }
695
            };
696
697
            // Expand any fields we're expanding
698
            var expandExpand = function(row) {
699
                expand.forEach(function(thisExpand) {
700
                    if (row.hasOwnProperty(thisExpand)) {
701
                        if (!expanded.hasOwnProperty(thisExpand)) {
702
                            expanded[thisExpand] = [];
703
                        }
704
                        var expandObj = row[thisExpand];
705
                        Object.keys(expandObj).forEach(
706
                            function(thisExpandCol) {
707
                                var expColName = thisExpand + '_' + thisExpandCol.replace(/\s/g,'_');
708
                                // Keep a list of fields that have been expanded
709
                                // so we can create toggle links for them
710
                                if (expanded[thisExpand].indexOf(expColName) == -1) {
711
                                    expanded[thisExpand].push(expColName);
712
                                }
713
                                expandObj[expColName] =
714
                                    expandObj[thisExpandCol];
715
                                delete expandObj[thisExpandCol];
716
                            }
717
                        );
718
                        $.extend(true, row, expandObj);
719
                        delete row[thisExpand];
720
                    }
721
                });
722
            };
723
724
            // Strip the expand prefix if it exists, we do this for display
725
            var stripPrefix = function(value) {
726
                expand.forEach(function(thisExpand) {
727
                    var regex = new RegExp(thisExpand + '_', 'g');
728
                    value = value.replace(regex, '');
729
                });
730
                return value;
731
            };
732
733
            // Our 'render' function for borrowerlink
734
            var createPatronLink = function(data, type, row) {
735
                var patronLink = '<a title="' + _("View borrower details") + '" ' +
736
                    'href="/cgi-bin/koha/members/moremember.pl?' +
737
                    'borrowernumber='+row.borrowernumber+'">';
738
                if ( row.patron_firstname ) {
739
                    patronLink = patronLink + row.patron_firstname + ' ';
740
                }
741
                patronLink = patronLink + row.patron_surname +
742
                    ' (' + row.patron_cardnumber + ')' + '</a>';
743
                return patronLink;
744
            };
745
746
            // Our 'render' function for biblio_id
747
            var createBiblioLink = function(data, type, row) {
748
                return (row.biblio_id) ?
749
                    '<a title="' + _("View biblio details") + '" ' +
750
                    'href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' +
751
                    row.biblio_id + '">' +
752
                    row.biblio_id +
753
                    '</a>' : '';
754
            };
755
756
            // Our 'render' function for title
757
            var createTitle = function(data, type, row) {
758
                return (
759
                    row.hasOwnProperty('metadata_container_title') &&
760
                    row.metadata_container_title
761
                ) ? row.metadata_container_title : row.metadata_title;
762
            };
763
764
            // Render function for request ID
765
            var createRequestId = function(data, type, row) {
766
                return row.id_prefix + row.illrequest_id;
767
            };
768
769
            // Render function for type
770
            var createType = function(data, type, row) {
771
                if (!row.hasOwnProperty('metadata_Type') || !row.metadata_Type) {
772
                    if (row.hasOwnProperty('medium') && row.medium) {
773
                        row.metadata_Type = row.medium;
774
                    } else {
775
                        row.metadata_Type = null;
776
                    }
777
                }
778
                return row.metadata_Type;
779
            };
780
781
            // Render function for request status
782
            var createStatus = function(data, type, row, meta) {
783
                if (row.status_alias) {
784
                    return row.status_alias.lib
785
                        ? row.status_alias.lib
786
                        : row.status_alias.authorised_value;
787
                } else {
788
                    var origData = meta.settings.oInit.originalData;
789
                    if (origData.length > 0) {
790
                        var status_name = meta.settings.oInit.originalData[0].capabilities[
791
                            row.status
792
                        ].name;
793
                        return getStatusName(status_name, row);
794
                    } else {
795
                        return '';
796
                    }
797
                }
798
            };
799
800
            var getStatusName = function(origName, row) {
801
                switch( origName ) {
802
                    case "New request":
803
                        return _("New request");
804
                    case "Requested":
805
                        return _("Requested");
806
                    case "Requested from partners":
807
                        var statStr = _("Requested from partners");
808
                        if (
809
                            row.hasOwnProperty('requested_partners') &&
810
                            row.requested_partners &&
811
                            row.requested_partners.length > 0
812
                        ) {
813
                            statStr += ' (' + row.requested_partners + ')';
814
                        }
815
                        return statStr;
816
                    case "Request reverted":
817
                        return _("Request reverted");
818
                    case "Queued request":
819
                        return _("Queued request");
820
                    case "Cancellation requested":
821
                        return _("Cancellation requested");
822
                    case "Completed":
823
                        return _("Completed");
824
                    case "Delete request":
825
                        return _("Delete request");
826
                    default:
827
                        return origName;
828
                }
829
            };
830
831
            // Render function for creating a row's action link
832
            var createActionLink = function(data, type, row) {
833
                return '<a class="btn btn-default btn-sm" ' +
834
                    'href="/cgi-bin/koha/ill/ill-requests.pl?' +
835
                    'method=illview&amp;illrequest_id=' +
836
                    row.illrequest_id +
837
                    '">' + _("Manage request") + '</a>';
838
            };
839
840
            // Columns that require special treatment
841
            var specialCols = {
842
                action: {
843
                    func: createActionLink,
844
                    skipSanitize: true
845
                },
846
                illrequest_id: {
847
                    func: createRequestId
848
                },
849
                status: {
850
                    func: createStatus
851
                },
852
                biblio_id: {
853
                    name: _("Bibliograpic record ID"),
854
                    func: createBiblioLink,
855
                    skipSanitize: true
856
                },
857
                metadata_title: {
858
                    func: createTitle
859
                },
860
                metadata_Type: {
861
                    func: createType
862
                },
863
                updated: {
864
                    name: _("Updated on"),
865
                },
866
                patron: {
867
                    skipSanitize: true,
868
                    func: createPatronLink
869
                }
870
            };
871
872
            // Display the modal containing request supplier metadata
873
            $('#ill-request-display-log').on('click', function(e) {
874
                e.preventDefault();
875
                $('#requestLog').modal({show:true});
876
            });
877
878
            // Toggle request attributes in Illview
879
            $('#toggle_requestattributes').on('click', function(e) {
880
                e.preventDefault();
881
                $('#requestattributes').toggleClass('content_hidden');
882
            });
883
884
            // Toggle new comment form in Illview
885
            $('#toggle_addcomment').on('click', function(e) {
886
                e.preventDefault();
887
                $('#addcomment').toggleClass('content_hidden');
888
            });
889
890
            // Filter partner list
891
            $('#partner_filter').keyup(function() {
892
                var needle = $('#partner_filter').val();
893
                $('#partners > option').each(function() {
894
                    var regex = new RegExp(needle, 'i');
895
                    if (
896
                        needle.length == 0 ||
897
                        $(this).is(':selected') ||
898
                        $(this).text().match(regex)
899
                    ) {
900
                        $(this).show();
901
                    } else {
902
                        $(this).hide();
903
                    }
904
                });
905
            });
906
907
            // Display the modal containing request supplier metadata
908
            $('#ill-request-display-metadata').on('click', function(e) {
909
                e.preventDefault();
910
                $('#dataPreview').modal({show:true});
911
            });
912
913
            // Allow us to chain Datatable render helpers together, so we
914
            // can use our custom functions and render.text(), which
915
            // provides us with data sanitization
916
            $.fn.dataTable.render.multi = function(renderArray) {
917
                return function(d, type, row, meta) {
918
                    for(var r = 0; r < renderArray.length; r++) {
919
                        var toCall = renderArray[r].hasOwnProperty('display') ?
920
                            renderArray[r].display :
921
                            renderArray[r];
922
                        d = toCall(d, type, row, meta);
923
                    }
924
                    return d;
925
                }
926
            }
927
928
            // Get our data from the API and process it prior to passing
929
            // it to datatables
930
            var ajax = $.ajax(
931
                '/api/v1/illrequests?embed=metadata,patron,capabilities,library,status_alias,comments,requested_partners'
932
                ).done(function() {
933
                    var data = JSON.parse(ajax.responseText);
934
                    // Make a copy, we'll be removing columns next and need
935
                    // to be able to refer to data that has been removed
936
                    var dataCopy = $.extend(true, [], data);
937
                    // Expand columns that need it and create an array
938
                    // of all column names
939
                    $.each(dataCopy, function(k, row) {
940
                        expandExpand(row);
941
                    });
942
943
                    // Assemble an array of column definitions for passing
944
                    // to datatables
945
                    var colData = [];
946
                    columns_settings.forEach(function(thisCol) {
947
                        var colName = thisCol.columnname;
948
                        // Create the base column object
949
                        var colObj = $.extend({}, thisCol);
950
                        colObj.name = colName;
951
                        colObj.className = colName;
952
                        colObj.defaultContent = '';
953
954
                        // We may need to process the data going in this
955
                        // column, so do it if necessary
956
                        if (
957
                            specialCols.hasOwnProperty(colName) &&
958
                            specialCols[colName].hasOwnProperty('func')
959
                        ) {
960
                            var renderArray = [
961
                                specialCols[colName].func
962
                            ];
963
                            if (!specialCols[colName].skipSanitize) {
964
                                renderArray.push(
965
                                    $.fn.dataTable.render.text()
966
                                );
967
                            }
968
969
                            colObj.render = $.fn.dataTable.render.multi(
970
                                renderArray
971
                            );
972
                        } else {
973
                            colObj.data = colName;
974
                            colObj.render = $.fn.dataTable.render.text()
975
                        }
976
                        // Make sure properties that aren't present in the API
977
                        // response are populated with null to avoid Datatables
978
                        // choking on their absence
979
                        dataCopy.forEach(function(thisData) {
980
                            if (!thisData.hasOwnProperty(colName)) {
981
                                thisData[colName] = null;
982
                            }
983
                        });
984
                        colData.push(colObj);
985
                    });
986
987
                    // Initialise the datatable
988
                    table = KohaTable("ill-requests", {
989
                        'aoColumnDefs': [
990
                            { // Last column shouldn't be sortable or searchable
991
                                'aTargets': [ 'actions' ],
992
                                'bSortable': false,
993
                                'bSearchable': false
994
                            },
995
                            { // When sorting 'placed', we want to use the
996
                              // unformatted column
997
                              'aTargets': [ 'placed_formatted'],
998
                              'iDataSort': 14
999
                            },
1000
                            { // When sorting 'updated', we want to use the
1001
                              // unformatted column
1002
                              'aTargets': [ 'updated_formatted'],
1003
                              'iDataSort': 16
1004
                            },
1005
                            { // When sorting 'completed', we want to use the
1006
                              // unformatted column
1007
                              'aTargets': [ 'completed_formatted'],
1008
                              'iDataSort': 19
1009
                            }
1010
                        ],
1011
                        'aaSorting': [[ 16, 'desc' ]], // Default sort, updated descending
1012
                        'processing': true, // Display a message when manipulating
1013
                        'sPaginationType': "full_numbers", // Pagination display
1014
                        'deferRender': true, // Improve performance on big datasets
1015
                        'data': dataCopy,
1016
                        'columns': colData,
1017
                        'originalData': data, // Enable render functions to access
1018
                                              // our original data
1019
                        'initComplete': function() {
1020
1021
                            // Prepare any filter elements that need it
1022
                            for (var el in filterable) {
1023
                                if (filterable.hasOwnProperty(el)) {
1024
                                    if (filterable[el].hasOwnProperty('prep')) {
1025
                                        filterable[el].prep(dataCopy, data);
1026
                                    }
1027
                                    if (filterable[el].hasOwnProperty('listener')) {
1028
                                        filterable[el].listener();
1029
                                    }
1030
                                }
1031
                            }
1032
1033
                        }
1034
                    }, columns_settings);
1035
1036
                    // Custom date range filtering
1037
                    $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
1038
                        var placedStart = $('#illfilter_dateplaced_start').datepicker('getDate');
1039
                        var placedEnd = $('#illfilter_dateplaced_end').datepicker('getDate');
1040
                        var modifiedStart = $('#illfilter_datemodified_start').datepicker('getDate');
1041
                        var modifiedEnd = $('#illfilter_datemodified_end').datepicker('getDate');
1042
                        var rowPlaced = data[14] ? new Date(data[14]) : null;
1043
                        var rowModified = data[16] ? new Date(data[16]) : null;
1044
                        var placedPassed = true;
1045
                        var modifiedPassed = true;
1046
                        if (placedStart && rowPlaced && rowPlaced < placedStart) {
1047
                            placedPassed = false
1048
                        };
1049
                        if (placedEnd && rowPlaced && rowPlaced > placedEnd) {
1050
                            placedPassed = false;
1051
                        }
1052
                        if (modifiedStart && rowModified && rowModified < modifiedStart) {
1053
                            modifiedPassed = false
1054
                        };
1055
                        if (modifiedEnd && rowModified && rowModified > modifiedEnd) {
1056
                            modifiedPassed = false;
1057
                        }
1058
1059
                        return placedPassed && modifiedPassed;
1060
1061
                    });
1062
1063
                }
1064
            );
1065
1066
            var clearSearch = function() {
1067
                table.api().search('').columns().search('');
1068
                activeFilters = {};
1069
                for (var filter in filterable) {
1070
                    if (
1071
                        filterable.hasOwnProperty(filter) &&
1072
                        filterable[filter].hasOwnProperty('clear')
1073
                    ) {
1074
                        filterable[filter].clear();
1075
                    }
1076
                }
1077
                table.api().draw();
1078
            };
1079
1080
            // Apply any search filters, or clear any previous
1081
            // ones
1082
            $('#illfilter_form').submit(function(event) {
1083
                event.preventDefault();
1084
                table.api().search('').columns().search('');
1085
                for (var active in activeFilters) {
1086
                    if (activeFilters.hasOwnProperty(active)) {
1087
                        activeFilters[active]();
1088
                    }
1089
                }
1090
                table.api().draw();
1091
            });
1092
1093
            // Clear all filters
1094
            $('#clear_search').click(function() {
1095
                clearSearch();
1096
            });
1097
1098
        });
1099
    </script>
540
    </script>
541
    [% INCLUDE 'ill-list-table-strings.inc' %]
542
    [% Asset.js("js/ill-list-table.js") | $raw %]
543
[% END %]
544
545
[% TRY %]
546
[% PROCESS backend_jsinclude %]
547
[% CATCH %]
1100
[% END %]
548
[% END %]
1101
549
1102
[% INCLUDE 'intranet-bottom.inc' %]
550
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/ill-requests.tt (+63 lines)
Line 0 Link Here
1
[% USE raw %]
2
[% USE Asset %]
3
[% USE Branches %]
4
[% USE Koha %]
5
[% USE KohaDates %]
6
[% SET footerjs = 1 %]
7
[% USE AuthorisedValues %]
8
[% USE ColumnsSettings %]
9
10
[% INCLUDE 'doc-head-open.inc' %]
11
<title>Koha &rsaquo; Patrons &rsaquo; ILL requests for [% INCLUDE 'patron-title.inc' no_html = 1 %]</title>
12
[% INCLUDE 'doc-head-close.inc' %]
13
</head>
14
15
<body>
16
	[% INCLUDE 'header.inc' %]
17
	[% INCLUDE 'patron-search.inc' %]
18
19
	<div id="breadcrumbs">
20
		<a href="/cgi-bin/koha/mainpage.pl">Home</a>
21
		&rsaquo; <a href="/cgi-bin/koha/members/members-home.pl">Patrons</a>
22
		&rsaquo; ILL requests for [% INCLUDE 'patron-title.inc' %]
23
	</div>
24
25
	<div id="doc3" class="yui-t1">
26
27
	<div id="bd">
28
		<div id="yui-main">
29
			<div class="yui-b">
30
				<div class="yui-g">
31
					<h2>ILL requests</h2>
32
					[% INCLUDE 'ill-list-table.inc' %]
33
				</div>
34
			</div>
35
		</div>
36
		<div class="yui-b">
37
			[% INCLUDE 'circ-menu.inc' %]
38
		</div>
39
	</div>
40
41
	[% MACRO jsinclude BLOCK %]
42
		[% INCLUDE 'datatables.inc' %]
43
		[% INCLUDE 'columns_settings.inc' %]
44
		[% INCLUDE 'calendar.inc' %]
45
		[% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
46
		<script>
47
			// Date format for datepickers
48
			var dateMap = {
49
				dmydot: 'dd.mm.yy',
50
				iso: 'yy-mm-dd',
51
				metric: 'dd/mm/yy',
52
				us: 'mm/dd/yy'
53
			};
54
			var dateFormat = dateMap['[% Koha.Preference('dateformat') | html %]'];
55
			var prefilters = '[% prefilters | html %]';
56
			// Set column settings
57
			var columns_settings = [% ColumnsSettings.GetColumns( 'illrequests', 'ill-requests', 'ill-requests', 'json' ) %];
58
		</script>
59
        [% INCLUDE 'ill-list-table-strings.inc' %]
60
		[% Asset.js("js/ill-list-table.js") | $raw %]
61
	[% END %]
62
63
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/js/ill-list-table.js (+533 lines)
Line 0 Link Here
1
$(document).ready(function() {
2
3
    // Illview Datatable setup
4
5
    var table;
6
7
    // Filters that are active
8
    var activeFilters = {};
9
10
    // Get any prefilters
11
    var prefilters = $('table#ill-requests').data('prefilters');
12
13
    // Fields we need to expand (flatten)
14
    var expand = [
15
        'metadata',
16
        'patron',
17
        'library'
18
    ];
19
20
    // Expanded fields
21
    // This is auto populated
22
    var expanded = {};
23
24
    // Filterable columns
25
    var filterable = {
26
        status: {
27
            prep: function(tableData, oData) {
28
                var uniques = {};
29
                tableData.forEach(function(row) {
30
                    var resolvedName;
31
                    if (row.status_alias) {
32
                        resolvedName = row.status_alias.lib;
33
                    } else {
34
                        resolvedName = getStatusName(
35
                            oData[0].capabilities[row.status].name
36
                        );
37
                    }
38
                    uniques[resolvedName] = 1
39
                });
40
                Object.keys(uniques).sort().forEach(function(unique) {
41
                    $('#illfilter_status').append(
42
                        '<option value="' + unique  +
43
                        '">' + unique +  '</option>'
44
                    );
45
                });
46
            },
47
            listener: function() {
48
                var me = 'status';
49
                $('#illfilter_status').change(function() {
50
                    var sel = $('#illfilter_status option:selected').val();
51
                    if (sel && sel.length > 0) {
52
                        activeFilters[me] = function() {
53
                            table.api().column(13).search(sel);
54
                        }
55
                    } else {
56
                        if (activeFilters.hasOwnProperty(me)) {
57
                            delete activeFilters[me];
58
                        }
59
                    }
60
                });
61
            },
62
            clear: function() {
63
                $('#illfilter_status').val('');
64
            }
65
        },
66
        pickupBranch: {
67
            prep: function(tableData, oData) {
68
                var uniques = {};
69
                tableData.forEach(function(row) {
70
                    uniques[row.library_branchname] = 1
71
                });
72
                Object.keys(uniques).sort().forEach(function(unique) {
73
                    $('#illfilter_branchname').append(
74
                        '<option value="' + unique  +
75
                        '">' + unique +  '</option>'
76
                    );
77
                });
78
            },
79
            listener: function() {
80
                var me = 'pickupBranch';
81
                $('#illfilter_branchname').change(function() {
82
                    var sel = $('#illfilter_branchname option:selected').val();
83
                    if (sel && sel.length > 0) {
84
                        activeFilters[me] = function() {
85
                            table.api().column(12).search(sel);
86
                        }
87
                    } else {
88
                        if (activeFilters.hasOwnProperty(me)) {
89
                            delete activeFilters[me];
90
                        }
91
                    }
92
                });
93
            },
94
            clear: function() {
95
                $('#illfilter_branchname').val('');
96
            }
97
        },
98
        patron: {
99
            listener: function() {
100
                var me = 'patron';
101
                $('#illfilter_patron').change(function() {
102
                    var val = $('#illfilter_patron').val();
103
                    if (val && val.length > 0) {
104
                        activeFilters[me] = function() {
105
                            table.api().column(10).search(val);
106
                        }
107
                    } else {
108
                        if (activeFilters.hasOwnProperty(me)) {
109
                            delete activeFilters[me];
110
                        }
111
                    }
112
                });
113
            },
114
            clear: function() {
115
                $('#illfilter_patron').val('');
116
            }
117
        },
118
        dateModified: {
119
            clear: function() {
120
                $('#illfilter_datemodified_start, #illfilter_datemodified_end').val('');
121
            }
122
        },
123
        datePlaced: {
124
            clear: function() {
125
                $('#illfilter_dateplaced_start, #illfilter_dateplaced_end').val('');
126
            }
127
        }
128
    };
129
130
    // Expand any fields we're expanding
131
    var expandExpand = function(row) {
132
        expand.forEach(function(thisExpand) {
133
            if (row.hasOwnProperty(thisExpand)) {
134
                if (!expanded.hasOwnProperty(thisExpand)) {
135
                    expanded[thisExpand] = [];
136
                }
137
                var expandObj = row[thisExpand];
138
                Object.keys(expandObj).forEach(
139
                    function(thisExpandCol) {
140
                        var expColName = thisExpand + '_' + thisExpandCol.replace(/\s/g,'_');
141
                        // Keep a list of fields that have been expanded
142
                        // so we can create toggle links for them
143
                        if (expanded[thisExpand].indexOf(expColName) == -1) {
144
                            expanded[thisExpand].push(expColName);
145
                        }
146
                        expandObj[expColName] =
147
                            expandObj[thisExpandCol];
148
                        delete expandObj[thisExpandCol];
149
                    }
150
                );
151
                $.extend(true, row, expandObj);
152
                delete row[thisExpand];
153
            }
154
        });
155
    };
156
157
    // Strip the expand prefix if it exists, we do this for display
158
    var stripPrefix = function(value) {
159
        expand.forEach(function(thisExpand) {
160
            var regex = new RegExp(thisExpand + '_', 'g');
161
            value = value.replace(regex, '');
162
        });
163
        return value;
164
    };
165
166
    // Our 'render' function for borrowerlink
167
    var createPatronLink = function(data, type, row) {
168
        var patronLink = '<a title="' + ill_borrower_details + '" ' +
169
            'href="/cgi-bin/koha/members/moremember.pl?' +
170
            'borrowernumber='+row.borrowernumber+'">';
171
        if ( row.patron_firstname ) {
172
            patronLink = patronLink + row.patron_firstname + ' ';
173
        }
174
        patronLink = patronLink + row.patron_surname +
175
            ' (' + row.patron_cardnumber + ')' + '</a>';
176
        return patronLink;
177
    };
178
179
    // Our 'render' function for biblio_id
180
    var createBiblioLink = function(data, type, row) {
181
        return (row.biblio_id) ?
182
            '<a title="' + ill_biblio_details + '" ' +
183
            'href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=' +
184
            row.biblio_id + '">' +
185
            row.biblio_id +
186
            '</a>' : '';
187
    };
188
189
    // Our 'render' function for title
190
    var createTitle = function(data, type, row) {
191
        return (
192
            row.hasOwnProperty('metadata_container_title') &&
193
            row.metadata_container_title
194
        ) ? row.metadata_container_title : row.metadata_title;
195
    };
196
197
    // Render function for request ID
198
    var createRequestId = function(data, type, row) {
199
        return row.id_prefix + row.illrequest_id;
200
    };
201
202
    // Render function for type
203
    var createType = function(data, type, row) {
204
        if (!row.hasOwnProperty('metadata_Type') || !row.metadata_Type) {
205
            if (row.hasOwnProperty('medium') && row.medium) {
206
                row.metadata_Type = row.medium;
207
            } else {
208
                row.metadata_Type = null;
209
            }
210
        }
211
        return row.metadata_Type;
212
    };
213
214
    // Render function for request status
215
    var createStatus = function(data, type, row, meta) {
216
        if (row.status_alias) {
217
            return row.status_alias.lib
218
                ? row.status_alias.lib
219
                : row.status_alias.authorised_value;
220
        } else {
221
            var origData = meta.settings.oInit.originalData;
222
            if (origData.length > 0) {
223
                var status_name = meta.settings.oInit.originalData[0].capabilities[
224
                    row.status
225
                ].name;
226
                return getStatusName(status_name, row);
227
            } else {
228
                return '';
229
            }
230
        }
231
    };
232
233
    var getStatusName = function(origName, row) {
234
        switch( origName ) {
235
            case "New request":
236
                return ill_statuses.new;
237
            case "Requested":
238
                return ill_statuses.req;
239
            case "Requested from partners":
240
                var statStr = ill_statuses.genreq;
241
                if (
242
                    row.hasOwnProperty('requested_partners') &&
243
                    row.requested_partners &&
244
                    row.requested_partners.length > 0
245
                ) {
246
                    statStr += ' (' + row.requested_partners + ')';
247
                }
248
                return statStr;
249
            case "Request reverted":
250
                return ill_statuses.rev;
251
            case "Queued request":
252
                return ill_statuses.que;
253
            case "Cancellation requested":
254
                return ill_statuses.canc;
255
            case "Completed":
256
                return ill_statuses.comp;
257
            case "Delete request":
258
                return ill_statuses.del;
259
            default:
260
                return origName;
261
        }
262
    };
263
264
    // Render function for creating a row's action link
265
    var createActionLink = function(data, type, row) {
266
        return '<a class="btn btn-default btn-sm" ' +
267
            'href="/cgi-bin/koha/ill/ill-requests.pl?' +
268
            'method=illview&amp;illrequest_id=' +
269
            row.illrequest_id +
270
            '">' + ill_manage + '</a>';
271
    };
272
273
    // Columns that require special treatment
274
    var specialCols = {
275
        action: {
276
            func: createActionLink,
277
            skipSanitize: true
278
        },
279
        illrequest_id: {
280
            func: createRequestId
281
        },
282
        status: {
283
            func: createStatus
284
        },
285
        biblio_id: {
286
            name: ill_columns.biblio_id,
287
            func: createBiblioLink,
288
            skipSanitize: true
289
        },
290
        metadata_title: {
291
            func: createTitle
292
        },
293
        metadata_Type: {
294
            func: createType
295
        },
296
        updated: {
297
            name: ill_columns.updated
298
        },
299
        patron: {
300
            skipSanitize: true,
301
            func: createPatronLink
302
        }
303
    };
304
305
    // Display the modal containing request supplier metadata
306
    $('#ill-request-display-log').on('click', function(e) {
307
        e.preventDefault();
308
        $('#requestLog').modal({show:true});
309
    });
310
311
    // Toggle request attributes in Illview
312
    $('#toggle_requestattributes').on('click', function(e) {
313
        e.preventDefault();
314
        $('#requestattributes').toggleClass('content_hidden');
315
    });
316
317
    // Toggle new comment form in Illview
318
    $('#toggle_addcomment').on('click', function(e) {
319
        e.preventDefault();
320
        $('#addcomment').toggleClass('content_hidden');
321
    });
322
323
    // Filter partner list
324
    $('#partner_filter').keyup(function() {
325
        var needle = $('#partner_filter').val();
326
        $('#partners > option').each(function() {
327
            var regex = new RegExp(needle, 'i');
328
            if (
329
                needle.length == 0 ||
330
                $(this).is(':selected') ||
331
                $(this).text().match(regex)
332
            ) {
333
                $(this).show();
334
            } else {
335
                $(this).hide();
336
            }
337
        });
338
    });
339
340
    // Display the modal containing request supplier metadata
341
    $('#ill-request-display-metadata').on('click', function(e) {
342
        e.preventDefault();
343
        $('#dataPreview').modal({show:true});
344
    });
345
346
    // Allow us to chain Datatable render helpers together, so we
347
    // can use our custom functions and render.text(), which
348
    // provides us with data sanitization
349
    $.fn.dataTable.render.multi = function(renderArray) {
350
        return function(d, type, row, meta) {
351
            for(var r = 0; r < renderArray.length; r++) {
352
                var toCall = renderArray[r].hasOwnProperty('display') ?
353
                    renderArray[r].display :
354
                    renderArray[r];
355
                d = toCall(d, type, row, meta);
356
            }
357
            return d;
358
        }
359
    }
360
361
    // Get our data from the API and process it prior to passing
362
    // it to datatables
363
    var filterParam = prefilters ? '&' + prefilters : '';
364
    var ajax = $.ajax(
365
        '/api/v1/illrequests?embed=metadata,patron,capabilities,library,status_alias,comments,requested_partners'
366
        + filterParam
367
        ).done(function() {
368
            var data = JSON.parse(ajax.responseText);
369
            // Make a copy, we'll be removing columns next and need
370
            // to be able to refer to data that has been removed
371
            var dataCopy = $.extend(true, [], data);
372
            // Expand columns that need it and create an array
373
            // of all column names
374
            $.each(dataCopy, function(k, row) {
375
                expandExpand(row);
376
            });
377
378
            // Assemble an array of column definitions for passing
379
            // to datatables
380
            var colData = [];
381
            columns_settings.forEach(function(thisCol) {
382
                var colName = thisCol.columnname;
383
                // Create the base column object
384
                var colObj = $.extend({}, thisCol);
385
                colObj.name = colName;
386
                colObj.className = colName;
387
                colObj.defaultContent = '';
388
389
                // We may need to process the data going in this
390
                // column, so do it if necessary
391
                if (
392
                    specialCols.hasOwnProperty(colName) &&
393
                    specialCols[colName].hasOwnProperty('func')
394
                ) {
395
                    var renderArray = [
396
                        specialCols[colName].func
397
                    ];
398
                    if (!specialCols[colName].skipSanitize) {
399
                        renderArray.push(
400
                            $.fn.dataTable.render.text()
401
                        );
402
                    }
403
404
                    colObj.render = $.fn.dataTable.render.multi(
405
                        renderArray
406
                    );
407
                } else {
408
                    colObj.data = colName;
409
                    colObj.render = $.fn.dataTable.render.text()
410
                }
411
                // Make sure properties that aren't present in the API
412
                // response are populated with null to avoid Datatables
413
                // choking on their absence
414
                dataCopy.forEach(function(thisData) {
415
                    if (!thisData.hasOwnProperty(colName)) {
416
                        thisData[colName] = null;
417
                    }
418
                });
419
                colData.push(colObj);
420
            });
421
422
            // Initialise the datatable
423
            table = KohaTable("ill-requests", {
424
                'aoColumnDefs': [
425
                    { // Last column shouldn't be sortable or searchable
426
                        'aTargets': [ 'actions' ],
427
                        'bSortable': false,
428
                        'bSearchable': false
429
                    },
430
                    { // When sorting 'placed', we want to use the
431
                        // unformatted column
432
                        'aTargets': [ 'placed_formatted'],
433
                        'iDataSort': 14
434
                    },
435
                    { // When sorting 'updated', we want to use the
436
                        // unformatted column
437
                        'aTargets': [ 'updated_formatted'],
438
                        'iDataSort': 16
439
                    },
440
                    { // When sorting 'completed', we want to use the
441
                        // unformatted column
442
                        'aTargets': [ 'completed_formatted'],
443
                        'iDataSort': 19
444
                    }
445
                ],
446
                'aaSorting': [[ 16, 'desc' ]], // Default sort, updated descending
447
                'processing': true, // Display a message when manipulating
448
                'sPaginationType': "full_numbers", // Pagination display
449
                'deferRender': true, // Improve performance on big datasets
450
                'data': dataCopy,
451
                'columns': colData,
452
                'originalData': data, // Enable render functions to access
453
                                        // our original data
454
                'initComplete': function() {
455
456
                    // Prepare any filter elements that need it
457
                    for (var el in filterable) {
458
                        if (filterable.hasOwnProperty(el)) {
459
                            if (filterable[el].hasOwnProperty('prep')) {
460
                                filterable[el].prep(dataCopy, data);
461
                            }
462
                            if (filterable[el].hasOwnProperty('listener')) {
463
                                filterable[el].listener();
464
                            }
465
                        }
466
                    }
467
468
                }
469
            }, columns_settings);
470
471
            // Custom date range filtering
472
            $.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
473
                var placedStart = $('#illfilter_dateplaced_start').datepicker('getDate');
474
                var placedEnd = $('#illfilter_dateplaced_end').datepicker('getDate');
475
                var modifiedStart = $('#illfilter_datemodified_start').datepicker('getDate');
476
                var modifiedEnd = $('#illfilter_datemodified_end').datepicker('getDate');
477
                var rowPlaced = data[14] ? new Date(data[14]) : null;
478
                var rowModified = data[16] ? new Date(data[16]) : null;
479
                var placedPassed = true;
480
                var modifiedPassed = true;
481
                if (placedStart && rowPlaced && rowPlaced < placedStart) {
482
                    placedPassed = false
483
                };
484
                if (placedEnd && rowPlaced && rowPlaced > placedEnd) {
485
                    placedPassed = false;
486
                }
487
                if (modifiedStart && rowModified && rowModified < modifiedStart) {
488
                    modifiedPassed = false
489
                };
490
                if (modifiedEnd && rowModified && rowModified > modifiedEnd) {
491
                    modifiedPassed = false;
492
                }
493
494
                return placedPassed && modifiedPassed;
495
496
            });
497
498
        }
499
    );
500
501
    var clearSearch = function() {
502
        table.api().search('').columns().search('');
503
        activeFilters = {};
504
        for (var filter in filterable) {
505
            if (
506
                filterable.hasOwnProperty(filter) &&
507
                filterable[filter].hasOwnProperty('clear')
508
            ) {
509
                filterable[filter].clear();
510
            }
511
        }
512
        table.api().draw();
513
    };
514
515
    // Apply any search filters, or clear any previous
516
    // ones
517
    $('#illfilter_form').submit(function(event) {
518
        event.preventDefault();
519
        table.api().search('').columns().search('');
520
        for (var active in activeFilters) {
521
            if (activeFilters.hasOwnProperty(active)) {
522
                activeFilters[active]();
523
            }
524
        }
525
        table.api().draw();
526
    });
527
528
    // Clear all filters
529
    $('#clear_search').click(function() {
530
        clearSearch();
531
    });
532
533
});
(-)a/members/ill-requests.pl (-1 / +55 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Copyright 2018 PTFS Europe
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::Output;
25
use C4::Members;
26
use C4::Members::Attributes qw(GetBorrowerAttributes);
27
use Koha::Patrons;
28
29
my $input = new CGI;
30
31
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
32
    {
33
        template_name   => "members/ill-requests.tt",
34
        query           => $input,
35
        type            => "intranet",
36
        authnotrequired => 0,
37
        flagsrequired   => { borrowers => 'edit_borrowers' },
38
        debug           => 1,
39
    }
40
);
41
42
my $borrowernumber = $input->param('borrowernumber');
43
44
my $logged_in_user = Koha::Patrons->find( $loggedinuser ) or die "Not logged in";
45
my $patron         = Koha::Patrons->find( $borrowernumber );
46
output_and_exit_if_error( $input, $cookie, $template, { module => 'members', logged_in_user => $logged_in_user, current_patron => $patron } );
47
48
my $category = $patron->category;
49
$template->param(
50
    prefilters => "borrowernumber=$borrowernumber",
51
    patron => $patron,
52
    illview  => 1,
53
);
54
55
output_html_with_http_headers $input, $cookie, $template->output;

Return to bug 18589