/[webpac]/trunk2/lib/WebPAC.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk2/lib/WebPAC.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 376 - (show annotations)
Sun Jun 20 18:39:30 2004 UTC (19 years, 9 months ago) by dpavlin
File size: 18769 byte(s)
fill_in will automatically decode utf8 formats if passed to it.

1 package WebPAC;
2
3 use warnings;
4 use strict;
5
6 use Carp;
7 use Text::Iconv;
8 use Config::IniFiles;
9 use XML::Simple;
10 use Template;
11 use Log::Log4perl qw(get_logger :levels);
12
13 use Data::Dumper;
14
15 #my $LOOKUP_REGEX = '\[[^\[\]]+\]';
16 #my $LOOKUP_REGEX_SAVE = '\[([^\[\]]+)\]';
17 my $LOOKUP_REGEX = 'lookup{[^\{\}]+}';
18 my $LOOKUP_REGEX_SAVE = 'lookup{([^\{\}]+)}';
19
20 =head1 NAME
21
22 WebPAC - base class for WebPAC
23
24 =head1 DESCRIPTION
25
26 This module implements methods used by WebPAC.
27
28 =head1 METHODS
29
30 =head2 new
31
32 This will create new instance of WebPAC using configuration specified by C<config_file>.
33
34 my $webpac = new WebPAC(
35 config_file => 'name.conf',
36 [code_page => 'ISO-8859-2',]
37 );
38
39 Default C<code_page> is C<ISO-8859-2>.
40
41 It will also read configuration files
42 C<global.conf> (used by indexer and Web font-end)
43 and configuration file specified by C<config_file>
44 which describes databases to be indexed.
45
46 =cut
47
48 # mapping between data type and tag which specify
49 # format in XML file
50 my %type2tag = (
51 'isis' => 'isis',
52 # 'excel' => 'column',
53 # 'marc' => 'marc',
54 # 'feed' => 'feed'
55 );
56
57 sub new {
58 my $class = shift;
59 my $self = {@_};
60 bless($self, $class);
61
62 my $log_file = $self->{'log'} || "log.conf";
63 Log::Log4perl->init($log_file);
64
65 my $log = $self->_get_logger();
66
67 # fill in default values
68 # output codepage
69 $self->{'code_page'} = 'ISO-8859-2' if (! $self->{'code_page'});
70
71 #
72 # read global.conf
73 #
74 $log->debug("read 'global.conf'");
75
76 my $config = new Config::IniFiles( -file => 'global.conf' ) || $log->logcroak("can't open 'global.conf'");
77
78 # read global config parametars
79 foreach my $var (qw(
80 dbi_dbd
81 dbi_dsn
82 dbi_user
83 dbi_passwd
84 show_progress
85 my_unac_filter
86 output_template
87 )) {
88 $self->{'global_config'}->{$var} = $config->val('global', $var);
89 }
90
91 #
92 # read indexer config file
93 #
94
95 $self->{indexer_config_file} = new Config::IniFiles( -file => $self->{config_file} ) || $log->logcroak("can't open '",$self->{config_file},"'");
96
97 # create UTF-8 convertor for import_xml files
98 $self->{'utf2cp'} = Text::Iconv->new('UTF-8' ,$self->{'code_page'});
99
100 # create Template toolkit instance
101 $self->{'tt'} = Template->new(
102 INCLUDE_PATH => ($self->{'global_config_file'}->{'output_template'} || './output_template'),
103 # FILTERS => {
104 # 'foo' => \&foo_filter,
105 # },
106 EVAL_PERL => 1,
107 );
108
109 return $self;
110 }
111
112 =head2 open_isis
113
114 Open CDS/ISIS database using OpenIsis module and read all records to memory.
115
116 $webpac->open_isis(
117 filename => '/data/ISIS/ISIS',
118 code_page => '852',
119 limit_mfn => '500',
120 lookup => [ ... ],
121 );
122
123 By default, ISIS code page is assumed to be C<852>.
124
125 If optional parametar C<limit_mfn> is set, it will read just 500 records
126 from database in example above.
127
128 C<lookup> argument is an array of lookups to create. Each lookup must have C<key> and
129 C<val>. Optional parametar C<eval> is perl code to evaluate before storing
130 value in index.
131
132 lookup => [
133 { 'key' => 'd:v900', 'val' => 'v250^a' },
134 { 'eval' => '"v901^a" eq "Podruèje"',
135 'key' => 'pa:v561^4:v562^4:v461^1',
136 'val' => 'v900' },
137 ]
138
139 Returns number of last record read into memory (size of database, really).
140
141 =cut
142
143 sub open_isis {
144 my $self = shift;
145 my $arg = {@_};
146
147 my $log = $self->_get_logger();
148
149 $log->logcroak("need filename") if (! $arg->{'filename'});
150 my $code_page = $arg->{'code_page'} || '852';
151
152 use OpenIsis;
153
154 #$self->{'isis_code_page'} = $code_page;
155
156 # create Text::Iconv object
157 my $cp = Text::Iconv->new($code_page,$self->{'code_page'});
158
159 $log->info("reading ISIS database '",$arg->{'filename'},"'");
160
161 my $isis_db = OpenIsis::open($arg->{'filename'});
162
163 my $maxmfn = OpenIsis::maxRowid( $isis_db ) || 1;
164
165 $maxmfn = $self->{limit_mfn} if ($self->{limit_mfn});
166
167 $log->info("processing $maxmfn records...");
168
169 # read database
170 for (my $mfn = 1; $mfn <= $maxmfn; $mfn++) {
171
172 # read record
173 my $row = OpenIsis::read( $isis_db, $mfn );
174 foreach my $k (keys %{$row}) {
175 if ($k ne "mfn") {
176 foreach my $l (@{$row->{$k}}) {
177 $l = $cp->convert($l);
178 # has subfields?
179 my $val;
180 if ($l =~ m/\^/) {
181 foreach my $t (split(/\^/,$l)) {
182 next if (! $t);
183 $val->{substr($t,0,1)} = substr($t,1);
184 }
185 } else {
186 $val = $l;
187 }
188
189 push @{$self->{'data'}->{$mfn}->{$k}}, $val;
190 }
191 } else {
192 push @{$self->{'data'}->{$mfn}->{'000'}}, $mfn;
193 }
194
195 }
196
197 # create lookup
198 my $rec = $self->{'data'}->{$mfn};
199 $self->create_lookup($rec, @{$arg->{'lookup'}});
200
201 }
202
203 $self->{'current_mfn'} = 1;
204
205 # store max mfn and return it.
206 return $self->{'max_mfn'} = $maxmfn;
207 }
208
209 =head2 fetch_rec
210
211 Fetch next record from database. It will also display progress bar (once
212 it's implemented, that is).
213
214 my $rec = $webpac->fetch_rec;
215
216 =cut
217
218 sub fetch_rec {
219 my $self = shift;
220
221 my $log = $self->_get_logger();
222
223 my $mfn = $self->{'current_mfn'}++ || $log->logconfess("it seems that you didn't load database!");
224
225 if ($mfn > $self->{'max_mfn'}) {
226 $self->{'current_mfn'} = $self->{'max_mfn'};
227 $log->debug("at EOF");
228 return;
229 }
230
231 return $self->{'data'}->{$mfn};
232 }
233
234 =head2 open_import_xml
235
236 Read file from C<import_xml/> directory and parse it.
237
238 $webpac->open_import_xml(type => 'isis');
239
240 =cut
241
242 sub open_import_xml {
243 my $self = shift;
244
245 my $log = $self->_get_logger();
246
247 my $arg = {@_};
248 $log->logconfess("need type to load file from import_xml/") if (! $arg->{'type'});
249
250 $self->{'type'} = $arg->{'type'};
251
252 my $type_base = $arg->{'type'};
253 $type_base =~ s/_.*$//g;
254
255 $self->{'tag'} = $type2tag{$type_base};
256
257 $log->info("using type '",$self->{'type'},"' tag <",$self->{'tag'},">");
258
259 my $f = "./import_xml/".$self->{'type'}.".xml";
260 $log->logconfess("import_xml file '$f' doesn't exist!") if (! -e "$f");
261
262 $log->info("reading '$f'");
263
264 $self->{'import_xml_file'} = $f;
265
266 $self->{'import_xml'} = XMLin($f,
267 ForceArray => [ $self->{'tag'}, 'config', 'format' ],
268 );
269
270 $log->debug("import xml is ",sub { Dumper($self->{'import_xml'}) });
271
272 }
273
274 =head2 create_lookup
275
276 Create lookup from record using lookup definition.
277
278 $self->create_lookup($rec, @lookups);
279
280 Called internally by C<open_*> methods.
281
282 =cut
283
284 sub create_lookup {
285 my $self = shift;
286
287 my $log = $self->_get_logger();
288
289 my $rec = shift || $log->logconfess("need record to create lookup");
290 $log->logconfess("need HASH as first argument!") if ($rec !~ /HASH/o);
291
292 foreach my $i (@_) {
293 if ($i->{'eval'}) {
294 my $eval = $self->fill_in($rec,$i->{'eval'});
295 my $key = $self->fill_in($rec,$i->{'key'});
296 my @val = $self->fill_in($rec,$i->{'val'});
297 if ($key && @val && eval $eval) {
298 $log->debug("stored $key = ",sub { join(" | ",@val) });
299 push @{$self->{'lookup'}->{$key}}, @val;
300 }
301 } else {
302 my $key = $self->fill_in($rec,$i->{'key'});
303 my @val = $self->fill_in($rec,$i->{'val'});
304 if ($key && @val) {
305 $log->debug("stored $key = ",sub { join(" | ",@val) });
306 push @{$self->{'lookup'}->{$key}}, @val;
307 }
308 }
309 }
310 }
311
312 =head2 get_data
313
314 Returns value from record.
315
316 my $text = $self->get_data(\$rec,$f,$sf,$i,\$found);
317
318 Arguments are:
319 record reference C<$rec>,
320 field C<$f>,
321 optional subfiled C<$sf>,
322 index for repeatable values C<$i>.
323
324 Optinal variable C<$found> will be incremeted if there
325 is field.
326
327 Returns value or empty string.
328
329 =cut
330
331 sub get_data {
332 my $self = shift;
333
334 my ($rec,$f,$sf,$i,$found) = @_;
335
336 if ($$rec->{$f}) {
337 return '' if (! $$rec->{$f}->[$i]);
338 if ($sf && $$rec->{$f}->[$i]->{$sf}) {
339 $$found++ if (defined($$found));
340 return $$rec->{$f}->[$i]->{$sf};
341 } elsif ($$rec->{$f}->[$i]) {
342 $$found++ if (defined($$found));
343 # it still might have subfield, just
344 # not specified, so we'll dump all
345 if ($$rec->{$f}->[$i] =~ /HASH/o) {
346 my $out;
347 foreach my $k (keys %{$$rec->{$f}->[$i]}) {
348 $out .= $$rec->{$f}->[$i]->{$k}." ";
349 }
350 return $out;
351 } else {
352 return $$rec->{$f}->[$i];
353 }
354 }
355 } else {
356 return '';
357 }
358 }
359
360 =head2 fill_in
361
362 Workhourse of all: takes record from in-memory structure of database and
363 strings with placeholders and returns string or array of with substituted
364 values from record.
365
366 my $text = $webpac->fill_in($rec,'v250^a');
367
368 Optional argument is ordinal number for repeatable fields. By default,
369 it's assume to be first repeatable field (fields are perl array, so first
370 element is 0).
371 Following example will read second value from repeatable field.
372
373 my $text = $webpac->fill_in($rec,'Title: v250^a',1);
374
375 This function B<does not> perform parsing of format to inteligenty skip
376 delimiters before fields which aren't used.
377
378 This method will automatically decode UTF-8 string to local code page
379 if needed.
380
381 =cut
382
383 sub fill_in {
384 my $self = shift;
385
386 my $log = $self->_get_logger();
387
388 my $rec = shift || $log->logconfess("need data record");
389 my $format = shift || $log->logconfess("need format to parse");
390 # iteration (for repeatable fields)
391 my $i = shift || 0;
392
393 # FIXME remove for speedup?
394 $log->logconfess("need HASH as first argument!") if ($rec !~ /HASH/o);
395
396 if (utf8::is_utf8($format)) {
397 $format = $self->_x($format);
398 }
399
400 my $found = 0;
401
402 my $eval_code;
403 # remove eval{...} from beginning
404 $eval_code = $1 if ($format =~ s/^eval{([^}]+)}//s);
405
406 # do actual replacement of placeholders
407 $format =~ s/v(\d+)(?:\^(\w))?/$self->get_data(\$rec,$1,$2,$i,\$found)/ges;
408
409 if ($found) {
410 $log->debug("format: $format");
411 if ($eval_code) {
412 my $eval = $self->fill_in($rec,$eval_code,$i);
413 return if (! $self->_eval($eval));
414 }
415 # do we have lookups?
416 if ($format =~ /$LOOKUP_REGEX/o) {
417 $log->debug("format '$format' has lookup");
418 return $self->lookup($format);
419 } else {
420 return $format;
421 }
422 } else {
423 return;
424 }
425 }
426
427 =head2 lookup
428
429 Perform lookups on format supplied to it.
430
431 my $text = $self->lookup('[v900]');
432
433 Lookups can be nested (like C<[d:[a:[v900]]]>).
434
435 =cut
436
437 sub lookup {
438 my $self = shift;
439
440 my $log = $self->_get_logger();
441
442 my $tmp = shift || $log->logconfess("need format");
443
444 if ($tmp =~ /$LOOKUP_REGEX/o) {
445 my @in = ( $tmp );
446
447 $log->debug("lookup for: ",$tmp);
448
449 my @out;
450 while (my $f = shift @in) {
451 if ($f =~ /$LOOKUP_REGEX_SAVE/o) {
452 my $k = $1;
453 if ($self->{'lookup'}->{$k}) {
454 foreach my $nv (@{$self->{'lookup'}->{$k}}) {
455 my $tmp2 = $f;
456 $tmp2 =~ s/lookup{$k}/$nv/g;
457 push @in, $tmp2;
458 }
459 } else {
460 undef $f;
461 }
462 } elsif ($f) {
463 push @out, $f;
464 }
465 }
466 $log->logconfess("return is array and it's not expected!") unless wantarray;
467 return @out;
468 } else {
469 return $tmp;
470 }
471 }
472
473 =head2 parse
474
475 Perform smart parsing of string, skipping delimiters for fields which aren't
476 defined. It can also eval code in format starting with C<eval{...}> and
477 return output or nothing depending on eval code.
478
479 my $text = $webpac->parse($rec,'eval{"v901^a" eq "Deskriptor"}descriptor: v250^a', $i);
480
481 =cut
482
483 sub parse {
484 my $self = shift;
485
486 my ($rec, $format_utf8, $i) = @_;
487
488 return if (! $format_utf8);
489
490 my $log = $self->_get_logger();
491
492 $log->logconfess("need HASH as first argument!") if ($rec !~ /HASH/o);
493 $log->logconfess("need utf2cp Text::Iconv object!") if (! $self->{'utf2cp'});
494
495 $i = 0 if (! $i);
496
497 my $format = $self->_x($format_utf8) || $log->logconfess("can't convert '$format_utf8' from UTF-8 to ",$self->{'code_page'});
498
499 my @out;
500
501 $log->debug("format: $format");
502
503 my $eval_code;
504 # remove eval{...} from beginning
505 $eval_code = $1 if ($format =~ s/^eval{([^}]+)}//s);
506
507 my $prefix;
508 my $all_found=0;
509
510 while ($format =~ s/^(.*?)v(\d+)(?:\^(\w))?//s) {
511
512 my $del = $1 || '';
513 $prefix ||= $del if ($all_found == 0);
514
515 my $found = 0;
516 my $tmp = $self->get_data(\$rec,$2,$3,$i,\$found);
517
518 if ($found) {
519 push @out, $del;
520 push @out, $tmp;
521 $all_found += $found;
522 }
523 }
524
525 return if (! $all_found);
526
527 my $out = join('',@out);
528
529 if ($out) {
530 # add rest of format (suffix)
531 $out .= $format;
532
533 # add prefix if not there
534 $out = $prefix . $out if ($out !~ m/^\Q$prefix\E/);
535
536 $log->debug("result: $out");
537 }
538
539 if ($eval_code) {
540 my $eval = $self->fill_in($rec,$eval_code,$i);
541 $log->debug("about to eval{",$eval,"} format: $out");
542 return if (! $self->_eval($eval));
543 }
544
545 return $out;
546 }
547
548 =head2 parse_to_arr
549
550 Similar to C<parse>, but returns array of all repeatable fields
551
552 my @arr = $webpac->parse_to_arr($rec,'v250^a');
553
554 =cut
555
556 sub parse_to_arr {
557 my $self = shift;
558
559 my ($rec, $format_utf8) = @_;
560
561 my $log = $self->_get_logger();
562
563 $log->logconfess("need HASH as first argument!") if ($rec !~ /HASH/o);
564 return if (! $format_utf8);
565
566 my $i = 0;
567 my @arr;
568
569 while (my $v = $self->parse($rec,$format_utf8,$i++)) {
570 push @arr, $v;
571 }
572
573 $log->debug("format '$format_utf8' returned ",--$i," elements: ", sub { join(" | ",@arr) }) if (@arr);
574
575 return @arr;
576 }
577
578 =head2 fill_in_to_arr
579
580 Similar to C<fill_in>, but returns array of all repeatable fields. Usable
581 for fields which have lookups, so they shouldn't be parsed but rather
582 C<fill_id>ed.
583
584 my @arr = $webpac->fill_in_to_arr($rec,'[v900];;[v250^a]');
585
586 =cut
587
588 sub fill_in_to_arr {
589 my $self = shift;
590
591 my ($rec, $format_utf8) = @_;
592
593 my $log = $self->_get_logger();
594
595 $log->logconfess("need HASH as first argument!") if ($rec !~ /HASH/o);
596 return if (! $format_utf8);
597
598 my $i = 0;
599 my @arr;
600
601 while (my @v = $self->fill_in($rec,$format_utf8,$i++)) {
602 push @arr, @v;
603 }
604
605 $log->debug("format '$format_utf8' returned ",--$i," elements: ", sub { join(" | ",@arr) }) if (@arr);
606
607 return @arr;
608 }
609
610
611 =head2 data_structure
612
613 Create in-memory data structure which represents layout from C<import_xml>.
614 It is used later to produce output.
615
616 my @ds = $webpac->data_structure($rec);
617
618 This method will also set C<$webpac->{'currnet_filename'}> if there is
619 <filename> tag in C<import_xml>.
620
621 =cut
622
623 sub data_structure {
624 my $self = shift;
625
626 my $log = $self->_get_logger();
627
628 my $rec = shift;
629 $log->logconfess("need HASH as first argument!") if ($rec !~ /HASH/o);
630
631 undef $self->{'currnet_filename'};
632
633 my @sorted_tags;
634 if ($self->{tags_by_order}) {
635 @sorted_tags = @{$self->{tags_by_order}};
636 } else {
637 @sorted_tags = sort { $self->_sort_by_order } keys %{$self->{'import_xml'}->{'indexer'}};
638 $self->{tags_by_order} = \@sorted_tags;
639 }
640
641 my @ds;
642
643 $log->debug("tags: ",sub { join(", ",@sorted_tags) });
644
645 foreach my $field (@sorted_tags) {
646
647 my $row;
648
649 #print "field $field [",$self->{'tag'},"] = ",Dumper($self->{'import_xml'}->{'indexer'}->{$field}->{$self->{'tag'}});
650
651 foreach my $tag (@{$self->{'import_xml'}->{'indexer'}->{$field}->{$self->{'tag'}}}) {
652 my $format = $tag->{'value'} || $tag->{'content'};
653
654 $log->debug("format: $format");
655
656 my @v;
657 if ($format =~ /$LOOKUP_REGEX/o) {
658 @v = $self->fill_in_to_arr($rec,$format);
659 } else {
660 @v = $self->parse_to_arr($rec,$format);
661 }
662 next if (! @v);
663
664 # use format?
665 if ($tag->{'format_name'}) {
666 @v = map { $self->apply_format($tag->{'format_name'},$tag->{'format_delimiter'},$_) } @v;
667 }
668
669 # does tag have type?
670 if ($tag->{'type'}) {
671 push @{$row->{$tag->{'type'}}}, @v;
672 } else {
673 push @{$row->{'display'}}, @v;
674 push @{$row->{'swish'}}, @v;
675 }
676
677 if ($field eq 'filename') {
678 $self->{'current_filename'} = join('',@v);
679 $log->debug("filename: ",$self->{'current_filename'});
680 }
681
682 }
683
684 if ($row) {
685 $row->{'tag'} = $field;
686
687 # TODO: name_sigular, name_plural
688 my $name = $self->{'import_xml'}->{'indexer'}->{$field}->{'name'};
689 $row->{'name'} = $name ? $self->_x($name) : $field;
690
691 push @ds, $row;
692
693 $log->debug("row $field: ",sub { Dumper($row) });
694 }
695
696 }
697
698 return @ds;
699
700 }
701
702 =head2 output
703
704 Create output from in-memory data structure using Template Toolkit template.
705
706 my $text = $webpac->output( template => 'text.tt', data => @ds );
707
708 =cut
709
710 sub output {
711 my $self = shift;
712
713 my $args = {@_};
714
715 my $log = $self->_get_logger();
716
717 $log->logconfess("need template name") if (! $args->{'template'});
718 $log->logconfess("need data array") if (! $args->{'data'});
719
720 my $out;
721
722 $self->{'tt'}->process(
723 $args->{'template'},
724 $args,
725 \$out
726 ) || confess $self->{'tt'}->error();
727
728 return $out;
729 }
730
731 =head2 apply_format
732
733 Apply format specified in tag with C<format_name="name"> and
734 C<format_delimiter=";;">.
735
736 my $text = $webpac->apply_format($format_name,$format_delimiter,$data);
737
738 Formats can contain C<lookup{...}> if you need them.
739
740 =cut
741
742 sub apply_format {
743 my $self = shift;
744
745 my ($name,$delimiter,$data) = @_;
746
747 my $log = $self->_get_logger();
748
749 if (! $self->{'import_xml'}->{'format'}->{$name}) {
750 $log->warn("<format name=\"$name\"> is not defined in ",$self->{'import_xml_file'});
751 return $data;
752 }
753
754 $log->warn("no delimiter for format $name") if (! $delimiter);
755
756 my $format = $self->_x($self->{'import_xml'}->{'format'}->{$name}->{'content'}) || $log->logdie("can't find format '$name'");
757
758 my @data = split(/\Q$delimiter\E/, $data);
759
760 my $out = sprintf($format, @data);
761 $log->debug("using format $name [$format] on $data to produce: $out");
762
763 if ($out =~ m/$LOOKUP_REGEX/o) {
764 return $self->lookup($out);
765 } else {
766 return $out;
767 }
768
769 }
770
771
772 #
773 #
774 #
775
776 =head1 INTERNAL METHODS
777
778 Here is a quick list of internal methods, mostly useful to turn debugging
779 on them (see L<LOGGING> below for explanation).
780
781 =cut
782
783 =head2 _eval
784
785 Internal function to eval code without C<strict 'subs'>.
786
787 =cut
788
789 sub _eval {
790 my $self = shift;
791
792 my $code = shift || return;
793
794 my $log = $self->_get_logger();
795
796 no strict 'subs';
797 my $ret = eval $code;
798 if ($@) {
799 $log->error("problem with eval code [$code]: $@");
800 }
801
802 $log->debug("eval: ",$code," [",$ret,"]");
803
804 return $ret || 0;
805 }
806
807 =head2 _sort_by_order
808
809 Sort xml tags data structure accoding to C<order=""> attribute.
810
811 =cut
812
813 sub _sort_by_order {
814 my $self = shift;
815
816 my $va = $self->{'import_xml'}->{'indexer'}->{$a}->{'order'} ||
817 $self->{'import_xml'}->{'indexer'}->{$a};
818 my $vb = $self->{'import_xml'}->{'indexer'}->{$b}->{'order'} ||
819 $self->{'import_xml'}->{'indexer'}->{$b};
820
821 return $va <=> $vb;
822 }
823
824 =head2 _get_logger
825
826 Get C<Log::Log4perl> object with a twist: domains are defined for each
827 method
828
829 my $log = $webpac->_get_logger();
830
831 =cut
832
833 sub _get_logger {
834 my $self = shift;
835
836 my $name = (caller(1))[3] || caller;
837 return get_logger($name);
838 }
839
840 =head2 _x
841
842 Convert string from UTF-8 to code page defined in C<import_xml>.
843
844 my $text = $webpac->_x('utf8 text');
845
846 =cut
847
848 sub _x {
849 my $self = shift;
850 my $utf8 = shift || return;
851
852 return $self->{'utf2cp'}->convert($utf8) ||
853 $self->_get_logger()->logwarn("can't convert '$utf8'");
854 }
855
856 #
857 #
858 #
859
860 =head1 LOGGING
861
862 Logging in WebPAC is performed by L<Log::Log4perl> with config file
863 C<log.conf>.
864
865 Methods defined above have different levels of logging, so
866 it's descriptions will be useful to turn (mostry B<debug> logging) on
867 or off to see why WabPAC isn't perforing as you expect it (it might even
868 be a bug!).
869
870 B<This is different from normal Log4perl behaviour>. To repeat, you can
871 also use method names, and not only classes (which are just few)
872 to filter logging.
873
874 =cut
875
876 1;

  ViewVC Help
Powered by ViewVC 1.1.26