From 33d0f6daf5edad9d25d34963997a0f3e90af63c2 Mon Sep 17 00:00:00 2001
From: Jonathan Druart <jonathan.druart@biblibre.com>
Date: Fri, 20 Apr 2012 14:44:01 +0200
Subject: [PATCH 1/1] Bug 7986: Export issues for patron

In the circulation page, you can now export (as csv or iso2709) a list
of items which are currently checked out by a borrower.

3 export types:
- iso2709 with items: Export the items list in iso2709 format with item
  informations.
- iso2709 without items: Export the items list in iso2709 format without
  item informations.
- CSV: Export the items list based on a csv profil.

2 new system preferences:
- DontExportFields: a list of fields not to be export
- CsvProfileForExport: The Csv profil name used for the csv export

Test plan:
- Fill the CsvProfileForExport syspref
- go on the borrower circulation page containing checkouts
- Select one or more items and export them to the 3 different formats.
- check if the result file is what you expected
---
 C4/Biblio.pm                                       |    6 +-
 C4/Csv.pm                                          |   13 +
 C4/Record.pm                                       |   43 +++-
 circ/circulation.pl                                |    2 +
 installer/data/mysql/updatedatabase.pl             |   22 ++
 .../prog/en/includes/checkouts-table-footer.inc    |    2 +-
 .../intranet-tmpl/prog/en/includes/prefs-menu.inc  |    1 +
 .../en/modules/admin/preferences/circulation.pref  |    6 +
 .../prog/en/modules/admin/preferences/tools.pref   |    4 +
 .../prog/en/modules/circ/circulation.tt            |   98 +++++++-
 .../intranet-tmpl/prog/en/modules/tools/export.tt  |    2 +-
 tools/export.pl                                    |  247 +++++++++++---------
 12 files changed, 318 insertions(+), 128 deletions(-)
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/tools.pref

