From 89349173365e88e693d11e8e82c40a5ce7a851ec Mon Sep 17 00:00:00 2001 From: Kyle Hall Date: Thu, 29 Sep 2022 09:06:28 -0400 Subject: [PATCH] Bug 24606: Implement item templates This patch set implements item editor templates for community Koha. Test Plan: 1) Apply this patch set 2) Run updatedatabase.pl 3) Restart all the things! 4) prove t/db_dependent/Koha/Item/Template* 5) As a non superlibrarian, enter the item editor 6) Set some item fields, save as a new template using the buttom and form below the editor. 7) Test loading a template without remembering for the session 8) Test loading a template while remembering for the session 9) Test deleting a template 10) Test updating a template 11) Create one or more shared templates 12) Log in as another non superlibrarian without the new permission manage_item_editor_templates, verify you cannot edit/delete templates shared to you 13) Enable the new permission manage_item_editor_templates, verify you can now edit and delete templates shared to you --- cataloguing/additem.pl | 127 ++++++++++++++---- .../en/includes/str/cataloging_additem.inc | 1 + .../prog/en/modules/cataloguing/additem.tt | 67 +++++++++ .../prog/js/cataloging_additem.js | 40 ++++++ 4 files changed, 210 insertions(+), 25 deletions(-) diff --git a/cataloguing/additem.pl b/cataloguing/additem.pl index 4617cb54db..aceacf118b 100755 --- a/cataloguing/additem.pl +++ b/cataloguing/additem.pl @@ -22,38 +22,34 @@ use Modern::Perl; use CGI qw ( -utf8 ); + use C4::Auth qw( get_template_and_user haspermission ); -use C4::Output qw( output_and_exit_if_error output_and_exit output_html_with_http_headers ); -use C4::Biblio qw( - GetFrameworkCode - GetMarcFromKohaField - GetMarcStructure - IsMarcStructureInternal - ModBiblio -); -use C4::Context; -use C4::Circulation qw( barcodedecode LostItem ); -use C4::Barcodes; use C4::Barcodes::ValueBuilder; +use C4::Barcodes; +use C4::Biblio qw( GetFrameworkCode GetMarcFromKohaField GetMarcStructure IsMarcStructureInternal ModBiblio ); +use C4::Circulation qw( barcodedecode LostItem ); +use C4::Context; +use C4::Members; +use C4::Output qw( output_and_exit_if_error output_and_exit output_html_with_http_headers ); +use C4::Search qw( enabled_staff_search_views ); use Koha::Biblios; -use Koha::Items; +use Koha::Item::Templates; use Koha::ItemTypes; use Koha::Items; +use Koha::Items; use Koha::Libraries; use Koha::Patrons; use Koha::SearchEngine::Indexer; -use C4::Search qw( enabled_staff_search_views ); -use Storable qw( freeze thaw ); -use URI::Escape qw( uri_escape_utf8 ); -use C4::Members; use Koha::UI::Form::Builder::Item; -use MARC::File::XML; -use URI::Escape qw( uri_escape_utf8 ); use Encode qw( encode_utf8 ); -use MIME::Base64 qw( decode_base64url encode_base64url ); -use List::Util qw( first ); use List::MoreUtils qw( any uniq ); +use List::Util qw( first ); +use MARC::File::XML; +use MIME::Base64 qw( decode_base64url encode_base64url ); +use Storable qw( freeze thaw ); +use URI::Escape qw( uri_escape_utf8 ); +use URI::Escape qw( uri_escape_utf8 ); our $dbh = C4::Context->dbh; @@ -85,6 +81,14 @@ sub add_item_to_item_group { )->store(); } +sub get_item_from_template { + my ( $template_id ) = @_; + + my $template = Koha::Item::Templates->find($template_id); + + return $template->decoded_contents if $template; +} + sub get_item_from_cookie { my ( $input ) = @_; @@ -174,12 +178,51 @@ my @errors; # store errors found while checking data BEFORE saving item. # Getting last created item cookie my $prefillitem = C4::Context->preference('PrefillItem'); +my $load_template_submit = $input->param('load_template_submit'); +my $delete_template_submit = $input->param('delete_template_submit'); +my $unload_template_submit = $input->param('unload_template_submit'); +my $use_template_for_session = $input->param('use_template_for_session') || $input->cookie('ItemEditorSessionTemplateId'); +my $template_id = $input->param('template_id') || $input->cookie('ItemEditorSessionTemplateId'); +if ( $delete_template_submit ) { + my $t = Koha::Item::Templates->find($template_id); + $t->delete if $t && $t->borrowernumber eq $loggedinuser; + $template_id = undef; + $use_template_for_session = undef; +} +if ($load_template_submit || $unload_template_submit) { + $op = q{} if $template_id; + + $template_id = undef if !$input->param('template_id'); + $template_id = undef if $unload_template_submit; + + # Unset the cookie if either no template id as submitted, or "use for session" checkbox as unchecked + my $cookie_value = $input->param('use_template_for_session') && $template_id ? $template_id : q{}; + $use_template_for_session = $cookie_value; + + # Update the cookie + my $template_cookie = $input->cookie( + -name => 'ItemEditorSessionTemplateId', + -value => $cookie_value, + -HttpOnly => 1, + -expires => '', + -sameSite => 'Lax' + ); + + $cookie = [ $cookie, $template_cookie ]; +} +$template->param( + template_id => $template_id, + item_templates => Koha::Item::Templates->get_available($loggedinuser), + use_template_for_session => $use_template_for_session, +); + #------------------------------------------------------------------------------- if ($op eq "additem") { my $add_submit = $input->param('add_submit'); my $add_duplicate_submit = $input->param('add_duplicate_submit'); my $add_multiple_copies_submit = $input->param('add_multiple_copies_submit'); + my $save_as_template_submit = $input->param('save_as_template_submit'); my $number_of_copies = $input->param('number_of_copies'); my @columns = Koha::Items->columns; @@ -225,8 +268,36 @@ if ($op eq "additem") { $item->barcode(barcodedecode($item->barcode)); + if ($save_as_template_submit) { + my $template_name = $input->param('template_name'); + my $template_is_shared = $input->param('template_is_shared'); + my $replace_template_id = $input->param('replace_template_id'); + + if ($replace_template_id) { + my $template = Koha::Item::Templates->find($replace_template_id); + if ($template) { + $template->update( + { + id => $replace_template_id, + is_shared => $template_is_shared ? 1 : 0, + contents => $item->unblessed, + } + ); + } + } + else { + my $template = Koha::Item::Template->new( + { + name => $template_name, + borrowernumber => $loggedinuser, + is_shared => $template_is_shared ? 1 : 0, + contents => $item->unblessed, + } + )->store(); + } + } # If we have to add or add & duplicate, we add the item - if ( $add_submit || $add_duplicate_submit || $prefillitem) { + elsif ( $add_submit || $add_duplicate_submit || $prefillitem) { # check for item barcode # being unique if ( defined $item->barcode @@ -585,13 +656,19 @@ my @header_value_loop = map { } sort keys %$subfieldcode_attribute_mappings; # Using last created item if it exists -if ( $prefillitem - && $op ne "additem" +if ( + $op ne "additem" && $op ne "edititem" && $op ne "dupeitem" ) { - my $item_from_cookie = get_item_from_cookie($input); - $current_item = $item_from_cookie if $item_from_cookie; + if ( $template_id ) { + my $item_from_template = get_item_from_template($template_id); + $current_item = $item_from_template if $item_from_template; + } + elsif ( $prefillitem ) { + my $item_from_cookie = get_item_from_cookie($input); + $current_item = $item_from_cookie if $item_from_cookie; + } } if ( $current_item->{more_subfields_xml} ) { diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/str/cataloging_additem.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/str/cataloging_additem.inc index 1a2009d6f1..c51447de9a 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/str/cataloging_additem.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/str/cataloging_additem.inc @@ -20,6 +20,7 @@ var MSG_CONFIRM_DELETE_ITEM = _("Are you sure you want to delete this item?"); var MSG_CONFIRM_ADD_ITEM = _("Are you sure you want to add a new item? Any changes made on this page will be lost."); var MSG_CONFIRM_SAVE = _("Are you sure you want to save?"); + var MSG_TEMPLATE_NAME_REQUIRED = _("Template name is required."); var table_settings = [% TablesSettings.GetTableSettings( 'cataloguing', 'additem', 'itemst', 'json' ) | $raw %]; diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tt index 61a442f6de..5cf882a73f 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tt @@ -158,6 +158,44 @@ [% ELSE %]

Edit item #[% itemnumber | html %][% IF ( barcode ) %] / Barcode [% barcode | html %][% END %]

[% END %] + +
+ + + + + + + + +
+
[% PROCESS subfields_for_item subfields => subfields %]
@@ -214,6 +252,35 @@

The barcode you enter will be incremented for each additional item.

+ + + +
+ Save template + + + + + + Required + + + + + + Cancel +
+ [% ELSE %] [% IF op != 'add_item' %] diff --git a/koha-tmpl/intranet-tmpl/prog/js/cataloging_additem.js b/koha-tmpl/intranet-tmpl/prog/js/cataloging_additem.js index 1427f916a9..9bd660ad3b 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/cataloging_additem.js +++ b/koha-tmpl/intranet-tmpl/prog/js/cataloging_additem.js @@ -69,6 +69,37 @@ $(document).ready(function(){ multiCopyControl.toggle(); }); + var saveAsTemplateControl = $("#save_as_template_span"); + var saveTemplateBlock = $("#savetemplate"); + saveAsTemplateControl.hide(); + $("#save_as_template").on("click",function(e){ + e.preventDefault; + saveTemplateBlock.toggle(); + saveAsTemplateControl.toggle(); + $('#template_name').focus(); + }); + $("#cancel_save_as_template").on("click",function(e){ + e.preventDefault(); + saveTemplateBlock.toggle(); + saveAsTemplateControl.toggle(); + }); + + $("#template_id").on("change", function() { + if ( $(this).find(":selected").data("owner") ) { + $("#delete_template_submit").removeAttr("disabled"); + } else { + $("#delete_template_submit").attr("disabled", "disabled"); + } + }); + $("#template_id").change(); // Trigger to enable delete button if patron's template is in use + $("#replace_template_id").on("change", function() { + if ( $(this).find(":selected").val() > 0 ) { + $("#template_name_block").hide(); + } else { + $("#template_name_block").show(); + } + }); + // Add new item to an item group if ( has_item_groups ) { $('#item-group-add-or-create-form-description-block').hide(); @@ -98,6 +129,15 @@ $(document).ready(function(){ }); }); +function CheckTemplateForm(f) { + if ( $('#replace_template_id').val() == "0" && $('#template_name').val() == "" ) { + alert(MSG_TEMPLATE_NAME_REQUIRED); + return false; + } else { + return true; + } +} + function Check(f) { var total_mandatory = CheckMandatorySubfields(f); var total_important = CheckImportantSubfields(f); -- 2.30.2