Bugzilla – Attachment 188787 Details for
Bug 17387
Add an undelete feature for items/biblios
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 17387: add restore records page
Bug-17387-add-restore-records-page.patch (text/plain), 16.55 KB, created by
Biblibre Sandboxes
on 2025-10-31 09:50:58 UTC
(
hide
)
Description:
Bug 17387: add restore records page
Filename:
MIME Type:
Creator:
Biblibre Sandboxes
Created:
2025-10-31 09:50:58 UTC
Size:
16.55 KB
patch
obsolete
>From dcd502c45f9cc6b10c29e25238e1c6f70f00285d Mon Sep 17 00:00:00 2001 >From: Jacob O'Mara <Jacob.omara@openfifth.co.uk> >Date: Thu, 30 Oct 2025 10:59:29 +0000 >Subject: [PATCH] Bug 17387: add restore records page > >Signed-off-by: Michaela <michaela.sieber@kit.edu> >--- > .../prog/en/includes/cat-menu.inc | 7 +- > .../prog/en/modules/tools/restore-records.tt | 322 ++++++++++++++++++ > tools/restore-records.pl | 36 ++ > 3 files changed, 364 insertions(+), 1 deletion(-) > create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/restore-records.tt > create mode 100755 tools/restore-records.pl > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/cat-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/cat-menu.inc >index 1687b18529..65d514cdca 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/cat-menu.inc >+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/cat-menu.inc >@@ -43,7 +43,7 @@ > </ul> > [% END %] > >- [% IF ( CAN_user_tools_items_batchmod || CAN_user_tools_items_batchdel || CAN_user_tools_records_batchmod || CAN_user_tools_records_batchdel || CAN_user_tools_marc_modification_templates ) %] >+ [% IF ( CAN_user_tools_items_batchmod || CAN_user_tools_items_batchdel || CAN_user_tools_records_batchmod || CAN_user_tools_records_batchdel || CAN_user_tools_marc_modification_templates || CAN_user_editcatalogue_edit_catalogue ) %] > <h5>Batch editing</h5> > <ul> > [% IF ( CAN_user_tools_items_batchmod ) %] >@@ -66,6 +66,11 @@ > <a href="/cgi-bin/koha/tools/batch_delete_records.pl">Batch record deletion</a> > </li> > [% END %] >+ [% IF ( CAN_user_editcatalogue_edit_catalogue ) %] >+ <li> >+ <a href="/cgi-bin/koha/tools/restore-records.pl">Restore deleted records</a> >+ </li> >+ [% END %] > [% IF ( CAN_user_tools_marc_modification_templates ) %] > <li> > <a href="/cgi-bin/koha/tools/marc_modification_templates.pl">MARC modification templates</a> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/restore-records.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/restore-records.tt >new file mode 100644 >index 0000000000..49d2012024 >--- /dev/null >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/restore-records.tt >@@ -0,0 +1,322 @@ >+[% USE raw %] >+[% USE Asset %] >+[% USE KohaDates %] >+[% USE Koha %] >+[% PROCESS 'i18n.inc' %] >+[% SET footerjs = 1 %] >+[% INCLUDE 'doc-head-open.inc' %] >+<title> >+ [% FILTER collapse %] >+ [% t("Restore deleted records") | html %] >+ › [% t("Tools") | html %] › [% t("Koha") | html %] >+ [% END %] >+</title> >+[% INCLUDE 'doc-head-close.inc' %] >+</head> >+ >+<body id="tools_restore-records" class="tools"> >+[% WRAPPER 'header.inc' %] >+ [% INCLUDE 'cat-search.inc' %] >+[% END %] >+ >+[% WRAPPER 'sub-header.inc' %] >+ [% WRAPPER breadcrumbs %] >+ [% WRAPPER breadcrumb_item %] >+ <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> >+ [% END %] >+ [% WRAPPER breadcrumb_item bc_active= 1 %] >+ <span>Restore deleted records</span> >+ [% END %] >+ [% END #/ WRAPPER breadcrumbs %] >+[% END #/ WRAPPER sub-header.inc %] >+ >+[% SET asides = [] %] >+[% asides.push('cat-menu') %] >+ >+[% WRAPPER 'main-container.inc' %] >+ <div class="row"> >+ <div class="col-md-10 col-md-push-2"> >+ <main> >+ <h1>Restore deleted records</h1> >+ >+ <div id="messages"></div> >+ >+ <h2>Deleted bibliographic records</h2> >+ <div class="page-section"> >+ <table id="deleted_biblios_table" class="table table-bordered table-striped"> >+ <thead> >+ <tr> >+ <th>Biblio ID</th> >+ <th>Title</th> >+ <th>Author</th> >+ <th>Deleted on</th> >+ <th class="NoSort">Actions</th> >+ </tr> >+ </thead> >+ </table> >+ </div> >+ >+ <h2>Deleted items</h2> >+ <div class="page-section"> >+ <table id="deleted_items_table" class="table table-bordered table-striped"> >+ <thead> >+ <tr> >+ <th>Item ID</th> >+ <th>Biblio ID</th> >+ <th>Barcode</th> >+ <th>Call number</th> >+ <th>Home library</th> >+ <th>Deleted on</th> >+ <th class="NoSort">Actions</th> >+ </tr> >+ </thead> >+ </table> >+ </div> >+ </main> >+ </div> >+ </div> >+[% END #/ WRAPPER main-container.inc %] >+ >+[% MACRO jsinclude BLOCK %] >+ [% Asset.js("js/tools-menu.js") | $raw %] >+ [% INCLUDE 'datatables.inc' %] >+ <script> >+ function showMessage(message, type) { >+ var alert = $('<div class="alert alert-' + type + '">' + message + "</div>"); >+ $("#messages").append(alert); >+ >+ setTimeout(function () { >+ alert.fadeOut(400, function () { >+ $(this).remove(); >+ }); >+ }, 5000); >+ } >+ >+ $(document).ready(function () { >+ // Deleted biblios DataTable >+ var biblios_table = $("#deleted_biblios_table").kohaTable({ >+ ajax: { >+ url: "/api/v1/deleted/biblios", >+ }, >+ order: [[3, "desc"]], >+ columns: [ >+ { >+ data: "biblio_id", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "title", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row) { >+ if (type === "display") { >+ return $("<div/>").text(data).html(); >+ } >+ return data; >+ }, >+ }, >+ { >+ data: "author", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row) { >+ if (type === "display") { >+ return data ? $("<div/>").text(data).html() : ""; >+ } >+ return data || ""; >+ }, >+ }, >+ { >+ data: "deleted_on", >+ searchable: false, >+ orderable: true, >+ render: function (data, type, row) { >+ if (type === "display" && data) { >+ return $date(data); >+ } >+ return data; >+ }, >+ }, >+ { >+ data: function (row, type) { >+ if (type === "display") { >+ return ( >+ '<button class="btn btn-sm btn-default restore-biblio" data-biblio-id="' + >+ row.biblio_id + >+ '" data-title="' + >+ $("<div/>").text(row.title).html() + >+ '"><i class="fa fa-undo" aria-hidden="true"></i> ' + >+ _("Restore") + >+ "</button>" >+ ); >+ } >+ return ""; >+ }, >+ searchable: false, >+ orderable: false, >+ }, >+ ], >+ }); >+ var biblios_table_api = biblios_table.DataTable(); >+ >+ // Deleted items DataTable >+ var items_table = $("#deleted_items_table").kohaTable({ >+ ajax: { >+ url: "/api/v1/deleted/items", >+ }, >+ order: [[5, "desc"]], >+ columns: [ >+ { >+ data: "item_id", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "biblio_id", >+ searchable: true, >+ orderable: true, >+ }, >+ { >+ data: "external_id", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row) { >+ if (type === "display") { >+ return data ? $("<div/>").text(data).html() : ""; >+ } >+ return data || ""; >+ }, >+ }, >+ { >+ data: "callnumber", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row) { >+ if (type === "display") { >+ return data ? $("<div/>").text(data).html() : ""; >+ } >+ return data || ""; >+ }, >+ }, >+ { >+ data: "home_library_id", >+ searchable: true, >+ orderable: true, >+ render: function (data, type, row) { >+ if (type === "display") { >+ return data ? $("<div/>").text(data).html() : ""; >+ } >+ return data || ""; >+ }, >+ }, >+ { >+ data: "deleted_on", >+ searchable: false, >+ orderable: true, >+ render: function (data, type, row) { >+ if (type === "display" && data) { >+ return $date(data); >+ } >+ return data || ""; >+ }, >+ }, >+ { >+ data: function (row, type) { >+ if (type === "display") { >+ return ( >+ '<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> ' + >+ _("Restore") + >+ "</button>" >+ ); >+ } >+ return ""; >+ }, >+ searchable: false, >+ orderable: false, >+ }, >+ ], >+ }); >+ var items_table_api = items_table.DataTable(); >+ >+ // Restore biblio handler >+ $("#deleted_biblios_table").on("click", ".restore-biblio", function (e) { >+ e.preventDefault(); >+ var button = $(this); >+ var biblio_id = button.data("biblio-id"); >+ var title = button.data("title"); >+ >+ if (!confirm(_("Are you sure you want to restore bibliographic record %s '%s'?").format(biblio_id, title))) { >+ return; >+ } >+ >+ button.prop("disabled", true); >+ >+ $.ajax({ >+ url: "/api/v1/deleted/biblios/" + biblio_id, >+ type: "POST", >+ headers: { >+ "x-koha-request-id": Math.random(), >+ }, >+ success: function (data) { >+ $("#messages").append( >+ '<div class="alert alert-success">' + '<button type="button" class="close" data-dismiss="alert">×</button>' + _("Bibliographic record %s restored successfully").format(biblio_id) + "</div>" >+ ); >+ biblios_table_api.ajax.reload(); >+ items_table_api.ajax.reload(); >+ }, >+ error: function (xhr) { >+ var error_msg = _("Error restoring bibliographic record %s").format(biblio_id); >+ if (xhr.responseJSON && xhr.responseJSON.error) { >+ error_msg += ": " + xhr.responseJSON.error; >+ } >+ $("#messages").append('<div class="alert alert-danger">' + '<button type="button" class="close" data-dismiss="alert">×</button>' + error_msg + "</div>"); >+ button.prop("disabled", false); >+ }, >+ }); >+ }); >+ >+ // Restore item handler >+ $("#deleted_items_table").on("click", ".restore-item", function (e) { >+ e.preventDefault(); >+ var button = $(this); >+ var item_id = button.data("item-id"); >+ var barcode = button.data("barcode"); >+ >+ if (!confirm(_("Are you sure you want to restore item %s (%s)?").format(item_id, barcode))) { >+ return; >+ } >+ >+ button.prop("disabled", true); >+ >+ $.ajax({ >+ url: "/api/v1/deleted/items/" + item_id, >+ type: "PUT", >+ headers: { >+ "x-koha-request-id": Math.random(), >+ }, >+ success: function (data) { >+ showMessage(_("Item %s restored successfully").format(item_id), "success"); >+ items_table_api.ajax.reload(); >+ }, >+ error: function (xhr) { >+ var error_msg = _("Error restoring item %s").format(item_id); >+ if (xhr.responseJSON && xhr.responseJSON.error) { >+ error_msg += ": " + xhr.responseJSON.error; >+ } >+ showMessage(error_msg, "danger"); >+ button.prop("disabled", false); >+ }, >+ }); >+ }); >+ }); >+ </script> >+[% END %] >+ >+[% INCLUDE 'intranet-bottom.inc' %] >diff --git a/tools/restore-records.pl b/tools/restore-records.pl >new file mode 100755 >index 0000000000..b3133f2cf1 >--- /dev/null >+++ b/tools/restore-records.pl >@@ -0,0 +1,36 @@ >+#!/usr/bin/perl >+ >+# This file is part of Koha. >+# >+# Koha is free software; you can redistribute it and/or modify it >+# under the terms of the GNU General Public License as published by >+# the Free Software Foundation; either version 3 of the License, or >+# (at your option) any later version. >+# >+# Koha is distributed in the hope that it will be useful, but >+# WITHOUT ANY WARRANTY; without even the implied warranty of >+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+# GNU General Public License for more details. >+# >+# You should have received a copy of the GNU General Public License >+# along with Koha; if not, see <https://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use CGI qw ( -utf8 ); >+ >+use C4::Auth qw( get_template_and_user ); >+use C4::Output qw( output_html_with_http_headers ); >+ >+my $input = CGI->new; >+ >+my ( $template, $loggedinuser, $cookie ) = get_template_and_user( >+ { >+ template_name => "tools/restore-records.tt", >+ query => $input, >+ type => "intranet", >+ flagsrequired => { editcatalogue => 'edit_catalogue' }, >+ } >+); >+ >+output_html_with_http_headers $input, $cookie, $template->output; >-- >2.39.5
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 17387
:
188670
|
188671
|
188672
|
188673
|
188674
|
188675
|
188676
|
188677
|
188678
|
188679
|
188680
|
188681
|
188682
|
188683
|
188684
|
188685
|
188686
|
188687
|
188688
|
188689
|
188690
|
188691
|
188692
|
188693
|
188694
|
188695
|
188696
|
188697
|
188698
|
188699
|
188700
|
188701
|
188780
|
188781
|
188782
|
188783
|
188784
|
188785
|
188786
|
188787
|
188788
|
188789
|
188790
|
188791
|
188792
|
188793
|
188794
|
188795
|
189046
|
189047
|
189048
|
189049
|
189050
|
189051
|
189052
|
189053
|
189054
|
189055
|
189056
|
189057
|
189058
|
189059
|
189060
|
189061
|
189062
|
189082
|
189083
|
189084
|
189085
|
189086
|
189087
|
189088
|
189089
|
189090
|
189091
|
189092
|
189093
|
189094
|
189095
|
189096
|
189097
|
189098
|
189099
|
189100
|
189101
|
189102
|
189103
|
189104
|
189105
|
189106
|
189107
|
189108
|
189109
|
189110
|
189111
|
189112
|
189113
|
189114
|
189115
|
189116
|
189117
|
189118
|
189119