diff --git a/C4/Biblio.pm b/C4/Biblio.pm
index 56cd94e..cef937c 100644
--- a/C4/Biblio.pm
+++ b/C4/Biblio.pm
@@ -2743,16 +2743,17 @@ sub GetNoZebraIndexes {
 
 =head2 EmbedItemsInMarcBiblio
 
-    EmbedItemsInMarcBiblio($marc, $biblionumber);
+    EmbedItemsInMarcBiblio($marc, $biblionumber, $itemnumbers);
 
 Given a MARC::Record object containing a bib record,
 modify it to include the items attached to it as 9XX
 per the bib's MARC framework.
+if $itemnumbers is defined, only specified itemnumbers are embedded
 
 =cut
 
 sub EmbedItemsInMarcBiblio {
-    my ($marc, $biblionumber) = @_;
+    my ($marc, $biblionumber, $itemnumbers) = @_;
     croak "No MARC record" unless $marc;
 
     my $frameworkcode = GetFrameworkCode($biblionumber);
@@ -2765,6 +2766,7 @@ sub EmbedItemsInMarcBiblio {
     my @item_fields;
     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField( "items.itemnumber", $frameworkcode );
     while (my ($itemnumber) = $sth->fetchrow_array) {
+        next if $itemnumbers and not grep { $_ == $itemnumber } @$itemnumbers;
         require C4::Items;
         my $item_marc = C4::Items::GetMarcItem($biblionumber, $itemnumber);
         push @item_fields, $item_marc->field($itemtag);
diff --git a/C4/Csv.pm b/C4/Csv.pm
index 07ce2eb..99619e5 100644
--- a/C4/Csv.pm
+++ b/C4/Csv.pm
@@ -35,6 +35,7 @@ $VERSION = 3.00;
 @EXPORT = qw(
   &GetCsvProfiles
   &GetCsvProfile
+  &GetCsvProfileId
   &GetCsvProfilesLoop
   &GetMarcFieldsForCsv
 );
@@ -64,6 +65,18 @@ sub GetCsvProfile {
     return ($sth->fetchrow_hashref);
 }
 
+# Returns id of csv profile about a given csv profile name
+sub GetCsvProfileId {
+    my ($name)  = @_;
+    my $dbh   = C4::Context->dbh;
+    my $query = "SELECT export_format_id FROM export_format WHERE profile=?";
+
+    $sth = $dbh->prepare($query);
+    $sth->execute($name);
+
+    return ( $sth->fetchrow );
+}
+
 # Returns fields to extract for the given csv profile
 sub GetMarcFieldsForCsv {
 
diff --git a/C4/Record.pm b/C4/Record.pm
index 89bde32..f6067a3 100644
--- a/C4/Record.pm
+++ b/C4/Record.pm
@@ -330,7 +330,7 @@ sub marc2endnote {
 
 =head2 marc2csv - Convert several records from UNIMARC to CSV
 
-  my ($csv) = marc2csv($biblios, $csvprofileid);
+  my ($csv) = marc2csv($biblios, $csvprofileid, $itemnumbers);
 
 Pre and postprocessing can be done through a YAML file
 
@@ -340,10 +340,11 @@ C<$biblio> - a list of biblionumbers
 
 C<$csvprofileid> - the id of the CSV profile to use for the export (see export_format.export_format_id and the GetCsvProfiles function in C4::Csv)
 
+C<$itemnumbers> - a list of itemnumbers to export
 =cut
 
 sub marc2csv {
-    my ($biblios, $id) = @_;
+    my ( $biblios, $id, $itemnumbers ) = @_;
     my $output;
     my $csv = Text::CSV::Encoded->new();
 
@@ -358,9 +359,18 @@ sub marc2csv {
     eval $preprocess if ($preprocess);
 
     my $firstpass = 1;
-    foreach my $biblio (@$biblios) {
-	$output .= marcrecord2csv($biblio, $id, $firstpass, $csv, $fieldprocessing) ;
-	$firstpass = 0;
+
+    if ( $itemnumbers ) {
+        for my $itemnumber ( @$itemnumbers) {
+            my $biblionumber = GetBiblionumberFromItemnumber $itemnumber;
+            $output .= marcrecord2csv( $biblionumber, $id, $firstpass, $csv, $fieldprocessing, [$itemnumber] );
+            $firstpass = 0;
+        }
+    } else {
+        foreach my $biblio (@$biblios) {
+            $output .= marcrecord2csv( $biblio, $id, $firstpass, $csv, $fieldprocessing );
+            $firstpass = 0;
+        }
     }
 
     # Postprocessing
@@ -383,16 +393,21 @@ C<$header> - true if the headers are to be printed (typically at first pass)
 
 C<$csv> - an already initialised Text::CSV object
 
+C<$fieldprocessing>
+
+C<$itemnumbers> a list of itemnumbers to export
+
 =cut
 
 
 sub marcrecord2csv {
-    my ($biblio, $id, $header, $csv, $fieldprocessing) = @_;
+    my ( $biblio, $id, $header, $csv, $fieldprocessing, $itemnumbers ) = @_;
     my $output;
 
     # Getting the record
-    my $record = GetMarcBiblio($biblio, 1);
+    my $record = GetMarcBiblio($biblio);
     next unless $record;
+    C4::Biblio::EmbedItemsInMarcBiblio( $record, $biblio, $itemnumbers );
     # Getting the framework
     my $frameworkcode = GetFrameworkCode($biblio);
 
@@ -503,8 +518,18 @@ sub marcrecord2csv {
 	    foreach (@fields) {
 		my $value;
 
-		# Getting authorised value
-		$value = defined $authvalues->{$_->as_string} ? $authvalues->{$_->as_string} : $_->as_string;
+       # If it is a control field
+       if ($_->is_control_field) {
+           $value = defined $authvalues->{$_->as_string} ? $authvalues->{$_->as_string} : $_->as_string;
+       } else {
+           # If it is a field, we gather all subfields, joined by the subfield separator
+           my @subvaluesarray;
+           my @subfields = $_->subfields;
+           foreach my $subfield (@subfields) {
+               push (@subvaluesarray, defined $authvalues->{$subfield->[1]} ? $authvalues->{$subfield->[1]} : $subfield->[1]);
+           }
+           $value = join ($subfieldseparator, @subvaluesarray);
+       }
 
 		# Field processing
 		eval $fieldprocessing if ($fieldprocessing);
diff --git a/circ/circulation.pl b/circ/circulation.pl
index bcbcb6f..ad3026c 100755
--- a/circ/circulation.pl
+++ b/circ/circulation.pl
@@ -726,6 +726,8 @@ $template->param(
 	AllowRenewalLimitOverride => C4::Context->preference("AllowRenewalLimitOverride"),
     dateformat                => C4::Context->preference("dateformat"),
     DHTMLcalendar_dateformat  => C4::Dates->DHTMLcalendar(),
+    dont_export_fields        => C4::Context->preference("DontExportFields"),
+    csv_profil_for_export     => C4::Context->preference("CsvProfilForExport"),
     canned_bor_notes_loop     => $canned_notes,
 );
 
diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl
index e140ff3..896799f 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -5199,6 +5199,28 @@ if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
 }
 
 
+
+
+
+
+$DBversion = "3.07.00.XXX";
+if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
+    $dbh->do(qq{
+        INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES('DontExportFields','','List of fields for non export in circulation.pl (separated by a space)','','');
+    });
+    print "Upgrade to $DBversion done (Add system preference DontExportFields)\n";
+    SetVersion($DBversion);
+}
+
+$DBversion = "3.07.00.XXX";
+if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
+    $dbh->do(qq{
+        INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) VALUES('CsvProfileForExport','','Set a profile name for CSV export','','');
+    });
+    print "Upgrade to $DBversion done (Adds New System preference CsvProfileForExport)\n";
+    SetVersion($DBversion)
+}
+
 =head1 FUNCTIONS
 
 =head2 TableExists($table)
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table-footer.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table-footer.inc
index 5638710..c00e7db 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table-footer.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/checkouts-table-footer.inc
@@ -3,7 +3,7 @@
         <td colspan="6" style="text-align: right; font-weight:bold;">Totals:</td>
 		<td>[% totaldue %]</td>
 		<td>[% totalprice %]</td>
-		<td colspan="2">
+		<td colspan="3">
 			<p>Renewal due date: <input type="text" size="8" id="newduedate" name="newduedate" value="[% newduedate %]" />
 			<img src="[% themelang %]/lib/calendar/cal.gif" id="newduedate_button" alt="Show Calendar" />
 			<script type="text/javascript"> //<![CDATA[
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/prefs-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/prefs-menu.inc
index 3ecaefc..af23ab7 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/prefs-menu.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/prefs-menu.inc
@@ -15,6 +15,7 @@
 [% IF ( searching ) %]<li class="active">[% ELSE %]<li>[% END %]<a title="Searching" href="/cgi-bin/koha/admin/preferences.pl?tab=searching">Searching</a></li>
 [% IF ( serials ) %]<li class="active">[% ELSE %]<li>[% END %]<a title="Serials" href="/cgi-bin/koha/admin/preferences.pl?tab=serials">Serials</a></li>
 [% IF ( staff_client ) %]<li class="active">[% ELSE %]<li>[% END %]<a title="Staff client" href="/cgi-bin/koha/admin/preferences.pl?tab=staff_client">Staff client</a></li>
+[% IF ( tools ) %]<li class="active">[% ELSE %]<li>[% END %]<a title="Tools" href="/cgi-bin/koha/admin/preferences.pl?tab=tools">Tools</a></li>
 [% IF ( web_services ) %]<li class="active">[% ELSE %]<li>[% END %]<a title="Web services" href="/cgi-bin/koha/admin/preferences.pl?tab=web_services">Web services</a></li>
 </ul>
 </div>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref
index 471aa0e..2f409f7 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref
@@ -103,6 +103,12 @@ Circulation:
             - pref: NoticeCSS
               class: url
             - on Notices. (This should be a complete URL, starting with <code>http://</code>)
+        -
+            - pref: DontExportFields
+            - choices:
+                yes: Export
+                no: "Don't export"
+            - fields for csv or iso2709 export
     Checkout Policy:
         -
             - pref: AllowNotForLoanOverride
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/tools.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/tools.pref
new file mode 100644
index 0000000..f5ea186
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/tools.pref
@@ -0,0 +1,4 @@
+Tools:
+    -
+        - pref: CsvProfileForExport
+        - CSV profile for export
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
index e79cef1..61bde2b 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/circ/circulation.tt
@@ -74,6 +74,15 @@ var allcheckboxes = $(".checkboxed");
 		$(allcheckboxes).unCheckCheckboxes(":input[name*=barcodes]"); return false;
 	});
 
+    $("#CheckAllexports").click(function(){
+        $(".checkboxed").checkCheckboxes(":input[name*=biblionumbers]");
+        $(".checkboxed").unCheckCheckboxes(":input[name*=items]");
+        return false;
+    });
+    $("#CheckNoexports").click(function(){
+        $(".checkboxed").unCheckCheckboxes(":input[name*=biblionumbers]");
+        return false;
+    });
     $("#relrenew_all").click(function(){
         $(allcheckboxes).checkCheckboxes(":input[name*=items]");
         $(allcheckboxes).unCheckCheckboxes(":input[name*=barcodes]");
@@ -136,11 +145,70 @@ var allcheckboxes = $(".checkboxed");
 		$("#add_message_form").show();
 	 });
 
-     $("input.radio").click(function(){
+    $("input.radio").click(function(){
         radioCheckBox($(this));
-     });
+    });
+    $("#exportmenuc").empty();
+    initExportButton();
+
+    var optionsFilters = {
+        additionalFilterTriggers: [$('#quickfind')],
+        clearFiltersControls: [$('#cleanfilters')],
+        selectOptionLabel: [_('...')],
+    };
+    $('#issuest').tableFilter(optionsFilters);
  });
 
+function initExportButton() {
+    var exportmenu = [
+        { text: _("ISO2709 with items"), onclick: {fn: function(){export_submit("iso2709_995")}} },
+        { text: _("ISO2709 without items"), onclick: {fn: function(){export_submit("iso2709")}} },
+        { text: _("CSV"), onclick: {fn: function(){export_submit("csv")}} },
+    ];
+    new YAHOO.widget.Button({
+        type: "menu",
+        label: _("Export"),
+        name: "exportmenubutton",
+        menu: exportmenu,
+        container: "exportmenuc"
+    });
+}
+
+function export_submit(format) {
+    if ($("input:checkbox[name='biblionumbers'][checked]").length < 1){
+        alert(_("You must select a checkout to export"));
+        return;
+    }
+
+    $("input:checkbox[name='biblionumbers']").each( function(){
+        var input_item = $(this).siblings("input:checkbox");
+        if ( $(this).is(":checked") ) {
+            $(input_item).attr("checked", "checked");
+        } else {
+            $(input_item).attr("checked", "");
+        }
+    } );
+
+    if (format == 'iso2709_995') {
+        format = 'iso2709';
+        $("#dont_export_item").val(0);
+    } else if (format == 'iso2709') {
+        $("#dont_export_item").val(1);
+    } else {
+        [% IF NOT ( csv_profil_for_export ) %]
+            alert(_("You must defined a csv profil for export (in tools>CSV export profiles)"));
+            return false;
+        [% END %]
+    }
+    document.issues.action="/cgi-bin/koha/tools/export.pl";
+    document.getElementById("export_format").value = format;
+    document.issues.submit();
+
+    /* Reset form action to its initial value */
+    document.issues.action="/cgi-bin/koha/reserve/renewscript.pl";
+
+};
+
 function validate1(date) {
     var today = new Date();
     if ( date < today ) {
@@ -690,7 +758,12 @@ No patron matched <span class="ex">[% message %]</span>
 <!-- SUMMARY : TODAY & PREVIOUS ISSUES -->
 <div id="checkouts">
 [% IF ( issuecount ) %]
-    <form action="/cgi-bin/koha/reserve/renewscript.pl" method="post" class="checkboxed">
+    <div class="search_filters" style="display: block; margin: 0.5em;">
+        <label for="filter">Quick Find: </label>
+        <input type="text" id="quickfind"/>
+        <a id="cleanfilters" href="#">Clear Filters</a>
+    </div>
+    <form name="issues" action="/cgi-bin/koha/reserve/renewscript.pl" method="post" class="checkboxed">
     <input type="hidden" value="circ" name="destination" />
     <input type="hidden" name="cardnumber" value="[% cardnumber %]" />
     <input type="hidden" name="borrowernumber" value="[% borrowernumber %]" />
@@ -707,6 +780,7 @@ No patron matched <span class="ex">[% message %]</span>
         <th scope="col">Price</th>
         <th scope="col">Renew <p class="column-tool"><a href="#" id="CheckAllitems">select all</a> | <a href="#" id="CheckNoitems">none</a></p></th>
         <th scope="col">Check in <p class="column-tool"><a href="#" id="CheckAllreturns">select all</a> | <a href="#" id="CheckNoreturns">none</a></p></th>
+        <th scope="col">Export <p class="column-tool"><a href="#" id="CheckAllexports">select all</a> | <a href="#" id="CheckNoexports">none</a></p></th>
     </tr>
 [% IF ( todayissues ) %]</thead>
 [% INCLUDE 'checkouts-table-footer.inc' %]
@@ -780,8 +854,8 @@ No patron matched <span class="ex">[% message %]</span>
     [% END %] <!-- /loop todayissues -->
     <!-- /if todayissues -->[% END %]
 [% IF ( previssues ) %]
-[% IF ( todayissues ) %]<tr><th colspan="10"><a name="previous" id="previous"></a>Previous checkouts</th></tr>[% ELSE %]
-<tr><th class="{sorter: false}" colspan="10"><a name="previous" id="previous"></a>Previous checkouts</th></tr></thead>
+[% IF ( todayissues ) %]<tr><th colspan="11"><a name="previous" id="previous"></a>Previous checkouts</th></tr>[% ELSE %]
+<tr><th class="{sorter: false}" colspan="11"><a name="previous" id="previous"></a>Previous checkouts</th></tr></thead>
 [% INCLUDE 'checkouts-table-footer.inc' %]
 	<tbody>
 [% END %]
@@ -850,6 +924,10 @@ No patron matched <span class="ex">[% message %]</span>
                 <input type="checkbox" name="all_barcodes[]" value="[% previssue.barcode %]" checked="checked" style="display: none;" />
             </td>
             [% END %]
+            <td style="text-align:center;">
+                <input type="checkbox" id="export_[% previssue.biblionumber %]" name="biblionumbers" value="[% previssue.biblionumber %]" />
+                <input type="checkbox" name="itemnumbers" value="[% previssue.itemnumber %]" style="visibility:hidden;" />
+            </td>
       [% END %]
     </tr>
     <!-- /loop previssues -->[% END %]
@@ -866,7 +944,13 @@ No patron matched <span class="ex">[% message %]</span>
         [% END %]
         <input type="submit" name="renew_checked" value="Renew or Return checked items" />
         <input type="submit" id="renew_all" name="renew_all" value="Renew all" />
-        </fieldset>
+        <br/><br/>
+        Don't export fields : <input type="text" id="dont_export_fields" name="dont_export_fields" value="[% dont_export_fields %]" />
+        <span id="exportmenuc">Export</span>
+        <input type="hidden" name="op" value="export" />
+        <input type="hidden" id="export_format" name="format" value="iso2709" />
+        <input type="hidden" id="dont_export_item" name="dont_export_item" value="0" />
+    </fieldset>
     [% END %]
     </form>
 [% ELSE %]
@@ -913,7 +997,7 @@ No patron matched <span class="ex">[% message %]</span>
     [% END %] <!-- /loop relissues -->
     <!-- /if relissues -->[% END %]
 [% IF ( relprevissues ) %]
-<tr><th class="{sorter: false}" colspan="10"><a name="relprevious" id="relprevious"></a>Previous checkouts</th></tr>
+<tr><th class="{sorter: false}" colspan="11"><a name="relprevious" id="relprevious"></a>Previous checkouts</th></tr>
     [% FOREACH relprevissue IN relprevissues %]
     [% IF ( loop.odd ) %]
         <tr>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/export.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/export.tt
index ae4d3ec..826fa60 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/export.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/export.tt
@@ -111,7 +111,7 @@ Calendar.setup(
         </li>
         <li>
         <label for="dont_export_fields">Don't export fields</label>
-        <input id="dont_export_fields" type="text" name="dont_export_fields" />
+        <input id="dont_export_fields" type="text" name="dont_export_fields" value="[% dont_export_fields %]" />
         separate by a blank. (e.g., 100a 200 606)
         </li></ol>
     </fieldset>
diff --git a/tools/export.pl b/tools/export.pl
index d3d8646..0acb9ac 100755
--- a/tools/export.pl
+++ b/tools/export.pl
@@ -19,18 +19,22 @@
 
 use strict;
 use warnings;
+use List::MoreUtils qw(uniq);
 use C4::Auth;
 use C4::Output;
 use C4::Biblio;  # GetMarcBiblio GetXmlBiblio
 use CGI;
 use C4::Koha;    # GetItemTypes
 use C4::Branch;  # GetBranches
+use C4::Record;
+use C4::Csv;
 
-my $query = new CGI;
-my $op=$query->param("op") || '';
-my $filename=$query->param("filename");
-my $dbh=C4::Context->dbh;
+my $query       = new CGI;
+my $op          = $query->param("op") || '';
+my $filename    = $query->param("filename") || 'koha.mrc';
+my $dbh         = C4::Context->dbh;
 my $marcflavour = C4::Context->preference("marcflavour");
+my $format      = $query->param("format") || 'iso2709';
 
 my ($template, $loggedinuser, $cookie)
     = get_template_and_user
@@ -57,119 +61,145 @@ my ($template, $loggedinuser, $cookie)
 	}
 
 if ($op eq "export") {
-    binmode STDOUT, ':encoding(UTF-8)';
-	print $query->header(   -type => 'application/octet-stream', 
-                            -charset => 'utf-8',
-                            -attachment=>$filename);
-     
-    my $StartingBiblionumber  = $query->param("StartingBiblionumber");
-    my $EndingBiblionumber    = $query->param("EndingBiblionumber");
-    my $output_format         = $query->param("output_format");
-    my $itemtype              = $query->param("itemtype");
-    my $start_callnumber      = $query->param("start_callnumber");
-    my $end_callnumber        = $query->param("end_callnumber");
-    my $start_accession      = ($query->param("start_accession")) ? C4::Dates->new($query->param("start_accession")) : '' ;
-    my $end_accession        = ($query->param("end_accession")) ? C4::Dates->new($query->param("end_accession")) : '' ;
-    my $dont_export_items     = $query->param("dont_export_item");
-    my $strip_nonlocal_items   = $query->param("strip_nonlocal_items");
-    my $dont_export_fields    = $query->param("dont_export_fields");
-    my @sql_params;
-    
-    my $items_filter =
-        $branch || $start_callnumber || $end_callnumber ||  
-        $start_accession || $end_accession || 
-        ($itemtype && C4::Context->preference('item-level_itypes'));
-    my $query = $items_filter ?
-        "SELECT DISTINCT biblioitems.biblionumber
-         FROM biblioitems JOIN items
-         USING (biblionumber) WHERE 1"
-        :
-        "SELECT biblioitems.biblionumber FROM biblioitems WHERE biblionumber >0 ";
-                  
-    if ( $StartingBiblionumber ) {
-        $query .= " AND biblioitems.biblionumber >= ? ";
-        push @sql_params, $StartingBiblionumber;
-    }
-    
-    if ( $EndingBiblionumber ) {
-        $query .= " AND biblioitems.biblionumber <= ? ";
-        push @sql_params, $EndingBiblionumber;    
-    }
+    if ( $format eq "iso2709" ) {
+        binmode STDOUT, ':encoding(UTF-8)';
+        print $query->header(
+            -type => 'application/octet-stream', 
+            -charset => 'utf-8',
+            -attachment=>$filename
+        );
 
-    if ($branch) {
-        $query .= " AND homebranch = ? ";
-        push @sql_params, $branch;
-    }
+        my $StartingBiblionumber  = $query->param("StartingBiblionumber");
+        my $EndingBiblionumber    = $query->param("EndingBiblionumber");
+        my $output_format         = $query->param("output_format") || 'marc';
+        my $itemtype              = $query->param("itemtype");
+        my $start_callnumber      = $query->param("start_callnumber");
+        my $end_callnumber        = $query->param("end_callnumber");
+        my $start_accession       = ($query->param("start_accession")) ? C4::Dates->new($query->param("start_accession")) : '' ;
+        my $end_accession         = ($query->param("end_accession")) ? C4::Dates->new($query->param("end_accession")) : '' ;
+        my $dont_export_items     = $query->param("dont_export_item");
+        my $strip_nonlocal_items  = $query->param("strip_nonlocal_items");
+        my $dont_export_fields    = $query->param("dont_export_fields");
+        my @biblionumbers         = $query->param("biblionumbers");
+        my @itemnumbers           = $query->param("itemnumbers");
+        my @sql_params;
 
-    if ($start_callnumber) {
-        $query .= " AND itemcallnumber <= ? ";
-        push @sql_params, $start_callnumber;
-    }
+        if ( not @biblionumbers ) {
+            my $items_filter =
+                   $branch
+                || $start_callnumber
+                || $end_callnumber
+                || $start_accession
+                || $end_accession
+                || ($itemtype && C4::Context->preference('item-level_itypes'));
+            my $query = $items_filter
+                ? "SELECT DISTINCT biblioitems.biblionumber
+                   FROM biblioitems JOIN items
+                   USING (biblionumber) WHERE 1"
+                : "SELECT biblioitems.biblionumber FROM biblioitems WHERE biblionumber >0 ";
 
-    if ($end_callnumber) {
-        $query .= " AND itemcallnumber >= ? ";
-        push @sql_params, $end_callnumber;
-    }
-    if ($start_accession) {
-        $query .= " AND dateaccessioned >= ? ";
-        push @sql_params, $start_accession->output('iso');
-    }
+            if ( $StartingBiblionumber ) {
+                $query .= " AND biblioitems.biblionumber >= ? ";
+                push @sql_params, $StartingBiblionumber;
+            }
 
-    if ($end_accession) {
-        $query .= " AND dateaccessioned <= ? ";
-        push @sql_params, $end_accession->output('iso');
-    }
-    
-    if ( $itemtype ) {
-        $query .= (C4::Context->preference('item-level_itypes')) ? " AND items.itype = ? " : " AND biblioitems.itemtype = ?";
-        push @sql_params, $itemtype;
-    }
-    my $sth = $dbh->prepare($query);
-    $sth->execute(@sql_params);
-    
-    while (my ($biblionumber) = $sth->fetchrow) {
-        my $record = eval{ GetMarcBiblio($biblionumber); };
-        # FIXME: decide how to handle records GetMarcBiblio can't parse or retrieve
-        if ($@) {
-            next;
-        }
-        next if not defined $record;
-        C4::Biblio::EmbedItemsInMarcBiblio($record, $biblionumber) unless $dont_export_items;
-        if ($strip_nonlocal_items || $limit_ind_branch) {
-            my ( $homebranchfield, $homebranchsubfield ) =
-                GetMarcFromKohaField( 'items.homebranch', '' );
-			for my $itemfield ($record->field($homebranchfield)){
-				# if stripping nonlocal items, use loggedinuser's branch if they didn't select one
-				$branch = C4::Context->userenv->{'branch'} unless $branch;
-                $record->delete_field($itemfield) if($itemfield->subfield($homebranchsubfield) ne $branch) ;
+            if ( $EndingBiblionumber ) {
+                $query .= " AND biblioitems.biblionumber <= ? ";
+                push @sql_params, $EndingBiblionumber;    
+            }
+
+            if ($branch) {
+                $query .= " AND homebranch = ? ";
+                push @sql_params, $branch;
+            }
+
+            if ($start_callnumber) {
+                $query .= " AND itemcallnumber <= ? ";
+                push @sql_params, $start_callnumber;
+            }
+
+            if ($end_callnumber) {
+                $query .= " AND itemcallnumber >= ? ";
+                push @sql_params, $end_callnumber;
             }
+            if ($start_accession) {
+                $query .= " AND dateaccessioned >= ? ";
+                push @sql_params, $start_accession->output('iso');
+            }
+
+            if ($end_accession) {
+                $query .= " AND dateaccessioned <= ? ";
+                push @sql_params, $end_accession->output('iso');
+            }
+
+            if ( $itemtype ) {
+                $query .= (C4::Context->preference('item-level_itypes')) ? " AND items.itype = ? " : " AND biblioitems.itemtype = ?";
+                push @sql_params, $itemtype;
+            }
+            my $sth = $dbh->prepare($query);
+            $sth->execute(@sql_params);
+            push @biblionumbers, map { map { $$_[0] } $_ } @{$sth->fetchall_arrayref};
         }
-        
-        if ( $dont_export_fields ) {
-            my @fields = split " ", $dont_export_fields;
-            foreach ( @fields ) {
-                /^(\d*)(\w)?$/;
-                my $field = $1;
-                my $subfield = $2;
-                # skip if this record doesn't have this field
-                next if not defined $record->field($field);
-                if( $subfield ) {
-                    $record->field($field)->delete_subfields($subfield);
+
+        for my $biblionumber (uniq @biblionumbers) {
+            my $record = eval{ GetMarcBiblio($biblionumber); };
+            # FIXME: decide how to handle records GetMarcBiblio can't parse or retrieve
+            if ($@) {
+                next;
+            }
+            next if not defined $record;
+            C4::Biblio::EmbedItemsInMarcBiblio($record, $biblionumber, \@itemnumbers) unless $dont_export_items;
+            if ( $strip_nonlocal_items || $limit_ind_branch || $dont_export_items ) {
+                my ( $homebranchfield, $homebranchsubfield ) =
+                    GetMarcFromKohaField( 'items.homebranch', '' );
+                for my $itemfield ($record->field($homebranchfield)){
+                    # if stripping nonlocal items, use loggedinuser's branch if they didn't select one
+                    $branch = C4::Context->userenv->{'branch'} unless $branch;
+                    $record->delete_field($itemfield) if( $dont_export_items || $itemfield->subfield($homebranchsubfield) ne $branch) ;
                 }
-                else {
-                    $record->delete_field($record->field($field));
+            }
+
+            if ( $dont_export_fields ) {
+                my @fields = split " ", $dont_export_fields;
+                foreach ( @fields ) {
+                    /^(\d*)(\w)?$/;
+                    my $field = $1;
+                    my $subfield = $2;
+                    # skip if this record doesn't have this field
+                    next if not defined $record->field($field);
+                    if( $subfield ) {
+                        $record->field($field)->delete_subfields($subfield);
+                    }
+                    else {
+                        $record->delete_field($record->field($field));
+                    }
                 }
             }
+            if ( $output_format eq "xml" ) {
+                print $record->as_xml_record($marcflavour);
+            }
+            else {
+                print $record->as_usmarc(); 
+            }
         }
-        if ( $output_format eq "xml" ) {
-            print $record->as_xml_record($marcflavour);
-        }
-        else {
-            print $record->as_usmarc(); 
-        }
+        exit;
+    }
+    elsif ( $format eq "csv" ) {
+        my @biblionumbers = uniq $query->param("biblionumbers");
+        my @itemnumbers          = $query->param("itemnumbers");
+        my $output = marc2csv(
+            \@biblionumbers,
+            GetCsvProfileId(C4::Context->preference('CsvProfileForExport')),
+            \@itemnumbers,
+        );
+        print $query->header(
+            -type                        => 'application/octet-stream',
+            -'Content-Transfer-Encoding' => 'binary',
+            -attachment                  => "export.csv"
+        );
+        print $output;
+        exit;
     }
-    exit;
-    
 } # if export
 
 else {
@@ -199,8 +229,9 @@ else {
     $template->param(
         branchloop   => \@branchloop,
         itemtypeloop => \@itemtypesloop,
-		DHTMLcalendar_dateformat => C4::Dates->DHTMLcalendar(),
+        DHTMLcalendar_dateformat => C4::Dates->DHTMLcalendar(),
+        dont_export_fields        => C4::Context->preference("DontExportFields"),
     );
-    
+
     output_html_with_http_headers $query, $cookie, $template->output;
 }
-- 
1.7.7.3