Bug 17387: (follow-up) take library groups in to account for permissions check

Signed-off-by: Michaela <michaela.sieber@kit.edu>
Sponsored-by: ByWater Solutions
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
Jacob O'Mara 2025-11-05 14:47:18 +00:00 committed by Lucas Gass
parent b1df5271f3
commit c07639777d
No known key found for this signature in database
GPG Key ID: 60B447398B0D36CA
6 changed files with 156 additions and 13 deletions

View File

@ -154,18 +154,32 @@ sub to_api_mapping {
=head3 restore
my $biblio = $deleted_biblio->restore;
my $biblio = $deleted_biblio->restore({
patron => $patron,
item_ids => \@item_ids,
restore_all => 0
});
Restores the deleted biblio record back to the biblio table along with
its biblioitems and metadata. This removes the record from the deleted tables
and re-inserts it into the active tables. The biblio record will be reindexed
after restoration.
Optional parameters:
patron - Koha::Patron object. If provided, only items the patron can edit will be restored.
item_ids - Arrayref of item IDs to restore. If not provided and restore_all is false,
no items will be restored.
restore_all - Boolean. If true and no item_ids provided, restore all items the patron
has permission to edit.
Returns the newly restored Koha::Biblio object.
=cut
sub restore {
my ($self) = @_;
my ( $self, $params ) = @_;
$params //= {};
my $biblio_data = $self->unblessed;
my $biblioitem = $self->biblioitem;
@ -187,13 +201,43 @@ sub restore {
$biblioitem->delete;
$self->delete;
my $patron = $params->{patron};
my $item_ids = $params->{item_ids};
my $restore_all = $params->{restore_all} // 0;
my @restored_items;
my @skipped_items;
if ( $restore_all || $item_ids ) {
my $deleted_items = $self->items;
while ( my $deleted_item = $deleted_items->next ) {
my $should_restore = 0;
if ($item_ids) {
$should_restore = grep { $_ == $deleted_item->itemnumber } @$item_ids;
} elsif ($restore_all) {
$should_restore = 1;
}
if ($should_restore) {
if ( $patron && !$patron->can_edit_items_from( $deleted_item->homebranch ) ) {
push @skipped_items, $deleted_item->itemnumber;
next;
}
$deleted_item->restore;
push @restored_items, $deleted_item->itemnumber;
}
}
}
my $indexer = Koha::SearchEngine::Indexer->new( { index => $Koha::SearchEngine::BIBLIOS_INDEX } );
$indexer->index_records( $new_biblio->biblionumber, "specialUpdate", "biblioserver" );
logaction( "CATALOGUING", "RESTORE", $new_biblio->biblionumber, "biblio", undef, $new_biblio )
if C4::Context->preference("CataloguingLog");
return $new_biblio;
return wantarray ? ( $new_biblio, \@restored_items, \@skipped_items ) : $new_biblio;
}
=head2 Internal methods

View File

@ -180,12 +180,31 @@ sub restore {
return $c->render_resource_not_found("Bibliographic record")
unless $deleted_biblio;
my $body = $c->req->json;
my $item_ids = $body->{item_ids};
my $restore_all = $body->{restore_all} // 0;
return try {
my $biblio = $deleted_biblio->restore;
my $patron = $c->stash('koha.user');
my ( $biblio, $restored_items, $skipped_items ) = $deleted_biblio->restore(
{
patron => $patron,
item_ids => $item_ids,
restore_all => $restore_all
}
);
my $response = { biblio_id => $biblio->biblionumber };
if ( $item_ids || $restore_all ) {
$response->{restored_items} = $restored_items if @$restored_items;
$response->{skipped_items} = $skipped_items if @$skipped_items;
}
return $c->render(
status => 200,
openapi => { biblio_id => $biblio->biblionumber }
openapi => $response
);
} catch {
$c->unhandled_exception($_);

View File

@ -85,6 +85,15 @@ sub restore {
unless $deleted_item;
return try {
my $patron = $c->stash('koha.user');
unless ( $patron->can_edit_items_from( $deleted_item->homebranch ) ) {
return $c->render(
status => 403,
openapi => { error => "You do not have permission to restore items from this library." }
);
}
my $item = $deleted_item->restore;
return $c->render(

View File

@ -135,6 +135,24 @@
summary: Restore deleted biblio
parameters:
- $ref: "../swagger.yaml#/parameters/biblio_id_pp"
- name: body
in: body
description: Restore options for items
required: false
schema:
type: object
properties:
item_ids:
type: array
description: Array of item IDs to restore with the biblio
items:
type: integer
restore_all:
type: boolean
description: Restore all items the user has permission to edit
additionalProperties: false
consumes:
- application/json
produces:
- application/json
responses:
@ -142,6 +160,23 @@
description: Biblio restored successfully
schema:
type: object
properties:
biblio_id:
type: integer
description: The ID of the restored biblio
restored_items:
type: array
description: List of item IDs that were successfully restored
items:
type: integer
skipped_items:
type: array
description: List of item IDs that were skipped during restoration due to insufficient permissions
items:
type: integer
required:
- biblio_id
additionalProperties: false
"400":
description: Bad request
schema:

View File

@ -190,6 +190,16 @@
}
$(document).ready(function () {
var librariesWhereCanEdit = [% libraries_where_can_edit_json | $raw %];
var canEditAnyLibrary = librariesWhereCanEdit.length === 0;
function canEditItem(homebranch) {
if (canEditAnyLibrary) {
return true;
}
return librariesWhereCanEdit.includes(homebranch);
}
var oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
var today = new Date();
@ -365,17 +375,22 @@
{
data: function (row, type) {
if (type === "display") {
return (
'<button class="btn btn-sm btn-default restore-item" data-item-id="' +
var canEdit = canEditItem(row.home_library_id);
var disabled = canEdit ? '' : ' disabled';
var button = '<button class="btn btn-sm btn-default restore-item" data-item-id="' +
row.item_id +
'" data-barcode="' +
$("<div/>")
.text(row.external_id || row.item_id)
.html() +
'"><i class="fa fa-undo" aria-hidden="true"></i> ' +
'"' + disabled + '><i class="fa fa-undo" aria-hidden="true"></i> ' +
_("Restore") +
"</button>"
);
"</button>";
if (!canEdit) {
return '<span title="' + _("You do not have permission to restore items from this library") + '" style="cursor: not-allowed;">' + button + '</span>';
}
return button;
}
return "";
},
@ -422,7 +437,14 @@
{
data: function (row, type) {
if (type === "display") {
return '<input type="checkbox" class="item-checkbox" data-item-id="' + row.item_id + '">';
var canEdit = canEditItem(row.home_library_id);
var disabled = canEdit ? '' : ' disabled';
var checkbox = '<input type="checkbox" class="item-checkbox" data-item-id="' + row.item_id + '"' + disabled + '>';
if (!canEdit) {
return '<span title="' + _("You do not have permission to restore items from this library") + '">' + checkbox + '</span>';
}
return checkbox;
}
return "";
},
@ -462,7 +484,12 @@
orderable: true,
render: function (data, type, row) {
if (type === "display") {
return data ? $("<div/>").text(data).html() : "";
var text = data ? $("<div/>").text(data).html() : "";
var canEdit = canEditItem(data);
if (!canEdit) {
text += ' <span class="badge bg-warning text-dark">' + _("No permission") + '</span>';
}
return text;
}
return data || "";
},
@ -570,7 +597,7 @@
// Select all items checkbox
$("#select-all-items").on("change", function () {
var checked = $(this).prop("checked");
$(".item-checkbox").prop("checked", checked);
$(".item-checkbox:not(:disabled)").prop("checked", checked);
});
// Restore biblio with items

View File

@ -17,10 +17,12 @@
use Modern::Perl;
use CGI qw ( -utf8 );
use CGI qw ( -utf8 );
use JSON qw( encode_json );
use C4::Auth qw( get_template_and_user );
use C4::Output qw( output_html_with_http_headers );
use Koha::Patrons;
my $input = CGI->new;
@ -33,4 +35,11 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
}
);
my $patron = Koha::Patrons->find($loggedinuser);
my @libraries_where_can_edit = $patron->libraries_where_can_edit_items;
$template->param(
libraries_where_can_edit_json => encode_json( \@libraries_where_can_edit ),
);
output_html_with_http_headers $input, $cookie, $template->output;