View | Details | Raw Unified | Return to bug 14722
Collapse All | Expand All

(-)a/Koha/Exporter/Record.pm (+219 lines)
Line 0 Link Here
1
package Koha::Exporter::Record;
2
3
use Modern::Perl;
4
use MARC::File::XML;
5
use MARC::File::USMARC;
6
7
use C4::AuthoritiesMarc;
8
use C4::Biblio;
9
use C4::Record;
10
11
sub _get_record_for_export {
12
    my ($params)           = @_;
13
    my $record_type        = $params->{record_type};
14
    my $record_id          = $params->{record_id};
15
    my $dont_export_fields = $params->{dont_export_fields};
16
    my $clean              = $params->{clean};
17
18
    my $record;
19
    if ( $record_type eq 'auths' ) {
20
        $record = _get_authority_for_export( { %$params, authid => $record_id } );
21
    } elsif ( $record_type eq 'bibs' ) {
22
        $record = _get_biblio_for_export( { %$params, biblionumber => $record_id } );
23
    } else {
24
25
        # TODO log "record_type not supported"
26
        return;
27
    }
28
29
    if ($dont_export_fields) {
30
        for my $f ( split / /, $dont_export_fields ) {
31
            if ( $f =~ m/^(\d{3})(.)?$/ ) {
32
                my ( $field, $subfield ) = ( $1, $2 );
33
34
                # skip if this record doesn't have this field
35
                if ( defined $record->field($field) ) {
36
                    if ( defined $subfield ) {
37
                        my @tags = $record->field($field);
38
                        foreach my $t (@tags) {
39
                            $t->delete_subfields($subfield);
40
                        }
41
                    } else {
42
                        $record->delete_fields( $record->field($field) );
43
                    }
44
                }
45
            }
46
        }
47
    }
48
    C4::Biblio::RemoveAllNsb($record) if $clean;
49
    return $record;
50
}
51
52
sub _get_authority_for_export {
53
    my ($params) = @_;
54
    my $authid = $params->{authid} || return;
55
    my $authority = Koha::Authority->get_from_authid($authid);
56
    return unless $authority;
57
    return $authority->record;
58
}
59
60
sub _get_biblio_for_export {
61
    my ($params)     = @_;
62
    my $biblionumber = $params->{biblionumber};
63
    my $itemnumbers  = $params->{itemnumbers};
64
    my $export_items = $params->{export_items} // 1;
65
    my $only_export_items_for_branch = $params->{only_export_items_for_branch};
66
67
    my $record = eval { C4::Biblio::GetMarcBiblio($biblionumber); };
68
69
    return if $@ or not defined $record;
70
71
    if ($export_items) {
72
        C4::Biblio::EmbedItemsInMarcBiblio( $record, $biblionumber, $itemnumbers );
73
        if ($only_export_items_for_branch) {
74
            my ( $homebranchfield, $homebranchsubfield ) = GetMarcFromKohaField( 'items.homebranch', '' );    # Should be GetFrameworkCode( $biblionumber )?
75
76
            for my $itemfield ( $record->field($homebranchfield) ) {
77
                my $homebranch = $itemfield->subfield($homebranchsubfield);
78
                if ( $only_export_items_for_branch ne $homebranch ) {
79
                    $record->delete_field($itemfield);
80
                }
81
            }
82
        }
83
    }
84
    return $record;
85
}
86
87
sub export {
88
    my ($params) = @_;
89
90
    my $record_type        = $params->{record_type};
91
    my $record_ids         = $params->{record_ids} || [];
92
    my $format             = $params->{format};
93
    my $itemnumbers        = $params->{itemnumbers} || [];    # Does not make sense with record_type eq auths
94
    my $export_items       = $params->{export_items};
95
    my $dont_export_fields = $params->{dont_export_fields};
96
    my $csv_profile_id     = $params->{csv_profile_id};
97
    my $output_filepath    = $params->{output_filepath};
98
99
    return unless $record_type;
100
    return unless @$record_ids;
101
102
    my $fh;
103
    if ( $output_filepath ) {
104
        open $fh, '>', $output_filepath or die "Cannot open file $output_filepath ($!)";
105
        select $fh;
106
        binmode $fh, ':encoding(UTF-8)' unless $format eq 'csv';
107
    } else {
108
        binmode STDOUT, ':encoding(UTF-8)' unless $format eq 'csv';
109
    }
110
111
    if ( $format eq 'iso2709' ) {
112
        for my $record_id (@$record_ids) {
113
            my $record = _get_record_for_export( { %$params, record_id => $record_id } );
114
            my $errorcount_on_decode = eval { scalar( MARC::File::USMARC->decode( $record->as_usmarc )->warnings() ) };
115
            if ( $errorcount_on_decode or $@ ) {
116
                warn $@ if $@;
117
                warn "record (number $record_id) is invalid and therefore not exported because its reopening generates warnings above";
118
                next;
119
            }
120
            print $record->as_usmarc();
121
        }
122
    } elsif ( $format eq 'xml' ) {
123
        my $marcflavour = C4::Context->preference("marcflavour");
124
        MARC::File::XML->default_record_format( ( $marcflavour eq 'UNIMARC' && $record_type eq 'auths' ) ? 'UNIMARCAUTH' : $marcflavour );
125
126
        print MARC::File::XML::header();
127
        print "\n";
128
        for my $record_id (@$record_ids) {
129
            my $record = _get_record_for_export( { %$params, record_id => $record_id } );
130
            print MARC::File::XML::record($record);
131
            print "\n";
132
        }
133
        print MARC::File::XML::footer();
134
        print "\n";
135
    } elsif ( $format eq 'csv' ) {
136
        $csv_profile_id ||= C4::Csv::GetCsvProfileId( C4::Context->preference('ExportWithCsvProfile') );
137
        print marc2csv( $record_ids, $csv_profile_id, $itemnumbers );
138
    }
139
140
    close $fh if $output_filepath;
141
}
142
143
1;
144
145
__END__
146
147
=head1 NAME
148
149
Koha::Exporter::Records - module to export records (biblios and authorities)
150
151
=head1 SYNOPSIS
152
153
This module provides a public subroutine to export records as xml, csv or iso2709.
154
155
=head2 FUNCTIONS
156
157
=head3 export
158
159
    Koha::Exporter::Record::export($params);
160
161
$params is a hashref with some keys:
162
163
It will displays on STDOUT the generated file.
164
165
=over 4
166
167
=item record_type
168
169
  Must be set to 'bibs' or 'auths'
170
171
=item record_ids
172
173
  The list of the records to export (a list of biblionumber or authid)
174
175
=item format
176
177
  The format must be 'csv', 'xml' or 'iso2709'.
178
179
=item itemnumbers
180
181
  Generate the item infos only for these itemnumbers.
182
183
  Must only be used with biblios.
184
185
=item export_items
186
187
  If this flag is set, the items will be exported.
188
  Default is ON.
189
190
=item dont_export_fields
191
192
  List of fields not to export.
193
194
=item csv_profile_id
195
196
  If the format is csv, a csv_profile_id can be provide to overwrite the default value (syspref ExportWithCsvProfile).
