Bug 18433: Allow to select results to export in item search

This adds a column of checkboxes in the results table to be able to select the
items to be exported

Test plan:
1. Go to item search and click 'Search'
2. Without checking any checkbox, verify that the export still works (it should
   export all results)
3. Tick some checkboxes and re-export, verify that only selected items are
   exported

Signed-off-by: Marc Véron <veron@veron.ch>
Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
This commit is contained in:
Julian Maurice 2017-04-14 15:45:04 +02:00 committed by Martin Renvoize
parent 2dbcc0303a
commit 40219cf2a6
No known key found for this signature in database
GPG Key ID: 422B469130441A0F
5 changed files with 163 additions and 84 deletions

64
catalogue/item-export.pl Executable file
View File

@ -0,0 +1,64 @@
#!/usr/bin/perl
# Copyright 2017 BibLibre
#
# 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, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use Modern::Perl;
use CGI;
use C4::Auth;
use C4::Output;
my $cgi = new CGI;
my ($template, $borrowernumber, $cookie) = get_template_and_user({
template_name => 'catalogue/itemsearch_csv.tt',
query => $cgi,
type => 'intranet',
authnotrequired => 0,
flagsrequired => { catalogue => 1 },
});
my @itemnumbers = $cgi->multi_param('itemnumber');
my $format = $cgi->param('format') // 'csv';
my @items = Koha::Items->search({ itemnumber => { -in => \@itemnumbers } });
if ($format eq 'barcodes') {
print $cgi->header({
type => 'text/plain',
attachment => 'barcodes.txt',
});
foreach my $item (@items) {
print $item->barcode . "\n";
}
exit;
}
$template->param(
results => \@items,
);
print $cgi->header({
type => 'text/csv',
attachment => 'items.csv',
});
for my $line ( split '\n', $template->output ) {
print "$line\n" unless $line =~ m|^\s*$|;
}

View File

