Bug 39600: Switch OPAC ILL request listing to server-side rendering

This brings it technologically closer to intranet's ILL table and also enables pagination.

Test plan:

1. In KTD, go to http://localhost:8081/cgi-bin/koha/admin/preferences.pl?tab=&op=search&searchfield=master+switch and enable the ILL module
2. Run the following in koha-shell to populate the request list for user `koha`:

    perl -Mt::lib::TestBuilder -e '
      my $builder = t::lib::TestBuilder->new;

      do {
        $builder->build_sample_ill_request({borrowernumber => 51});
      } for (1..50);
    '
3. Navigate to http://localhost:8080/cgi-bin/koha/opac-illrequests.pl, log in as `koha` as needed
4. Observe the new request list being paginated and server-side-sortable

Sponsored-by: Wiko <https://www.wiko-berlin.de/>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
This commit is contained in:
Tadeusz „tadzik” Sośnierz 2025-03-06 16:18:44 +01:00 committed by Katrin Fischer
parent c095e09ff9
commit 44743bc3c7
No known key found for this signature in database
GPG Key ID: 0EF6E2C03357A834
6 changed files with 220 additions and 29 deletions

View File

@ -58,6 +58,31 @@ sub list {
};
}
sub patron_list {
my $c = shift->openapi->valid_input or return;
my $user = $c->stash('koha.user');
if ($user->borrowernumber != $c->param('patron_id') and !$user->is_superlibrarian) {
return $c->render(
status => 403,
openapi => { error => "Cannot lookup ILL requests for other users" }
);
}
return try {
my $reqs = $c->objects->search(Koha::ILL::Requests->search(
{ borrowernumber => $c->param('patron_id') }
));
return $c->render(
status => 200,
openapi => $reqs,
);
} catch {
$c->unhandled_exception($_);
};
}
=head3 add
Adds a new ILL request

View File

@ -662,3 +662,69 @@
x-koha-authorization:
permissions:
borrowers: delete_borrowers
"/patrons/{patron_id}/ill/requests":
get:
x-mojo-to: ILL::Requests#patron_list
operationId: getPatronIllRequests
tags:
- patrons
- ill_requests
summary: Get patron's ILL requests
parameters:
- $ref: "../swagger.yaml#/parameters/patron_id_pp"
- $ref: "../swagger.yaml#/parameters/page"
- $ref: "../swagger.yaml#/parameters/per_page"
- $ref: "../swagger.yaml#/parameters/match"
- $ref: "../swagger.yaml#/parameters/order_by"
- $ref: "../swagger.yaml#/parameters/q_param"
- $ref: "../swagger.yaml#/parameters/q_body"
- $ref: "../swagger.yaml#/parameters/request_id_header"
- name: x-koha-embed
in: header
required: false
description: Embed list sent as a request header
type: array
items:
type: string
enum:
- +strings
- extended_attributes
collectionFormat: csv
produces:
- application/json
responses:
"200":
description: A list of parton's ILL requests
schema:
type: array
items:
$ref: "../swagger.yaml#/definitions/ill_request"
"400":
description: Bad request
schema:
$ref: "../swagger.yaml#/definitions/error"
"401":
description: Authentication required
schema:
$ref: "../swagger.yaml#/definitions/error"
"403":
description: Access forbidden
schema:
$ref: "../swagger.yaml#/definitions/error"
"404":
description: Patron not found
schema:
$ref: "../swagger.yaml#/definitions/error"
"500":
description: |
Internal server error. Possible `error_code` attribute values:
* `internal_server_error`
schema:
$ref: "../swagger.yaml#/definitions/error"
"503":
description: Under maintenance
schema:
$ref: "../swagger.yaml#/definitions/error"
x-koha-authorization:
permissions: {}

View File

@ -477,6 +477,8 @@ paths:
$ref: "./paths/patrons_password.yaml#/~1patrons~1{patron_id}~1password~1expiration_date"
"/patrons/{patron_id}/recalls":
$ref: "./paths/patrons_recalls.yaml#/~1patrons~1{patron_id}~1recalls"
"/patrons/{patron_id}/ill/requests":
$ref: "./paths/patrons.yaml#/~1patrons~1{patron_id}~1ill~1requests"
/patron_categories:
$ref: ./paths/patron_categories.yaml#/~1patron_categories
/preservation/config:

View File

@ -87,5 +87,6 @@
[% Asset.css("lib/fontawesome/css/fontawesome.min.css") | $raw %]
[% Asset.css("lib/fontawesome/css/brands.min.css") | $raw %]
[% Asset.css("lib/fontawesome/css/solid.min.css") | $raw %]
[% Asset.css("lib/datatables/datatables.min.css") | $raw %]
[% PROCESS 'html_helpers.inc' %]
[% KohaPlugins.get_plugins_opac_head | $raw %]

View File