197
198
=cut
199
200
=back
201
202
=head1 LICENSE
203
204
This file is part of Koha.
205
206
Copyright Koha Development Team
207
208
Koha is free software; you can redistribute it and/or modify it
209
under the terms of the GNU General Public License as published by
210
the Free Software Foundation; either version 3 of the License, or
211
(at your option) any later version.
212
213
Koha is distributed in the hope that it will be useful, but
214
WITHOUT ANY WARRANTY; without even the implied warranty of
215
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
216
GNU General Public License for more details.
217
218
You should have received a copy of the GNU General Public License
219
along with Koha; if not, see <http://www.gnu.org/licenses>.
(-)a/misc/export.pl (+308 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
#
4
# This file is part of Koha.
5
#
6
# Koha is free software; you can redistribute it and/or modify it
7
# under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 3 of the License, or
9
# (at your option) any later version.
10
#
11
# Koha is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with Koha; if not, see <http://www.gnu.org/licenses>.
18
19
use Modern::Perl;
20
use MARC::File::XML;
21
use List::MoreUtils qw(uniq);
22
use Getopt::Long;
23
use Pod::Usage;
24
25
use C4::Auth;
26
use C4::Context;
27
use C4::Csv;
28
use C4::Record;
29
30
use Koha::Biblioitems;
31
use Koha::Database;
32
use Koha::Exporter::Record;
33
use Koha::DateUtils qw( dt_from_string output_pref );
34
35
my ( $output_format, $timestamp, $dont_export_items, $csv_profile_id, $deleted_barcodes, $clean, $filename, $record_type, $id_list_file, $starting_authid, $ending_authid, $authtype, $starting_biblionumber, $ending_biblionumber, $itemtype, $starting_callnumber, $ending_callnumber, $start_accession, $end_accession, $help );
36
GetOptions(
37
    'format=s'                => \$output_format,
38
    'date=s'                  => \$timestamp,
39
    'dont_export_items'       => \$dont_export_items,
40
    'csv_profile_id=s'        => \$csv_profile_id,
41
    'deleted_barcodes'        => \$deleted_barcodes,
42
    'clean'                   => \$clean,
43
    'filename=s'              => \$filename,
44
    'record-type=s'           => \$record_type,
45
    'id_list_file=s'          => \$id_list_file,
46
    'starting_authid=s'       => \$starting_authid,
47
    'ending_authid=s'         => \$ending_authid,
48
    'authtype=s'              => \$authtype,
49
    'starting_biblionumber=s' => \$starting_biblionumber,
50
    'ending_biblionumber=s'   => \$ending_biblionumber,
51
    'itemtype=s'              => \$itemtype,
52
    'starting_callnumber=s'   => \$starting_callnumber,
53
    'ending_callnumber=s'     => \$ending_callnumber,
54
    'start_accession=s'       => \$start_accession,
55
    'end_accession=s'         => \$end_accession,
56
    'h|help|?'                => \$help
57
) || pod2usage(1);
58
59
if ($help) {
60
    pod2usage(1);
61
}
62
63
$filename ||= 'koha.mrc';
64
$output_format ||= 'iso2709';
65
$record_type ||= 'bibs';
66
67
# Retrocompatibility for the format parameter
68
$output_format = 'iso2709' if $output_format eq 'marc';
69
70
71
if ( $timestamp and $record_type ne 'bibs' ) {
72
    pod2usage(q|--timestamp can only be used with biblios|);
73
}
74
75
if ( $record_type ne 'bibs' and $record_type ne 'auths' ) {
76
    pod2usage(q|--record_type is not valid|);
77
}
78
79
if ( $deleted_barcodes and $record_type ne 'bibs' ) {
80
    pod2usage(q|--deleted_barcodes can only be used with biblios|);
81
}
82
83
$start_accession = dt_from_string( $start_accession ) if $start_accession;
84
$end_accession   = dt_from_string( $end_accession )   if $end_accession;
85
86
my $dbh = C4::Context->dbh;
87
88
# Redirect stdout
89
open STDOUT, '>', $filename if $filename;
90
91
92
my @record_ids;
93
94
$timestamp = ($timestamp) ? output_pref({ dt => dt_from_string($timestamp), dateformat => 'iso', dateonly => 1, }): '';
95
96
if ( $record_type eq 'bibs' ) {
97
    if ( $timestamp ) {
98
        push @record_ids, $_->{biblionumber} for @{
99
            $dbh->selectall_arrayref(q| (
100
                SELECT biblionumber
101
                FROM biblioitems
102
                  LEFT JOIN items USING(biblionumber)
103
                WHERE biblioitems.timestamp >= ?
104
                  OR items.timestamp >= ?
105
            ) UNION (
106
                SELECT biblionumber
107
                FROM biblioitems
108
                  LEFT JOIN deleteditems USING(biblionumber)
109
                WHERE biblioitems.timestamp >= ?
110
                  OR deleteditems.timestamp >= ?
111
            ) |, { Slice => {} }, ( $timestamp ) x 4 );
112
        };
113
    } else {
114
        my $conditions = {
115
            ( $starting_biblionumber or $ending_biblionumber )
116
                ? (
117
                    "me.biblionumber" => {
118
                        ( $starting_biblionumber ? ( '>=' => $starting_biblionumber ) : () ),
119
                        ( $ending_biblionumber   ? ( '<=' => $ending_biblionumber   ) : () ),
120
                    }
121
                )
122
                : (),
123
            ( $starting_callnumber or $ending_callnumber )
124
                ? (
125
                    callnumber => {
126
                        ( $starting_callnumber ? ( '>=' => $starting_callnumber ) : () ),
127
                        ( $ending_callnumber   ? ( '<=' => $ending_callnumber   ) : () ),
128
                    }
129
                )
130
                : (),
131
            ( $start_accession or $end_accession )
132
                ? (
133
                    dateaccessioned => {
134
                        ( $start_accession ? ( '>=' => $start_accession ) : () ),
135
                        ( $end_accession   ? ( '<=' => $end_accession   ) : () ),
136
                    }
137
                )
138
                : (),
139
            ( $itemtype
140
                ?
141
                  C4::Context->preference('item-level_itypes')
142
                    ? ( 'items.itype' => $itemtype )
143
                    : ( 'biblioitems.itemtype' => $itemtype )
144
                : ()
145
            ),
146
147
        };
148
        my $biblioitems = Koha::Biblioitems->search( $conditions, { join => 'items' } );
149
        while ( my $biblioitem = $biblioitems->next ) {
150
            push @record_ids, $biblioitem->biblionumber;
151
        }
152
    }
153
}
154
elsif ( $record_type eq 'auths' ) {
155
    my $conditions = {
156
        ( $starting_authid or $ending_authid )
157
            ? (
158
                authid => {
159
                    ( $starting_authid ? ( '>=' => $starting_authid ) : () ),
160
                    ( $ending_authid   ? ( '<=' => $ending_authid   ) : () ),
161
                }
162
            )
163
            : (),
164
        ( $authtype ? ( authtypecode => $authtype ) : () ),
165
    };
166
    # Koha::Authority is not a Koha::Object...
167
    my $authorities = Koha::Database->new->schema->resultset('AuthHeader')->search( $conditions );
168
    @record_ids = map { $_->authid } $authorities->all;
169
}
170
171
@record_ids = uniq @record_ids;
172
if ( @record_ids and my $id_list_file ) {
173
    my @filter_record_ids = <$id_list_file>;
174
    @filter_record_ids = map { my $id = $_; $id =~ s/[\r\n]*$// } @filter_record_ids;
175
    # intersection
176
    my %record_ids = map { $_ => 1 } @record_ids;
177
    @record_ids = grep $record_ids{$_}, @filter_record_ids;
178
}
179
180
if ($deleted_barcodes) {
181
    for my $record_id ( @record_ids ) {
182
        my $q = q|
183
        |;
184
        my $barcode = $dbh->selectall_arrayref(q| (
185
            SELECT DISTINCT barcode
186
            FROM deleteditems
187
            WHERE deleteditems.biblionumber = ?
188
        |, { Slice => {} }, $record_id );
189
        say $_->{barcode} for @$barcode
190
    }
191
}
192
else {
193
    Koha::Exporter::Record::export(
194
        {   record_type        => $record_type,
195
            record_ids         => \@record_ids,
196
            format             => $output_format,
197
            csv_profile_id     => ( $csv_profile_id || GetCsvProfileId( C4::Context->preference('ExportWithCsvProfile') ) || undef ),
198
            export_items       => (not $dont_export_items),
199
            clean              => $clean || 0,
200
        }
201
    );
202
}
203
exit;
204
205
206
=head1 NAME
207
208
export records - This script exports record (biblios or authorities)
209
210
=head1 SYNOPSIS
211
212
export.pl [-h|--help] [--format=format] [--date=date] [--record-type=TYPE] [--dont_export_items] [--deleted_barcodes] [--clean] [--id_list_file=PATH] --filename=outputfile
213
214
=head1 OPTIONS
215
216
=over
217
218
=item B<-h|--help>
219
220
Print a brief help message.
221
222
=item B<--format>
223
224
 --format=FORMAT        FORMAT is either 'xml', 'csv'  or 'marc' (default).
225
226
=item B<--date>
227
228
 --date=DATE            DATE should be entered as the 'dateformat' syspref is
229
                        set (dd/mm/yyyy for metric, yyyy-mm-dd for iso,
230
                        mm/dd/yyyy for us) records exported are the ones that
231
                        have been modified since DATE.
232
233
=item B<--record-type>
234
235
 --record-type=TYPE     TYPE is 'bibs' or 'auths'.
236
237
=item B<--dont_export_items>
238
239
 --dont_export_items    If enabled, the item infos won't be exported.
240
241
=item B<--csv_profile_id>
242
243
 --csv_profile_id=ID    Generate a CSV file with the given CSV profile id (see tools/csv-profiles.pl)
244
                        Unless provided, the one defined in the system preference 'ExportWithCsvProfile' will be used.
245
246
=item B<--deleted_barcodes>
247
248
 --deleted_barcodes     If used, a list of barcodes of items deleted since DATE
249
                        is produced (or from all deleted items if no date is
250
                        specified). Used only if TYPE is 'bibs'.
251
252
=item B<--clean>
253
254
 --clean                removes NSE/NSB.
255
256
=item B<--id_list_file>
257
258
 --id_list_file=PATH    PATH is a path to a file containing a list of
259
                        IDs (biblionumber or authid) with one ID per line.
260
                        This list works as a filter; it is compatible with
261
                        other parameters for selecting records.
262
263
=item B<--filename>
264
265
 --filename=FILENAME   FILENAME used to export the data.
266
267
=item B<--starting_authid>
268
269
=item B<--ending_authid>
270
271
=item B<--authtype>
272
273
=item B<--starting_biblionumber>
274
275
=item B<--ending_biblionumber>
276
277
=item B<--itemtype>
278
279
=item B<--starting_callnumber>
280
281
=item B<--ending_callnumber>
282
283
=item B<--start_accession>
284
285
=item B<--end_accession>
286
287
=back
288
289
=head1 AUTHOR
290
291
Koha Development Team
292
293
=head1 COPYRIGHT
294
295
Copyright Koha Team
296
297
=head1 LICENSE
298
299
This file is part of Koha.
300
301
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
302
Foundation; either version 3 of the License, or (at your option) any later version.
303
304
You should have received a copy of the GNU General Public License along
305
with Koha; if not, write to the Free Software Foundation, Inc.,
306
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
307
308
=cut
(-)a/t/db_dependent/Exporter/Record.t (+166 lines)
Line 0 Link Here
1
use Modern::Perl;
2
use Test::More tests => 3;
3
use MARC::Record;
4
use MARC::File::USMARC;
5
use MARC::File::XML;# ( BinaryEncoding => 'utf-8' );
6
#use XML::Simple;
7
use MARC::Batch;
8
use t::lib::TestBuilder;
9
use File::Slurp;
10
#use utf8;
11
use Encode;
12
13
use C4::Biblio;
14
use C4::Context;
15
16
use Koha::Exporter::Record;
17
18
my $dbh = C4::Context->dbh;
19
#$dbh->{AutoCommit} = 0;
20
#$dbh->{RaiseError} = 1;
21
22
#$dbh->do(q|DELETE FROM issues|);
23
#$dbh->do(q|DELETE FROM reserves|);
24
#$dbh->do(q|DELETE FROM items|);
25
#$dbh->do(q|DELETE FROM biblio|);
26
#$dbh->do(q|DELETE FROM auth_header|);
27
28
my $biblio_1_title = 'Silence in the library';
29
#my $biblio_2_title = Encode::encode('UTF-8', 'The art of computer programming ກ ຂ ຄ ງ ຈ ຊ ຍ é');
30
my $biblio_2_title = 'The art of computer programming ກ ຂ ຄ ງ ຈ ຊ ຍ é';
31
my $biblio_1 = MARC::Record->new();
32
$biblio_1->leader('00266nam a22001097a 4500');
33
$biblio_1->append_fields(
34
    MARC::Field->new('100', ' ', ' ', a => 'Moffat, Steven'),
35
    MARC::Field->new('245', ' ', ' ', a => $biblio_1_title),
36
);
37
my ($biblionumber_1, $biblioitemnumber_1) = AddBiblio($biblio_1, '');
38
my $biblio_2 = MARC::Record->new();
39
$biblio_2->leader('00266nam a22001097a 4500');
40
$biblio_2->append_fields(
41
    MARC::Field->new('100', ' ', ' ', a => 'Knuth, Donald Ervin'),
42
    MARC::Field->new('245', ' ', ' ', a => $biblio_2_title),
43
);
44
my ($biblionumber_2, $biblioitemnumber_2) = AddBiblio($biblio_2, '');
45
46
my $builder = t::lib::TestBuilder->new;
47
my $item_1_1 = $builder->build({
48
    source => 'Item',
49
    value => {
50
        biblionumber => $biblionumber_1,
51
        more_subfields_xml => '',
52
    }
53
});
54
my $item_1_2 = $builder->build({
55
    source => 'Item',
56
    value => {
57
        biblionumber => $biblionumber_1,
58
        more_subfields_xml => '',
59
    }
60
});
61
my $item_2_1 = $builder->build({
62
    source => 'Item',
63
    value => {
64
        biblionumber => $biblionumber_2,
65
        more_subfields_xml => '',
66
    }
67
});
68
69
subtest 'export csv' => sub {
70
    plan tests => 2;
71
    my $csv_content = q{Title=245$a|Barcode=952$p};
72
    $dbh->do(q|INSERT INTO export_format(profile, description, content, csv_separator, field_separator, subfield_separator, encoding, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?)|, {}, "TEST_PROFILE_Records.t", "my useless desc", $csv_content, '|', ';', ',', 'utf8', 'marc');
73
    my $csv_profile_id = $dbh->last_insert_id( undef, undef, 'export_format', undef );
74
    my $generated_csv_file = '/tmp/test_export_1.csv';
75
76
    # Get all item infos
77
    Koha::Exporter::Record::export(
78
        {
79
            record_type => 'bibs',
80
            record_ids => [ $biblionumber_1, $biblionumber_2 ],
81
            format => 'csv',
82
            csv_profile_id => $csv_profile_id,
83
            output_filepath => $generated_csv_file,
84
        }
85
    );
86
    my $expected_csv = <<EOF;
87
Title|Barcode
88
"$biblio_1_title"|$item_1_1->{barcode},$item_1_2->{barcode}
89
"$biblio_2_title"|$item_2_1->{barcode}
90
EOF
91
    my $generated_csv_content = read_file( $generated_csv_file );
92
    is( $generated_csv_content, $expected_csv, "Export CSV: All item's infos should have been retrieved" );
93
94
    $generated_csv_file = '/tmp/test_export.csv';
95
    # Get only 1 item info
96
    Koha::Exporter::Record::export(
97
        {
98
            record_type => 'bibs',
99
            record_ids => [ $biblionumber_1, $biblionumber_2 ],
100
            itemnumbers => [ $item_1_1->{itemnumber}, $item_2_1->{itemnumber} ],
101
            format => 'csv',
102
            csv_profile_id => $csv_profile_id,
103
            output_filepath => $generated_csv_file,
104
        }
105
    );
106
    $expected_csv = <<EOF;
107
Title|Barcode
108
"$biblio_1_title"|$item_1_1->{barcode}
109
"$biblio_2_title"|$item_2_1->{barcode}
110
EOF
111
    $generated_csv_content = read_file( $generated_csv_file );
112
    is( $generated_csv_content, $expected_csv, "Export CSV: Only 1 item info should have been retrieved" );
113
};
114
115
subtest 'export xml' => sub {
116
    plan tests => 2;
117
    my $generated_xml_file = '/tmp/test_export.xml';
118
    Koha::Exporter::Record::export(
119
        {
120
            record_type => 'bibs',
121
            record_ids => [ $biblionumber_1, $biblionumber_2 ],
122
            format => 'xml',
123
            output_filepath => $generated_xml_file,
124
        }
125
    );
126
    my $generated_xml_content = read_file( $generated_xml_file );
127
    $MARC::File::XML::_load_args{BinaryEncoding} = 'utf-8';
128
    open my $fh, '<', $generated_xml_file;
129
    my $records = MARC::Batch->new( 'XML', $fh );
130
    my @records;
131
    # The following statement produces
132
    # Use of uninitialized value in concatenation (.) or string at /usr/share/perl5/MARC/File/XML.pm line 398, <$fh> chunk 5.
133
    # Why?
134
    while ( my $record = $records->next ) {
135
        push @records, $record;
136
    }
137
    is( scalar( @records ), 2, 'Export XML: 2 records should have been exported' );
138
    my $second_record = $records[1];
139
    my $title = $second_record->subfield(245, 'a');
140
    $title = Encode::encode('UTF-8', $title);
141
    is( $title, $biblio_2_title, 'Export XML: The title is correctly encoded' );
142
};
143
144
subtest 'export iso2709' => sub {
145
    plan tests => 2;
146
    my $generated_mrc_file = '/tmp/test_export.mrc';
147
    # Get all item infos
148
    Koha::Exporter::Record::export(
149
        {
150
            record_type => 'bibs',
151
            record_ids => [ $biblionumber_1, $biblionumber_2 ],
152
            format => 'iso2709',
153
            output_filepath => $generated_mrc_file,
154
        }
155
    );
156
    my $records = MARC::File::USMARC->in( $generated_mrc_file );
157
    my @records;
158
    while ( my $record = $records->next ) {
159
        push @records, $record;
160
    }
161
    is( scalar( @records ), 2, 'Export ISO2709: 2 records should have been exported' );
162
    my $second_record = $records[1];
163
    my $title = $second_record->subfield(245, 'a');
164
    $title = Encode::encode('UTF-8', $title);
165
    is( $title, $biblio_2_title, 'Export ISO2709: The title is correctly encoded' );
166
};
(-)a/tools/export.pl (-531 / +185 lines)
Lines 17-119 Link Here
17
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
# along with Koha; if not, see <http://www.gnu.org/licenses>.
18
18
19
use Modern::Perl;
19
use Modern::Perl;
20
use CGI qw ( -utf8 );
20
use MARC::File::XML;
21
use MARC::File::XML;
21
use List::MoreUtils qw(uniq);
22
use List::MoreUtils qw(uniq);
22
use Getopt::Long;
23
use CGI qw ( -utf8 );
24
use C4::Auth;
23
use C4::Auth;
25
use C4::AuthoritiesMarc;    # GetAuthority
26
use C4::Biblio;             # GetMarcBiblio
27
use C4::Branch;             # GetBranches
24
use C4::Branch;             # GetBranches
28
use C4::Csv;
25
use C4::Csv;
29
use C4::Koha;               # GetItemTypes
26
use C4::Koha;               # GetItemTypes
30
use C4::Output;
27
use C4::Output;
31
use C4::Record;
32
33
my $query = new CGI;
34
35
my $clean;
36
my $dont_export_items;
37
my $deleted_barcodes;
38
my $timestamp;
39
my $record_type;
40
my $id_list_file;
41
my $help;
42
my $op       = $query->param("op")       || '';
43
my $filename = $query->param("filename") || 'koha.mrc';
44
my $dbh      = C4::Context->dbh;
45
my $marcflavour = C4::Context->preference("marcflavour");
46
my $output_format = $query->param("format") || $query->param("output_format") || 'iso2709';
47
48
# Checks if the script is called from commandline
49
my $commandline = not defined $ENV{GATEWAY_INTERFACE};
50
51
52
# @biblionumbers is only use for csv export from circulation.pl
53
my @biblionumbers = uniq $query->param("biblionumbers");
54
55
if ( $commandline ) {
56
57
    # Getting parameters
58
    $op = 'export';
59
    GetOptions(
60
        'format=s'          => \$output_format,
61
        'date=s'            => \$timestamp,
62
        'dont_export_items' => \$dont_export_items,
63
        'deleted_barcodes'  => \$deleted_barcodes,
64
        'clean'             => \$clean,
65
        'filename=s'        => \$filename,
66
        'record-type=s'     => \$record_type,
67
        'id_list_file=s'    => \$id_list_file,
68
        'help|?'            => \$help
69
    );
70
71
    if ($help) {
72
        print <<_USAGE_;
73
export.pl [--format=format] [--date=date] [--record-type=TYPE] [--dont_export_items] [--deleted_barcodes] [--clean] [--id_list_file=PATH] --filename=outputfile
74
28
29
use Koha::Biblioitems;
30
use Koha::Database;
31
use Koha::DateUtils qw( dt_from_string output_pref );
32
use Koha::Exporter::Record;
75
33
76
 --format=FORMAT        FORMAT is either 'xml' or 'marc' (default)
34
my $query = new CGI;
77
78
 --date=DATE            DATE should be entered as the 'dateformat' syspref is
79
                        set (dd/mm/yyyy for metric, yyyy-mm-dd for iso,
80
                        mm/dd/yyyy for us) records exported are the ones that
81
                        have been modified since DATE
82
83
 --record-type=TYPE     TYPE is 'bibs' or 'auths'
84
85
 --deleted_barcodes     If used, a list of barcodes of items deleted since DATE
86
                        is produced (or from all deleted items if no date is
87
                        specified). Used only if TYPE is 'bibs'
88
89
 --clean                removes NSE/NSB
90
91
 --id_list_file=PATH    PATH is a path to a file containing a list of
92
                        IDs (biblionumber or authid) with one ID per line.
93
                        This list works as a filter; it is compatible with
94
                        other parameters for selecting records
95
_USAGE_
96
        exit;
97
    }
98
99
    # Default parameters values :
100
    $timestamp         ||= '';
101
    $dont_export_items ||= 0;
102
    $deleted_barcodes  ||= 0;
103
    $clean             ||= 0;
104
    $record_type       ||= "bibs";
105
    $id_list_file       ||= 0;
106
107
    # Redirect stdout
108
    open STDOUT, '>', $filename if $filename;
109
110
}
111
else {
112
113
    $op       = $query->param("op")       || '';
114
    $filename = $query->param("filename") || 'koha.mrc';
115
    $filename =~ s/(\r|\n)//;
116
35
36
my $dont_export_items = $query->param("dont_export_item") || 0;
37
my $record_type       = $query->param("record_type");
38
my $op                = $query->param("op") || '';
39
my $output_format     = $query->param("format") || $query->param("output_format") || 'iso2709';
40
my $backupdir         = C4::Context->config('backupdir');
41
my $filename          = $query->param("filename") || 'koha.mrc';
42
$filename =~ s/(\r|\n)//;
43
44
my $dbh = C4::Context->dbh;
45
46
my @record_ids;
47
# biblionumbers is sent from circulation.pl only
48
if ( $query->param("biblionumbers") ) {
49
    $record_type = 'bibs';
50
    @record_ids = $query->param("biblionumbers");
117
}
51
}
118
52
119
# Default value for output_format is 'iso2709'
53
# Default value for output_format is 'iso2709'
Lines 126-480 my ( $template, $loggedinuser, $cookie, $flags ) = get_template_and_user( Link Here
126
        template_name   => "tools/export.tt",
60
        template_name   => "tools/export.tt",
127
        query           => $query,
61
        query           => $query,
128
        type            => "intranet",
62
        type            => "intranet",
129
        authnotrequired => $commandline,
63
        authnotrequired => 0,
130
        flagsrequired   => { tools => 'export_catalog' },
64
        flagsrequired   => { tools => 'export_catalog' },
131
        debug           => 1,
65
        debug           => 1,
132
    }
66
    }
133
);
67
);
134
68
135
my $limit_ind_branch =
136
  (      C4::Context->preference('IndependentBranches')
137
      && C4::Context->userenv
138
      && !C4::Context->IsSuperLibrarian()
139
      && C4::Context->userenv->{branch} ) ? 1 : 0;
