synch'ing 2.2 and head
[koha.git] / updater / updatedatabase
1 #!/usr/bin/perl
2
3 # $Id$
4
5 # Database Updater
6 # This script checks for required updates to the database.
7
8 # Part of the Koha Library Software www.koha.org
9 # Licensed under the GPL.
10
11 # Bugs/ToDo:
12 # - Would also be a good idea to offer to do a backup at this time...
13
14 # NOTE:  If you do something more than once in here, make it table driven.
15 use strict;
16
17 # CPAN modules
18 use DBI;
19 use Getopt::Long;
20 # Koha modules
21 use C4::Context;
22
23 # FIXME - The user might be installing a new database, so can't rely
24 # on /etc/koha.conf anyway.
25
26 my $debug = 0;
27
28 my (
29     $sth, $sti,
30     $query,
31     %existingtables,    # tables already in database
32     %types,
33     $table,
34     $column,
35     $type, $null, $key, $default, $extra,
36     $prefitem,          # preference item in systempreferences table
37 );
38
39 my $silent;
40 GetOptions(
41         's' =>\$silent
42         );
43 my $dbh = C4::Context->dbh;
44 print "connected to your DB. Checking & modifying it\n" unless $silent;
45
46 #-------------------
47 # Defines
48
49 # Tables to add if they don't exist
50 my %requiretables = (
51     categorytable       => "(categorycode char(5) NOT NULL default '',
52                              description text default '',
53                              itemtypecodes text default '',
54                              PRIMARY KEY (categorycode)
55                             )",
56     subcategorytable       => "(subcategorycode char(5) NOT NULL default '',
57                              description text default '',
58                              itemtypecodes text default '',
59                              PRIMARY KEY (subcategorycode)
60                             )",
61     mediatypetable       => "(mediatypecode char(5) NOT NULL default '',
62                              description text default '',
63                              itemtypecodes text default '',
64                              PRIMARY KEY (mediatypecode)
65                             )",
66     action_logs         => "(
67                                     `timestamp` TIMESTAMP NOT NULL ,
68                                     `user` INT( 11 ) NOT NULL ,
69                                     `module` TEXT default '',
70                                     `action` TEXT default '' ,
71                                     `object` INT(11) default '' ,
72                                     `info` TEXT default '' ,
73                                     PRIMARY KEY ( `timestamp` , `user` )
74                             )",
75         letter          => "(
76                                         module varchar(20) NOT NULL default '',
77                                         code varchar(20) NOT NULL default '',
78                                         name varchar(100) NOT NULL default '',
79                                         title varchar(200) NOT NULL default '',
80                                         content text,
81                                         PRIMARY KEY  (module,code)
82                                 )",
83         alert           =>"(
84                                         alertid int(11) NOT NULL auto_increment,
85                                         borrowernumber int(11) NOT NULL default '0',
86                                         type varchar(10) NOT NULL default '',
87                                         externalid varchar(20) NOT NULL default '',
88                                         PRIMARY KEY  (alertid),
89                                         KEY borrowernumber (borrowernumber),
90                                         KEY type (type,externalid)
91                                 )"
92 );
93
94 my %requirefields = (
95         subscription => { 'letter' => 'char(20) NULL'},
96 #    tablename        => { 'field' => 'fieldtype' },
97 );
98
99 my %dropable_table = (
100 # tablename => 'tablename',
101 );
102
103 my %uselessfields = (
104 # tablename => "field1,field2",
105         );
106 # the other hash contains other actions that can't be done elsewhere. they are done
107 # either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
108
109 # The tabledata hash contains data that should be in the tables.
110 # The uniquefieldrequired hash entry is used to determine which (if any) fields
111 # must not exist in the table for this row to be inserted.  If the
112 # uniquefieldrequired entry is already in the table, the existing data is not
113 # modified, unless the forceupdate hash entry is also set.  Fields in the
114 # anonymous "forceupdate" hash will be forced to be updated to the default
115 # values given in the %tabledata hash.
116
117 my %tabledata = (
118 # tablename => [
119 #       {       uniquefielrequired => 'fieldname', # the primary key in the table
120 #               fieldname => fieldvalue,
121 #               fieldname2 => fieldvalue2,
122 #       },
123 # ],
124     systempreferences => [
125                 {
126             uniquefieldrequired => 'variable',
127             variable            => 'Activate_Log',
128             value               => 'On',
129             forceupdate         => { 'explanation' => 1,
130                                      'type' => 1},
131             explanation         => 'Turn Log Actions on DB On an Off',
132             type                => 'YesNo',
133         },
134                 {
135             uniquefieldrequired => 'variable',
136             variable            => 'ReturnBeforeExpiry',
137             value               => 'Off',
138             forceupdate         => { 'explanation' => 1,
139                                      'type' => 1},
140             explanation         => 'If Yes, Returndate on issuing can\'t be after borrower card expiry',
141             type                => 'YesNo',
142         },
143         {
144             uniquefieldrequired => 'variable',
145             variable            => 'opacstylesheet',
146             value               => '',
147             forceupdate         => { 'explanation' => 1,
148                                      'type' => 1},
149             explanation         => 'Enter a complete URL to use an alternate stylesheet in OPAC',
150             type                => 'free',
151         },
152         {
153             uniquefieldrequired => 'variable',
154             variable            => 'opacsmallimage',
155             value               => '',
156             forceupdate         => { 'explanation' => 1,
157                                      'type' => 1},
158             explanation         => 'Enter a complete URL to an image, will be on top/left instead of the Koha logo',
159             type                => 'free',
160         },
161         {
162             uniquefieldrequired => 'variable',
163             variable            => 'opaclargeimage',
164             value               => '',
165             forceupdate         => { 'explanation' => 1,
166                                      'type' => 1},
167             explanation         => 'Enter a complete URL to an image, will be on the main page, instead of the Koha logo',
168             type                => 'free',
169         },
170         {
171             uniquefieldrequired => 'variable',
172             variable            => 'delimiter',
173             value               => ';',
174             forceupdate         => { 'explanation' => 1,
175                                      'type' => 1},
176             explanation         => 'separator for reports exported to spreadsheet',
177             type                => 'free',
178         },
179         {
180             uniquefieldrequired => 'variable',
181             variable            => 'MIME',
182             value               => 'OPENOFFICE.ORG',
183             forceupdate         => { 'explanation' => 1,
184                                      'type' => 1,
185                                      'options' => 1},
186             explanation         => 'Define the default application for report exportations into files',
187                 type            => 'Choice',
188                 options         => 'EXCEL|OPENOFFICE.ORG'
189         },
190         {
191             uniquefieldrequired => 'variable',
192             variable            => 'Delimiter',
193             value               => ';',
194                 forceupdate             => { 'explanation' => 1,
195                                      'type' => 1,
196                                      'options' => 1},
197             explanation         => 'Define the default separator character for report exportations into files',
198                 type            => 'Choice',
199                 options         => ';|tabulation|,|/|\|#'
200         },
201         {
202             uniquefieldrequired => 'variable',
203             variable            => 'SubscriptionHistory',
204             value               => ';',
205                 forceupdate             => { 'explanation' => 1,
206                                      'type' => 1,
207                                      'options' => 1},
208             explanation         => 'Define the information level for serials history in OPAC',
209                 type            => 'Choice',
210                 options         => 'simplified|full'
211         },
212         {
213             uniquefieldrequired => 'variable',
214             variable            => 'hidelostitems',
215             value               => 'No',
216             forceupdate         => { 'explanation' => 1,
217                                      'type' => 1},
218             explanation         => 'show or hide "lost" items in OPAC.',
219             type                => 'YesNo',
220         },
221     ],
222
223 );
224
225 my %fielddefinitions = (
226 # fieldname => [
227 #       {                 field => 'fieldname',
228 #             type    => 'fieldtype',
229 #             null    => '',
230 #             key     => '',
231 #             default => ''
232 #         },
233 #     ],
234 );
235
236 #-------------------
237 # Initialize
238
239 # Start checking
240
241 # Get version of MySQL database engine.
242 my $mysqlversion = `mysqld --version`;
243 $mysqlversion =~ /Ver (\S*) /;
244 $mysqlversion = $1;
245 if ( $mysqlversion ge '3.23' ) {
246     print "Could convert to MyISAM database tables...\n" unless $silent;
247 }
248
249 #---------------------------------
250 # Tables
251
252 # Collect all tables into a list
253 $sth = $dbh->prepare("show tables");
254 $sth->execute;
255 while ( my ($table) = $sth->fetchrow ) {
256     $existingtables{$table} = 1;
257 }
258
259
260 # Now add any missing tables
261 foreach $table ( keys %requiretables ) {
262     unless ( $existingtables{$table} ) {
263         print "Adding $table table...\n" unless $silent;
264         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
265         $sth->execute;
266         if ( $sth->err ) {
267             print "Error : $sth->errstr \n";
268             $sth->finish;
269         }    # if error
270     }    # unless exists
271 }    # foreach
272
273 # now drop useless tables
274 foreach $table ( keys %dropable_table ) {
275         if ( $existingtables{$table} ) {
276                 print "Dropping unused table $table\n" if $debug and not $silent;
277                 $dbh->do("drop table $table");
278                 if ( $dbh->err ) {
279                         print "Error : $dbh->errstr \n";
280                 }
281         }
282 }
283
284 #---------------------------------
285 # Columns
286
287 foreach $table ( keys %requirefields ) {
288     print "Check table $table\n" if $debug and not $silent;
289     $sth = $dbh->prepare("show columns from $table");
290     $sth->execute();
291     undef %types;
292     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
293     {
294         $types{$column} = $type;
295     }    # while
296     foreach $column ( keys %{ $requirefields{$table} } ) {
297         print "  Check column $column  [$types{$column}]\n" if $debug and not $silent;
298         if ( !$types{$column} ) {
299
300             # column doesn't exist
301             print "Adding $column field to $table table...\n" unless $silent;
302             $query = "alter table $table
303                         add column $column " . $requirefields{$table}->{$column};
304             print "Execute: $query\n" if $debug;
305             my $sti = $dbh->prepare($query);
306             $sti->execute;
307             if ( $sti->err ) {
308                 print "**Error : $sti->errstr \n";
309                 $sti->finish;
310             }    # if error
311         }    # if column
312     }    # foreach column
313 }    # foreach table
314
315 foreach $table ( keys %fielddefinitions ) {
316         print "Check table $table\n" if $debug;
317         $sth = $dbh->prepare("show columns from $table");
318         $sth->execute();
319         my $definitions;
320         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
321         {
322                 $definitions->{$column}->{type}    = $type;
323                 $definitions->{$column}->{null}    = $null;
324                 $definitions->{$column}->{key}     = $key;
325                 $definitions->{$column}->{default} = $default;
326                 $definitions->{$column}->{extra}   = $extra;
327         }    # while
328         my $fieldrow = $fielddefinitions{$table};
329         foreach my $row (@$fieldrow) {
330                 my $field   = $row->{field};
331                 my $type    = $row->{type};
332                 my $null    = $row->{null};
333                 my $key     = $row->{key};
334                 my $default = $row->{default};
335                 $default="''" unless $default;
336                 my $extra   = $row->{extra};
337                 my $def     = $definitions->{$field};
338                 unless ( $type eq $def->{type}
339                         && $null eq $def->{null}
340                         && $key eq $def->{key}
341                         && $default eq $def->{default}
342                         && $extra eq $def->{extra} )
343                 {
344
345                         if ( $null eq '' ) {
346                                 $null = 'NOT NULL';
347                         }
348                         if ( $key eq 'PRI' ) {
349                                 $key = 'PRIMARY KEY';
350                         }
351                         unless ( $extra eq 'auto_increment' ) {
352                                 $extra = '';
353                         }
354                         # if it's a new column use "add", if it's an old one, use "change".
355                         my $action;
356                         if ($definitions->{$field}->{type}) {
357                                 $action="change $field"
358                         } else {
359                                 $action="add";
360                         }
361 # if it's a primary key, drop the previous pk, before altering the table
362                         my $sth;
363                         if ($key ne 'PRIMARY KEY') {
364                                 $sth =$dbh->prepare("alter table $table $action $field $type $null $key $extra default ?");
365                         } else {
366                                 $sth =$dbh->prepare("alter table $table drop primary key, $action $field $type $null $key $extra default ?");
367                         }
368                         $sth->execute($default);
369                         print "  Alter $field in $table\n" unless $silent;
370                 }
371         }
372 }
373
374
375 # Populate tables with required data
376 foreach my $table ( keys %tabledata ) {
377     print "Checking for data required in table $table...\n" unless $silent;
378     my $tablerows = $tabledata{$table};
379     foreach my $row (@$tablerows) {
380         my $uniquefieldrequired = $row->{uniquefieldrequired};
381         my $uniquevalue         = $row->{$uniquefieldrequired};
382         my $forceupdate         = $row->{forceupdate};
383         my $sth                 =
384           $dbh->prepare(
385 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
386         );
387         $sth->execute($uniquevalue);
388         if ($sth->rows) {
389             foreach my $field (keys %$forceupdate) {
390                 if ($forceupdate->{$field}) {
391                     my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
392                     $sth->execute($row->{$field}, $uniquevalue);
393                 }
394             }
395         } else {
396             print "Adding row to $table: " unless $silent;
397             my @values;
398             my $fieldlist;
399             my $placeholders;
400             foreach my $field ( keys %$row ) {
401                 next if $field eq 'uniquefieldrequired';
402                 next if $field eq 'forceupdate';
403                 my $value = $row->{$field};
404                 push @values, $value;
405                 print "  $field => $value" unless $silent;
406                 $fieldlist .= "$field,";
407                 $placeholders .= "?,";
408             }
409             print "\n" unless $silent;
410             $fieldlist    =~ s/,$//;
411             $placeholders =~ s/,$//;
412             my $sth =
413               $dbh->prepare(
414                 "insert into $table ($fieldlist) values ($placeholders)");
415             $sth->execute(@values);
416         }
417     }
418 }
419
420 # at last, remove useless fields
421 foreach $table ( keys %uselessfields ) {
422         my @fields = split /,/,$uselessfields{$table};
423         my $fields;
424         my $exists;
425         foreach my $fieldtodrop (@fields) {
426                 $fieldtodrop =~ s/\t//g;
427                 $fieldtodrop =~ s/\n//g;
428                 $exists =0;
429                 $sth = $dbh->prepare("show columns from $table");
430                 $sth->execute;
431                 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
432                 {
433                         $exists =1 if ($column eq $fieldtodrop);
434                 }
435                 if ($exists) {
436                         print "deleting $fieldtodrop field in $table...\n" unless $silent;
437                         my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
438                         $sth->execute;
439                 }
440         }
441 }    # foreach
442
443
444 $sth->finish;
445
446 exit;
447
448 # $Log$
449 # Revision 1.117  2005/08/04 14:24:39  tipaul
450 # synch'ing 2.2 and head
451 #
452 # Revision 1.116  2005/08/04 08:55:54  tipaul
453 # Letters / alert system, continuing...
454 #
455 # * adding a package Letters.pm, that manages Letters & alerts.
456 # * adding feature : it's now possible to define a "letter" for any subscription created. If a letter is defined, users in OPAC can put an alert on the subscription. When an issue is marked "arrived", all users in the alert will recieve a mail (as defined in the "letter"). This last part (= send the mail) is not yet developped. (Should be done this week)
457 # * adding feature : it's now possible to "put to an alert" in OPAC, for any serial subscription. The alert is stored in a new table, called alert. An alert can be put only if the librarian has activated them in subscription (and they activate it just by choosing a "letter" to sent to borrowers on new issues)
458 # * adding feature : librarian can see in borrower detail which alerts they have put, and a user can see in opac-detail which alert they have put too.
459 #
460 # Note that the system should be generic enough to manage any type of alert.
461 # I plan to extend it soon to virtual shelves : a borrower will be able to put an alert on a virtual shelf, to be warned when something is changed in the virtual shelf (mail being sent once a day by cron, or manually by the shelf owner. Anyway, a mail won't be sent on every change, users would be spammed by Koha ;-) )
462 #
463 # Revision 1.115  2005/08/02 16:15:34  tipaul
464 # adding 2 fields to letter system :
465 # * module (acquisition, catalogue...) : it will be usefull to show the librarian only letters he may be interested by.
466 # * title, that will be used as mail subject.
467 #
468 # Revision 1.114  2005/07/28 15:10:13  tipaul
469 # Introducing new "Letters" system : Letters will be used everytime you want to sent something to someone (through mail or paper). For example, sending a mail for overdues use letter that you can put as parameters. Sending a mail to a borrower when a suggestion is validated uses a letter too.
470 # the letter table contains 3 fields :
471 # * code => the code of the letter
472 # * name => the complete name of the letter
473 # * content => the complete text. It's a TEXT field type, so has no limits.
474 #
475 # My next goal now is to work on point 2-I "serial issue alert"
476 # With this feature, in serials, a user can subscribe the "issue alert". For every issue arrived/missing, a mail is sent to all subscribers of this list. The mail warns the user that the issue is arrive or missing. Will be in head.
477 # (see mail on koha-devel, 2005/04/07)
478 #
479 # The "serial issue alert" will be the 1st to use this letter system that probably needs some tweaking ;-)
480 #
481 # Once it will be stabilised default letters (in any languages) could be added during installer to help the library begin with this new feature.
482 #
483 # Revision 1.113  2005/07/28 08:38:41  tipaul
484 # For instance, the return date does not rely on the borrower expiration date. A systempref will be added in Koha, to modify return date calculation schema :
485 # * ReturnBeforeExpiry = yes => return date can't be after expiry date
486 # * ReturnBeforeExpiry = no  => return date can be after expiry date
487 #
488 # Revision 1.112  2005/07/26 08:19:47  hdl
489 # Adding IndependantBranches System preference variable in order to manage Branch independancy.
490 #
491 # Revision 1.111  2005/07/25 15:35:38  tipaul
492 # we have decided that moving to Koha 3.0 requires being already in Koha 2.2.x
493 # So, the updatedatabase script can highly be cleaned (90% removed).
494 # Let's play with the new Koha DB structure now ;-)
495 #
496 #!/usr/bin/perl
497
498 # $Id$
499
500 # Database Updater
501 # This script checks for required updates to the database.
502
503 # Part of the Koha Library Software www.koha.org
504 # Licensed under the GPL.
505
506 # Bugs/ToDo:
507 # - Would also be a good idea to offer to do a backup at this time...
508
509 # NOTE:  If you do something more than once in here, make it table driven.
510 use strict;
511
512 # CPAN modules
513 use DBI;
514 use Getopt::Long;
515 # Koha modules
516 use C4::Context;
517
518 # FIXME - The user might be installing a new database, so can't rely
519 # on /etc/koha.conf anyway.
520
521 my $debug = 0;
522
523 my (
524     $sth, $sti,
525     $query,
526     %existingtables,    # tables already in database
527     %types,
528     $table,
529     $column,
530     $type, $null, $key, $default, $extra,
531     $prefitem,          # preference item in systempreferences table
532 );
533
534 my $silent;
535 GetOptions(
536         's' =>\$silent
537         );
538 my $dbh = C4::Context->dbh;
539 print "connected to your DB. Checking & modifying it\n" unless $silent;
540
541 #-------------------
542 # Defines
543
544 # Tables to add if they don't exist
545 my %requiretables = (
546     categorytable       => "(categorycode char(5) NOT NULL default '',
547                              description text default '',
548                              itemtypecodes text default '',
549                              PRIMARY KEY (categorycode)
550                             )",
551     subcategorytable       => "(subcategorycode char(5) NOT NULL default '',
552                              description text default '',
553                              itemtypecodes text default '',
554                              PRIMARY KEY (subcategorycode)
555                             )",
556     mediatypetable       => "(mediatypecode char(5) NOT NULL default '',
557                              description text default '',
558                              itemtypecodes text default '',
559                              PRIMARY KEY (mediatypecode)
560                             )",
561     action_logs         => "(
562                                     `timestamp` TIMESTAMP NOT NULL ,
563                                     `user` INT( 11 ) NOT NULL ,
564                                     `module` TEXT default '',
565                                     `action` TEXT default '' ,
566                                     `object` INT(11) default '' ,
567                                     `info` TEXT default '' ,
568                                     PRIMARY KEY ( `timestamp` , `user` )
569                             )",
570 );
571
572 my %requirefields = (
573 #    tablename        => { 'field' => 'fieldtype' },
574 );
575
576 my %dropable_table = (
577 # tablename => 'tablename',
578 );
579
580 my %uselessfields = (
581 # tablename => "field1,field2",
582         );
583 # the other hash contains other actions that can't be done elsewhere. they are done
584 # either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
585
586 # The tabledata hash contains data that should be in the tables.
587 # The uniquefieldrequired hash entry is used to determine which (if any) fields
588 # must not exist in the table for this row to be inserted.  If the
589 # uniquefieldrequired entry is already in the table, the existing data is not
590 # modified, unless the forceupdate hash entry is also set.  Fields in the
591 # anonymous "forceupdate" hash will be forced to be updated to the default
592 # values given in the %tabledata hash.
593
594 my %tabledata = (
595 # tablename => [
596 #       {       uniquefielrequired => 'fieldname', # the primary key in the table
597 #               fieldname => fieldvalue,
598 #               fieldname2 => fieldvalue2,
599 #       },
600 # ],
601     systempreferences => [
602                 {
603             uniquefieldrequired => 'variable',
604             variable            => 'Activate_Log',
605             value               => 'On',
606             forceupdate         => { 'explanation' => 1,
607                                      'type' => 1},
608             explanation         => 'Turn Log Actions on DB On an Off',
609             type                => 'YesNo',
610         },
611         {
612             uniquefieldrequired => 'variable',
613             variable            => 'IndependantBranches',
614             value               => 0,
615             forceupdate         => { 'explanation' => 1,
616                                      'type' => 1},
617             explanation         => 'Turn Branch independancy management On an Off',
618             type                => 'YesNo',
619         },
620
621     ],
622
623 );
624
625 my %fielddefinitions = (
626 # fieldname => [
627 #       {                 field => 'fieldname',
628 #             type    => 'fieldtype',
629 #             null    => '',
630 #             key     => '',
631 #             default => ''
632 #         },
633 #     ],
634 );
635
636 #-------------------
637 # Initialize
638
639 # Start checking
640
641 # Get version of MySQL database engine.
642 my $mysqlversion = `mysqld --version`;
643 $mysqlversion =~ /Ver (\S*) /;
644 $mysqlversion = $1;
645 if ( $mysqlversion ge '3.23' ) {
646     print "Could convert to MyISAM database tables...\n" unless $silent;
647 }
648
649 #---------------------------------
650 # Tables
651
652 # Collect all tables into a list
653 $sth = $dbh->prepare("show tables");
654 $sth->execute;
655 while ( my ($table) = $sth->fetchrow ) {
656     $existingtables{$table} = 1;
657 }
658
659
660 # Now add any missing tables
661 foreach $table ( keys %requiretables ) {
662     unless ( $existingtables{$table} ) {
663         print "Adding $table table...\n" unless $silent;
664         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
665         $sth->execute;
666         if ( $sth->err ) {
667             print "Error : $sth->errstr \n";
668             $sth->finish;
669         }    # if error
670     }    # unless exists
671 }    # foreach
672
673 # now drop useless tables
674 foreach $table ( keys %dropable_table ) {
675         if ( $existingtables{$table} ) {
676                 print "Dropping unused table $table\n" if $debug and not $silent;
677                 $dbh->do("drop table $table");
678                 if ( $dbh->err ) {
679                         print "Error : $dbh->errstr \n";
680                 }
681         }
682 }
683
684 #---------------------------------
685 # Columns
686
687 foreach $table ( keys %requirefields ) {
688     print "Check table $table\n" if $debug and not $silent;
689     $sth = $dbh->prepare("show columns from $table");
690     $sth->execute();
691     undef %types;
692     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
693     {
694         $types{$column} = $type;
695     }    # while
696     foreach $column ( keys %{ $requirefields{$table} } ) {
697         print "  Check column $column  [$types{$column}]\n" if $debug and not $silent;
698         if ( !$types{$column} ) {
699
700             # column doesn't exist
701             print "Adding $column field to $table table...\n" unless $silent;
702             $query = "alter table $table
703                         add column $column " . $requirefields{$table}->{$column};
704             print "Execute: $query\n" if $debug;
705             my $sti = $dbh->prepare($query);
706             $sti->execute;
707             if ( $sti->err ) {
708                 print "**Error : $sti->errstr \n";
709                 $sti->finish;
710             }    # if error
711         }    # if column
712     }    # foreach column
713 }    # foreach table
714
715 foreach $table ( keys %fielddefinitions ) {
716         print "Check table $table\n" if $debug;
717         $sth = $dbh->prepare("show columns from $table");
718         $sth->execute();
719         my $definitions;
720         while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
721         {
722                 $definitions->{$column}->{type}    = $type;
723                 $definitions->{$column}->{null}    = $null;
724                 $definitions->{$column}->{key}     = $key;
725                 $definitions->{$column}->{default} = $default;
726                 $definitions->{$column}->{extra}   = $extra;
727         }    # while
728         my $fieldrow = $fielddefinitions{$table};
729         foreach my $row (@$fieldrow) {
730                 my $field   = $row->{field};
731                 my $type    = $row->{type};
732                 my $null    = $row->{null};
733                 my $key     = $row->{key};
734                 my $default = $row->{default};
735                 $default="''" unless $default;
736                 my $extra   = $row->{extra};
737                 my $def     = $definitions->{$field};
738                 unless ( $type eq $def->{type}
739                         && $null eq $def->{null}
740                         && $key eq $def->{key}
741                         && $default eq $def->{default}
742                         && $extra eq $def->{extra} )
743                 {
744
745                         if ( $null eq '' ) {
746                                 $null = 'NOT NULL';
747                         }
748                         if ( $key eq 'PRI' ) {
749                                 $key = 'PRIMARY KEY';
750                         }
751                         unless ( $extra eq 'auto_increment' ) {
752                                 $extra = '';
753                         }
754                         # if it's a new column use "add", if it's an old one, use "change".
755                         my $action;
756                         if ($definitions->{$field}->{type}) {
757                                 $action="change $field"
758                         } else {
759                                 $action="add";
760                         }
761 # if it's a primary key, drop the previous pk, before altering the table
762                         my $sth;
763                         if ($key ne 'PRIMARY KEY') {
764                                 $sth =$dbh->prepare("alter table $table $action $field $type $null $key $extra default ?");
765                         } else {
766                                 $sth =$dbh->prepare("alter table $table drop primary key, $action $field $type $null $key $extra default ?");
767                         }
768                         $sth->execute($default);
769                         print "  Alter $field in $table\n" unless $silent;
770                 }
771         }
772 }
773
774
775 # Populate tables with required data
776 foreach my $table ( keys %tabledata ) {
777     print "Checking for data required in table $table...\n" unless $silent;
778     my $tablerows = $tabledata{$table};
779     foreach my $row (@$tablerows) {
780         my $uniquefieldrequired = $row->{uniquefieldrequired};
781         my $uniquevalue         = $row->{$uniquefieldrequired};
782         my $forceupdate         = $row->{forceupdate};
783         my $sth                 =
784           $dbh->prepare(
785 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
786         );
787         $sth->execute($uniquevalue);
788         if ($sth->rows) {
789             foreach my $field (keys %$forceupdate) {
790                 if ($forceupdate->{$field}) {
791                     my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
792                     $sth->execute($row->{$field}, $uniquevalue);
793                 }
794             }
795         } else {
796             print "Adding row to $table: " unless $silent;
797             my @values;
798             my $fieldlist;
799             my $placeholders;
800             foreach my $field ( keys %$row ) {
801                 next if $field eq 'uniquefieldrequired';
802                 next if $field eq 'forceupdate';
803                 my $value = $row->{$field};
804                 push @values, $value;
805                 print "  $field => $value" unless $silent;
806                 $fieldlist .= "$field,";
807                 $placeholders .= "?,";
808             }
809             print "\n" unless $silent;
810             $fieldlist    =~ s/,$//;
811             $placeholders =~ s/,$//;
812             my $sth =
813               $dbh->prepare(
814                 "insert into $table ($fieldlist) values ($placeholders)");
815             $sth->execute(@values);
816         }
817     }
818 }
819
820 # at last, remove useless fields
821 foreach $table ( keys %uselessfields ) {
822         my @fields = split /,/,$uselessfields{$table};
823         my $fields;
824         my $exists;
825         foreach my $fieldtodrop (@fields) {
826                 $fieldtodrop =~ s/\t//g;
827                 $fieldtodrop =~ s/\n//g;
828                 $exists =0;
829                 $sth = $dbh->prepare("show columns from $table");
830                 $sth->execute;
831                 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
832                 {
833                         $exists =1 if ($column eq $fieldtodrop);
834                 }
835                 if ($exists) {
836                         print "deleting $fieldtodrop field in $table...\n" unless $silent;
837                         my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
838                         $sth->execute;
839                 }
840         }
841 }    # foreach
842
843
844 $sth->finish;
845
846 exit;
847
848 # $Log$
849 # Revision 1.117  2005/08/04 14:24:39  tipaul
850 # synch'ing 2.2 and head
851 #
852 # Revision 1.116  2005/08/04 08:55:54  tipaul
853 # Letters / alert system, continuing...
854 #
855 # * adding a package Letters.pm, that manages Letters & alerts.
856 # * adding feature : it's now possible to define a "letter" for any subscription created. If a letter is defined, users in OPAC can put an alert on the subscription. When an issue is marked "arrived", all users in the alert will recieve a mail (as defined in the "letter"). This last part (= send the mail) is not yet developped. (Should be done this week)
857 # * adding feature : it's now possible to "put to an alert" in OPAC, for any serial subscription. The alert is stored in a new table, called alert. An alert can be put only if the librarian has activated them in subscription (and they activate it just by choosing a "letter" to sent to borrowers on new issues)
858 # * adding feature : librarian can see in borrower detail which alerts they have put, and a user can see in opac-detail which alert they have put too.
859 #
860 # Note that the system should be generic enough to manage any type of alert.
861 # I plan to extend it soon to virtual shelves : a borrower will be able to put an alert on a virtual shelf, to be warned when something is changed in the virtual shelf (mail being sent once a day by cron, or manually by the shelf owner. Anyway, a mail won't be sent on every change, users would be spammed by Koha ;-) )
862 #
863 # Revision 1.115  2005/08/02 16:15:34  tipaul
864 # adding 2 fields to letter system :
865 # * module (acquisition, catalogue...) : it will be usefull to show the librarian only letters he may be interested by.
866 # * title, that will be used as mail subject.
867 #
868 # Revision 1.114  2005/07/28 15:10:13  tipaul
869 # Introducing new "Letters" system : Letters will be used everytime you want to sent something to someone (through mail or paper). For example, sending a mail for overdues use letter that you can put as parameters. Sending a mail to a borrower when a suggestion is validated uses a letter too.
870 # the letter table contains 3 fields :
871 # * code => the code of the letter
872 # * name => the complete name of the letter
873 # * content => the complete text. It's a TEXT field type, so has no limits.
874 #
875 # My next goal now is to work on point 2-I "serial issue alert"
876 # With this feature, in serials, a user can subscribe the "issue alert". For every issue arrived/missing, a mail is sent to all subscribers of this list. The mail warns the user that the issue is arrive or missing. Will be in head.
877 # (see mail on koha-devel, 2005/04/07)
878 #
879 # The "serial issue alert" will be the 1st to use this letter system that probably needs some tweaking ;-)
880 #
881 # Once it will be stabilised default letters (in any languages) could be added during installer to help the library begin with this new feature.
882 #
883 # Revision 1.113  2005/07/28 08:38:41  tipaul
884 # For instance, the return date does not rely on the borrower expiration date. A systempref will be added in Koha, to modify return date calculation schema :
885 # * ReturnBeforeExpiry = yes => return date can't be after expiry date
886 # * ReturnBeforeExpiry = no  => return date can be after expiry date
887 #
888 # Revision 1.112  2005/07/26 08:19:47  hdl
889 # Adding IndependantBranches System preference variable in order to manage Branch independancy.
890 #
891 # Revision 1.111  2005/07/25 15:35:38  tipaul
892 # we have decided that moving to Koha 3.0 requires being already in Koha 2.2.x
893 # So, the updatedatabase script can highly be cleaned (90% removed).
894 # Let's play with the new Koha DB structure now ;-)
895 #