@ -152,23 +152,6 @@
</tr>
</thead>
<tbody>
[% FOREACH request IN requests %]
[% status = request.status | html %]
[% type = request.get_type %]
<tr>
<td>[% request.id | html %]</td>
<td> [% IF request.metadata.Author %][% request.metadata.Author | html %][% ELSE %]<span>N/A</span>[% END %] </td>
<td> [% IF request.metadata.Title %][% request.metadata.Title | html %][% ELSE %]<span>N/A</span>[% END %] </td>
<td>[% request.backend | html %]</td>
<td> [% IF type %][% type | html %][% ELSE %]<span>N/A</span>[% END %] </td>
<td>[% request.status_alias ? request.statusalias.lib_opac : request.capabilities.$status.name | html %]</td>
<td data-order="[% request.placed | html %]">[% request.placed | $KohaDates %]</td>
<td data-order="[% request.updated | html %]">[% request.updated | $KohaDates %]</td>
<td>
<a href="/cgi-bin/koha/opac-illrequests.pl?op=view&amp;illrequest_id=[% request.id | uri %]" class="btn btn-primary btn-sm">View</a>
</td>
</tr>
[% END %]
</tbody>
</table>
[% ELSIF op == 'view' %]
@ -369,9 +352,131 @@
[% BLOCK jsinclude %]
[% INCLUDE 'datatables.inc' %]
<script>
$("#illrequestlist").kohaTable({
order: [[3, "desc"]],
});
function display_extended_attribute(row, type) {
var arr = $.grep(row.extended_attributes, ( x => x.type === type ));
if (arr.length > 0) {
return escape_str(arr[0].value);
}
return '';
}
function display_request_status(row) {
let status = row._strings.status.str;
let status_alias = row._strings.status_av ?
row._strings.status_av.str ?
row._strings.status_av.str :
row._strings.status_av.code :
null;
let status_label = status + (status_alias ? " <i><strong>"+status_alias+"</strong></i>" : "");
return status_label;
}
$("#illrequestlist").dataTable($.extend(true, {}, dataTablesDefaults, {
columns: [
{
data: 'ill_request_id',
sortable: true,
render: (data, type, row, meta) => {
return data;
},
},
{
data: 'author',
sortable: false,
render: (data, type, row, meta) => {
return display_extended_attribute(row, 'author');
},
},
{
data: 'title',
sortable: false,
render: (data, type, row, meta) => {
return display_extended_attribute(row, 'title');
},
},
{
data: 'ill_backend_id',
sortable: true,
render: (data, type, row, meta) => {
return escape_str(data);
},
},
{
data: 'type',
sortable: false,
render: (data, type, row, meta) => {
return display_extended_attribute(row, 'type');
},
},
{
data: 'status',
sortable: true,
render: (data, type, row, meta) => {
return display_request_status(row);
},
},
{
data: 'requested_date',
sortable: true,
render: (data, type, row, meta) => {
return $date(data);
},
},
{
data: 'timestamp',
sortable: true,
render: (data, type, row, meta) => {
return $date(data);
},
},
{
data: '',
sortable: false,
render: (data, type, row, meta) => {
return `<a href="/cgi-bin/koha/opac-illrequests.pl?op=view&amp;illrequest_id=${row.ill_request_id}" class="btn btn-primary btn-sm">View</a>`;
},
}
],
"order": [[ 0, "desc" ]],
"dom": '<"dt-info"i><"top pager"<"table_entries"lp>>tr<"bottom pager"ip>',
"paging": true,
"serverSide": true,
"ajax": {
"cache": true,
"url": '/api/v1/patrons/[% logged_in_user.borrowernumber %]/ill/requests',
'beforeSend': function (xhr, settings) {
this._xhr = xhr;
xhr.setRequestHeader('x-koha-embed', '+strings,extended_attributes');
},
"data": (data, settings) => {
const order = data.order[0];
const order_by = (order.dir == 'asc' ? '+' : '-') + 'me.' + data.columns[order.column].data;
const page = Math.floor(data.start / data.length) + 1;
return {
_page: page,
_per_page: data.length,
_order_by: order_by,
};
},
'dataFilter': function (data, type) {
const json = {data: JSON.parse(data)};
if (total = this._xhr.getResponseHeader('x-total-count')) {
json.recordsTotal = total;
json.recordsFiltered = total;
}
if (total = this._xhr.getResponseHeader('x-base-total-count')) {
json.recordsTotal = total;
}
if (draw = this._xhr.getResponseHeader('x-koha-request-id')) {
json.draw = draw;
}
return JSON.stringify(json);
},
},
}));
$("#backend-dropdown-options").removeClass("nojs");
[% IF services_json.length > 0 %]
var services = [% services_json | $raw %];

View File

@ -94,19 +94,11 @@ if ( ( $op eq 'cud-create' || $op eq 'cancreq' || $op eq 'cud-update' ) && !$can
}
if ( $op eq 'list' ) {
my $requests = Koha::ILL::Requests->search( { borrowernumber => $loggedinuser } );
$template->param(
requests => $requests,
backends => $backends
);
$template->param( backends => $backends );
} elsif ( $op eq 'view' ) {
$template->param( request => $request );
} elsif ( $op eq 'cud-update' ) {
$request->notesopac( $params->{notesopac} )->store;
# Send a notice to staff alerting them of the update
$request->send_staff_notice('ILL_REQUEST_MODIFIED');
print $query->redirect(