140
141
my @branch = $query->param("branch");
69
my @branch = $query->param("branch");
142
if (   C4::Context->preference("IndependentBranches")
70
my $only_my_branch;
143
    && C4::Context->userenv
71
# Limit to local branch if IndependentBranches and not superlibrarian
144
    && !C4::Context->IsSuperLibrarian() )
72
if (
145
{
73
    (
74
          C4::Context->preference('IndependentBranches')
75
        && C4::Context->userenv
76
        && !C4::Context->IsSuperLibrarian()
77
        && C4::Context->userenv->{branch}
78
    )
79
    # Limit result to local branch strip_nonlocal_items
80
    or $query->param('strip_nonlocal_items')
81
) {
82
    $only_my_branch = 1;
146
    @branch = ( C4::Context->userenv->{'branch'} );
83
    @branch = ( C4::Context->userenv->{'branch'} );
147
}
84
}
148
# if stripping nonlocal items, use loggedinuser's branch
149
my $localbranch = C4::Context->userenv ? C4::Context->userenv->{'branch'} : undef;
150
85
151
my %branchmap = map { $_ => 1 } @branch; # for quick lookups
86
my %branchmap = map { $_ => 1 } @branch; # for quick lookups
152
87
153
my $backupdir = C4::Context->config('backupdir');
154
155
if ( $op eq "export" ) {
88
if ( $op eq "export" ) {
156
    if (
157
        $output_format eq "iso2709"
158
            or $output_format eq "xml"
159
            or (
160
                $output_format eq 'csv'
161
                    and not @biblionumbers
162
            )
163
    ) {
164
        my $charset  = 'utf-8';
165
        my $mimetype = 'application/octet-stream';
166
167
        binmode STDOUT, ':encoding(UTF-8)'
168
            if $filename =~ m/\.gz$/
169
               or $filename =~ m/\.bz2$/
170
               or $output_format ne 'csv';
171
172
        if ( $filename =~ m/\.gz$/ ) {
173
            $mimetype = 'application/x-gzip';
174
            $charset  = '';
175
            binmode STDOUT;
176
        }
177
        elsif ( $filename =~ m/\.bz2$/ ) {
178
            $mimetype = 'application/x-bzip2';
179
            binmode STDOUT;
180
            $charset = '';
181
        }
182
        print $query->header(
183
            -type       => $mimetype,
184
            -charset    => $charset,
185
            -attachment => $filename,
186
        ) unless ($commandline);
187
188
        $record_type = $query->param("record_type") unless ($commandline);
189
        my $export_remove_fields = $query->param("export_remove_fields");
190
        my @biblionumbers      = $query->param("biblionumbers");
191
        my @itemnumbers        = $query->param("itemnumbers");
192
        my @sql_params;
193
        my $sql_query;
194
        my @recordids;
195
196
        my $StartingBiblionumber = $query->param("StartingBiblionumber");
197
        my $EndingBiblionumber   = $query->param("EndingBiblionumber");
198
        my $itemtype             = $query->param("itemtype");
199
        my $start_callnumber     = $query->param("start_callnumber");
200
        my $end_callnumber       = $query->param("end_callnumber");
201
        $timestamp = ($timestamp) ? C4::Dates->new($timestamp) : ''
202
          if ($commandline);
203
        my $start_accession =
204
          ( $query->param("start_accession") )
205
          ? C4::Dates->new( $query->param("start_accession") )
206
          : '';
207
        my $end_accession =
208
          ( $query->param("end_accession") )
209
          ? C4::Dates->new( $query->param("end_accession") )
210
          : '';
211
        $dont_export_items = $query->param("dont_export_item")
212
          unless ($commandline);
213
214
        my $strip_nonlocal_items = $query->param("strip_nonlocal_items");
215
216
        my $biblioitemstable =
217
          ( $commandline and $deleted_barcodes )
218
          ? 'deletedbiblioitems'
219
          : 'biblioitems';
220
        my $itemstable =
221
          ( $commandline and $deleted_barcodes )
222
          ? 'deleteditems'
223
          : 'items';
224
225
        my $starting_authid = $query->param('starting_authid');
226
        my $ending_authid   = $query->param('ending_authid');
227
        my $authtype        = $query->param('authtype');
228
        my $filefh;
229
        if ($commandline) {
230
            open $filefh,"<", $id_list_file or die "cannot open $id_list_file: $!" if $id_list_file;
231
        } else {
232
            $filefh = $query->upload("id_list_file");
233
        }
234
        my %id_filter;
235
        if ($filefh) {
236
            while (my $number=<$filefh>){
237
                $number=~s/[\r\n]*$//;
238
                $id_filter{$number}=1 if $number=~/^\d+$/;
239
            }
240
        }
241
242
        if ( $record_type eq 'bibs' and not @biblionumbers ) {
243
            if ($timestamp) {
244
245
            # Specific query when timestamp is used
246
            # Actually it's used only with CLI and so all previous filters
247
            # are not used.
248
            # If one day timestamp is used via the web interface, this part will
249
            # certainly have to be rewrited
250
                my ( $query, $params ) = construct_query(
251
                    {
252
                        recordtype       => $record_type,
253
                        timestamp        => $timestamp,
254
                        biblioitemstable => $biblioitemstable,
255
                    }
256
                );
257
                $sql_query  = $query;
258
                @sql_params = @$params;
259
89
90
    my $export_remove_fields = $query->param("export_remove_fields") || q||;
91
    my @biblionumbers      = $query->param("biblionumbers");
92
    my @itemnumbers        = $query->param("itemnumbers");
93
    my @sql_params;
94
    my $sql_query;
95
96
    if ( $record_type eq 'bibs' or $record_type eq 'auths' ) {
97
        # No need to retrieve the record_ids if we already get them
98
        unless ( @record_ids ) {
99
            if ( $record_type eq 'bibs' ) {
100
                my $starting_biblionumber = $query->param("StartingBiblionumber");
101
                my $ending_biblionumber   = $query->param("EndingBiblionumber");
102
                my $itemtype             = $query->param("itemtype");
103
                my $start_callnumber     = $query->param("start_callnumber");
104
                my $end_callnumber       = $query->param("end_callnumber");
105
                my $start_accession =
106
                  ( $query->param("start_accession") )
107
                  ? dt_from_string( $query->param("start_accession") )
108
                  : '';
109
                my $end_accession =
110
                  ( $query->param("end_accession") )
111
                  ? dt_from_string( $query->param("end_accession") )
112
                  : '';
113
114
115
                my $conditions = {
116
                    ( $starting_biblionumber or $ending_biblionumber )
117
                        ? (
118
                            "me.biblionumber" => {
119
                                ( $starting_biblionumber ? ( '>=' => $starting_biblionumber ) : () ),
120
                                ( $ending_biblionumber   ? ( '<=' => $ending_biblionumber   ) : () ),
121
                            }
122
                        )
123
                        : (),
124
                    ( $start_callnumber or $end_callnumber )
125
                        ? (
126
                            callnumber => {
127
                                ( $start_callnumber ? ( '>=' => $start_callnumber ) : () ),
128
                                ( $end_callnumber   ? ( '<=' => $end_callnumber   ) : () ),
129
                            }
130
                        )
131
                        : (),
132
                    ( $start_accession or $end_accession )
133
                        ? (
134
                            dateaccessioned => {
135
                                ( $start_accession ? ( '>=' => $start_accession ) : () ),
136
                                ( $end_accession   ? ( '<=' => $end_accession   ) : () ),
137
                            }
138
                        )
139
                        : (),
140
                    ( @branch ? ( 'items.homebranch' => { in => \@branch } ) : () ),
141
                    ( $itemtype
142
                        ?
143
                          C4::Context->preference('item-level_itypes')
144
                            ? ( 'items.itype' => $itemtype )
145
                            : ( 'biblioitems.itemtype' => $itemtype )
146
                        : ()
147
                    ),
148
149
                };
150
                my $biblioitems = Koha::Biblioitems->search( $conditions, { join => 'items' } );
151
                while ( my $biblioitem = $biblioitems->next ) {
152
                    push @record_ids, $biblioitem->biblionumber;
153
                }
260
            }
154
            }
261
            else {
155
            elsif ( $record_type eq 'auths' ) {
262
                my ( $query, $params ) = construct_query(
156
                my $starting_authid = $query->param('starting_authid');
263
                    {
157
                my $ending_authid   = $query->param('ending_authid');
264
                        recordtype           => $record_type,
158
                my $authtype        = $query->param('authtype');
265
                        biblioitemstable     => $biblioitemstable,
159
266
                        itemstable           => $itemstable,
160
                my $conditions = {
267
                        StartingBiblionumber => $StartingBiblionumber,
161
                    ( $starting_authid or $ending_authid )
268
                        EndingBiblionumber   => $EndingBiblionumber,
162
                        ? (
269
                        branch               => \@branch,
163
                            authid => {
270
                        start_callnumber     => $start_callnumber,
164
                                ( $starting_authid ? ( '>=' => $starting_authid ) : () ),
271
                        end_callnumber       => $end_callnumber,
165
                                ( $ending_authid   ? ( '<=' => $ending_authid   ) : () ),
272
                        start_accession      => $start_accession,
166
                            }
273
                        end_accession        => $end_accession,
167
                        )
274
                        itemtype             => $itemtype,
168
                        : (),
275
                    }
169
                    ( $authtype ? ( authtypecode => $authtype ) : () ),
276
                );
170
                };
277
                $sql_query  = $query;
171
                # Koha::Authority is not a Koha::Object...
278
                @sql_params = @$params;
172
                my $authorities = Koha::Database->new->schema->resultset('AuthHeader')->search( $conditions );
173
                @record_ids = map { $_->authid } $authorities->all;
279
            }
174
            }
280
        }
175
        }
281
        elsif ( $record_type eq 'auths' ) {
282
            my ( $query, $params ) = construct_query(
283
                {
284
                    recordtype      => $record_type,
285
                    starting_authid => $starting_authid,
286
                    ending_authid   => $ending_authid,
287
                    authtype        => $authtype,
288
                }
289
            );
290
            $sql_query  = $query;
291
            @sql_params = @$params;
292
176
177
        @record_ids = uniq @record_ids;
178
        if ( @record_ids and my $filefh = $query->upload("id_list_file") ) {
179
            my @filter_record_ids = <$filefh>;
180
            @filter_record_ids = map { my $id = $_; $id =~ s/[\r\n]*$// } @filter_record_ids;
181
            # intersection
182
            my %record_ids = map { $_ => 1 } @record_ids;
183
            @record_ids = grep $record_ids{$_}, @filter_record_ids;
293
        }
184
        }
294
        elsif ( $record_type eq 'db' ) {
185
295
            my $successful_export;
186
        print CGI->new->header(
296
            if ( $flags->{superlibrarian}
187
            -type       => 'application/octet-stream',
297
                && C4::Context->config('backup_db_via_tools') )
188
            -charset    => 'utf-8',
298
            {
189
            -attachment => $filename,
299
                $successful_export = download_backup(
190
        );
300
                    {
191
301
                        directory => "$backupdir",
192
        Koha::Exporter::Record::export(
302
                        extension => 'sql',
193
            {   record_type        => $record_type,
303
                        filename  => "$filename"
194
                record_ids         => \@record_ids,
304
                    }
195
                format             => $output_format,
305
                );
196
                filename           => $filename,
197
                itemnumbers        => \@itemnumbers,
198
                dont_export_fields => $export_remove_fields,
199
                csv_profile_id     => ( $query->param('csv_profile_id') || GetCsvProfileId( C4::Context->preference('ExportWithCsvProfile') ) || undef ),
200
                export_items       => (not $dont_export_items),
306
            }
201
            }
307
            unless ($successful_export) {
202
        );
308
                my $remotehost = $query->remote_host();
203
    }
309
                $remotehost =~ s/(\n|\r)//;
204
    elsif ( $record_type eq 'db' or $record_type eq 'conf' ) {
310
                warn
205
        my $successful_export;
311
"A suspicious attempt was made to download the db at '$filename' by someone at "
206
312
                  . $remotehost . "\n";
207
        if ( $flags->{superlibrarian}
208
            and (
209
                    $record_type eq 'db' and C4::Context->config('backup_db_via_tools')
210
                 or
211
                    $record_type eq 'conf' and C4::Context->config('backup_conf_via_tools')
212
            )
213
        ) {
214
            binmode STDOUT, ':encoding(UTF-8)';
215
216
            my $charset  = 'utf-8';
217
            my $mimetype = 'application/octet-stream';
218
            if ( $filename =~ m/\.gz$/ ) {
219
                $mimetype = 'application/x-gzip';
220
                $charset  = '';
221
                binmode STDOUT;
313
            }
222
            }
314
            exit;
223
            elsif ( $filename =~ m/\.bz2$/ ) {
315
        }
224
                $mimetype = 'application/x-bzip2';
316
        elsif ( $record_type eq 'conf' ) {
225
                binmode STDOUT;
317
            my $successful_export;
226
                $charset = '';
318
            if ( $flags->{superlibrarian}
319
                && C4::Context->config('backup_conf_via_tools') )
320
            {
321
                $successful_export = download_backup(
322
                    {
323
                        directory => "$backupdir",
324
                        extension => 'tar',
325
                        filename  => "$filename"
326
                    }
327
                );
328
            }
227
            }
228
            print $query->header(
229
                -type       => $mimetype,
230
                -charset    => $charset,
231
                -attachment => $filename,
232
            );
233
234
            my $extension = $record_type eq 'db' ? 'sql' : 'tar';
235
236
            $successful_export = download_backup(
237
                {
238
                    directory => $backupdir,
239
                    extension => $extension,
240
                    filename  => $filename,
241
                }
242
            );
329
            unless ($successful_export) {
243
            unless ($successful_export) {
330
                my $remotehost = $query->remote_host();
244
                my $remotehost = $query->remote_host();
331
                $remotehost =~ s/(\n|\r)//;
245
                $remotehost =~ s/(\n|\r)//;
332
                warn
246
                warn
333
"A suspicious attempt was made to download the configuration at '$filename' by someone at "
247
    "A suspicious attempt was made to download the " . ( $record_type eq 'db' ? 'db' : 'configuration' ) . "at '$filename' by someone at "
334
                  . $remotehost . "\n";
248
                  . $remotehost . "\n";
335
            }
249
            }
336
            exit;
337
        }
338
        elsif (@biblionumbers) {
339
            push @recordids, (@biblionumbers);
340
        }
341
        else {
342
343
            # Someone is trying to mess us up
344
            exit;
345
        }
346
        unless (@biblionumbers) {
347
            my $sth = $dbh->prepare($sql_query);
348
            $sth->execute(@sql_params);
349
            push @recordids, map {
350
                map { $$_[0] } $_
351
            } @{ $sth->fetchall_arrayref };
352
            @recordids = grep { exists($id_filter{$_}) } @recordids if scalar(%id_filter);
353
        }
250
        }
354
355
        my $xml_header_written = 0;
356
        for my $recordid ( uniq @recordids ) {
357
            if ($deleted_barcodes) {
358
                my $q = "
359
                    SELECT DISTINCT barcode
360
                    FROM deleteditems
361
                    WHERE deleteditems.biblionumber = ?
362
                ";
363
                my $sth = $dbh->prepare($q);
364
                $sth->execute($recordid);
365
                while ( my $row = $sth->fetchrow_array ) {
366
                    print "$row\n";
367
                }
368
            }
369
            else {
370
                my $record;
371
                if ( $record_type eq 'bibs' ) {
372
                    $record = eval { GetMarcBiblio($recordid); };
373
374
                    next if $@;
375
                    next if not defined $record;
376
                    C4::Biblio::EmbedItemsInMarcBiblio( $record, $recordid,
377
                        \@itemnumbers )
378
                      unless $dont_export_items;
379
                    if (   $strip_nonlocal_items
380
                        || $limit_ind_branch
381
                        || $dont_export_items )
382
                    {
383
                        my ( $homebranchfield, $homebranchsubfield ) =
384
                          GetMarcFromKohaField( 'items.homebranch', '' );
385
                        for my $itemfield ( $record->field($homebranchfield) ) {
386
                            $record->delete_field($itemfield)
387
                              if ( $dont_export_items
388
                                || $localbranch ne $itemfield->subfield(
389
                                        $homebranchsubfield) );
390
                        }
391
                    }
392
                }
393
                elsif ( $record_type eq 'auths' ) {
394
                    $record = C4::AuthoritiesMarc::GetAuthority($recordid);
395
                    next if not defined $record;
396
                }
397
398
                if ($export_remove_fields) {
399
                    for my $f ( split / /, $export_remove_fields ) {
400
                        if ( $f =~ m/^(\d{3})(.)?$/ ) {
401
                            my ( $field, $subfield ) = ( $1, $2 );
402
403
                            # skip if this record doesn't have this field
404
                            if ( defined $record->field($field) ) {
405
                                if ( defined $subfield ) {
406
                                    my @tags = $record->field($field);
407
                                    foreach my $t (@tags) {
408
                                        $t->delete_subfields($subfield);
409
                                    }
410
                                }
411
                                else {
412
                                    $record->delete_fields($record->field($field));
413
                                }
414
                            }
415
                        }
416
                    }
417
                }
418
                RemoveAllNsb($record) if ($clean);
419
                if ( $output_format eq "xml" ) {
420
                    unless ($xml_header_written) {
421
                        MARC::File::XML->default_record_format(
422
                            (
423
                                     $marcflavour eq 'UNIMARC'
424
                                  && $record_type eq 'auths'
425
                            ) ? 'UNIMARCAUTH' : $marcflavour
426
                        );
427
                        print MARC::File::XML::header();
428
                        print "\n";
429
                        $xml_header_written = 1;
430
                    }
431
                    print MARC::File::XML::record($record);
432
                    print "\n";
433
                }
434
                elsif ( $output_format eq 'iso2709' ) {
435
                    my $errorcount_on_decode = eval { scalar(MARC::File::USMARC->decode( $record->as_usmarc )->warnings()) };
436
                    if ($errorcount_on_decode or $@){
437
                        warn $@ if $@;
438
                        warn "record (number $recordid) is invalid and therefore not exported because its reopening generates warnings above";
439
                        next;
440
                    }
441
                    print $record->as_usmarc();
442
                }
443
            }
444
        }
445
        if ($xml_header_written) {
446
            print MARC::File::XML::footer();
447
            print "\n";
448
        }
449
        if ( $output_format eq 'csv' ) {
450
            my $csv_profile_id = $query->param('csv_profile')
451
                || GetCsvProfileId( C4::Context->preference('ExportWithCsvProfile') );
452
            my $output =
453
              marc2csv( \@recordids,
454
                $csv_profile_id );
455
456
            print $output;
457
        }
458
459
        exit;
460
    }
461
    elsif ( $output_format eq "csv" ) {
462
        my @biblionumbers = uniq $query->param("biblionumbers");
463
        my @itemnumbers   = $query->param("itemnumbers");
464
        my $csv_profile_id = $query->param('csv_profile') || GetCsvProfileId( C4::Context->preference('ExportWithCsvProfile') );
465
        my $output =
466
          marc2csv( \@biblionumbers,
467
            $csv_profile_id,
468
            \@itemnumbers, );
469
        print $query->header(
470
            -type                        => 'application/octet-stream',
471
            -'Content-Transfer-Encoding' => 'binary',
472
            -attachment                  => "export.csv"
473
        );
474
        print $output;
475
        exit;
476
    }
251
    }
477
}    # if export
252
253
    exit;
254
}
478
255
479
else {
256
else {
480
257
Lines 487-493 else { Link Here
487
        );
264
        );
488
        push @itemtypesloop, \%row;
265
        push @itemtypesloop, \%row;
489
    }
266
    }