@ -97,13 +97,7 @@ my ($template, $borrowernumber, $cookie) = get_template_and_user({
flagsrequired => { catalogue => 1 },
});
my $mss = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.notforloan', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
my $notforloan_values = $mss->count ? GetAuthorisedValues($mss->next->authorised_value) : [];
$mss = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.location', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
my $location_values = $mss->count ? GetAuthorisedValues($mss->next->authorised_value) : [];
$mss = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.itemlost', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
my $mss = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.itemlost', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
my $itemlost_values = $mss->count ? GetAuthorisedValues($mss->next->authorised_value) : [];
$mss = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.withdrawn', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
@ -227,38 +221,10 @@ if (scalar keys %params > 0) {
}
if ($results) {
# Get notforloan labels
my $notforloan_map = {};
foreach my $nfl_value (@$notforloan_values) {
$notforloan_map->{$nfl_value->{authorised_value}} = $nfl_value->{lib};
}
# Get location labels
my $location_map = {};
foreach my $loc_value (@$location_values) {
$location_map->{$loc_value->{authorised_value}} = $loc_value->{lib};
}
# Get itemlost labels
my $itemlost_map = {};
foreach my $il_value (@$itemlost_values) {
$itemlost_map->{$il_value->{authorised_value}} = $il_value->{lib};
}
# Get withdrawn labels
my $withdrawn_map = {};
foreach my $wd_value (@$withdrawn_values) {
$withdrawn_map->{$wd_value->{authorised_value}} = $wd_value->{lib};
}
foreach my $item (@$results) {
my $biblio = Koha::Biblios->find( $item->{biblionumber} );
$item->{biblio} = $biblio;
$item->{biblioitem} = $biblio->biblioitem->unblessed;
$item->{status} = $notforloan_map->{$item->{notforloan}};
if (defined $item->{location}) {
$item->{location} = $location_map->{$item->{location}};
}
}
}
@ -289,13 +255,6 @@ if (scalar keys %params > 0) {
# Display the search form
my @branches = map { value => $_->branchcode, label => $_->branchname }, Koha::Libraries->search( {}, { order_by => 'branchname' } );
my @locations;
foreach my $location (@$location_values) {
push @locations, {
value => $location->{authorised_value},
label => $location->{lib} // $location->{authorised_value},
};
}
my @itemtypes;
foreach my $itemtype ( Koha::ItemTypes->search ) {
push @itemtypes, {
@ -304,23 +263,10 @@ foreach my $itemtype ( Koha::ItemTypes->search ) {
};
}
$mss = Koha::MarcSubfieldStructures->search({ frameworkcode => '', kohafield => 'items.ccode', authorised_value => [ -and => {'!=' => undef }, {'!=' => ''}] });
my $ccode_avcode = $mss->count ? $mss->next->authorised_value : 'CCODE';
my $ccodes = GetAuthorisedValues($ccode_avcode);
my @ccodes;
foreach my $ccode (@$ccodes) {
push @ccodes, {
value => $ccode->{authorised_value},
label => $ccode->{lib},
};
}
my @notforloans;
foreach my $value (@$notforloan_values) {
push @notforloans, {
value => $value->{authorised_value},
label => $value->{lib},
};
my @ccodes = Koha::AuthorisedValues->get_descriptions_by_koha_field({ kohafield => 'items.ccode' });
foreach my $ccode (@ccodes) {
$ccode->{value} = $ccode->{authorised_value},
$ccode->{label} = $ccode->{lib},
}
my @itemlosts;
@ -350,10 +296,8 @@ foreach my $field (@items_search_fields) {
$template->param(
branches => \@branches,
locations => \@locations,
itemtypes => \@itemtypes,
ccodes => \@ccodes,
notforloans => \@notforloans,
itemlosts => \@itemlosts,
withdrawns => \@withdrawns,
items_search_fields => \@items_search_fields,

View File

@ -1,7 +1,7 @@
[%- USE Branches -%]
[%- USE Koha -%]
[%- USE ItemTypes -%]
[% USE AuthorisedValues %]
[%- USE AuthorisedValues -%]
[%- SET biblio = item.biblio -%]
[%- SET biblioitem = item.biblioitem -%]
"[% biblio.title | html %] [% IF ( Koha.Preference( 'marcflavour' ) == 'UNIMARC' && biblio.author ) %]by [% END %][% biblio.author | html %]", "[% (biblioitem.publicationyear || biblio.copyrightdate) | html %]", "[% biblioitem.publishercode | html %]", "[% AuthorisedValues.GetByCode( 'CCODE', item.ccode ) | html %]", "[% item.barcode | html %]", "[% item.itemcallnumber | html %]", "[% Branches.GetName(item.homebranch) | html %]", "[% Branches.GetName(item.holdingbranch) | html %]", "[% item.location | html %]", "[% ItemTypes.GetDescription(item.itype) | html %]", "[% item.stocknumber | html %]", "[% item.status | html %]","[% AuthorisedValues.GetByCode( 'LOST', item.itemlost ) || "" | html %]","[% AuthorisedValues.GetByCode( 'WITHDRAWN', item.withdrawn ) || "" | html %]", "[% (item.issues || 0) | html %]"
"[% biblio.title | html %] [% IF ( Koha.Preference( 'marcflavour' ) == 'UNIMARC' && biblio.author ) %]by [% END %][% biblio.author | html %]", "[% (biblioitem.publicationyear || biblio.copyrightdate) | html %]", "[% biblioitem.publishercode | html %]", "[% AuthorisedValues.GetByCode( 'CCODE', item.ccode ) | html %]", "[% item.barcode | html %]", "[% item.itemcallnumber | html %]", "[% Branches.GetName(item.homebranch) | html %]", "[% Branches.GetName(item.holdingbranch) | html %]", "[% AuthorisedValues.GetDescriptionByKohaField(frameworkcode => biblio.frameworkcode, kohafield => 'items.location', authorised_value => item.location) | html %]", "[% ItemTypes.GetDescription(item.itype) | html %]", "[% item.stocknumber | html %]", "[% AuthorisedValues.GetDescriptionByKohaField(frameworkcode => biblio.frameworkcode, kohafield => 'items.notforloan', authorised_value => item.notforloan) | html %]","[% AuthorisedValues.GetByCode( 'LOST', item.itemlost ) || "" | html %]","[% AuthorisedValues.GetByCode( 'WITHDRAWN', item.withdrawn ) || "" | html %]", "[% (item.issues || 0) | html %]"

View File

@ -2,11 +2,14 @@
[%- USE Koha -%]
[%- USE To -%]
[%- USE ItemTypes -%]
[% USE AuthorisedValues %]
[%- USE AuthorisedValues -%]
[%- biblio = item.biblio -%]
[%- biblioitem = item.biblioitem -%]
[
"[% FILTER escape_quotes = replace('"', '\"') ~%]
<input type="checkbox" name="itemnumber" value="[% item.itemnumber %]"/>
[%~ END %]",
"[% FILTER escape_quotes ~%]
<a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% biblio.biblionumber | uri %]" title="Go to record detail page">[% biblio.title | html %]</a>[% IF ( Koha.Preference( 'marcflavour' ) == 'UNIMARC' && biblio.author ) %] by[% END %] [% biblio.author | html %]
[%~ END %]",
"[% (biblioitem.publicationyear || biblio.copyrightdate) | html %]",
@ -18,10 +21,10 @@
"[% item.itemcallnumber | html %]",
"[% Branches.GetName(item.homebranch) | html %]",
"[% Branches.GetName(item.holdingbranch) | html %]",
"[% item.location | html %]",
"[% AuthorisedValues.GetDescriptionByKohaField( frameworkcode => biblio.frameworkcode, kohafield => 'items.location', authorised_value => item.location) | html %]",
"[% ItemTypes.GetDescription(item.itype) | html %]",
"[% item.stocknumber | html %]",
"[% item.status | html %]",
"[% AuthorisedValues.GetDescriptionByKohaField( frameworkcode => biblio.frameworkcode, kohafield => 'items.notforloan', authorised_value => item.notforloan) | html %]",
"[% AuthorisedValues.GetByCode( 'LOST', item.itemlost ) || "" | html %]",
"[% AuthorisedValues.GetByCode( 'WITHDRAWN', item.withdrawn ) || "" | html %]",
"[% (item.issues || 0) | html %]",

View File

@ -1,6 +1,7 @@
[% USE raw %]
[% USE To %]
[% USE Asset %]
[% USE AuthorisedValues %]
[% BLOCK form_label %]
[% SWITCH label %]
@ -110,6 +111,18 @@
[%- To.json(escaped) | $raw -%]
[%- END -%]
[% notforloans = AuthorisedValues.GetDescriptionsByKohaField({ kohafield = 'items.notforloan' }) %]
[% FOREACH nfl IN notforloans %]
[% nfl.value = nfl.authorised_value %]
[% nfl.label = nfl.lib %]
[% END %]
[% locations = AuthorisedValues.GetDescriptionsByKohaField({ kohafield = 'items.location' }) %]
[% FOREACH loc IN locations %]
[% loc.value = loc.authorised_value %]
[% loc.label = loc.lib %]
[% END %]
[%# Page starts here %]
[% SET footerjs = 1 %]
@ -266,6 +279,7 @@
function submitForm($form) {
var tr = ''
+ ' <tr>'
+ ' <th id="items_checkbox"></th>'
+ ' <th id="items_title">' + _("Title") + '</th>'
+ ' <th id="items_pubdate">' + _("Publication date") + '</th>'
+ ' <th id="items_publisher">' + _("Publisher") + '</th>'
@ -301,38 +315,79 @@
$('#item-search-block').show();
});
function exportItems(format) {
var itemnumbers = [];
$('#results').find('input[name="itemnumber"]:checked').each(function() {
itemnumbers.push($(this).val());
});
if (itemnumbers.length) {
var href = '/cgi-bin/koha/catalogue/item-export.pl?format=' + format;
href += '&itemnumber=' + itemnumbers.join('&itemnumber=');
location = href;
} else {
$('#format-' + format).prop('checked', true);
$('#itemsearchform').submit();
$('#format-html').prop('checked', true);
}
}
var csvExportLink = $('<a>')
.attr('href', '#')
.html(_("Export results to CSV"))
.addClass('btn btn-default btn-xs')
.html("CSV")
.on('click', function(e) {
e.preventDefault();
$('#format-csv').prop('checked', true);
$('#itemsearchform').submit();
$('#format-html').prop('checked', true);
exportItems('csv');
});
var barcodesExportLink = $('<a>')
.attr('href', '#')
.html(_("Export results to barcodes file"))
.addClass('btn btn-default btn-xs')
.html(_("Barcodes file"))
.on('click', function(e) {
e.preventDefault();
$('#format-barcodes').prop('checked', true);
$('#itemsearchform').submit();
$('#format-html').prop('checked', true);
exportItems('barcodes');
});
var editSearchAndExportLinks = $('<p>')
.append(editSearchLink)
.append(' | ')
.append(csvExportLink)
.append(' ')
.append(barcodesExportLink);
var exportButton = $('<div>')
.addClass('btn-group')
.append($('<button>')
.addClass('btn btn-default btn-xs dropdown-toggle')
.attr('id', 'export-button')
.attr('data-toggle', 'dropdown')
.attr('aria-haspopup', 'true')
.attr('aria-expanded', 'false')
.html(_("Export all results to") + ' <span class="caret"></span>'))
.append($('<ul>')
.addClass('dropdown-menu')
.append($('<li>').append(csvExportLink))
.append($('<li>').append(barcodesExportLink)));
var selectVisibleRows = $('<a>')
.attr('href', '#')
.append('<i class="fa fa-check"></i> ')
.append(_("Select visible rows"))
.on('click', function(e) {
e.preventDefault();
$('#results input[type="checkbox"]').prop('checked', true).change();
});
var clearSelection = $('<a>')
.attr('href', '#')
.append('<i class="fa fa-remove"></i> ')
.append(_("Clear selection"))
.on('click', function(e) {
e.preventDefault();
$('#results input[type="checkbox"]').prop('checked', false).change();
});
var exportLinks = $('<p>')
.append(selectVisibleRows)
.append(' ')
.append(clearSelection)
.append(' | ')
.append(exportButton);
var results_heading = $('<div>').addClass('results-heading')
.append("<h1>" + _("Item search results") + "</h1>")
.append($('<p>').append(advSearchLink))
.append(editSearchAndExportLinks);
.append($('<p>').append(editSearchLink))
.append(exportLinks);
$('#results-wrapper').empty()
.append(results_heading)
.append(table);
@ -369,7 +424,9 @@
});
},
'sDom': '<"top pager"ilp>t<"bottom pager"ip>r',
'aaSorting': [[1, 'asc']],
'aoColumns': [
{ 'sName': 'checkbox', 'bSortable': false },
{ 'sName': 'title' },
{ 'sName': 'publicationyear' },
{ 'sName': 'publishercode' },
@ -385,12 +442,13 @@
{ 'sName': 'itemlost' },
{ 'sName': 'withdrawn' },
{ 'sName': 'issues' },
{ 'sName': 'checkbox', 'bSortable': false }
{ 'sName': 'actions', 'bSortable': false }
],
"sPaginationType": "full_numbers"
})).columnFilter({
'sPlaceHolder': 'head:after',
'aoColumns': [
null,
{ 'type': 'text' },
{ 'type': 'text' },
{ 'type': 'text' },
@ -433,6 +491,16 @@
null
]
});
$('#results').on('change', 'input[type="checkbox"]', function() {
var countSelected = $(this).parents('table').find('input:checked').length;
var caret = ' <span class="caret">';
if (countSelected > 0) {
$('#export-button').html(_("Export selected results to") + caret);
} else {
$('#export-button').html(_("Export all results to") + caret);
}
});
}
var Sticky;
$(document).ready(function () {