Cleaning up some unessecary my statements
[koha.git] / C4 / AuthoritiesMarc.pm
1 package C4::AuthoritiesMarc;
2 # Copyright 2000-2002 Katipo Communications
3 #
4 # This file is part of Koha.
5 #
6 # Koha is free software; you can redistribute it and/or modify it under the
7 # terms of the GNU General Public License as published by the Free Software
8 # Foundation; either version 2 of the License, or (at your option) any later
9 # version.
10 #
11 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
12 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License along with
16 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
17 # Suite 330, Boston, MA  02111-1307 USA
18
19 use strict;
20 require Exporter;
21 use C4::Context;
22 use C4::Database;
23 use C4::Koha;
24 use MARC::Record;
25 use C4::Biblio;
26
27 use vars qw($VERSION @ISA @EXPORT);
28
29 # set the version for version checking
30 $VERSION = 0.01;
31
32 @ISA = qw(Exporter);
33 @EXPORT = qw(
34         &AUTHgettagslib
35         &AUTHfindsubfield
36         &AUTHfind_authtypecode
37
38         &AUTHaddauthority
39         &AUTHmodauthority
40         &AUTHdelauthority
41         &AUTHaddsubfield
42         &AUTHgetauthority
43         
44         &AUTHgetauth_type
45         &AUTHcount_usage
46         
47         &authoritysearch
48         
49         &MARCmodsubfield
50         &AUTHhtml2marc
51         &AUTHaddword
52         &MARCaddword &MARCdelword
53         &char_decode
54         &FindDuplicate
55  );
56
57 sub authoritysearch {
58         my ($dbh, $tags, $and_or, $excluding, $operator, $value, $offset,$length,$authtypecode) = @_;
59         # build the sql request. She will look like :
60         # select m1.bibid
61         #               from auth_subfield_table as m1, auth_subfield_table as m2
62         #               where m1.authid=m2.authid and
63         #               (m1.subfieldvalue like "Des%" and m2.subfieldvalue like "27%")
64
65         # the marclist may contain "mainentry". In this case, search the tag_to_report, that depends on
66         # the authtypecode. Then, search on $a of this tag_to_report
67         # also store main entry MARC tag, to extract it at end of search
68         my $mainentrytag;
69         my $sth = $dbh->prepare("select auth_tag_to_report from auth_types where authtypecode=?");
70         $sth->execute($authtypecode);
71         my ($tag_to_report) = $sth->fetchrow;
72         $mainentrytag = $tag_to_report;
73         for (my $i=0;$i<$#{$tags};$i++) {
74                 if (@$tags[$i] eq "mainentry") {
75                         @$tags[$i] = $tag_to_report."a";
76                 }
77         }
78
79         # "Normal" statements
80         # quote marc fields/subfields
81         for (my $i=0;$i<=$#{$tags};$i++) {
82                 if (@$tags[$i]) {
83                         @$tags[$i] = $dbh->quote(@$tags[$i]);
84                 }
85         }
86         my @normal_tags = ();
87         my @normal_and_or = ();
88         my @normal_operator = ();
89         my @normal_value = ();
90         # Extracts the NOT statements from the list of statements
91         for(my $i = 0 ; $i <= $#{$value} ; $i++)
92         {
93                 # replace * by %
94                 @$value[$i] =~ s/\*/%/g;
95                 # remove % at the beginning
96                 @$value[$i] =~ s/^%//g;
97             @$value[$i] =~ s/(\.|\?|\:|\!|\'|,|\-|\"|\(|\)|\[|\]|\{|\}|\/)/ /g if @$operator[$i] eq "contains";
98                 if(@$operator[$i] eq "contains") # if operator is contains, splits the words in separate requests
99                 {
100                         foreach my $word (split(/ /, @$value[$i]))
101                         {
102                                 unless (C4::Context->stopwords->{uc($word)}) {  #it's NOT a stopword => use it. Otherwise, ignore
103                                         my $tag = substr(@$tags[$i],0,3);
104                                         my $subf = substr(@$tags[$i],3,1);
105                                         push @normal_tags, @$tags[$i];
106                                         push @normal_and_or, "and";     # assumes "foo" and "bar" if "foo bar" is entered
107                                         push @normal_operator, @$operator[$i];
108                                         push @normal_value, $word;
109                                 }
110                         }
111                 }
112                 else
113                 {
114                         push @normal_tags, @$tags[$i];
115                         push @normal_and_or, @$and_or[$i];
116                         push @normal_operator, @$operator[$i];
117                         push @normal_value, @$value[$i];
118                 }
119         }
120
121         # Finds the basic results without the NOT requests
122         my ($sql_tables, $sql_where1, $sql_where2) = create_request($dbh,\@normal_tags, \@normal_and_or, \@normal_operator, \@normal_value);
123
124
125
126         if ($sql_where2) {
127                 $sth = $dbh->prepare("select distinct m1.authid from auth_header,$sql_tables where  m1.authid=auth_header.authid and auth_header.authtypecode=? and $sql_where2 and ($sql_where1)");
128                 warn "Q2 : select distinct m1.authid from auth_header,$sql_tables where  m1.authid=auth_header.authid and auth_header.authtypecode=? and $sql_where2 and ($sql_where1)";
129         } else {
130                 $sth = $dbh->prepare("select distinct m1.authid from auth_header,$sql_tables where  m1.authid=auth_header.authid and auth_header.authtypecode=? and $sql_where1");
131                 warn "Q : select distinct m1.authid from auth_header,$sql_tables where  m1.authid=auth_header.authid and auth_header.authtypecode=? and $sql_where1";
132         }
133         $sth->execute($authtypecode);
134         my @result = ();
135         while (my ($authid) = $sth->fetchrow) {
136                         push @result,$authid;
137                 }
138         # we have authid list. Now, loads summary from [offset] to [offset]+[length]
139 #       my $counter = $offset;
140         my @finalresult = ();
141         my $oldline;
142 #       while (($counter <= $#result) && ($counter <= ($offset + $length))) {
143         # retrieve everything
144         for (my $counter=0;$counter <=$#result;$counter++) {
145 #               warn " HERE : $counter, $#result, $offset, $length";
146                 # get MARC::Record of the authority
147                 my $record = AUTHgetauthority($dbh,$result[$counter]);
148                 # then build the summary
149                 my $authtypecode = AUTHfind_authtypecode($dbh,$result[$counter]);
150                 my $authref = getauthtype($authtypecode);
151                 my $summary = $authref->{summary};
152                 my @fields = $record->fields();
153                 foreach my $field (@fields) {
154                         my $tag = $field->tag();
155                         if ($tag<10) {
156                         } else {
157                                 my @subf = $field->subfields;
158                                 for my $i (0..$#subf) {
159                                         my $subfieldcode = $subf[$i][0];
160                                         my $subfieldvalue = $subf[$i][1];
161                                         my $tagsubf = $tag.$subfieldcode;
162                                         $summary =~ s/\[(.?.?.?.?)$tagsubf(.*?)]/$1$subfieldvalue$2\[$1$tagsubf$2]/g;
163                                 }
164                         }
165                 }
166                 $summary =~ s/\[(.*?)]//g;
167                 $summary =~ s/\n/<br>/g;
168
169                 # find biblio MARC field using this authtypecode (to jump to biblio)
170                 $authtypecode = AUTHfind_authtypecode($dbh,$result[$counter]);
171                 my $sth = $dbh->prepare("select distinct tagfield from marc_subfield_structure where authtypecode=?");
172                 $sth->execute($authtypecode);
173                 my $tags_using_authtype;
174                 while (my ($tagfield) = $sth->fetchrow) {
175 #                       warn "TAG : $tagfield";
176                         $tags_using_authtype.= $tagfield."9,";
177                 }
178                 chop $tags_using_authtype;
179                 
180                 # then add a line for the template loop
181                 my %newline;
182                 $newline{summary} = $summary;
183                 $newline{authid} = $result[$counter];
184                 $newline{used} = &AUTHcount_usage($result[$counter]);
185                 $newline{biblio_fields} = $tags_using_authtype;
186                 $newline{even} = $counter % 2;
187                 $newline{mainentry} = $record->field($mainentrytag)->subfield('a')." ".$record->field($mainentrytag)->subfield('b') if $record->field($mainentrytag);
188                 push @finalresult, \%newline;
189         }
190         # sort everything
191         my @finalresult3= sort {$a->{summary} cmp $b->{summary}} @finalresult;
192         # cut from $offset to $offset+$length;
193         my @finalresult2;
194         for (my $i=$offset;$i<=$offset+$length;$i++) {
195                 push @finalresult2,$finalresult3[$i] if $finalresult3[$i];
196         }
197         my $nbresults = $#result + 1;
198
199         return (\@finalresult2, $nbresults);
200 }
201
202 # Creates the SQL Request
203
204 sub create_request {
205         my ($dbh,$tags, $and_or, $operator, $value) = @_;
206
207         my $sql_tables; # will contain marc_subfield_table as m1,...
208         my $sql_where1; # will contain the "true" where
209         my $sql_where2 = "("; # will contain m1.authid=m2.authid
210         my $nb_active=0; # will contain the number of "active" entries. and entry is active is a value is provided.
211         my $nb_table=1; # will contain the number of table. ++ on each entry EXCEPT when an OR  is provided.
212
213
214         for(my $i=0; $i<=@$value;$i++) {
215                 if (@$value[$i]) {
216                         $nb_active++;
217 #                       warn " @$tags[$i]";
218                         if ($nb_active==1) {
219                                 if (@$operator[$i] eq "start") {
220                                         $sql_tables .= "auth_subfield_table as m$nb_table,";
221                                         $sql_where1 .= "(m1.subfieldvalue like ".$dbh->quote("@$value[$i]%");
222                                         if (@$tags[$i]) {
223                                                 $sql_where1 .=" and m1.tag+m1.subfieldcode in (@$tags[$i])";
224                                         }
225                                         $sql_where1.=")";
226                                 } elsif (@$operator[$i] eq "contains") {        
227                                 $sql_tables .= "auth_word as m$nb_table,";
228                                         $sql_where1 .= "(m1.word  like ".$dbh->quote("@$value[$i]%");
229                                         if (@$tags[$i]) {
230                                                  $sql_where1 .=" and m1.tagsubfield in (@$tags[$i])";
231                                         }
232                                         $sql_where1.=")";
233                                 } else {
234
235                                         $sql_tables .= "auth_subfield_table as m$nb_table,";
236                                         $sql_where1 .= "(m1.subfieldvalue @$operator[$i] ".$dbh->quote("@$value[$i]");
237                                         if (@$tags[$i]) {
238                                                  $sql_where1 .=" and m1.tag+m1.subfieldcode in (@$tags[$i])";
239                                         }
240                                         $sql_where1.=")";
241                                 }
242                         } else {
243                                 if (@$operator[$i] eq "start") {
244                                         $nb_table++;
245                                         $sql_tables .= "auth_subfield_table as m$nb_table,";
246                                         $sql_where1 .= "@$and_or[$i] (m$nb_table.subfieldvalue like ".$dbh->quote("@$value[$i]%");
247                                         if (@$tags[$i]) {
248                                                 $sql_where1 .=" and m$nb_table.tag+m$nb_table.subfieldcode in (@$tags[$i])";
249                                         }
250                                         $sql_where1.=")";
251                                         $sql_where2 .= "m1.authid=m$nb_table.authid and ";
252                                 } elsif (@$operator[$i] eq "contains") {
253                                         if (@$and_or[$i] eq 'and') {
254                                                 $nb_table++;
255                                                 $sql_tables .= "auth_word as m$nb_table,";
256                                                 $sql_where1 .= "@$and_or[$i] (m$nb_table.word like ".$dbh->quote("@$value[$i]%");
257                                                 if (@$tags[$i]) {
258                                                         $sql_where1 .=" and m$nb_table.tagsubfield in(@$tags[$i])";
259                                                 }
260                                                 $sql_where1.=")";
261                                                 $sql_where2 .= "m1.authid=m$nb_table.authid and ";
262                                         } else {
263                                                 $sql_where1 .= "@$and_or[$i] (m$nb_table.word like ".$dbh->quote("@$value[$i]%");
264                                                 if (@$tags[$i]) {
265                                                         $sql_where1 .="  and m$nb_table.tag+m$nb_table.subfieldid in (@$tags[$i])";
266                                                 }
267                                                 $sql_where1.=")";
268                                                 $sql_where2 .= "m1.authid=m$nb_table.authid and ";
269                                         }
270                                 } else {
271                                         $nb_table++;
272                                         $sql_tables .= "auth_subfield_table as m$nb_table,";
273                                         $sql_where1 .= "@$and_or[$i] (m$nb_table.subfieldvalue @$operator[$i] ".$dbh->quote(@$value[$i]);
274                                         if (@$tags[$i]) {
275                                                 $sql_where1 .="  and m$nb_table.tag+m$nb_table.subfieldcode in (@$tags[$i])";
276                                         }
277                                         $sql_where2 .= "m1.authid=m$nb_table.authid and ";
278                                         $sql_where1.=")";
279                                 }
280                         }
281                 }
282         }
283
284         if($sql_where2 ne "(")  # some datas added to sql_where2, processing
285         {
286                 $sql_where2 = substr($sql_where2, 0, (length($sql_where2)-5)); # deletes the trailing ' and '
287                 $sql_where2 .= ")";
288         }
289         else    # no sql_where2 statement, deleting '('
290         {
291                 $sql_where2 = "";
292         }
293         chop $sql_tables;       # deletes the trailing ','
294         
295         return ($sql_tables, $sql_where1, $sql_where2);
296 }
297
298
299 sub AUTHcount_usage {
300         my ($authid) = @_;
301         my $dbh = C4::Context->dbh;
302         # find MARC fields using this authtype
303         my $authtypecode = AUTHfind_authtypecode($dbh,$authid);
304         my $sth = $dbh->prepare("select distinct tagfield from marc_subfield_structure where authtypecode=?");
305         $sth->execute($authtypecode);
306         my $tags_using_authtype;
307         while (my ($tagfield) = $sth->fetchrow) {
308 #               warn "TAG : $tagfield";
309                 $tags_using_authtype.= "'".$tagfield."9',";
310         }
311         chop $tags_using_authtype;
312         if ($tags_using_authtype) {
313                 $sth = $dbh->prepare("select count(*) from marc_subfield_table where concat(tag,subfieldcode) in ($tags_using_authtype) and subfieldvalue=?");
314 #       } else {
315 #               $sth = $dbh->prepare("select count(*) from marc_subfield_table where subfieldvalue=?");
316         }
317 #       warn "Q : select count(*) from marc_subfield_table where concat(tag,subfieldcode) in ($tags_using_authtype) and subfieldvalue=$authid";
318         $sth->execute($authid);
319         my ($result) = $sth->fetchrow;
320 #       warn "Authority $authid TOTAL USED : $result";
321         return $result;
322 }
323
324 # merging 2 authority entries. After a merge, the "from" can be deleted.
325 # sub AUTHmerge {
326 #       my ($auth_merge_from,$auth_merge_to) = @_;
327 #       my $dbh = C4::Context->dbh;
328 #       # find MARC fields using this authtype
329 #       my $authtypecode = AUTHfind_authtypecode($dbh,$authid);
330 #       # retrieve records
331 #       my $record_from = AUTHgetauthority($dbh,$auth_merge_from);
332 #       my $record_to = AUTHgetauthority($dbh,$auth_merge_to);
333 #       my $sth = $dbh->prepare("select distinct tagfield from marc_subfield_structure where authtypecode=?");
334 #       $sth->execute($authtypecode);
335 #       my $tags_using_authtype;
336 #       while (my ($tagfield) = $sth->fetchrow) {
337 #               warn "TAG : $tagfield";
338 #               $tags_using_authtype.= "'".$tagfield."9',";
339 #       }
340 #       chop $tags_using_authtype;
341 #       # now, find every biblio using this authority
342 #       $sth = $dbh->prepare("select bibid,tag,tag_indicator,tagorder from marc_subfield_table where tag+subfieldid in ($tags_using_authtype) and subfieldvalue=?");
343 #       $sth->execute($authid);
344 #       # and delete entries before recreating them
345 #       while (my ($bibid,$tag,$tag_indicator,$tagorder) = $sth->fetchrow) {
346 #               &MARCdelsubfield($dbh,$bibid,$tag);
347 #               
348 #       }
349
350 # }
351
352 sub AUTHfind_authtypecode {
353         my ($dbh,$authid) = @_;
354         my $sth = $dbh->prepare("select authtypecode from auth_header where authid=?");
355         $sth->execute($authid);
356         my ($authtypecode) = $sth->fetchrow;
357         return $authtypecode;
358 }
359  
360
361 sub AUTHgettagslib {
362         my ($dbh,$forlibrarian,$authtypecode)= @_;
363         $authtypecode="" unless $authtypecode;
364         my $sth;
365         my $libfield = ($forlibrarian eq 1)? 'liblibrarian' : 'libopac';
366         # check that framework exists
367         $sth=$dbh->prepare("select count(*) from auth_tag_structure where authtypecode=?");
368         $sth->execute($authtypecode);
369         my ($total) = $sth->fetchrow;
370         $authtypecode="" unless ($total >0);
371         $sth=$dbh->prepare("select tagfield,$libfield as lib,mandatory,repeatable from auth_tag_structure where authtypecode=? order by tagfield");
372         $sth->execute($authtypecode);
373         my ($lib,$tag,$res,$tab,$mandatory,$repeatable);
374         while ( ($tag,$lib,$mandatory,$repeatable) = $sth->fetchrow) {
375                 $res->{$tag}->{lib}=$lib;
376                 $res->{$tab}->{tab}=""; # XXX
377                 $res->{$tag}->{mandatory}=$mandatory;
378                 $res->{$tag}->{repeatable}=$repeatable;
379         }
380
381         $sth=$dbh->prepare("select tagfield,tagsubfield,$libfield as lib,tab, mandatory, repeatable,authorised_value,value_builder,seealso from auth_subfield_structure where authtypecode=? order by tagfield,tagsubfield");
382         $sth->execute($authtypecode);
383
384         my $subfield;
385         my $authorised_value;
386         my $thesaurus_category;
387         my $value_builder;
388         my $kohafield;
389         my $seealso;
390         my $hidden;
391         my $isurl;
392         while ( ($tag, $subfield, $lib, $tab, $mandatory, $repeatable,$authorised_value,$value_builder,$seealso) = $sth->fetchrow) {
393                 $res->{$tag}->{$subfield}->{lib}=$lib;
394                 $res->{$tag}->{$subfield}->{tab}=$tab;
395                 $res->{$tag}->{$subfield}->{mandatory}=$mandatory;
396                 $res->{$tag}->{$subfield}->{repeatable}=$repeatable;
397                 $res->{$tag}->{$subfield}->{authorised_value}=$authorised_value;
398                 $res->{$tag}->{$subfield}->{thesaurus_category}=$thesaurus_category;
399                 $res->{$tag}->{$subfield}->{value_builder}=$value_builder;
400                 $res->{$tag}->{$subfield}->{seealso}=$seealso;
401                 $res->{$tag}->{$subfield}->{hidden}=$hidden;
402                 $res->{$tag}->{$subfield}->{isurl}=$isurl;
403         }
404         return $res;
405 }
406
407 sub AUTHaddauthority {
408 # pass the MARC::Record to this function, and it will create the records in the marc tables
409         my ($dbh,$record,$authid,$authtypecode) = @_;
410         my @fields=$record->fields();
411 # adding main table, and retrieving authid
412 # if authid is sent, then it's not a true add, it's only a re-add, after a delete (ie, a mod)
413 # if authid empty => true add, find a new authid number
414         unless ($authid) {
415                 $dbh->do("lock tables auth_header WRITE,auth_subfield_table WRITE, auth_word WRITE, stopwords READ");
416                 my $sth=$dbh->prepare("insert into auth_header (datecreated,authtypecode) values (now(),?)");
417                 $sth->execute($authtypecode);
418                 $sth=$dbh->prepare("select max(authid) from auth_header");
419                 $sth->execute;
420                 ($authid)=$sth->fetchrow;
421                 $sth->finish;
422         }
423         my $fieldcount=0;
424         # now, add subfields...
425         foreach my $field (@fields) {
426                 $fieldcount++;
427                 if ($field->tag() <10) {
428                                 &AUTHaddsubfield($dbh,$authid,
429                                                 $field->tag(),
430                                                 '',
431                                                 $fieldcount,
432                                                 '',
433                                                 1,
434                                                 $field->data()
435                                                 );
436                 } else {
437                         my @subfields=$field->subfields();
438                         my $subfieldorder;
439                         foreach my $subfield (@subfields) {
440                                 foreach (split /\|/,@$subfield[1]) {
441                                         $subfieldorder++;
442                                         &AUTHaddsubfield($dbh,$authid,
443                                                         $field->tag(),
444                                                         $field->indicator(1).$field->indicator(2),
445                                                         $fieldcount,
446                                                         @$subfield[0],
447                                                         $subfieldorder,
448                                                         $_
449                                                         );
450                                 }
451                         }
452                 }
453         }
454         $dbh->do("unlock tables");
455         return $authid;
456 }
457
458
459 sub AUTHaddsubfield {
460 # Add a new subfield to a tag into the DB.
461         my ($dbh,$authid,$tagid,$tag_indicator,$tagorder,$subfieldcode,$subfieldorder,$subfieldvalues) = @_;
462         # if not value, end of job, we do nothing
463         if (length($subfieldvalues) ==0) {
464                 return;
465         }
466         if (not($subfieldcode)) {
467                 $subfieldcode=' ';
468         }
469         my @subfieldvalues = split /\|/,$subfieldvalues;
470         foreach my $subfieldvalue (@subfieldvalues) {
471                 my $sth=$dbh->prepare("insert into auth_subfield_table (authid,tag,tagorder,tag_indicator,subfieldcode,subfieldorder,subfieldvalue) values (?,?,?,?,?,?,?)");
472 #               warn "==> $authid,".(sprintf "%03s",$tagid).",TAG : $tagorder,$tag_indicator,$subfieldcode,$subfieldorder,$subfieldvalue";
473                 $sth->execute($authid,(sprintf "%03s",$tagid),$tagorder,$tag_indicator,$subfieldcode,$subfieldorder,$subfieldvalue);
474                 if ($sth->errstr) {
475                         warn "ERROR ==> insert into auth_subfield_table (authid,tag,tagorder,tag_indicator,subfieldcode,subfieldorder,subfieldvalue) values ($authid,$tagid,$tagorder,$tag_indicator,$subfieldcode,$subfieldorder,$subfieldvalue)\n";
476                 }
477                 &AUTHaddword($dbh,$authid,$tagid,$tagorder,$subfieldcode,$subfieldorder,$subfieldvalue);
478         }
479 }
480
481 sub AUTHgetauthority {
482 # Returns MARC::Record of the biblio passed in parameter.
483     my ($dbh,$authid)=@_;
484     my $record = MARC::Record->new();
485 #---- TODO : the leader is missing
486         $record->leader('                        ');
487     my $sth=$dbh->prepare("select authid,subfieldid,tag,tagorder,tag_indicator,subfieldcode,subfieldorder,subfieldvalue
488                                  from auth_subfield_table
489                                  where authid=? order by tag,tagorder,subfieldorder
490                          ");
491         $sth->execute($authid);
492         my $prevtagorder=1;
493         my $prevtag='XXX';
494         my $previndicator;
495         my $field; # for >=10 tags
496         my $prevvalue; # for <10 tags
497         while (my $row=$sth->fetchrow_hashref) {
498                 if ($row->{tagorder} ne $prevtagorder || $row->{tag} ne $prevtag) {
499                         $previndicator.="  ";
500                         if ($prevtag <10) {
501                         $record->add_fields((sprintf "%03s",$prevtag),$prevvalue) unless $prevtag eq "XXX"; # ignore the 1st loop
502                         } else {
503                                 $record->add_fields($field) unless $prevtag eq "XXX";
504                         }
505                         undef $field;
506                         $prevtagorder=$row->{tagorder};
507                         $prevtag = $row->{tag};
508                         $previndicator=$row->{tag_indicator};
509                         if ($row->{tag}<10) {
510                                 $prevvalue = $row->{subfieldvalue};
511                         } else {
512                                 $field = MARC::Field->new((sprintf "%03s",$prevtag), substr($row->{tag_indicator}.'  ',0,1), substr($row->{tag_indicator}.'  ',1,1), $row->{'subfieldcode'}, $row->{'subfieldvalue'} );
513                         }
514                 } else {
515                         if ($row->{tag} <10) {
516                                 $record->add_fields((sprintf "%03s",$row->{tag}), $row->{'subfieldvalue'});
517                         } else {
518                                 $field->add_subfields($row->{'subfieldcode'}, $row->{'subfieldvalue'} );
519                         }
520                         $prevtag= $row->{tag};
521                         $previndicator=$row->{tag_indicator};
522                 }
523         }
524         # the last has not been included inside the loop... do it now !
525         if ($prevtag ne "XXX") { # check that we have found something. Otherwise, prevtag is still XXX and we
526                                                 # must return an empty record, not make MARC::Record fail because we try to
527                                                 # create a record with XXX as field :-(
528                 if ($prevtag <10) {
529                         $record->add_fields($prevtag,$prevvalue);
530                 } else {
531         #               my $field = MARC::Field->new( $prevtag, "", "", %subfieldlist);
532                         $record->add_fields($field);
533                 }
534         }
535         return $record;
536 }
537
538 sub AUTHgetauth_type {
539         my ($authtypecode) = @_;
540         my $dbh=C4::Context->dbh;
541         my $sth=$dbh->prepare("select * from auth_types where authtypecode=?");
542         $sth->execute($authtypecode);
543         return $sth->fetchrow_hashref;
544 }
545 sub AUTHmodauthority {
546         my ($dbh,$authid,$record,$delete)=@_;
547         my $oldrecord=&AUTHgetauthority($dbh,$authid);
548         if ($oldrecord eq $record) {
549                 return;
550         }
551 # 1st delete the authority,
552 # 2nd recreate it
553         &AUTHdelauthority($dbh,$authid,1);
554         &AUTHaddauthority($dbh,$record,$authid,AUTHfind_authtypecode($dbh,$authid));
555         # save the file in localfile/modified_authorities
556         my $cgidir = C4::Context->intranetdir ."/cgi-bin";
557         unless (opendir(DIR, "$cgidir")) {
558                         $cgidir = C4::Context->intranetdir."/";
559         } 
560
561         my $filename = $cgidir."/localfile/modified_authorities/$authid.authid";
562         open AUTH, "> $filename";
563         print AUTH $authid;
564         close AUTH;
565 }
566
567 sub AUTHdelauthority {
568         my ($dbh,$authid,$keep_biblio) = @_;
569 # if the keep_biblio is set to 1, then authority entries in biblio are preserved.
570 # This flag is set when the delauthority is called by modauthority
571 # due to a too complex structure of MARC (repeatable fields and subfields),
572 # the best solution for a modif is to delete / recreate the record.
573
574         my $record = AUTHgetauthority($dbh,$authid);
575         $dbh->do("delete from auth_header where authid=$authid") unless $keep_biblio;
576         $dbh->do("delete from auth_subfield_table where authid=$authid");
577         $dbh->do("delete from auth_word where authid=$authid");
578 # FIXME : delete or not in biblio tables (depending on $keep_biblio flag)
579 }
580
581 sub AUTHmodsubfield {
582 # Subroutine changes a subfield value given a subfieldid.
583         my ($dbh, $subfieldid, $subfieldvalue )=@_;
584         $dbh->do("lock tables auth_subfield_table WRITE");
585         my $sth=$dbh->prepare("update auth_subfield_table set subfieldvalue=? where subfieldid=?");
586         $sth->execute($subfieldvalue, $subfieldid);
587         $dbh->do("unlock tables");
588         $sth->finish;
589         $sth=$dbh->prepare("select authid,tag,tagorder,subfieldcode,subfieldid,subfieldorder from auth_subfield_table where subfieldid=?");
590         $sth->execute($subfieldid);
591         my ($authid,$tagid,$tagorder,$subfieldcode,$x,$subfieldorder) = $sth->fetchrow;
592         $subfieldid=$x;
593         &AUTHdelword($dbh,$authid,$tagid,$tagorder,$subfieldcode,$subfieldorder);
594         &AUTHaddword($dbh,$authid,$tagid,$tagorder,$subfieldcode,$subfieldorder,$subfieldvalue);
595         return($subfieldid, $subfieldvalue);
596 }
597
598 sub AUTHfindsubfield {
599     my ($dbh,$authid,$tag,$subfieldcode,$subfieldorder,$subfieldvalue) = @_;
600     my $resultcounter=0;
601     my $subfieldid;
602     my $lastsubfieldid;
603     my $query="select subfieldid from auth_subfield_table where authid=? and tag=? and subfieldcode=?";
604     my @bind_values = ($authid,$tag, $subfieldcode);
605     if ($subfieldvalue) {
606         $query .= " and subfieldvalue=?";
607         push(@bind_values,$subfieldvalue);
608     } else {
609         if ($subfieldorder<1) {
610             $subfieldorder=1;
611         }
612         $query .= " and subfieldorder=?";
613         push(@bind_values,$subfieldorder);
614     }
615     my $sti=$dbh->prepare($query);
616     $sti->execute(@bind_values);
617     while (($subfieldid) = $sti->fetchrow) {
618         $resultcounter++;
619         $lastsubfieldid=$subfieldid;
620     }
621     if ($resultcounter>1) {
622                 # Error condition.  Values given did not resolve into a unique record.  Don't know what to edit
623                 # should rarely occur (only if we use subfieldvalue with a value that exists twice, which is strange)
624                 return -1;
625     } else {
626                 return $lastsubfieldid;
627     }
628 }
629
630 sub AUTHfindsubfieldid {
631         my ($dbh,$authid,$tag,$tagorder,$subfield,$subfieldorder) = @_;
632         my $sth=$dbh->prepare("select subfieldid from auth_subfield_table
633                                 where authid=? and tag=? and tagorder=?
634                                         and subfieldcode=? and subfieldorder=?");
635         $sth->execute($authid,$tag,$tagorder,$subfield,$subfieldorder);
636         my ($res) = $sth->fetchrow;
637         unless ($res) {
638                 $sth=$dbh->prepare("select subfieldid from auth_subfield_table
639                                 where authid=? and tag=? and tagorder=?
640                                         and subfieldcode=?");
641                 $sth->execute($authid,$tag,$tagorder,$subfield);
642                 ($res) = $sth->fetchrow;
643         }
644     return $res;
645 }
646
647 sub AUTHfind_authtypecode {
648         my ($dbh,$authid) = @_;
649         my $sth = $dbh->prepare("select authtypecode from auth_header where authid=?");
650         $sth->execute($authid);
651         my ($authtypecode) = $sth->fetchrow;
652         return $authtypecode;
653 }
654
655 sub AUTHdelsubfield {
656 # delete a subfield for $authid / tag / tagorder / subfield / subfieldorder
657     my ($dbh,$authid,$tag,$tagorder,$subfield,$subfieldorder) = @_;
658     $dbh->do("delete from auth_subfield_table where authid='$authid' and
659                         tag='$tag' and tagorder='$tagorder'
660                         and subfieldcode='$subfield' and subfieldorder='$subfieldorder'
661                         ");
662 }
663
664 sub AUTHhtml2marc {
665         my ($dbh,$rtags,$rsubfields,$rvalues,%indicators) = @_;
666         my $prevtag = -1;
667         my $record = MARC::Record->new();
668 #       my %subfieldlist=();
669         my $prevvalue; # if tag <10
670         my $field; # if tag >=10
671         for (my $i=0; $i< @$rtags; $i++) {
672                 # rebuild MARC::Record
673                 if (@$rtags[$i] ne $prevtag) {
674                         if ($prevtag < 10) {
675                                 if ($prevvalue) {
676                                         $record->add_fields((sprintf "%03s",$prevtag),$prevvalue);
677                                 }
678                         } else {
679                                 if ($field) {
680                                         $record->add_fields($field);
681                                 }
682                         }
683                         $indicators{@$rtags[$i]}.='  ';
684                         if (@$rtags[$i] <10) {
685                                 $prevvalue= @$rvalues[$i];
686                                 undef $field;
687                         } else {
688                                 undef $prevvalue;
689                                 $field = MARC::Field->new( (sprintf "%03s",@$rtags[$i]), substr($indicators{@$rtags[$i]},0,1),substr($indicators{@$rtags[$i]},1,1), @$rsubfields[$i] => @$rvalues[$i]);
690                         }
691                         $prevtag = @$rtags[$i];
692                 } else {
693                         if (@$rtags[$i] <10) {
694                                 $prevvalue=@$rvalues[$i];
695                         } else {
696                                 if (length(@$rvalues[$i])>0) {
697                                         $field->add_subfields(@$rsubfields[$i] => @$rvalues[$i]);
698                                 }
699                         }
700                         $prevtag= @$rtags[$i];
701                 }
702         }
703         # the last has not been included inside the loop... do it now !
704         $record->add_fields($field) if $field;
705         return $record;
706 }
707
708 sub AUTHaddword {
709 # split a subfield string and adds it into the word table.
710 # removes stopwords
711     my ($dbh,$authid,$tag,$tagorder,$subfieldid,$subfieldorder,$sentence) =@_;
712     $sentence =~ s/(\.|\?|\:|\!|\'|,|\-|\"|\(|\)|\[|\]|\{|\})/ /g;
713     my @words = split / /,$sentence;
714     my $stopwords= C4::Context->stopwords;
715     my $sth=$dbh->prepare("insert into auth_word (authid, tagsubfield, tagorder, subfieldorder, word, sndx_word)
716                         values (?,concat(?,?),?,?,?,soundex(?))");
717     foreach my $word (@words) {
718 # we record only words longer than 2 car and not in stopwords hash
719         if (length($word)>2 and !($stopwords->{uc($word)})) {
720             $sth->execute($authid,$tag,$subfieldid,$tagorder,$subfieldorder,$word,$word);
721             if ($sth->err()) {
722                 warn "ERROR ==> insert into auth_word (authid, tagsubfield, tagorder, subfieldorder, word, sndx_word) values ($authid,concat($tag,$subfieldid),$tagorder,$subfieldorder,$word,soundex($word))\n";
723             }
724         }
725     }
726 }
727
728 sub AUTHdelword {
729 # delete words. this sub deletes all the words from a sentence. a subfield modif is done by a delete then a add
730     my ($dbh,$authid,$tag,$tagorder,$subfield,$subfieldorder) = @_;
731     my $sth=$dbh->prepare("delete from auth_word where authid=? and tagsubfield=concat(?,?) and tagorder=? and subfieldorder=?");
732     $sth->execute($authid,$tag,$subfield,$tagorder,$subfieldorder);
733 }
734
735 sub char_decode {
736         # converts ISO 5426 coded string to ISO 8859-1
737         # sloppy code : should be improved in next issue
738         my ($string,$encoding) = @_ ;
739         $_ = $string ;
740 #       $encoding = C4::Context->preference("marcflavour") unless $encoding;
741         if ($encoding eq "UNIMARC") {
742                 s/\xe1/Æ/gm ;
743                 s/\xe2/Ð/gm ;
744                 s/\xe9/Ø/gm ;
745                 s/\xec/þ/gm ;
746                 s/\xf1/æ/gm ;
747                 s/\xf3/ð/gm ;
748                 s/\xf9/ø/gm ;
749                 s/\xfb/ß/gm ;
750                 s/\xc1\x61/à/gm ;
751                 s/\xc1\x65/è/gm ;
752                 s/\xc1\x69/ì/gm ;
753                 s/\xc1\x6f/ò/gm ;
754                 s/\xc1\x75/ù/gm ;
755                 s/\xc1\x41/À/gm ;
756                 s/\xc1\x45/È/gm ;
757                 s/\xc1\x49/Ì/gm ;
758                 s/\xc1\x4f/Ò/gm ;
759                 s/\xc1\x55/Ù/gm ;
760                 s/\xc2\x41/Á/gm ;
761                 s/\xc2\x45/É/gm ;
762                 s/\xc2\x49/Í/gm ;
763                 s/\xc2\x4f/Ó/gm ;
764                 s/\xc2\x55/Ú/gm ;
765                 s/\xc2\x59/Ý/gm ;
766                 s/\xc2\x61/á/gm ;
767                 s/\xc2\x65/é/gm ;
768                 s/\xc2\x69/í/gm ;
769                 s/\xc2\x6f/ó/gm ;
770                 s/\xc2\x75/ú/gm ;
771                 s/\xc2\x79/ý/gm ;
772                 s/\xc3\x41/Â/gm ;
773                 s/\xc3\x45/Ê/gm ;
774                 s/\xc3\x49/Î/gm ;
775                 s/\xc3\x4f/Ô/gm ;
776                 s/\xc3\x55/Û/gm ;
777                 s/\xc3\x61/â/gm ;
778                 s/\xc3\x65/ê/gm ;
779                 s/\xc3\x69/î/gm ;
780                 s/\xc3\x6f/ô/gm ;
781                 s/\xc3\x75/û/gm ;
782                 s/\xc4\x41/Ã/gm ;
783                 s/\xc4\x4e/Ñ/gm ;
784                 s/\xc4\x4f/Õ/gm ;
785                 s/\xc4\x61/ã/gm ;
786                 s/\xc4\x6e/ñ/gm ;
787                 s/\xc4\x6f/õ/gm ;
788                 s/\xc8\x45/Ë/gm ;
789                 s/\xc8\x49/Ï/gm ;
790                 s/\xc8\x65/ë/gm ;
791                 s/\xc8\x69/ï/gm ;
792                 s/\xc8\x76/ÿ/gm ;
793                 s/\xc9\x41/Ä/gm ;
794                 s/\xc9\x4f/Ö/gm ;
795                 s/\xc9\x55/Ü/gm ;
796                 s/\xc9\x61/ä/gm ;
797                 s/\xc9\x6f/ö/gm ;
798                 s/\xc9\x75/ü/gm ;
799                 s/\xca\x41/Å/gm ;
800                 s/\xca\x61/å/gm ;
801                 s/\xd0\x43/Ç/gm ;
802                 s/\xd0\x63/ç/gm ;
803                 # this handles non-sorting blocks (if implementation requires this)
804                 $string = nsb_clean($_) ;
805         } elsif ($encoding eq "USMARC" || $encoding eq "MARC21") {
806                 if(/[\xc1-\xff]/) {
807                         s/\xe1\x61/à/gm ;
808                         s/\xe1\x65/è/gm ;
809                         s/\xe1\x69/ì/gm ;
810                         s/\xe1\x6f/ò/gm ;
811                         s/\xe1\x75/ù/gm ;
812                         s/\xe1\x41/À/gm ;
813                         s/\xe1\x45/È/gm ;
814                         s/\xe1\x49/Ì/gm ;
815                         s/\xe1\x4f/Ò/gm ;
816                         s/\xe1\x55/Ù/gm ;
817                         s/\xe2\x41/Á/gm ;
818                         s/\xe2\x45/É/gm ;
819                         s/\xe2\x49/Í/gm ;
820                         s/\xe2\x4f/Ó/gm ;
821                         s/\xe2\x55/Ú/gm ;
822                         s/\xe2\x59/Ý/gm ;
823                         s/\xe2\x61/á/gm ;
824                         s/\xe2\x65/é/gm ;
825                         s/\xe2\x69/í/gm ;
826                         s/\xe2\x6f/ó/gm ;
827                         s/\xe2\x75/ú/gm ;
828                         s/\xe2\x79/ý/gm ;
829                         s/\xe3\x41/Â/gm ;
830                         s/\xe3\x45/Ê/gm ;
831                         s/\xe3\x49/Î/gm ;
832                         s/\xe3\x4f/Ô/gm ;
833                         s/\xe3\x55/Û/gm ;
834                         s/\xe3\x61/â/gm ;
835                         s/\xe3\x65/ê/gm ;
836                         s/\xe3\x69/î/gm ;
837                         s/\xe3\x6f/ô/gm ;
838                         s/\xe3\x75/û/gm ;
839                         s/\xe4\x41/Ã/gm ;
840                         s/\xe4\x4e/Ñ/gm ;
841                         s/\xe4\x4f/Õ/gm ;
842                         s/\xe4\x61/ã/gm ;
843                         s/\xe4\x6e/ñ/gm ;
844                         s/\xe4\x6f/õ/gm ;
845                         s/\xe8\x45/Ë/gm ;
846                         s/\xe8\x49/Ï/gm ;
847                         s/\xe8\x65/ë/gm ;
848                         s/\xe8\x69/ï/gm ;
849                         s/\xe8\x76/ÿ/gm ;
850                         s/\xe9\x41/Ä/gm ;
851                         s/\xe9\x4f/Ö/gm ;
852                         s/\xe9\x55/Ü/gm ;
853                         s/\xe9\x61/ä/gm ;
854                         s/\xe9\x6f/ö/gm ;
855                         s/\xe9\x75/ü/gm ;
856                         s/\xea\x41/Å/gm ;
857                         s/\xea\x61/å/gm ;
858                         # this handles non-sorting blocks (if implementation requires this)
859                         $string = nsb_clean($_) ;
860                 }
861         }
862         return($string) ;
863 }
864
865 sub nsb_clean {
866         my $NSB = '\x88' ;              # NSB : begin Non Sorting Block
867         my $NSE = '\x89' ;              # NSE : Non Sorting Block end
868         # handles non sorting blocks
869         my ($string) = @_ ;
870         $_ = $string ;
871         s/$NSB/(/gm ;
872         s/[ ]{0,1}$NSE/) /gm ;
873         $string = $_ ;
874         return($string) ;
875 }
876
877 sub FindDuplicate {
878         my ($record,$authtypecode)=@_;
879         warn "IN for ".$record->as_formatted;
880         my $dbh = C4::Context->dbh;
881
882 #       warn "".$record->as_formatted;
883         my $sth = $dbh->prepare("select auth_tag_to_report,summary from auth_types where authtypecode=?");
884         $sth->execute($authtypecode);
885         my ($auth_tag_to_report,$taglist) = $sth->fetchrow;
886         $sth->finish;
887         # build a request for authoritysearch
888         my (@tags, @and_or, @excluding, @operator, @value, $offset, $length);
889         # search on biblio.title
890 #       warn " tag a reporter : $auth_tag_to_report";
891 #       warn "taglist ".$taglist;
892         my @subfield = split /\[/,  $taglist;
893         my $max = @subfield;
894         for (my $i=1; $i<$max;$i++){
895                 warn " ".$subfield[$i];
896                 $subfield[$i]=substr($subfield[$i],3,1);
897 #               warn " ".$subfield[$i];
898         }
899         
900         if ($record->fields($auth_tag_to_report)) {
901                 my $sth = $dbh->prepare("select tagfield,tagsubfield from auth_subfield_structure where tagfield=? and authtypecode=? ");
902                 $sth->execute($auth_tag_to_report,$authtypecode);
903 #               warn " field $auth_tag_to_report exists";
904                 while (my ($tag,$subfield) = $sth->fetchrow){
905                         if ($record->field($tag)->subfield($subfield)) {
906                                 warn "tag :".$tag." subfield: $subfield value : ".$record->field($tag)->subfield($subfield);
907                                 push @tags, $tag.$subfield;
908 #                               warn "'".$tag.$subfield."' value :". $record->field($tag)->subfield($subfield);
909                                 push @and_or, "and";
910                                 push @excluding, "";
911                                 push @operator, "=";
912                                 push @value, $record->field($tag)->subfield($subfield);
913                         }
914                 }
915         }
916  
917         my ($finalresult,$nbresult) = authoritysearch($dbh,\@tags,\@and_or,\@excluding,\@operator,\@value,0,10,$authtypecode);
918         # there is at least 1 result => return the 1st one
919         if ($nbresult) {
920                 warn "XXXXX $nbresult => ".@$finalresult[0]->{authid},@$finalresult[0]->{summary};
921                 return @$finalresult[0]->{authid},@$finalresult[0]->{summary};
922         }
923         # no result, returns nothing
924         return;
925 }
926
927
928 END { }       # module clean-up code here (global destructor)
929
930 =back
931
932 =head1 AUTHOR
933
934 Koha Developement team <info@koha.org>
935
936 Paul POULAIN paul.poulain@free.fr
937
938 =cut
939
940 # $Id$
941 # $Log$
942 # Revision 1.23  2006/02/09 01:52:14  rangi
943 # Cleaning up some unessecary my statements
944 #
945 # Revision 1.22  2006/01/06 16:39:37  tipaul
946 # synch'ing head and rel_2_2 (from 2.2.5, including npl templates)
947 # Seems not to break too many things, but i'm probably wrong here.
948 # at least, new features/bugfixes from 2.2.5 are here (tested on some features on my head local copy)
949 #
950 # - removing useless directories (koha-html and koha-plucene)
951 #
952 # Revision 1.21  2005/10/26 09:12:33  tipaul
953 # big commit, still breaking things...
954 #
955 # * synch with rel_2_2. Probably the last non manual synch, as rel_2_2 should not be modified deeply.
956 # * code cleaning (cleaning warnings from perl -w) continued
957 #
958 # Revision 1.9.2.8  2005/10/25 12:38:59  tipaul
959 # * fixing bug in summary (separator before subfield was in fact after)
960 # * fixing bug in authority order : authorities are not ordered alphabetically instead of no order. Requires all the dataset to be retrieved, but the benefits is important !
961 #
962 # Revision 1.9.2.7  2005/08/01 15:14:50  tipaul
963 # minor change in summary handling (accepting 4 digits before the field)
964 #
965 # Revision 1.9.2.6  2005/06/07 10:02:00  tipaul
966 # porting dictionnary search from head to 2.2. there is now a ... facing titles, author & subject, to search in biblio & authorities existing values.
967 #
968 # Revision 1.9.2.5  2005/05/31 14:50:46  tipaul
969 # fix for authority merging. There was a bug on official installs
970 #
971 # Revision 1.9.2.4  2005/05/30 11:24:15  tipaul
972 # fixing a bug : when a field was repeated, the last field was also repeated. (Was due to the "empty" field in html between fields : to separate fields, in html, an empty field is automatically added. in AUTHhtml2marc, this empty field was not discarded correctly)
973 #
974 # Revision 1.9.2.3  2005/04/28 08:45:33  tipaul
975 # porting FindDuplicate feature for authorities from HEAD to rel_2_2, works correctly now.
976 #
977 # Revision 1.9.2.2  2005/02/28 14:03:13  tipaul
978 # * adding search on "main entry" (ie $a subfield) on a given authority (the "search everywhere" field is still here).
979 # * adding a select box to requet "contain" or "begin with" search.
980 # * fixing some bug in authority search (related to "main entry" search)
981 #
982 # Revision 1.9.2.1  2005/02/24 13:12:13  tipaul
983 # saving authority modif in a text file. This will be used soon with another script (in crontab). The script in crontab will retrieve every authorityid in the directory localfile/authorities and modify every biblio using this authority. Those modifs may be long. So they can't be done through http, because we may encounter a webserver timeout, and kill the process before end of the job.
984 # So, it will be done through a cron job.
985 # (/me agree we need some doc for command line scripts)
986 #
987 # Revision 1.9  2004/12/23 09:48:11  tipaul
988 # Minor changes in summary "exploding" (the 3 digits AFTER the subfield were not on the right place).
989 #
990 # Revision 1.8  2004/11/05 10:11:39  tipaul
991 # export auth_count_usage (bugfix)
992 #
993 # Revision 1.7  2004/09/23 16:13:00  tipaul
994 # Bugfix in modification
995 #
996 # Revision 1.6  2004/08/18 16:00:24  tipaul
997 # fixes for authorities management
998 #
999 # Revision 1.5  2004/07/05 13:37:22  doxulting
1000 # First step for working authorities
1001 #
1002 # Revision 1.4  2004/06/22 11:35:37  tipaul
1003 # removing % at the beginning of a string to avoid loooonnnngggg searchs
1004 #
1005 # Revision 1.3  2004/06/17 08:02:13  tipaul
1006 # merging tag & subfield in auth_word for better perfs
1007 #
1008 # Revision 1.2  2004/06/10 08:29:01  tipaul
1009 # MARC authority management (continued)
1010 #
1011 # Revision 1.1  2004/06/07 07:35:01  tipaul
1012 # MARC authority management package
1013 #