Merge remote-tracking branch 'origin/new/bug_6488'
[koha.git] / misc / migration_tools / rebuild_zebra.pl
1 #!/usr/bin/perl
2
3 use strict;
4 #use warnings; FIXME - Bug 2505
5
6 use C4::Context;
7 use Getopt::Long;
8 use File::Temp qw/ tempdir /;
9 use File::Path;
10 use C4::Biblio;
11 use C4::AuthoritiesMarc;
12 use C4::Items;
13
14
15 # script that checks zebradir structure & create directories & mandatory files if needed
16 #
17 #
18
19 $|=1; # flushes output
20 # If the cron job starts us in an unreadable dir, we will break without
21 # this.
22 chdir $ENV{HOME} if (!(-r '.'));
23 my $directory;
24 my $nosanitize;
25 my $skip_export;
26 my $keep_export;
27 my $reset;
28 my $biblios;
29 my $authorities;
30 my $noxml;
31 my $noshadow;
32 my $do_munge;
33 my $want_help;
34 my $as_xml;
35 my $process_zebraqueue;
36 my $do_not_clear_zebraqueue;
37 my $length;
38 my $where;
39 my $offset;
40 my $verbose_logging = 0;
41 my $zebraidx_log_opt = " -v none,fatal,warn ";
42 my $result = GetOptions(
43     'd:s'           => \$directory,
44     'r|reset'       => \$reset,
45     's'             => \$skip_export,
46     'k'             => \$keep_export,
47     'nosanitize'    => \$nosanitize,
48     'b'             => \$biblios,
49     'noxml'         => \$noxml,
50     'w'             => \$noshadow,
51     'munge-config'  => \$do_munge,
52     'a'             => \$authorities,
53     'h|help'        => \$want_help,
54         'x'                             => \$as_xml,
55     'y'             => \$do_not_clear_zebraqueue,
56     'z'             => \$process_zebraqueue,
57     'where:s'        => \$where,
58     'length:i'        => \$length,
59     'offset:i'      => \$offset,
60     'v+'             => \$verbose_logging,
61 );
62
63
64 if (not $result or $want_help) {
65     print_usage();
66     exit 0;
67 }
68
69 if (not $biblios and not $authorities) {
70     my $msg = "Must specify -b or -a to reindex bibs or authorities\n";
71     $msg   .= "Please do '$0 --help' to see usage.\n";
72     die $msg;
73 }
74
75 if ($authorities and $as_xml) {
76     my $msg = "Cannot specify both -a and -x\n";
77     $msg   .= "Please do '$0 --help' to see usage.\n";
78     die $msg;
79 }
80
81 if ( !$as_xml and $nosanitize ) {
82     my $msg = "Cannot specify both -no_xml and -nosanitize\n";
83     $msg   .= "Please do '$0 --help' to see usage.\n";
84     die $msg;
85 }
86
87 if ($process_zebraqueue and ($skip_export or $reset)) {
88     my $msg = "Cannot specify -r or -s if -z is specified\n";
89     $msg   .= "Please do '$0 --help' to see usage.\n";
90     die $msg;
91 }
92
93 if ($process_zebraqueue and $do_not_clear_zebraqueue) {
94     my $msg = "Cannot specify both -y and -z\n";
95     $msg   .= "Please do '$0 --help' to see usage.\n";
96     die $msg;
97 }
98
99 if ($noshadow) {
100     $noshadow = ' -n ';
101 }
102
103 #  -v is for verbose, which seems backwards here because of how logging is set
104 #    on the CLI of zebraidx.  It works this way.  The default is to not log much
105 if ($verbose_logging >= 2) {
106     $zebraidx_log_opt = '-v none,fatal,warn,all';
107 }
108
109 my $use_tempdir = 0;
110 unless ($directory) {
111     $use_tempdir = 1;
112     $directory = tempdir(CLEANUP => ($keep_export ? 0 : 1));
113
114
115
116 my $biblioserverdir = C4::Context->zebraconfig('biblioserver')->{directory};
117 my $authorityserverdir = C4::Context->zebraconfig('authorityserver')->{directory};
118
119 my $kohadir = C4::Context->config('intranetdir');
120 my $dbh = C4::Context->dbh;
121 my ($biblionumbertagfield,$biblionumbertagsubfield) = &GetMarcFromKohaField("biblio.biblionumber","");
122 my ($biblioitemnumbertagfield,$biblioitemnumbertagsubfield) = &GetMarcFromKohaField("biblioitems.biblioitemnumber","");
123
124 if ( $verbose_logging ) {
125     print "Zebra configuration information\n";
126     print "================================\n";
127     print "Zebra biblio directory      = $biblioserverdir\n";
128     print "Zebra authorities directory = $authorityserverdir\n";
129     print "Koha directory              = $kohadir\n";
130     print "BIBLIONUMBER in :     $biblionumbertagfield\$$biblionumbertagsubfield\n";
131     print "BIBLIOITEMNUMBER in : $biblioitemnumbertagfield\$$biblioitemnumbertagsubfield\n";
132     print "================================\n";
133 }
134
135 if ($do_munge) {
136     munge_config();
137 }
138
139 if ($authorities) {
140     index_records('authority', $directory, $skip_export, $process_zebraqueue, $as_xml, $noxml, $nosanitize, $do_not_clear_zebraqueue, $verbose_logging, $zebraidx_log_opt, $authorityserverdir);
141 } else {
142     print "skipping authorities\n" if ( $verbose_logging );
143 }
144
145 if ($biblios) {
146     index_records('biblio', $directory, $skip_export, $process_zebraqueue, $as_xml, $noxml, $nosanitize, $do_not_clear_zebraqueue, $verbose_logging, $zebraidx_log_opt, $biblioserverdir);
147 } else {
148     print "skipping biblios\n" if ( $verbose_logging );
149 }
150
151
152 if ( $verbose_logging ) {
153     print "====================\n";
154     print "CLEANING\n";
155     print "====================\n";
156 }
157 if ($keep_export) {
158     print "NOTHING cleaned : the export $directory has been kept.\n";
159     print "You can re-run this script with the -s ";
160     if ($use_tempdir) {
161         print " and -d $directory parameters";
162     } else {
163         print "parameter";
164     }
165     print "\n";
166     print "if you just want to rebuild zebra after changing the record.abs\n";
167     print "or another zebra config file\n";
168 } else {
169     unless ($use_tempdir) {
170         # if we're using a temporary directory
171         # created by File::Temp, it will be removed
172         # automatically.
173         rmtree($directory, 0, 1);
174         print "directory $directory deleted\n";
175     }
176 }
177
178 # This checks to see if the zebra directories exist under the provided path.
179 # If they don't, then zebra is likely to spit the dummy. This returns true
180 # if the directories had to be created, false otherwise.
181 sub check_zebra_dirs {
182         my ($base) = shift() . '/';
183         my $needed_repairing = 0;
184         my @dirs = ( '', 'key', 'register', 'shadow', 'tmp' );
185         foreach my $dir (@dirs) {
186                 my $bdir = $base . $dir;
187         if (! -d $bdir) {
188                 $needed_repairing = 1;
189                 mkdir $bdir || die "Unable to create '$bdir': $!\n";
190                 print "$0: needed to create '$bdir'\n";
191         }
192     }
193     return $needed_repairing;
194 }       # ----------  end of subroutine check_zebra_dirs  ----------
195
196 sub index_records {
197     my ($record_type, $directory, $skip_export, $process_zebraqueue, $as_xml, $noxml, $nosanitize, $do_not_clear_zebraqueue, $verbose_logging, $zebraidx_log_opt, $server_dir) = @_;
198
199     my $num_records_exported = 0;
200     my $records_deleted;
201     my $need_reset = check_zebra_dirs($server_dir);
202     if ($need_reset) {
203         print "$0: found broken zebra server directories: forcing a rebuild\n";
204         $reset = 1;
205     }
206     if ($skip_export && $verbose_logging) {
207         print "====================\n";
208         print "SKIPPING $record_type export\n";
209         print "====================\n";
210     } else {
211         if ( $verbose_logging ) {
212             print "====================\n";
213             print "exporting $record_type\n";
214             print "====================\n";
215         }
216         mkdir "$directory" unless (-d $directory);
217         mkdir "$directory/$record_type" unless (-d "$directory/$record_type");
218         if ($process_zebraqueue) {
219             my $entries = select_zebraqueue_records($record_type, 'deleted');
220             mkdir "$directory/del_$record_type" unless (-d "$directory/del_$record_type");
221             $records_deleted = generate_deleted_marc_records($record_type, $entries, "$directory/del_$record_type", $as_xml);
222             mark_zebraqueue_batch_done($entries);
223             $entries = select_zebraqueue_records($record_type, 'updated');
224             mkdir "$directory/upd_$record_type" unless (-d "$directory/upd_$record_type");
225             $num_records_exported = export_marc_records_from_list($record_type, 
226                                                                   $entries, "$directory/upd_$record_type", $as_xml, $noxml, $records_deleted);
227             mark_zebraqueue_batch_done($entries);
228         } else {
229             my $sth = select_all_records($record_type);
230             $num_records_exported = export_marc_records_from_sth($record_type, $sth, "$directory/$record_type", $as_xml, $noxml, $nosanitize);
231             unless ($do_not_clear_zebraqueue) {
232                 mark_all_zebraqueue_done($record_type);
233             }
234         }
235     }
236     
237     #
238     # and reindexing everything
239     #
240     if ( $verbose_logging ) {
241         print "====================\n";
242         print "REINDEXING zebra\n";
243         print "====================\n";
244     }
245         my $record_fmt = ($as_xml) ? 'marcxml' : 'iso2709' ;
246     if ($process_zebraqueue) {
247         do_indexing($record_type, 'delete', "$directory/del_$record_type", $reset, $noshadow, $record_fmt, $zebraidx_log_opt) 
248             if %$records_deleted;
249         do_indexing($record_type, 'update', "$directory/upd_$record_type", $reset, $noshadow, $record_fmt, $zebraidx_log_opt)
250             if $num_records_exported;
251     } else {
252         do_indexing($record_type, 'update', "$directory/$record_type", $reset, $noshadow, $record_fmt, $zebraidx_log_opt)
253             if ($num_records_exported or $skip_export);
254     }
255 }
256
257
258 sub select_zebraqueue_records {
259     my ($record_type, $update_type) = @_;
260
261     my $server = ($record_type eq 'biblio') ? 'biblioserver' : 'authorityserver';
262     my $op = ($update_type eq 'deleted') ? 'recordDelete' : 'specialUpdate';
263
264     my $sth = $dbh->prepare("SELECT id, biblio_auth_number 
265                              FROM zebraqueue
266                              WHERE server = ?
267                              AND   operation = ?
268                              AND   done = 0
269                              ORDER BY id DESC");
270     $sth->execute($server, $op);
271     my $entries = $sth->fetchall_arrayref({});
272 }
273
274 sub mark_all_zebraqueue_done {
275     my ($record_type) = @_;
276
277     my $server = ($record_type eq 'biblio') ? 'biblioserver' : 'authorityserver';
278
279     my $sth = $dbh->prepare("UPDATE zebraqueue SET done = 1
280                              WHERE server = ?
281                              AND done = 0");
282     $sth->execute($server);
283 }
284
285 sub mark_zebraqueue_batch_done {
286     my ($entries) = @_;
287
288     $dbh->{AutoCommit} = 0;
289     my $sth = $dbh->prepare("UPDATE zebraqueue SET done = 1 WHERE id = ?");
290     $dbh->commit();
291     foreach my $id (map { $_->{id} } @$entries) {
292         $sth->execute($id);
293     }
294     $dbh->{AutoCommit} = 1;
295 }
296
297 sub select_all_records {
298     my $record_type = shift;
299     return ($record_type eq 'biblio') ? select_all_biblios() : select_all_authorities();
300 }
301
302 sub select_all_authorities {
303     my $strsth=qq{SELECT authid FROM auth_header};
304     $strsth.=qq{ WHERE $where } if ($where);
305     $strsth.=qq{ LIMIT $length } if ($length && !$offset);
306     $strsth.=qq{ LIMIT $offset,$length } if ($length && $offset);
307     my $sth = $dbh->prepare($strsth);
308     $sth->execute();
309     return $sth;
310 }
311
312 sub select_all_biblios {
313     my $strsth = qq{ SELECT biblionumber FROM biblioitems };
314     $strsth.=qq{ WHERE $where } if ($where);
315     $strsth.=qq{ LIMIT $length } if ($length && !$offset);
316     $strsth.=qq{ LIMIT $offset,$length } if ($offset);
317     my $sth = $dbh->prepare($strsth);
318     $sth->execute();
319     return $sth;
320 }
321
322 sub export_marc_records_from_sth {
323     my ($record_type, $sth, $directory, $as_xml, $noxml, $nosanitize) = @_;
324
325     my $num_exported = 0;
326     open my $fh, '>:encoding(UTF-8) ', "$directory/exported_records" or die $!;
327     my $i = 0;
328     my ( $itemtag, $itemsubfield ) = GetMarcFromKohaField("items.itemnumber",'');
329     while (my ($record_number) = $sth->fetchrow_array) {
330         print "." if ( $verbose_logging );
331         print "\r$i" unless ($i++ %100 or !$verbose_logging);
332         if ( $nosanitize ) {
333             my $marcxml = $record_type eq 'biblio'
334                           ? GetXmlBiblio( $record_number )
335                           : GetAuthorityXML( $record_number );
336             if ($record_type eq 'biblio'){
337                 my @items = GetItemsInfo($record_number);
338                 if (@items){
339                     my $record = MARC::Record->new;
340                     $record->encoding('UTF-8');
341                     my @itemsrecord;
342                     foreach my $item (@items){
343                         my $record = Item2Marc($item, $record_number);                        
344                         push @itemsrecord, $record->field($itemtag);
345                     }
346                     $record->insert_fields_ordered(@itemsrecord);
347                     my $itemsxml = $record->as_xml_record();
348                     $marcxml =
349                         substr($marcxml, 0, length($marcxml)-10) .
350                         substr($itemsxml, index($itemsxml, "</leader>\n", 0) + 10);
351                 }
352             }
353             if ( $marcxml ) {
354                 print {$fh} $marcxml if $marcxml;
355                 $num_exported++;
356             }
357             next;
358         }
359         my ($marc) = get_corrected_marc_record($record_type, $record_number, $noxml);
360         if (defined $marc) {
361             # FIXME - when more than one record is exported and $as_xml is true,
362             # the output file is not valid XML - it's just multiple <record> elements
363             # strung together with no single root element.  zebraidx doesn't seem
364             # to care, though, at least if you're using the GRS-1 filter.  It does
365             # care if you're using the DOM filter, which requires valid XML file(s).
366             eval {
367                 print {$fh} ($as_xml) ? $marc->as_xml_record(C4::Context->preference('marcflavour')) : $marc->as_usmarc();
368                 $num_exported++;
369             };
370             if ($@) {
371               warn "Error exporting record $record_number ($record_type) ".($noxml ? "not XML" : "XML");
372             }
373         }
374     }
375     print "\nRecords exported: $num_exported\n" if ( $verbose_logging );
376     close $fh;
377     return $num_exported;
378 }
379
380 sub export_marc_records_from_list {
381     my ($record_type, $entries, $directory, $as_xml, $noxml, $records_deleted) = @_;
382
383     my $num_exported = 0;
384     open my $fh, '>:encoding(UTF-8)', "$directory/exported_records" or die $!;
385     my $i = 0;
386
387     # Skip any deleted records. We check for this anyway, but this reduces error spam
388     my %found = %$records_deleted;
389     foreach my $record_number ( map { $_->{biblio_auth_number} }
390                                 grep { !$found{ $_->{biblio_auth_number} }++ }
391                                 @$entries ) {
392         print "." if ( $verbose_logging );
393         print "\r$i" unless ($i++ %100 or !$verbose_logging);
394         my ($marc) = get_corrected_marc_record($record_type, $record_number, $noxml);
395         if (defined $marc) {
396             # FIXME - when more than one record is exported and $as_xml is true,
397             # the output file is not valid XML - it's just multiple <record> elements
398             # strung together with no single root element.  zebraidx doesn't seem
399             # to care, though, at least if you're using the GRS-1 filter.  It does
400             # care if you're using the DOM filter, which requires valid XML file(s).
401             print {$fh} ($as_xml) ? $marc->as_xml_record(C4::Context->preference('marcflavour')) : $marc->as_usmarc();
402             $num_exported++;
403         }
404     }
405     print "\nRecords exported: $num_exported\n" if ( $verbose_logging );
406     close $fh;
407     return $num_exported;
408 }
409
410 sub generate_deleted_marc_records {
411     my ($record_type, $entries, $directory, $as_xml) = @_;
412
413     my $records_deleted = {};
414     open my $fh, '>:encoding(UTF-8)', "$directory/exported_records" or die $!;
415     my $i = 0;
416     foreach my $record_number (map { $_->{biblio_auth_number} } @$entries ) {
417         print "\r$i" unless ($i++ %100 or !$verbose_logging);
418         print "." if ( $verbose_logging );
419
420         my $marc = MARC::Record->new();
421         if ($record_type eq 'biblio') {
422             fix_biblio_ids($marc, $record_number, $record_number);
423         } else {
424             fix_authority_id($marc, $record_number);
425         }
426         if (C4::Context->preference("marcflavour") eq "UNIMARC") {
427             fix_unimarc_100($marc);
428         }
429
430         print {$fh} ($as_xml) ? $marc->as_xml_record(C4::Context->preference("marcflavour")) : $marc->as_usmarc();
431
432         $records_deleted->{$record_number} = 1;
433     }
434     print "\nRecords exported: $i\n" if ( $verbose_logging );
435     close $fh;
436     return $records_deleted;
437     
438
439 }
440
441 sub get_corrected_marc_record {
442     my ($record_type, $record_number, $noxml) = @_;
443
444     my $marc = get_raw_marc_record($record_type, $record_number, $noxml); 
445
446     if (defined $marc) {
447         fix_leader($marc);
448         if ($record_type eq 'authority') {
449             fix_authority_id($marc, $record_number);
450         }
451         if (C4::Context->preference("marcflavour") eq "UNIMARC") {
452             fix_unimarc_100($marc);
453         }
454     }
455
456     return $marc;
457 }
458
459 sub get_raw_marc_record {
460     my ($record_type, $record_number, $noxml) = @_;
461   
462     my $marc; 
463     if ($record_type eq 'biblio') {
464         if ($noxml) {
465             my $fetch_sth = $dbh->prepare_cached("SELECT marc FROM biblioitems WHERE biblionumber = ?");
466             $fetch_sth->execute($record_number);
467             if (my ($blob) = $fetch_sth->fetchrow_array) {
468                 $marc = MARC::Record->new_from_usmarc($blob);
469                 unless ($marc) {
470                     warn "error creating MARC::Record from $blob";
471                 }
472             }
473             # failure to find a bib is not a problem -
474             # a delete could have been done before
475             # trying to process a record update
476
477             $fetch_sth->finish();
478             return unless $marc;
479         } else {
480             eval { $marc = GetMarcBiblio($record_number, 1); };
481             if ($@ || !$marc) {
482                 # here we do warn since catching an exception
483                 # means that the bib was found but failed
484                 # to be parsed
485                 warn "error retrieving biblio $record_number";
486                 return;
487             }
488         }
489     } else {
490         eval { $marc = GetAuthority($record_number); };
491         if ($@) {
492             warn "error retrieving authority $record_number";
493             return;
494         }
495     }
496     return $marc;
497 }
498
499 sub fix_leader {
500     # FIXME - this routine is suspect
501     # It blanks the Leader/00-05 and Leader/12-16 to
502     # force them to be recalculated correct when
503     # the $marc->as_usmarc() or $marc->as_xml() is called.
504     # But why is this necessary?  It would be a serious bug
505     # in MARC::Record (definitely) and MARC::File::XML (arguably) 
506     # if they are emitting incorrect leader values.
507     my $marc = shift;
508
509     my $leader = $marc->leader;
510     substr($leader,  0, 5) = '     ';
511     substr($leader, 10, 7) = '22     ';
512     $marc->leader(substr($leader, 0, 24));
513 }
514
515 sub fix_biblio_ids {
516     # FIXME - it is essential to ensure that the biblionumber is present,
517     #         otherwise, Zebra will choke on the record.  However, this
518     #         logic belongs in the relevant C4::Biblio APIs.
519     my $marc = shift;
520     my $biblionumber = shift;
521     my $biblioitemnumber;
522     if (@_) {
523         $biblioitemnumber = shift;
524     } else {    
525         my $sth = $dbh->prepare(
526             "SELECT biblioitemnumber FROM biblioitems WHERE biblionumber=?");
527         $sth->execute($biblionumber);
528         ($biblioitemnumber) = $sth->fetchrow_array;
529         $sth->finish;
530         unless ($biblioitemnumber) {
531             warn "failed to get biblioitemnumber for biblio $biblionumber";
532             return 0;
533         }
534     }
535
536     # FIXME - this is cheating on two levels
537     # 1. C4::Biblio::_koha_marc_update_bib_ids is meant to be an internal function
538     # 2. Making sure that the biblionumber and biblioitemnumber are correct and
539     #    present in the MARC::Record object ought to be part of GetMarcBiblio.
540     #
541     # On the other hand, this better for now than what rebuild_zebra.pl used to
542     # do, which was duplicate the code for inserting the biblionumber 
543     # and biblioitemnumber
544     C4::Biblio::_koha_marc_update_bib_ids($marc, '', $biblionumber, $biblioitemnumber);
545
546     return 1;
547 }
548
549 sub fix_authority_id {
550     # FIXME - as with fix_biblio_ids, the authid must be present
551     #         for Zebra's sake.  However, this really belongs
552     #         in C4::AuthoritiesMarc.
553     my ($marc, $authid) = @_;
554     unless ($marc->field('001') and $marc->field('001')->data() eq $authid){
555         $marc->delete_field($marc->field('001'));
556         $marc->insert_fields_ordered(MARC::Field->new('001',$authid));
557     }
558 }
559
560 sub fix_unimarc_100 {
561     # FIXME - again, if this is necessary, it belongs in C4::AuthoritiesMarc.
562     my $marc = shift;
563
564     my $string;
565     if ( length($marc->subfield( 100, "a" )) == 35 ) {
566         $string = $marc->subfield( 100, "a" );
567         my $f100 = $marc->field(100);
568         $marc->delete_field($f100);
569     }
570     else {
571         $string = POSIX::strftime( "%Y%m%d", localtime );
572         $string =~ s/\-//g;
573         $string = sprintf( "%-*s", 35, $string );
574     }
575     substr( $string, 22, 6, "frey50" );
576     unless ( length($marc->subfield( 100, "a" )) == 35 ) {
577         $marc->delete_field($marc->field(100));
578         $marc->insert_grouped_field(MARC::Field->new( 100, "", "", "a" => $string ));
579     }
580 }
581
582 sub do_indexing {
583     my ($record_type, $op, $record_dir, $reset_index, $noshadow, $record_format, $zebraidx_log_opt) = @_;
584
585     my $zebra_server  = ($record_type eq 'biblio') ? 'biblioserver' : 'authorityserver';
586     my $zebra_db_name = ($record_type eq 'biblio') ? 'biblios' : 'authorities';
587     my $zebra_config  = C4::Context->zebraconfig($zebra_server)->{'config'};
588     my $zebra_db_dir  = C4::Context->zebraconfig($zebra_server)->{'directory'};
589
590     system("zebraidx -c $zebra_config $zebraidx_log_opt -g $record_format -d $zebra_db_name init") if $reset_index;
591     system("zebraidx -c $zebra_config $zebraidx_log_opt $noshadow -g $record_format -d $zebra_db_name $op $record_dir");
592     system("zebraidx -c $zebra_config $zebraidx_log_opt -g $record_format -d $zebra_db_name commit") unless $noshadow;
593
594 }
595
596 sub print_usage {
597     print <<_USAGE_;
598 $0: reindex MARC bibs and/or authorities in Zebra.
599
600 Use this batch job to reindex all biblio or authority
601 records in your Koha database.  This job is useful
602 only if you are using Zebra; if you are using the 'NoZebra'
603 mode, this job should not be used.
604
605 Parameters:
606     -b                      index bibliographic records
607
608     -a                      index authority records
609
610     -z                      select only updated and deleted
611                             records marked in the zebraqueue
612                             table.  Cannot be used with -r
613                             or -s.
614
615     -r                      clear Zebra index before
616                             adding records to index
617
618     -d                      Temporary directory for indexing.
619                             If not specified, one is automatically
620                             created.  The export directory
621                             is automatically deleted unless
622                             you supply the -k switch.
623
624     -k                      Do not delete export directory.
625
626     -s                      Skip export.  Used if you have
627                             already exported the records 
628                             in a previous run.
629
630     -noxml                  index from ISO MARC blob
631                             instead of MARC XML.  This
632                             option is recommended only
633                             for advanced user.
634
635     -x                      export and index as xml instead of is02709 (biblios only).
636                             use this if you might have records > 99,999 chars,
637                                                         
638     -nosanitize             export biblio/authority records directly from DB marcxml
639                             field without sanitizing records. It speed up
640                             dump process but could fail if DB contains badly
641                             encoded records. Works only with -x,
642
643     -w                      skip shadow indexing for this batch
644
645     -y                      do NOT clear zebraqueue after indexing; normally,
646                             after doing batch indexing, zebraqueue should be
647                             marked done for the affected record type(s) so that
648                             a running zebraqueue_daemon doesn't try to reindex
649                             the same records - specify -y to override this.  
650                             Cannot be used with -z.
651
652     -v                      increase the amount of logging.  Normally only 
653                             warnings and errors from the indexing are shown.
654                             Use log level 2 (-v -v) to include all Zebra logs.
655
656     --length   1234         how many biblio you want to export
657     --offset 1243           offset you want to start to
658                                 example: --offset 500 --length=500 will result in a LIMIT 500,1000 (exporting 1000 records, starting by the 500th one)
659                                 note that the numbers are NOT related to biblionumber, that's the intended behaviour.
660     --where                 let you specify a WHERE query, like itemtype='BOOK'
661                             or something like that
662
663     --munge-config          Deprecated option to try
664                             to fix Zebra config files.
665     --help or -h            show this message.
666 _USAGE_
667 }
668
669 # FIXME: the following routines are deprecated and 
670 # will be removed once it is determined whether
671 # a script to fix Zebra configuration files is 
672 # actually needed.
673 sub munge_config {
674 #
675 # creating zebra-biblios.cfg depending on system
676 #
677
678 # getting zebraidx directory
679 my $zebraidxdir;
680 foreach (qw(/usr/local/bin/zebraidx
681         /opt/bin/zebraidx
682         /usr/bin/zebraidx
683         )) {
684     if ( -f $_ ) {
685         $zebraidxdir=$_;
686     }
687 }
688
689 unless ($zebraidxdir) {
690     print qq|
691     ERROR: could not find zebraidx directory
692     ERROR: Either zebra is not installed,
693     ERROR: or it's in a directory I don't checked.
694     ERROR: do a which zebraidx and edit this file to add the result you get
695 |;
696     exit;
697 }
698 $zebraidxdir =~ s/\/bin\/.*//;
699 print "Info : zebra is in $zebraidxdir \n";
700
701 # getting modules directory
702 my $modulesdir;
703 foreach (qw(/usr/local/lib/idzebra-2.0/modules/mod-grs-xml.so
704             /usr/local/lib/idzebra/modules/mod-grs-xml.so
705             /usr/lib/idzebra/modules/mod-grs-xml.so
706             /usr/lib/idzebra-2.0/modules/mod-grs-xml.so
707         )) {
708     if ( -f $_ ) {
709         $modulesdir=$_;
710     }
711 }
712
713 unless ($modulesdir) {
714     print qq|
715     ERROR: could not find mod-grs-xml.so directory
716     ERROR: Either zebra is not properly compiled (libxml2 is not setup and you don t have mod-grs-xml.so,
717     ERROR: or it's in a directory I don't checked.
718     ERROR: find where mod-grs-xml.so is and edit this file to add the result you get
719 |;
720     exit;
721 }
722 $modulesdir =~ s/\/modules\/.*//;
723 print "Info: zebra modules dir : $modulesdir\n";
724
725 # getting tab directory
726 my $tabdir;
727 foreach (qw(/usr/local/share/idzebra/tab/explain.att
728             /usr/local/share/idzebra-2.0/tab/explain.att
729             /usr/share/idzebra/tab/explain.att
730             /usr/share/idzebra-2.0/tab/explain.att
731         )) {
732     if ( -f $_ ) {
733         $tabdir=$_;
734     }
735 }
736
737 unless ($tabdir) {
738     print qq|
739     ERROR: could not find explain.att directory
740     ERROR: Either zebra is not properly compiled,
741     ERROR: or it's in a directory I don't checked.
742     ERROR: find where explain.att is and edit this file to add the result you get
743 |;
744     exit;
745 }
746 $tabdir =~ s/\/tab\/.*//;
747 print "Info: tab dir : $tabdir\n";
748
749 #
750 # AUTHORITIES creating directory structure
751 #
752 my $created_dir_or_file = 0;
753 if ($authorities) {
754     if ( $verbose_logging ) {
755         print "====================\n";
756         print "checking directories & files for authorities\n";
757         print "====================\n";
758     }
759     unless (-d "$authorityserverdir") {
760         system("mkdir -p $authorityserverdir");
761         print "Info: created $authorityserverdir\n";
762         $created_dir_or_file++;
763     }
764     unless (-d "$authorityserverdir/lock") {
765         mkdir "$authorityserverdir/lock";
766         print "Info: created $authorityserverdir/lock\n";
767         $created_dir_or_file++;
768     }
769     unless (-d "$authorityserverdir/register") {
770         mkdir "$authorityserverdir/register";
771         print "Info: created $authorityserverdir/register\n";
772         $created_dir_or_file++;
773     }
774     unless (-d "$authorityserverdir/shadow") {
775         mkdir "$authorityserverdir/shadow";
776         print "Info: created $authorityserverdir/shadow\n";
777         $created_dir_or_file++;
778     }
779     unless (-d "$authorityserverdir/tab") {
780         mkdir "$authorityserverdir/tab";
781         print "Info: created $authorityserverdir/tab\n";
782         $created_dir_or_file++;
783     }
784     unless (-d "$authorityserverdir/key") {
785         mkdir "$authorityserverdir/key";
786         print "Info: created $authorityserverdir/key\n";
787         $created_dir_or_file++;
788     }
789     
790     unless (-d "$authorityserverdir/etc") {
791         mkdir "$authorityserverdir/etc";
792         print "Info: created $authorityserverdir/etc\n";
793         $created_dir_or_file++;
794     }
795     
796     #
797     # AUTHORITIES : copying mandatory files
798     #
799     # the record model, depending on marc flavour
800     unless (-f "$authorityserverdir/tab/record.abs") {
801         if (C4::Context->preference("marcflavour") eq "UNIMARC") {
802             system("cp -f $kohadir/etc/zebradb/marc_defs/unimarc/authorities/record.abs $authorityserverdir/tab/record.abs");
803             print "Info: copied record.abs for UNIMARC\n";
804         } else {
805             system("cp -f $kohadir/etc/zebradb/marc_defs/marc21/authorities/record.abs $authorityserverdir/tab/record.abs");
806             print "Info: copied record.abs for USMARC\n";
807         }
808         $created_dir_or_file++;
809     }
810     unless (-f "$authorityserverdir/tab/sort-string-utf.chr") {
811         system("cp -f $kohadir/etc/zebradb/lang_defs/fr/sort-string-utf.chr $authorityserverdir/tab/sort-string-utf.chr");
812         print "Info: copied sort-string-utf.chr\n";
813         $created_dir_or_file++;
814     }
815     unless (-f "$authorityserverdir/tab/word-phrase-utf.chr") {
816         system("cp -f $kohadir/etc/zebradb/lang_defs/fr/sort-string-utf.chr $authorityserverdir/tab/word-phrase-utf.chr");
817         print "Info: copied word-phase-utf.chr\n";
818         $created_dir_or_file++;
819     }
820     unless (-f "$authorityserverdir/tab/auth1.att") {
821         system("cp -f $kohadir/etc/zebradb/authorities/etc/bib1.att $authorityserverdir/tab/auth1.att");
822         print "Info: copied auth1.att\n";
823         $created_dir_or_file++;
824     }
825     unless (-f "$authorityserverdir/tab/default.idx") {
826         system("cp -f $kohadir/etc/zebradb/etc/default.idx $authorityserverdir/tab/default.idx");
827         print "Info: copied default.idx\n";
828         $created_dir_or_file++;
829     }
830     
831     unless (-f "$authorityserverdir/etc/ccl.properties") {
832 #         system("cp -f $kohadir/etc/zebradb/ccl.properties ".C4::Context->zebraconfig('authorityserver')->{ccl2rpn});
833         system("cp -f $kohadir/etc/zebradb/ccl.properties $authorityserverdir/etc/ccl.properties");
834         print "Info: copied ccl.properties\n";
835         $created_dir_or_file++;
836     }
837     unless (-f "$authorityserverdir/etc/pqf.properties") {
838 #         system("cp -f $kohadir/etc/zebradb/pqf.properties ".C4::Context->zebraconfig('authorityserver')->{ccl2rpn});
839         system("cp -f $kohadir/etc/zebradb/pqf.properties $authorityserverdir/etc/pqf.properties");
840         print "Info: copied pqf.properties\n";
841         $created_dir_or_file++;
842     }
843     
844     #
845     # AUTHORITIES : copying mandatory files
846     #
847     unless (-f C4::Context->zebraconfig('authorityserver')->{config}) {
848     open my $zd, '>:encoding(UTF-8)' ,C4::Context->zebraconfig('authorityserver')->{config};
849     print {$zd} "
850 # generated by KOHA/misc/migration_tools/rebuild_zebra.pl 
851 profilePath:\${srcdir:-.}:$authorityserverdir/tab/:$tabdir/tab/:\${srcdir:-.}/tab/
852
853 encoding: UTF-8
854 # Files that describe the attribute sets supported.
855 attset: auth1.att
856 attset: explain.att
857 attset: gils.att
858
859 modulePath:$modulesdir/modules/
860 # Specify record type
861 iso2709.recordType:grs.marcxml.record
862 recordType:grs.xml
863 recordId: (auth1,Local-Number)
864 storeKeys:1
865 storeData:1
866
867
868 # Lock File Area
869 lockDir: $authorityserverdir/lock
870 perm.anonymous:r
871 perm.kohaadmin:rw
872 register: $authorityserverdir/register:4G
873 shadow: $authorityserverdir/shadow:4G
874
875 # Temp File area for result sets
876 setTmpDir: $authorityserverdir/tmp
877
878 # Temp File area for index program
879 keyTmpDir: $authorityserverdir/key
880
881 # Approx. Memory usage during indexing
882 memMax: 40M
883 rank:rank-1
884     ";
885         print "Info: creating zebra-authorities.cfg\n";
886         $created_dir_or_file++;
887     }
888     
889     if ($created_dir_or_file) {
890         print "Info: created : $created_dir_or_file directories & files\n";
891     } else {
892         print "Info: file & directories OK\n";
893     }
894     
895 }
896 if ($biblios) {
897     if ( $verbose_logging ) {
898         print "====================\n";
899         print "checking directories & files for biblios\n";
900         print "====================\n";
901     }
902
903     #
904     # BIBLIOS : creating directory structure
905     #
906     unless (-d "$biblioserverdir") {
907         system("mkdir -p $biblioserverdir");
908         print "Info: created $biblioserverdir\n";
909         $created_dir_or_file++;
910     }
911     unless (-d "$biblioserverdir/lock") {
912         mkdir "$biblioserverdir/lock";
913         print "Info: created $biblioserverdir/lock\n";
914         $created_dir_or_file++;
915     }
916     unless (-d "$biblioserverdir/register") {
917         mkdir "$biblioserverdir/register";
918         print "Info: created $biblioserverdir/register\n";
919         $created_dir_or_file++;
920     }
921     unless (-d "$biblioserverdir/shadow") {
922         mkdir "$biblioserverdir/shadow";
923         print "Info: created $biblioserverdir/shadow\n";
924         $created_dir_or_file++;
925     }
926     unless (-d "$biblioserverdir/tab") {
927         mkdir "$biblioserverdir/tab";
928         print "Info: created $biblioserverdir/tab\n";
929         $created_dir_or_file++;
930     }
931     unless (-d "$biblioserverdir/key") {
932         mkdir "$biblioserverdir/key";
933         print "Info: created $biblioserverdir/key\n";
934         $created_dir_or_file++;
935     }
936     unless (-d "$biblioserverdir/etc") {
937         mkdir "$biblioserverdir/etc";
938         print "Info: created $biblioserverdir/etc\n";
939         $created_dir_or_file++;
940     }
941     
942     #
943     # BIBLIOS : copying mandatory files
944     #
945     # the record model, depending on marc flavour
946     unless (-f "$biblioserverdir/tab/record.abs") {
947         if (C4::Context->preference("marcflavour") eq "UNIMARC") {
948             system("cp -f $kohadir/etc/zebradb/marc_defs/unimarc/biblios/record.abs $biblioserverdir/tab/record.abs");
949             print "Info: copied record.abs for UNIMARC\n";
950         } else {
951             system("cp -f $kohadir/etc/zebradb/marc_defs/marc21/biblios/record.abs $biblioserverdir/tab/record.abs");
952             print "Info: copied record.abs for USMARC\n";
953         }
954         $created_dir_or_file++;
955     }
956     unless (-f "$biblioserverdir/tab/sort-string-utf.chr") {
957         system("cp -f $kohadir/etc/zebradb/lang_defs/fr/sort-string-utf.chr $biblioserverdir/tab/sort-string-utf.chr");
958         print "Info: copied sort-string-utf.chr\n";
959         $created_dir_or_file++;
960     }
961     unless (-f "$biblioserverdir/tab/word-phrase-utf.chr") {
962         system("cp -f $kohadir/etc/zebradb/lang_defs/fr/sort-string-utf.chr $biblioserverdir/tab/word-phrase-utf.chr");
963         print "Info: copied word-phase-utf.chr\n";
964         $created_dir_or_file++;
965     }
966     unless (-f "$biblioserverdir/tab/bib1.att") {
967         system("cp -f $kohadir/etc/zebradb/biblios/etc/bib1.att $biblioserverdir/tab/bib1.att");
968         print "Info: copied bib1.att\n";
969         $created_dir_or_file++;
970     }
971     unless (-f "$biblioserverdir/tab/default.idx") {
972         system("cp -f $kohadir/etc/zebradb/etc/default.idx $biblioserverdir/tab/default.idx");
973         print "Info: copied default.idx\n";
974         $created_dir_or_file++;
975     }
976     unless (-f "$biblioserverdir/etc/ccl.properties") {
977 #         system("cp -f $kohadir/etc/zebradb/ccl.properties ".C4::Context->zebraconfig('biblioserver')->{ccl2rpn});
978         system("cp -f $kohadir/etc/zebradb/ccl.properties $biblioserverdir/etc/ccl.properties");
979         print "Info: copied ccl.properties\n";
980         $created_dir_or_file++;
981     }
982     unless (-f "$biblioserverdir/etc/pqf.properties") {
983 #         system("cp -f $kohadir/etc/zebradb/pqf.properties ".C4::Context->zebraconfig('biblioserver')->{ccl2rpn});
984         system("cp -f $kohadir/etc/zebradb/pqf.properties $biblioserverdir/etc/pqf.properties");
985         print "Info: copied pqf.properties\n";
986         $created_dir_or_file++;
987     }
988     
989     #
990     # BIBLIOS : copying mandatory files
991     #
992     unless (-f C4::Context->zebraconfig('biblioserver')->{config}) {
993     open my $zd, '>:encoding(UTF-8)', C4::Context->zebraconfig('biblioserver')->{config};
994     print {$zd} "
995 # generated by KOHA/misc/migrtion_tools/rebuild_zebra.pl 
996 profilePath:\${srcdir:-.}:$biblioserverdir/tab/:$tabdir/tab/:\${srcdir:-.}/tab/
997
998 encoding: UTF-8
999 # Files that describe the attribute sets supported.
1000 attset:bib1.att
1001 attset:explain.att
1002 attset:gils.att
1003
1004 modulePath:$modulesdir/modules/
1005 # Specify record type
1006 iso2709.recordType:grs.marcxml.record
1007 recordType:grs.xml
1008 recordId: (bib1,Local-Number)
1009 storeKeys:1
1010 storeData:1
1011
1012
1013 # Lock File Area
1014 lockDir: $biblioserverdir/lock
1015 perm.anonymous:r
1016 perm.kohaadmin:rw
1017 register: $biblioserverdir/register:4G
1018 shadow: $biblioserverdir/shadow:4G
1019
1020 # Temp File area for result sets
1021 setTmpDir: $biblioserverdir/tmp
1022
1023 # Temp File area for index program
1024 keyTmpDir: $biblioserverdir/key
1025
1026 # Approx. Memory usage during indexing
1027 memMax: 40M
1028 rank:rank-1
1029     ";
1030         print "Info: creating zebra-biblios.cfg\n";
1031         $created_dir_or_file++;
1032     }
1033     
1034     if ($created_dir_or_file) {
1035         print "Info: created : $created_dir_or_file directories & files\n";
1036     } else {
1037         print "Info: file & directories OK\n";
1038     }
1039     
1040 }
1041 }