490
    my $branches = GetBranches($limit_ind_branch);
267
    my $branches = GetBranches($only_my_branch);
491
    my @branchloop;
268
    my @branchloop;
492
    for my $thisbranch (
269
    for my $thisbranch (
493
        sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname} }
270
        sort { $branches->{$a}->{branchname} cmp $branches->{$b}->{branchname} }
Lines 544-671 else { Link Here
544
    output_html_with_http_headers $query, $cookie, $template->output;
321
    output_html_with_http_headers $query, $cookie, $template->output;
545
}
322
}
546
323
547
sub construct_query {
548
    my ($params) = @_;
549
550
    my ( $sql_query, @sql_params );
551
552
    if ( $params->{recordtype} eq "bibs" ) {
553
        if ( $params->{timestamp} ) {
554
            my $biblioitemstable = $params->{biblioitemstable};
555
            $sql_query = " (
556
                SELECT biblionumber
557
                FROM $biblioitemstable
558
                  LEFT JOIN items USING(biblionumber)
559
                WHERE $biblioitemstable.timestamp >= ?
560
                  OR items.timestamp >= ?
561
            ) UNION (
562
                SELECT biblionumber
563
                FROM $biblioitemstable
564
                  LEFT JOIN deleteditems USING(biblionumber)
565
                WHERE $biblioitemstable.timestamp >= ?
566
                  OR deleteditems.timestamp >= ?
567
            ) ";
568
            my $ts = $timestamp->output('iso');
569
            @sql_params = ( $ts, $ts, $ts, $ts );
570
        }
571
        else {
572
            my $biblioitemstable     = $params->{biblioitemstable};
573
            my $itemstable           = $params->{itemstable};
574
            my $StartingBiblionumber = $params->{StartingBiblionumber};
575
            my $EndingBiblionumber   = $params->{EndingBiblionumber};
576
            my @branch               = @{ $params->{branch} };
577
            my $start_callnumber     = $params->{start_callnumber};
578
            my $end_callnumber       = $params->{end_callnumber};
579
            my $start_accession      = $params->{start_accession};
580
            my $end_accession        = $params->{end_accession};
581
            my $itemtype             = $params->{itemtype};
582
            my $items_filter =
583
                 @branch
584
              || $start_callnumber
585
              || $end_callnumber
586
              || $start_accession
587
              || $end_accession
588
              || ( $itemtype && C4::Context->preference('item-level_itypes') );
589
            $sql_query = $items_filter
590
              ? "SELECT DISTINCT $biblioitemstable.biblionumber
591
                FROM $biblioitemstable JOIN $itemstable
592
                USING (biblionumber) WHERE 1"
593
              : "SELECT $biblioitemstable.biblionumber FROM $biblioitemstable WHERE biblionumber >0 ";
594
595
            if ($StartingBiblionumber) {
596
                $sql_query .= " AND $biblioitemstable.biblionumber >= ? ";
597
                push @sql_params, $StartingBiblionumber;
598
            }
599
600
            if ($EndingBiblionumber) {
601
                $sql_query .= " AND $biblioitemstable.biblionumber <= ? ";
602
                push @sql_params, $EndingBiblionumber;
603
            }
604
605
            if (@branch) {
606
                $sql_query .= " AND homebranch IN (".join(',',map({'?'} @branch)).")";
607
                push @sql_params, @branch;
608
            }
609
610
            if ($start_callnumber) {
611
                $sql_query .= " AND itemcallnumber >= ? ";
612
                push @sql_params, $start_callnumber;
613
            }
614
615
            if ($end_callnumber) {
616
                $sql_query .= " AND itemcallnumber <= ? ";
617
                push @sql_params, $end_callnumber;
618
            }
619
            if ($start_accession) {
620
                $sql_query .= " AND dateaccessioned >= ? ";
621
                push @sql_params, $start_accession->output('iso');
622
            }
623
624
            if ($end_accession) {
625
                $sql_query .= " AND dateaccessioned <= ? ";
626
                push @sql_params, $end_accession->output('iso');
627
            }
628
629
            if ($itemtype) {
630
                $sql_query .=
631
                  ( C4::Context->preference('item-level_itypes') )
632
                  ? " AND items.itype = ? "
633
                  : " AND biblioitems.itemtype = ?";
634
                push @sql_params, $itemtype;
635
            }
636
        }
637
    }
638
    elsif ( $params->{recordtype} eq "auths" ) {
639
        if ( $params->{timestamp} ) {
640
641
            #TODO
642
        }
643
        else {
644
            my $starting_authid = $params->{starting_authid};
645
            my $ending_authid   = $params->{ending_authid};
646
            my $authtype        = $params->{authtype};
647
            $sql_query =
648
              "SELECT DISTINCT auth_header.authid FROM auth_header WHERE 1";
649
650
            if ($starting_authid) {
651
                $sql_query .= " AND auth_header.authid >= ? ";
652
                push @sql_params, $starting_authid;
653
            }
654
655
            if ($ending_authid) {
656
                $sql_query .= " AND auth_header.authid <= ? ";
657
                push @sql_params, $ending_authid;
658
            }
659
660
            if ($authtype) {
661
                $sql_query .= " AND auth_header.authtypecode = ? ";
662
                push @sql_params, $authtype;
663
            }
664
        }
665
    }
666
    return ( $sql_query, \@sql_params );
667
}
668
669
sub getbackupfilelist {
324
sub getbackupfilelist {
670
    my $args      = shift;
325
    my $args      = shift;
671
    my $directory = $args->{directory};
326
    my $directory = $args->{directory};
672
- 

Return to bug 14722