Bugzilla – Attachment 191468 Details for
Bug 41620
Make itemsearch.pl use Koha::CSV for CSV generation
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 41620: Use Koha::CSV::ItemSearch for CSV exports
813cb88.patch (text/plain), 9.70 KB, created by
Tomás Cohen Arazi (tcohen)
on 2026-01-14 20:09:47 UTC
(
hide
)
Description:
Bug 41620: Use Koha::CSV::ItemSearch for CSV exports
Filename:
MIME Type:
Creator:
Tomás Cohen Arazi (tcohen)
Created:
2026-01-14 20:09:47 UTC
Size:
9.70 KB
patch
obsolete
>From 813cb88dcff88a24e629d757cab2a99da70b838a Mon Sep 17 00:00:00 2001 >From: =?UTF-8?q?Tom=C3=A1s=20Cohen=20Arazi?= <tomascohen@theke.io> >Date: Wed, 14 Jan 2026 20:05:52 +0000 >Subject: [PATCH] Bug 41620: Use Koha::CSV::ItemSearch for CSV exports > >This patch replaces template-based CSV generation with Koha::CSV::ItemSearch >in both itemsearch.pl and item-export.pl, eliminating spacing issues around >delimiters that caused Excel to display quotes as literal characters. > >Changes: >- Update itemsearch.pl to use Koha::CSV::ItemSearch >- Update item-export.pl to use Koha::CSV::ItemSearch >- Remove obsolete CSV templates >- Stream output directly without template overhead > >This fixes the issue where extra spaces around commas in the templates >caused Excel to treat quotes as part of the data rather than CSV delimiters. > >Test plan: >1. Apply patches >2. Test itemsearch.pl: > a. Go to Cataloging > Item search > b. Perform a search > c. Click "Export results" > "CSV" > d. Open items.csv in Excel > => SUCCESS: Fields display without quotes >3. Test item-export.pl: > a. Go to a bibliographic record > b. Select multiple items > c. Click "Export selected items" > d. Open items.csv in Excel > => SUCCESS: Fields display without quotes >4. Verify all 21 columns are present and correctly formatted >5. Verify dates, authorized values, and library names display correctly >6. Sign off :-D >--- > catalogue/item-export.pl | 17 ++++++++++------- > catalogue/itemsearch.pl | 16 +++++++++++++--- > .../includes/catalogue/itemsearch_item.csv.inc | 11 ----------- > .../csv_headers/catalogue/itemsearch.tt | 7 ------- > .../prog/en/modules/catalogue/itemsearch_csv.tt | 6 ------ > 5 files changed, 23 insertions(+), 34 deletions(-) > delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.csv.inc > delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/csv_headers/catalogue/itemsearch.tt > delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch_csv.tt > >diff --git a/catalogue/item-export.pl b/catalogue/item-export.pl >index 422f7490e9..5dbfbf3931 100755 >--- a/catalogue/item-export.pl >+++ b/catalogue/item-export.pl >@@ -23,12 +23,14 @@ use CGI; > > use C4::Auth qw( get_template_and_user ); > use C4::Output; >+use Koha::CSV::ItemSearch; >+use Koha::Items; > > my $cgi = CGI->new; > > my ( $template, $borrowernumber, $cookie ) = get_template_and_user( > { >- template_name => 'catalogue/itemsearch_csv.tt', >+ template_name => 'catalogue/itemsearch.tt', > query => $cgi, > type => 'intranet', > flagsrequired => { catalogue => 1 }, >@@ -54,16 +56,17 @@ if ( $format eq 'barcodes' ) { > exit; > } > >-$template->param( >- results => $items, >-); >- >+# CSV export > print $cgi->header( > { > type => 'text/csv', > attachment => 'items.csv', > } > ); >-for my $line ( split '\n', $template->output ) { >- print "$line\n" unless $line =~ m|^\s*$|; >+ >+my $csv = Koha::CSV::ItemSearch->new(); >+$csv->print_headers(*STDOUT); >+ >+while ( my $item = $items->next ) { >+ $csv->print_item( *STDOUT, $item ); > } >diff --git a/catalogue/itemsearch.pl b/catalogue/itemsearch.pl >index ae8dd8241d..d5f4784810 100755 >--- a/catalogue/itemsearch.pl >+++ b/catalogue/itemsearch.pl >@@ -85,8 +85,8 @@ if ( defined $format and $format eq 'json' ) { > $cgi->param( 'op', @op ); > $cgi->param( 'c', @c ); > } elsif ( defined $format and $format eq 'csv' ) { >- $template_name = 'catalogue/itemsearch_csv.tt'; > >+ # CSV export will be handled directly, no template needed > # Retrieve all results > $cgi->param( 'rows', 0 ); > } elsif ( defined $format and $format eq 'barcodes' ) { >@@ -304,6 +304,9 @@ if ( defined $format and $format ne 'shareable' ) { > ); > > if ( $format eq 'csv' ) { >+ require Koha::CSV::ItemSearch; >+ require Koha::Items; >+ > print $cgi->header( > { > type => 'text/csv', >@@ -311,8 +314,15 @@ if ( defined $format and $format ne 'shareable' ) { > } > ); > >- for my $line ( split '\n', $template->output ) { >- print "$line\n" unless $line =~ m|^\s*$|; >+ my $csv = Koha::CSV::ItemSearch->new(); >+ >+ # Print headers >+ $csv->print_headers(*STDOUT); >+ >+ # Print each item >+ foreach my $item_data (@$results) { >+ my $item = Koha::Items->find( $item_data->{itemnumber} ); >+ $csv->print_item( *STDOUT, $item ) if $item; > } > } elsif ( $format eq 'json' ) { > $template->param( draw => scalar $cgi->param('draw') ); >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.csv.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.csv.inc >deleted file mode 100644 >index 9dd823445e..0000000000 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/catalogue/itemsearch_item.csv.inc >+++ /dev/null >@@ -1,11 +0,0 @@ >-[%- USE raw -%] >-[%- USE Branches -%] >-[%- USE Koha -%] >-[%- USE KohaDates -%] >-[%- USE ItemTypes -%] >-[%- USE AuthorisedValues -%] >-[%- SET biblio = item.biblio -%] >-[%- SET biblioitem = item.biblioitem -%] >-[%- SET delimiter = Koha.CSVDelimiter() -%] >-"[% biblio.title | replace('"', '""') | $raw %] >-[% IF ( Koha.Preference( 'marcflavour' ) == 'UNIMARC' && biblio.author ) %]by[% END %][% biblio.author | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% (biblioitem.publicationyear || biblio.copyrightdate) | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% biblioitem.publishercode | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.ccode', authorised_value => item.ccode ) | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% item.barcode | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% item.itemnumber | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% item.enumchron | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% item.itemcallnumber | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% Branches.GetName(item.homebranch) | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% Branches.GetName(item.holdingbranch) | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% AuthorisedValues.GetDescriptionByKohaField(frameworkcode => biblio.frameworkcode, kohafield => 'items.location', authorised_value => item.location) | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% ItemTypes.GetDescription(item.itype) | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% item.stocknumber | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% AuthorisedValues.GetDescriptionByKohaField(frameworkcode => biblio.frameworkcode, kohafield => 'items.notforloan', authorised_value => item.notforloan) | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.itemlost', authorised_value => item.itemlost ) || "" | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.withdrawn', authorised_value => item.withdrawn ) || "" | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% AuthorisedValues.GetDescriptionByKohaField( kohafield => 'items.damaged', authorised_value => item.damaged ) || "" | replace('"', '""') | $raw %]"[%- delimiter | $raw -%]"[% item.dateaccessioned | $KohaDates | $raw %]"[%- delimiter | $raw -%]"[% (item.issues || 0) | $raw %]"[%- delimiter | $raw -%]"[% item.datelastborrowed | $KohaDates | $raw %]"[%- delimiter | $raw -%]"[% IF item.checkout %][% item.checkout.date_due | $KohaDates | $raw %][% END %]" >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/csv_headers/catalogue/itemsearch.tt b/koha-tmpl/intranet-tmpl/prog/en/includes/csv_headers/catalogue/itemsearch.tt >deleted file mode 100644 >index c6b49eecf8..0000000000 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/csv_headers/catalogue/itemsearch.tt >+++ /dev/null >@@ -1,7 +0,0 @@ >-[%- USE raw -%] >-[%- USE Koha -%] >-[%- PROCESS 'i18n.inc' -%] >-[%- SET delimiter = Koha.CSVDelimiter() -%] >-[%- BLOCK -%] >- "[% t("Title") | html %]"[%- delimiter | $raw -%]"[% t("Publication date") | html %]"[%- delimiter | $raw -%]"[% t("Publisher") | html %]"[%- delimiter | $raw -%]"[% t("Collection") | html %]"[%- delimiter | $raw -%]"[% t("Barcode") | html %]"[%- delimiter | $raw -%]"[% t("Item number") | html %]"[%- delimiter | $raw -%]"[% t("Serial enumeration") | html %]"[%- delimiter | $raw -%]"[% t("Call number") | html %]"[%- delimiter | $raw -%]"[% t("Home library") | html %]"[%- delimiter | $raw -%]"[% t("Current library") | html %]"[%- delimiter | $raw -%]"[% t("Shelving location") | html %]"[%- delimiter | $raw -%]"[% t("Item type") | html %]"[%- delimiter | $raw -%]"[% t("Inventory number") | html %]"[%- delimiter | $raw -%]"[% t("Not for loan status") | html %]"[%- delimiter | $raw -%]"[% t("Lost status") | html %]"[%- delimiter | $raw -%]"[% t("Withdrawn status") | html %]"[%- delimiter | $raw -%]"[% t("Damaged status") | html %]"[%- delimiter | $raw %]"[% t("Date accessioned") | html %]"[%- delimiter | $raw -%]"[% t("Checkouts") | html %]"[%- delimiter | $raw -%]"[% t("Last checkout date") | html %]"[%- delimiter | $raw -%]"[% t("Due date") | html %]" >-[%- END -%] >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch_csv.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch_csv.tt >deleted file mode 100644 >index 6b58dc7b02..0000000000 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/itemsearch_csv.tt >+++ /dev/null >@@ -1,6 +0,0 @@ >-[%- INCLUDE csv_headers/catalogue/itemsearch.tt -%] >-[%- INCLUDE empty_line.inc -%] >-[%- FOREACH item IN results -%] >- [%- INCLUDE 'catalogue/itemsearch_item.csv.inc' item = item -%] >- [%- INCLUDE empty_line.inc -%] >-[%- END -%] >-- >2.50.1 (Apple Git-155) >
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 41620
:
191467
| 191468