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 Signed-off-by: David Nind <david@davidnind.com> Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de> Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
This commit is contained in:
parent
c5a3b14bf8
commit
859a917618
4 changed files with 210 additions and 25 deletions
|
@ -22,39 +22,35 @@
|
|||
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 Koha::Result::Boolean;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -86,6 +82,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 ) = @_;
|
||||
|
||||
|
@ -175,12 +179,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;
|
||||
|
@ -226,8 +269,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
|
||||
|
@ -591,13 +662,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} ) {
|
||||
|
|
|
@ -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 %];
|
||||
</script>
|
||||
<!-- / str/cataloging_additem.inc -->
|
||||
|
|
|
@ -162,6 +162,44 @@
|
|||
[% ELSE %]
|
||||
<h2 id="edititem">Edit item #[% itemnumber | html %][% IF ( barcode ) %] / Barcode [% barcode | html %][% END %]</h2>
|
||||
[% END %]
|
||||
|
||||
<div id="item-template-toolbar" class="btn-toolbar">
|
||||
<select name="template_id" id="template_id" class="select2" style="width: 20em">
|
||||
<option value="0" selected="selected">Do not use template</option>
|
||||
<optgroup label="My templates">
|
||||
[% FOREACH t IN item_templates.owned %]
|
||||
[% IF t.id == template_id %]
|
||||
<option data-owner="1" value="[% t.id | html %]" selected="selected">[% t.name | html %][% IF t.is_shared %] (shared)[% END %]</option>
|
||||
[% ELSE %]
|
||||
<option data-owner="1" value="[% t.id | html %]">[% t.name | html %][% IF t.is_shared %] (shared)[% END %]</option>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</optgroup>
|
||||
<optgroup label="Shared templates">
|
||||
[% FOREACH t IN item_templates.shared %]
|
||||
[% IF t.id == template_id %]
|
||||
<option data-owner="0" value="[% t.id | html %]" selected="selected">[% t.name | html %]</option>
|
||||
[% ELSE %]
|
||||
<option data-owner="0" value="[% t.id | html %]">[% t.name | html %]</option>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</optgroup>
|
||||
</select>
|
||||
<button type="submit" id="load_template_submit" name="load_template_submit" value="1"><i class="fa fa-wpforms"></i> Apply template</button>
|
||||
<label for="use_template_for_session">
|
||||
[% IF use_template_for_session %]
|
||||
<input type="checkbox" id="use_template_for_session" name="use_template_for_session" checked="checked">
|
||||
[% ELSE %]
|
||||
<input type="checkbox" id="use_template_for_session" name="use_template_for_session">
|
||||
[% END %]
|
||||
For session</label>
|
||||
|
||||
<button type="submit" id="unload_template_submit" name="unload_template_submit" value="1"><i class="fa fa-eraser"></i> Clear template</button>
|
||||
|
||||
<button type="submit" id="delete_template_submit" name="delete_template_submit" value="1" disabled><i class="fa fa-trash"></i> Delete template</button>
|
||||
|
||||
</div>
|
||||
|
||||
<fieldset class="rows">
|
||||
[% PROCESS subfields_for_item subfields => subfields %]
|
||||
</fieldset>
|
||||
|
@ -218,6 +256,35 @@
|
|||
<div class="hint"><p>The barcode you enter will be incremented for each additional item.</p></div>
|
||||
</fieldset>
|
||||
|
||||
<span id="savetemplate">
|
||||
<input type="button" name="save_as_template" id="save_as_template" value="Save as template" />
|
||||
</span>
|
||||
<fieldset id="save_as_template_span">
|
||||
<legend>Save template</legend>
|
||||
<select name="replace_template_id" id="replace_template_id" class="select2" style="width: 20em">
|
||||
<option value="0" selected="selected">Save as new</option>
|
||||
<optgroup label="Update existing">
|
||||
[% FOREACH t IN item_templates.owned %]
|
||||
<option data-owner="1" value="[% t.id | html %]">[% t.name | html %][% IF t.is_shared %] (shared)[% END %]</option>
|
||||
[% END %]
|
||||
</optgroup>
|
||||
</select>
|
||||
|
||||
<span id="template_name_block">
|
||||
<label for="template_name" class="required">Template name: </label>
|
||||
<input type="text" id="template_name" name="template_name" class="required"/>
|
||||
<span class="required">Required</span>
|
||||
</span>
|
||||
|
||||
<label for="template_is_shared">
|
||||
<input type="checkbox" id="template_is_shared" name="template_is_shared"/>
|
||||
Share template
|
||||
</label>
|
||||
|
||||
<input type="submit" id="save_as_template_submit" name="save_as_template_submit" value="Save" onclick="javascript:return CheckTemplateForm(this.form);" />
|
||||
<a href="#" id="cancel_save_as_template" class="cancel">Cancel</a>
|
||||
</fieldset>
|
||||
|
||||
[% ELSE %]
|
||||
[% IF op != 'add_item' %]
|
||||
<input type="hidden" name="itemnumber" value="[% itemnumber | html %]" />
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue