uptodate SQL structure
[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 use MARC::Record;
24 use MARC::File::XML ( BinaryEncoding => 'utf8' );
25  
26 # FIXME - The user might be installing a new database, so can't rely
27 # on /etc/koha.conf anyway.
28
29 my $debug = 0;
30
31 my (
32     $sth, $sti,
33     $query,
34     %existingtables,    # tables already in database
35     %types,
36     $table,
37     $column,
38     $type, $null, $key, $default, $extra,
39     $prefitem,          # preference item in systempreferences table
40 );
41
42 my $silent;
43 GetOptions(
44     's' =>\$silent
45     );
46 my $dbh = C4::Context->dbh;
47 print "connected to your DB. Checking & modifying it\n" unless $silent;
48 $|=1; # flushes output
49
50 #-------------------
51 # Defines
52
53 # Tables to add if they don't exist
54 my %requiretables = (
55     categorytable       => "(categorycode char(5) NOT NULL default '',
56                              description text default '',
57                              itemtypecodes text default '',
58                              PRIMARY KEY (categorycode)
59                             )",
60     subcategorytable       => "(subcategorycode char(5) NOT NULL default '',
61                              description text default '',
62                              itemtypecodes text default '',
63                              PRIMARY KEY (subcategorycode)
64                             )",
65     mediatypetable       => "(mediatypecode char(5) NOT NULL default '',
66                              description text default '',
67                              itemtypecodes text default '',
68                              PRIMARY KEY (mediatypecode)
69                             )",
70     action_logs     => "(
71                     `timestamp` TIMESTAMP NOT NULL ,
72                     `user` INT( 11 ) NOT NULL ,
73                     `module` TEXT default '',
74                     `action` TEXT default '' ,
75                     `object` INT(11) NULL ,
76                     `info` TEXT default '' ,
77                     PRIMARY KEY ( `timestamp` , `user` )
78                 )",
79     letter        => "(
80                     module varchar(20) NOT NULL default '',
81                     code varchar(20) NOT NULL default '',
82                     name varchar(100) NOT NULL default '',
83                     title varchar(200) NOT NULL default '',
84                     content text,
85                     PRIMARY KEY  (module,code)
86                 )",
87     alert        =>"(
88                     alertid int(11) NOT NULL auto_increment,
89                     borrowernumber int(11) NOT NULL default '0',
90                     type varchar(10) NOT NULL default '',
91                     externalid varchar(20) NOT NULL default '',
92                     PRIMARY KEY  (alertid),
93                     KEY borrowernumber (borrowernumber),
94                     KEY type (type,externalid)
95                 )",
96     opac_news => "(
97                 `idnew` int(10) unsigned NOT NULL auto_increment,
98                 `title` varchar(250) NOT NULL default '',
99                 `new` text NOT NULL,
100                 `lang` varchar(4) NOT NULL default '',
101                 `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
102                 PRIMARY KEY  (`idnew`)
103                 )",
104     repeatable_holidays => "(
105                 `id` int(11) NOT NULL auto_increment,
106                 `branchcode` varchar(4) NOT NULL default '',
107                 `weekday` smallint(6) default NULL,
108                 `day` smallint(6) default NULL,
109                 `month` smallint(6) default NULL,
110                 `title` varchar(50) NOT NULL default '',
111                 `description` text NOT NULL,
112                 PRIMARY KEY  (`id`)
113                 )",
114     special_holidays => "(
115                 `id` int(11) NOT NULL auto_increment,
116                 `branchcode` varchar(4) NOT NULL default '',
117                 `day` smallint(6) NOT NULL default '0',
118                 `month` smallint(6) NOT NULL default '0',
119                 `year` smallint(6) NOT NULL default '0',
120                 `isexception` smallint(1) NOT NULL default '1',
121                 `title` varchar(50) NOT NULL default '',
122                 `description` text NOT NULL,
123                 PRIMARY KEY  (`id`)
124                 )",
125     overduerules    =>"(`branchcode` varchar(255) NOT NULL default '',
126                     `categorycode` char(2) NOT NULL default '',
127                     `delay1` int(4) default '0',
128                     `letter1` varchar(20) default NULL,
129                     `debarred1` char(1) default '0',
130                     `delay2` int(4) default '0',
131                     `debarred2` char(1) default '0',
132                     `letter2` varchar(20) default NULL,
133                     `delay3` int(4) default '0',
134                     `letter3` varchar(20) default NULL,
135                     `debarred3` int(1) default '0',
136                     PRIMARY KEY  (`branchcode`,`categorycode`)
137                     )",
138     cities            => "(`cityid` int auto_increment,
139                         `city_name` char(100) NOT NULL,
140                         `city_zipcode` char(20),
141                         PRIMARY KEY (`cityid`)
142                     )",
143     roadtype            => "(`roadtypeid` int auto_increment,
144                         `road_type` char(100) NOT NULL,
145                         PRIMARY KEY (`roadtypeid`)
146                     )",
147
148     labels                     => "(
149                 labelid int(11) NOT NULL auto_increment,
150                                itemnumber varchar(100) NOT NULL default '',
151                                timestamp timestamp(14) NOT NULL,
152                                PRIMARY KEY  (labelid)
153                                )",
154
155     labels_conf                => "(
156                 id int(4) NOT NULL auto_increment,
157                                barcodetype char(100) default '',
158                                title tinyint(1) default '0',
159                                isbn tinyint(1) default '0',
160                                itemtype tinyint(1) default '0',
161                                barcode tinyint(1) default '0',
162                                dewey tinyint(1) default '0',
163                                class tinyint(1) default '0',
164                                author tinyint(1) default '0',
165                                papertype char(100) default '',
166                                startrow int(2) default NULL,
167                                PRIMARY KEY  (id)
168                                )",
169        reviews                  => "(
170                             reviewid integer NOT NULL auto_increment,
171                             borrowernumber integer,
172                             biblionumber integer,
173                             review text,
174                             approved tinyint,
175                             datereviewed datetime,
176                             PRIMARY KEY (reviewid)
177                             )",
178     borrowers_to_borrowers  => "(
179                             borrower1 integer,
180                             borrower2 integer
181                             )",
182     subscriptionroutinglist=>"(
183                              routingid integer NOT NULL auto_increment,
184                              borrowernumber integer,
185                              ranking integer,
186                              subscriptionid integer,
187                             PRIMARY KEY (routingid)
188                              )",
189
190     notifys    => "(
191               notify_id int(11) NOT NULL default '0',
192                 `borrowernumber` int(11) NOT NULL default '0',
193               `itemnumber` int(11) NOT NULL default '0',
194               `notify_date` date NOT NULL default '0000-00-00',
195                       `notify_send_date` date default NULL,
196                       `notify_level` int(1) NOT NULL default '0',
197                       `method` varchar(20) NOT NULL default ''
198               )",
199
200    charges    => "(
201               `charge_id` varchar(5) NOT NULL default '',
202                 `description` text NOT NULL,
203                 `amount` decimal(28,6) NOT NULL default '0.000000',
204                           `min` int(4) NOT NULL default '0',
205                 `max` int(4) NOT NULL default '0',
206                           `level` int(1) NOT NULL default '0',
207                           PRIMARY KEY  (`charge_id`)
208               )",
209     tags => "(
210         `entry` varchar(255) NOT NULL default '',
211         `weight` bigint(20) NOT NULL default '0',
212          PRIMARY KEY  (`entry`)
213     )
214     ",
215    zebraqueue    => "(
216                 `id` int NOT NULL auto_increment,
217                 `biblio_auth_number` int NOT NULL,
218                 `operation` char(20) NOT NULL,
219                 `server` char(20) NOT NULL ,
220                 PRIMARY KEY  (`id`)
221               ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=1",
222
223 );
224
225 my %requirefields = (
226     subscription => { 'letter' => 'char(20) NULL', 'distributedto' => 'text NULL', 'firstacquidate'=>'date NOT NULL','irregularity'=>'TEXT NULL default \'\'','numberpattern'=>'TINYINT(3) NULL default 0', 'callnumber'=>'text NULL', 'hemisphere' =>'TINYINT(3) NULL default 0', 'issuesatonce'=>'TINYINT(3) NOT NULL default 1',  'branchcode' =>'varchar(12) NOT NULL default \'\'', 'manualhistory'=>'TINYINT(1) NOT NULL default 0','internalnotes'=>'LONGTEXT NULL default \'\''},
227     itemtypes => { 'imageurl' => 'char(200) NULL'},
228     aqbookfund => { 'branchcode' => 'varchar(4) NULL'},
229     aqbudget => { 'branchcode' => 'varchar(4) NULL'},
230     auth_header => { 'marc' => 'BLOB NOT NULL', 'linkid' => 'BIGINT(20) NULL'},
231     auth_subfield_structure =>{ 'hidden' => 'TINYINT(3) NOT NULL default 0', 'kohafield' => 'VARCHAR(45) NOT NULL', 'linkid' =>  'TINYINT(1) NOT NULL default 0', 'isurl' => 'TINYINT(1)', 'frameworkcode'=>'VARCHAR(8) NOT  NULL'},
232     marc_breeding => { 'isbn' => 'varchar(13) NOT NULL'},
233     serial =>{ 'publisheddate' => 'date', 'claimdate' => 'date', 'itemnumber'=>'text NULL','routingnotes'=>'text NULL',},
234     statistics => { 'associatedborrower' => 'integer'},
235     z3950servers =>{  "name" =>"text",  "description" => "text NOT NULL",
236                     "position" =>"enum('primary','secondary','') NOT NULL default 'primary'",  "icon" =>"text",
237                     "type" =>"enum('zed','opensearch') NOT NULL default 'zed'",
238                     },
239     issues =>{ 'issuedate'=>"date NOT NULL default '0000-00-00'", },
240
241 #    tablename        => { 'field' => 'fieldtype' },
242 );
243
244 # Enter here the table to delete.
245 my @TableToDelete = qw(
246     additionalauthors
247     bibliosubject
248     bibliosubtitle
249     bibliothesaurus
250 );
251
252 my %uselessfields = (
253 # tablename => "field1,field2",
254     borrowers => "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
255     deletedborrowers=> "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
256     );
257 # the other hash contains other actions that can't be done elsewhere. they are done
258 # either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
259
260 # The tabledata hash contains data that should be in the tables.
261 # The uniquefieldrequired hash entry is used to determine which (if any) fields
262 # must not exist in the table for this row to be inserted.  If the
263 # uniquefieldrequired entry is already in the table, the existing data is not
264 # modified, unless the forceupdate hash entry is also set.  Fields in the
265 # anonymous "forceupdate" hash will be forced to be updated to the default
266 # values given in the %tabledata hash.
267
268 my %tabledata = (
269 # tablename => [
270 #    {    uniquefielrequired => 'fieldname', # the primary key in the table
271 #        fieldname => fieldvalue,
272 #        fieldname2 => fieldvalue2,
273 #    },
274 # ],
275     systempreferences => [
276         {
277             uniquefieldrequired => 'variable',
278             variable            => 'useDaysMode',
279             value               => 'Calendar',
280             forceupdate         => { 'explanation' => 1,
281                                      'type' => 1},
282             explanation            => 'How to calculate return dates : Calendar means holidays will be controled, Days means the return date don\'t depend on holidays',
283             type        => 'Choice',
284             options        => 'Calendar|Days'
285         },
286         {
287             uniquefieldrequired => 'variable',
288             variable            => 'BorrowersTitles',
289             value               => 'Mr|Mrs|Miss|Ms',
290             forceupdate         => { 'explanation' => 1,
291                                      'type' => 1},
292             explanation         => 'List all Titles for borrowers',
293             type                => 'free',
294         },
295         {
296             uniquefieldrequired => 'variable',
297             variable            => 'BorrowerMandatoryField',
298             value               => 'cardnumber|surname|address',
299             forceupdate         => { 'explanation' => 1,
300                                      'type' => 1},
301             explanation         => 'List all mandatory fields for borrowers',
302             type                => 'free',
303         },
304         {
305             uniquefieldrequired => 'variable',
306             variable            => 'borrowerRelationship',
307             value               => 'father|mother,grand-mother',
308             forceupdate         => { 'explanation' => 1,
309                                      'type' => 1},
310             explanation         => 'The relationships between a guarantor & a guarantee (separated by | or ,)',
311             type                => 'free',
312         },
313         {
314             uniquefieldrequired => 'variable',
315             variable            => 'ReservesMaxPickUpDelay',
316             value               => '10',
317             forceupdate         => { 'explanation' => 1,
318                                      'type' => 1},
319             explanation         => 'Maximum delay to pick up a reserved document',
320             type                => 'free',
321         },
322         {
323             uniquefieldrequired => 'variable',
324             variable            => 'TransfersMaxDaysWarning',
325             value               => '3',
326             forceupdate         => { 'explanation' => 1,
327                                      'type' => 1},
328             explanation         => 'Max delay before considering the transfer has potentialy a problem',
329             type                => 'free',
330         },
331         {
332             uniquefieldrequired => 'variable',
333             variable            => 'memberofinstitution',
334             value               => '0',
335             forceupdate         => { 'explanation' => 1,
336                                      'type' => 1},
337             explanation         => 'Are your patrons members of institutions',
338             type                => 'YesNo',
339         },
340     {
341             uniquefieldrequired => 'variable',
342             variable            => 'ReadingHistory',
343             value               => '0',
344             forceupdate         => { 'explanation' => 1,
345                                      'type' => 1},
346             explanation         => 'Allow reading record info retrievable from issues and oldissues tables',
347             type                => 'YesNo',
348         },
349     {
350             uniquefieldrequired => 'variable',
351             variable            => 'IssuingInProcess',
352             value               => '0',
353             forceupdate         => { 'explanation' => 1,
354                                      'type' => 1},
355             explanation         => 'Allow no debt alert if the patron is issuing item that accumulate debt',
356             type                => 'YesNo',
357         },
358     {
359             uniquefieldrequired => 'variable',
360             variable            => 'AutomaticItemReturn',
361             value               => '1',
362             forceupdate         => { 'explanation' => 1,
363                                      'type' => 1},
364             explanation         => 'This Variable allow or not to return automaticly to his homebranch',
365             type                => 'YesNo',
366         },
367     {
368             uniquefieldrequired => 'variable',
369             variable            => 'reviewson',
370             value               => '0',
371             forceupdate         => { 'explanation' => 1,
372                                      'type' => 1},
373             explanation         => 'Allows patrons to submit reviews from the opac',
374             type                => 'YesNo',
375         },
376     {
377             uniquefieldrequired => 'variable',
378             variable            => 'intranet_includes',
379             value               => 'includes',
380             forceupdate         => { 'explanation' => 1,
381                                      'type' => 1},
382             explanation         => 'The includes directory you want for specific look of Koha (includes or includes_npl for example)',
383             type                => 'Free',
384         },
385         {
386             uniquefieldrequired => 'variable',
387             variable            => 'AutoLocation',
388             value               => '0',
389             forceupdate         => { 'explanation' => 1,
390                                      'type' => 1},
391             explanation         => 'switch to activate or not Autolocation, if Yes, the Librarian can\'t change his location, it\'s defined by branchip',
392             type                => 'YesNo',
393         },
394         {
395             uniquefieldrequired => 'variable',
396             variable            => 'serialsadditems',
397             value               => '0',
398             forceupdate         => {
399                 'explanation' => 1,
400                 'type' => 1
401             },
402             explanation => 'If set, a new item will be automatically added when receiving an issue',
403             type => 'YesNo',
404         },
405         {
406             uniquefieldrequired => 'variable',
407             variable            => 'expandedSearchOption',
408             value               => '0',
409             forceupdate         => {
410                 'explanation' => 1,
411                 'type' => 1
412             },
413             explanation => 'search among marc field',
414             type => 'YesNo',
415         },
416        {
417             uniquefieldrequired => 'variable',
418             variable            => 'RequestOnOpac',
419             value               => '1',
420             forceupdate         => { 'explanation' => 1,
421                                      'type' => 1},
422             explanation         => 'option to allow reserves on opac',
423             type                => 'YesNo',
424         },
425        {
426             uniquefieldrequired => 'variable',
427             variable            => 'OpacCloud',
428             value               => '1',
429             forceupdate         => { 'explanation' => 1,
430                                      'type' => 1},
431             explanation         => 'Enable / Disable cloud link on OPAC (Require to run misc/cronjobs/build_browser_and_cloud.pl on the server)',
432             type                => 'YesNo',
433         },
434        {
435             uniquefieldrequired => 'variable',
436             variable            => 'OpacBrowser',
437             value               => '1',
438             forceupdate         => { 'explanation' => 1,
439                                      'type' => 1},
440             explanation         => 'Enable/Disable browser link on OPAC (Require to run misc/cronjobs/build_browser_and_cloud.pl on the server)',
441             type                => 'YesNo',
442         },
443        {
444             uniquefieldrequired => 'variable',
445             variable            => 'OpacTopissue',
446             value               => '1',
447             forceupdate         => { 'explanation' => 1,
448                                      'type' => 1},
449             explanation         => 'Enable / Disable the top issue link on OPAC',
450             type                => 'YesNo',
451         },
452        {
453             uniquefieldrequired => 'variable',
454             variable            => 'OpacAuthorities',
455             value               => '1',
456             forceupdate         => { 'explanation' => 1,
457                                      'type' => 1},
458             explanation         => 'Enable / Disable the search authority link on OPAC',
459             type                => 'YesNo',
460         },
461         {
462             uniquefieldrequired => 'variable',
463             variable            => 'CataloguingLog',
464             value               => '0',
465             forceupdate         => {'explanation' => 1, 'type' => 1},
466             explanation         => 'Active this if you want to log cataloguing action.',
467             type                => 'YesNo',
468         },
469         {
470             uniquefieldrequired => 'variable',
471             variable            => 'BorrowersLog',
472             value               => '0',
473             forceupdate         => {'explanation' => 1, 'type' => 1},
474             explanation         => 'Active this if you want to log borrowers edition/creation/deletion...',
475             type                => 'YesNo',
476         },
477         {
478             uniquefieldrequired => 'variable',
479             variable            => 'SubscriptionLog',
480             value               => '0',
481             forceupdate         => {'explanation' => 1, 'type' => 1},
482             explanation         => 'Active this if you want to log Subscription action',
483             type                => 'YesNo',
484         },
485         {
486             uniquefieldrequired => 'variable',
487             variable            => 'IssueLog',
488             value               => '0',
489             forceupdate         => {'explanation' => 1, 'type' => 1},
490             explanation         => 'Active this if you want to log issue.',
491             type                => 'YesNo',
492         },
493         {
494             uniquefieldrequired => 'variable',
495             variable            => 'ReturnLog',
496             value               => '0',
497             forceupdate         => {'explanation' => 1, 'type' => 1},
498             explanation         => 'Active this if you want to log the circulation return',
499             type                => 'YesNo',
500         },
501         {
502             uniquefieldrequired => 'variable',
503             variable            => 'Version',
504             value               => '3.0',
505             forceupdate         => {'explanation' => 1, 'type' => 1},
506             explanation         => 'Koha Version',
507             type                => 'Free',
508         },
509         {   
510             uniquefieldrequired => 'variable',
511             variable            => 'LetterLog',
512             value               => '0',
513             forceupdate         => {'explanation' => 1, 'type' => 1},
514             explanation         => 'Active this if you want to log all the letter sent',
515             type                => 'YesNo',
516         },
517         {
518             uniquefieldrequired => 'variable',
519             variable            => 'FinesLog',
520             value               => '0',
521             forceupdate         => {'explanation' => 1, 'type' => 1},
522             explanation         => 'Active this if you want to log fines',
523             type                => 'YesNo',
524         },
525         {
526             uniquefieldrequired => 'variable',
527             variable            => 'NoZebra',
528             value               => '0',
529             forceupdate         => {'explanation' => 1, 'type' => 1},
530             explanation         => 'Active this if you want NOT to use zebra (large libraries should avoid this parameters)',
531             type                => 'YesNo',
532         },
533         {
534             uniquefieldrequired => 'variable',
535             variable            => 'NoZebraIndexes',
536             value               => '0',
537             forceupdate         => {'explanation' => 1, 'type' => 1},
538             explanation         => "Enter a specific hash for NoZebra indexes. Enter : 'indexname' => '100a,245a,500*','index2' => '...'",
539             type                => 'Free',
540         },
541     ],
542     userflags => [
543         {
544             uniquefieldrequired => 'bit',
545             bit                 => '14',
546             flag                => 'editauthorities',
547             flagdesc            => 'allow to edit authorities',
548             defaulton           => '0',
549         },
550         {
551             uniquefieldrequired => 'bit',
552             bit                 => '15',
553             flag                 => 'serials',
554             flagdesc            => 'allow to manage serials subscriptions',
555             defaulton           => '0',
556         },
557         {
558             uniquefieldrequired => 'bit',
559             bit                 => '16',
560             flag                 => 'reports',
561             flagdesc            => 'allow to access to the reports module',
562             defaulton           => '0',
563         },
564     ],
565     authorised_values => [
566         {
567             uniquefieldrequired => 'id',
568             category            => 'SUGGEST',
569             authorised_value    => 'Not enough budget',
570             lib                 => 'This book it too much expensive',
571         }
572     ],
573 );
574
575 my %fielddefinitions = (
576 # fieldname => [
577 #    {          field => 'fieldname',
578 #             type    => 'fieldtype',
579 #             null    => '',
580 #             key     => '',
581 #             default => ''
582 #         },
583 #     ],
584     aqbasket =>  [
585         {
586             field    => 'booksellerid',
587             type    => 'int(11)',
588             null    => 'NOT NULL',
589             key        => '',
590             default    => '1',
591             extra    => '',
592         },
593     ],
594     aqbooksellers =>  [
595         {
596             field    => 'id',
597             type    => 'int(11)',
598             null    => 'NOT NULL',
599             key        => 'PRI',
600             default    => '',
601             extra    => 'auto_increment',
602         },
603         {
604             field    => 'listprice',
605             type    => 'varchar(10)',
606             null    => 'NULL',
607             key        => '',
608             default    => '',
609             extra    => '',
610         },
611         {
612             field    => 'invoiceprice',
613             type    => 'varchar(10)',
614             null    => 'NULL',
615             key        => '',
616             default    => '',
617             extra    => '',
618         },
619     ],
620     
621     accountlines =>  [
622         {
623             field    => 'notify_id',
624             type    => 'int(11)',
625             null    => 'NOT NULL',
626             key        => '',
627             default    => '0',
628             extra    => '',
629         },
630         {
631             field    => 'notify_level',
632             type    => 'int(2)',
633             null    => 'NOT NULL',
634             key        => '',
635             default    => '0',
636             extra    => '',
637         },
638     
639     ],
640     
641     borrowers => [
642         {    field => 'firstname',
643              type => 'text',
644              null => 'NULL',
645          },
646         {    field => 'initials',
647              type => 'text',
648              null => 'NULL',
649          },
650         {    field => 'B_email',
651              type => 'text',
652              null => 'NULL',
653              after => 'B_zipcode',
654          },
655          {
656             field => 'streetnumber', # street number (hidden if streettable table is empty)
657             type => 'char(10)',
658             null => 'NULL',
659             after => 'initials',
660         },
661         {
662             field => 'streettype', # street table, list builded from a system table
663             type => 'char(50)',
664             null => 'NULL',
665             after => 'streetnumber',
666         },
667         {    field => 'phone',
668              type => 'text',
669              null => 'NULL',
670          },
671         {
672             field => 'B_streetnumber', # street number (hidden if streettable table is empty)
673             type => 'char(10)',
674             null => 'NULL',
675             after => 'fax',
676         },
677         {
678             field => 'B_streettype', # street table, list builded from a system table
679             type => 'char(50)',
680             null => 'NULL',
681             after => 'B_streetnumber',
682         },
683         {
684             field => 'phonepro',
685             type => 'text',
686             null => 'NULL',
687             after => 'fax',
688         },
689         {
690             field => 'address2', # complement address
691             type => 'text',
692             null => 'NULL',
693             after => 'address',
694         },
695         {
696             field => 'emailpro',
697             type => 'text',
698             null => 'NULL',
699             after => 'fax',
700         },
701         {
702             field => 'contactfirstname', # contact's firstname
703             type => 'text',
704             null => 'NULL',
705             after => 'contactname',
706         },
707         {
708             field => 'contacttitle', # contact's title
709             type => 'text',
710             null => 'NULL',
711             after => 'contactfirstname',
712         },
713         {
714             field => 'branchcode',
715             type  => 'varchar(10)',
716             null  => 'NOT NULL',
717             default    => '',
718             extra => '',
719         },
720         {
721             field => 'categorycode',
722             type  => 'varchar(10)',
723             null  => 'NOT NULL',
724             default    => '',
725             extra => '',
726         }
727     ],
728     
729     biblioitems =>  [
730         {
731             field    => 'lcsort',
732             type    => 'varchar(25)',
733             null    => 'NULL',
734             key        => '',
735             default    => '',
736             extra    => '',
737         },
738         {
739             field    => 'ccode',
740             type    => 'varchar(4)',
741             null    => 'NULL',
742             key        => '',
743             default    => '',
744             extra    => '',
745         },
746         {
747             field   => 'dewey',
748             type    => 'varchar(30)',
749             null    => 'null',
750             default => '',
751             extra   => '',
752         },
753     ],
754     deletedbiblioitems => [
755         {
756             field   => 'dewey',
757             type    => 'varchar(30)',
758             null    => 'null',
759             default => '',
760             extra   => '',
761         },
762     ],
763     branches =>  [
764         {
765             field    => 'branchip',
766             type    => 'varchar(15)',
767             null    => 'NULL',
768             key        => '',
769             default    => '',
770             extra    => '',
771         },
772         {
773             field    => 'branchprinter',
774             type    => 'varchar(100)',
775             null    => 'NULL',
776             key        => '',
777             default    => '',
778             extra    => '',
779         },
780         {
781             field   => 'branchcode',
782             type    => 'varchar(10)',
783             null    => 'NOT NULL',
784             default => '',
785             extra   => '',
786         }
787     ],
788     branchtransfers =>[
789         {
790             field   => 'frombranch',
791             type    => 'VARCHAR(10)',
792             null    => 'NOT NULL',
793             key     => '',
794             default => '',
795             extra   => '',
796         },
797         {
798             field   => 'tobranch',
799             type    => 'VARCHAR(10)',
800             null    => 'NOT NULL',
801             key     => '',
802             default => '',
803         }
804     ],
805     
806     categories =>  [
807         {
808             field    => 'category_type',
809             type    => 'char(1)',
810             null    => 'NOT NULL',
811             key        => '',
812             default    => 'A',
813             extra    => '',
814         },
815         {
816             field   => 'categorycode',
817             type    => 'varchar(10)',
818             null    => 'NOT NULL',
819             key     => 'PRI',
820             default => '',
821             extra   => '',
822         },
823     ],
824     
825     deletedborrowers => [
826         {    field => 'firstname',
827              type => 'text',
828              null => 'NULL',
829          },
830         {    field => 'initials',
831              type => 'text',
832              null => 'NULL',
833          },
834         {    field => 'B_email',
835              type => 'text',
836              null => 'NULL',
837              after => 'B_zipcode',
838          },
839          {
840             field => 'streetnumber', # street number (hidden if streettable table is empty)
841             type => 'char(10)',
842             null => 'NULL',
843             after => 'initials',
844         },
845         {
846             field => 'streettype', # street table, list builded from a system table
847             type => 'char(50)',
848             null => 'NULL',
849             after => 'streetnumber',
850         },
851         {    field => 'phone',
852              type => 'text',
853              null => 'NULL',
854          },
855          {
856             field => 'B_streetnumber', # street number (hidden if streettable table is empty)
857             type => 'char(10)',
858             null => 'NULL',
859             after => 'fax',
860         },
861         {
862             field => 'B_streettype', # street table, list builded from a system table
863             type => 'char(50)',
864             null => 'NULL',
865             after => 'B_streetnumber',
866         },
867         {
868             field => 'phonepro',
869             type => 'text',
870             null => 'NULL',
871             after => 'fax',
872         },
873         {
874             field => 'address2', # complement address
875             type => 'text',
876             null => 'NULL',
877             after => 'address',
878         },
879         {
880             field => 'emailpro',
881             type => 'text',
882             null => 'NULL',
883             after => 'fax',
884         },
885         {
886             field => 'contactfirstname', # contact's firstname
887             type => 'text',
888             null => 'NULL',
889             after => 'contactname',
890         },
891         {
892             field => 'contacttitle', # contact's title
893             type => 'text',
894             null => 'NULL',
895             after => 'contactfirstname',
896         },
897     ],
898     
899     issues =>  [
900         {
901             field    => 'borrowernumber',
902             type    => 'int(11)',
903             null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
904             key        => '',
905             default    => '',
906             extra    => '',
907         },
908         {
909             field    => 'itemnumber',
910             type    => 'int(11)',
911             null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
912             key        => '',
913             default    => '',
914             extra    => '',
915         },
916         {
917             field   => 'branchcode',
918             type    => 'varchar(10)',
919             null    => 'NULL',
920             key     => '',
921             default => '',
922             extra   => '',
923         },
924         {
925             field   => 'issuedate',
926             type    => 'date',
927             null    => '',
928             key     => '',
929             default => '0000-00-00',
930             extra   => '',
931         },
932     ],
933     
934     items => [
935         {
936             field    => 'onloan',
937             type    => 'date',
938             null    => 'NULL',
939             key        => '',
940             default    => '0000-00-00',
941             extra    => '',
942         },
943         {
944             field    => 'cutterextra',
945             type    => 'varchar(45)',
946             null    => 'NULL',
947             key        => '',
948             default    => '',
949             extra    => '',
950         },
951         {
952             field    => 'issue_date',
953             type    => 'date',
954             null    => 'NULL',
955             key        => '',
956             default    => '',
957             extra    => '',
958         },
959         {
960             field    => 'homebranch',
961             type    => 'varchar(10)',
962             null    => 'NULL',
963             key        => '',
964             default    => '',
965             extra    => '',
966         },
967         {
968             field    => 'holdingbranch',
969             type    => 'varchar(10)',
970             null    => 'NULL',
971             key        => '',
972             default    => '',
973             extra    => '',
974         },
975         {
976             field    => 'itype',
977             type    => 'varchar(10)',
978             null    => 'NULL',
979             key        => '',
980             default    => '',
981             extra    => '',
982         },
983     ],
984     itemtypes => [
985         {
986             field  => 'itemtype',
987             type   => 'varchar(10)',
988             default    => '',
989             null   => 'NOT NULL',
990             key    => 'PRI',
991             extra  => 'UNIQUE',
992         },
993         {
994             field  => 'summary',
995             type   => 'TEXT',
996             null   => 'NULL',
997             key    => '',
998             extra  => '',
999         },
1000     ],
1001     marc_breeding => [
1002         {
1003             field => 'marc',
1004             type  => 'LONGBLOB',
1005             null  => 'NULL',
1006             key    => '',
1007             extra  => '',
1008         }
1009     ],
1010     marc_subfield_structure => [
1011         {
1012             field => 'defaultvalue',
1013             type  => 'TEXT',
1014             null  => 'NULL',
1015             key    => '',
1016             extra  => '',
1017         }
1018     ],
1019     opac_news => [
1020         {
1021             field  => 'expirationdate',
1022             type   => 'date',
1023             null   => 'null',
1024             key    => '',
1025             extra  => '',
1026         },
1027         {
1028             field   => 'number',
1029             type    => 'int(11)',
1030             null    => 'NULL',
1031             key     => '',
1032             default => '0',
1033             extra   => '',
1034         },
1035     ],
1036     reserves =>  [
1037         {
1038             field    => 'waitingdate',
1039             type    => 'date',
1040             null    => 'NULL',
1041             key        => '',
1042             default    => '',
1043             extra    => '',
1044         },
1045     ],
1046     serial => [
1047         {
1048             field   => 'notes',
1049             type    => 'TEXT',
1050             null    => 'NULL',
1051             key     => '',
1052             default => '',
1053             extra   => ''
1054         },
1055     ],
1056     shelfcontents => [
1057         {
1058             field => 'dateadded',
1059             type => 'timestamp',
1060             null    => 'NULL',
1061         },
1062     ],
1063     systempreferences =>  [
1064         {
1065             field    => 'value',
1066             type    => 'text',
1067             null    => 'NULL',
1068             key        => '',
1069             default    => '',
1070             extra    => '',
1071         },
1072         {
1073             field    => 'explanation',
1074             type    => 'text',
1075             null    => 'NULL',
1076             key        => '',
1077             default    => '',
1078             extra    => '',
1079         },
1080     ],
1081     suggestions => [
1082         {
1083             field   => 'reason',
1084             type    => 'text',
1085             null    => 'NULL',
1086             key     => ''  ,
1087             default => '',
1088             extra   =>    '',
1089         }
1090     ],
1091 );
1092
1093 my %indexes = (
1094 #    table => [
1095 #         {    indexname => 'index detail'
1096 #         }
1097 #    ],
1098     aqbooksellers => [
1099         {    indexname => 'PRIMARY',
1100             content => 'id',
1101             type => 'PRI',
1102         }
1103     ],
1104     aqbasket => [
1105         {    indexname => 'booksellerid',
1106             content => 'booksellerid',
1107         },
1108     ],
1109     aqorders => [
1110         {    indexname => 'basketno',
1111             content => 'basketno',
1112         },
1113     ],
1114     aqorderbreakdown => [
1115         {    indexname => 'ordernumber',
1116             content => 'ordernumber',
1117         },
1118         {    indexname => 'bookfundid',
1119             content => 'bookfundid',
1120         },
1121     ],
1122     biblioitems => [
1123         {    indexname => 'isbn',
1124             content => 'isbn',
1125         },
1126         {    indexname => 'publishercode',
1127             content => 'publishercode',
1128         },
1129     ],
1130     branches => [
1131         {
1132             indexname => 'branchcode',
1133             content   => 'branchcode',
1134             type => 'PRI',
1135         }
1136     ],
1137     branchrelations => [
1138         {
1139             indexname => 'PRIMARY',
1140             content => 'categorycode',
1141             type => 'PRI',
1142         }
1143     ],
1144     branchrelations => [
1145         {    indexname => 'PRIMARY',
1146             content => 'branchcode,categorycode',
1147             type => 'PRI',
1148         },
1149         {    indexname => 'branchcode',
1150             content => 'branchcode',
1151         },
1152         {    indexname => 'categorycode',
1153             content => 'categorycode',
1154         }
1155     ],
1156     currency => [
1157         {    indexname => 'PRIMARY',
1158             content => 'currency',
1159             type => 'PRI',
1160         }
1161     ],
1162     categories => [
1163         {
1164             indexname => 'categorycode',
1165             content   => 'categorycode',
1166         }
1167     ],
1168     items => [
1169         {    indexname => 'homebranch',
1170             content => 'homebranch',
1171         },
1172         {    indexname => 'holdingbranch',
1173             content => 'holdingbranch',
1174         }
1175     ],
1176     itemtypes => [
1177         {
1178             indexname => 'itemtype',
1179             content   => 'itemtype',
1180         }
1181     ],
1182     shelfcontents => [
1183         {    indexname => 'shelfnumber',
1184             content => 'shelfnumber',
1185         },
1186         {    indexname => 'itemnumber',
1187             content => 'itemnumber',
1188         }
1189     ],
1190         userflags => [
1191                 {       indexname => 'PRIMARY',
1192                         content => 'bit',
1193                         type => 'PRI',
1194                 }
1195         ]
1196 );
1197
1198 my %foreign_keys = (
1199 #    table => [
1200 #         {    key => 'the key in table' (must be indexed)
1201 #            foreigntable => 'the foreigntable name', # (the parent)
1202 #            foreignkey => 'the foreign key column(s)' # (in the parent)
1203 #            onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1204 #            onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1205 #         }
1206 #    ],
1207     branchrelations => [
1208         {    key => 'branchcode',
1209             foreigntable => 'branches',
1210             foreignkey => 'branchcode',
1211             onUpdate => 'CASCADE',
1212             onDelete => 'CASCADE',
1213         },
1214         {    key => 'categorycode',
1215             foreigntable => 'branchcategories',
1216             foreignkey => 'categorycode',
1217             onUpdate => 'CASCADE',
1218             onDelete => 'CASCADE',
1219         },
1220     ],
1221     shelfcontents => [
1222         {    key => 'shelfnumber',
1223             foreigntable => 'bookshelf',
1224             foreignkey => 'shelfnumber',
1225             onUpdate => 'CASCADE',
1226             onDelete => 'CASCADE',
1227         },
1228         {    key => 'itemnumber',
1229             foreigntable => 'items',
1230             foreignkey => 'itemnumber',
1231             onUpdate => 'CASCADE',
1232             onDelete => 'CASCADE',
1233         },
1234     ],
1235     # onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be
1236     # easily deleted, but branches/itemtype not too easy to empty...
1237     biblioitems => [
1238         {    key => 'biblionumber',
1239             foreigntable => 'biblio',
1240             foreignkey => 'biblionumber',
1241             onUpdate => 'CASCADE',
1242             onDelete => 'CASCADE',
1243         },
1244         {    key => 'itemtype',
1245             foreigntable => 'itemtypes',
1246             foreignkey => 'itemtype',
1247             onUpdate => 'CASCADE',
1248             onDelete => 'RESTRICT',
1249         },
1250     ],
1251     items => [
1252         {    key => 'biblioitemnumber',
1253             foreigntable => 'biblioitems',
1254             foreignkey => 'biblioitemnumber',
1255             onUpdate => 'CASCADE',
1256             onDelete => 'CASCADE',
1257         },
1258         {    key => 'homebranch',
1259             foreigntable => 'branches',
1260             foreignkey => 'branchcode',
1261             onUpdate => 'CASCADE',
1262             onDelete => 'RESTRICT',
1263         },
1264         {    key => 'holdingbranch',
1265             foreigntable => 'branches',
1266             foreignkey => 'branchcode',
1267             onUpdate => 'CASCADE',
1268             onDelete => 'RESTRICT',
1269         },
1270     ],
1271     aqbasket => [
1272         {    key => 'booksellerid',
1273             foreigntable => 'aqbooksellers',
1274             foreignkey => 'id',
1275             onUpdate => 'CASCADE',
1276             onDelete => 'RESTRICT',
1277         },
1278     ],
1279     aqorders => [
1280         {    key => 'basketno',
1281             foreigntable => 'aqbasket',
1282             foreignkey => 'basketno',
1283             onUpdate => 'CASCADE',
1284             onDelete => 'CASCADE',
1285         },
1286         {    key => 'biblionumber',
1287             foreigntable => 'biblio',
1288             foreignkey => 'biblionumber',
1289             onUpdate => 'SET NULL',
1290             onDelete => 'SET NULL',
1291         },
1292     ],
1293     aqbooksellers => [
1294         {    key => 'listprice',
1295             foreigntable => 'currency',
1296             foreignkey => 'currency',
1297             onUpdate => 'CASCADE',
1298             onDelete => 'CASCADE',
1299         },
1300         {    key => 'invoiceprice',
1301             foreigntable => 'currency',
1302             foreignkey => 'currency',
1303             onUpdate => 'CASCADE',
1304             onDelete => 'CASCADE',
1305         },
1306     ],
1307     aqorderbreakdown => [
1308         {    key => 'ordernumber',
1309             foreigntable => 'aqorders',
1310             foreignkey => 'ordernumber',
1311             onUpdate => 'CASCADE',
1312             onDelete => 'CASCADE',
1313         },
1314         {    key => 'bookfundid',
1315             foreigntable => 'aqbookfund',
1316             foreignkey => 'bookfundid',
1317             onUpdate => 'CASCADE',
1318             onDelete => 'CASCADE',
1319         },
1320     ],
1321     branchtransfers => [
1322         {    key => 'frombranch',
1323             foreigntable => 'branches',
1324             foreignkey => 'branchcode',
1325             onUpdate => 'CASCADE',
1326             onDelete => 'CASCADE',
1327         },
1328         {    key => 'tobranch',
1329             foreigntable => 'branches',
1330             foreignkey => 'branchcode',
1331             onUpdate => 'CASCADE',
1332             onDelete => 'CASCADE',
1333         },
1334         {    key => 'itemnumber',
1335             foreigntable => 'items',
1336             foreignkey => 'itemnumber',
1337             onUpdate => 'CASCADE',
1338             onDelete => 'CASCADE',
1339         },
1340     ],
1341     issuingrules => [
1342         {    key => 'categorycode',
1343             foreigntable => 'categories',
1344             foreignkey => 'categorycode',
1345             onUpdate => 'CASCADE',
1346             onDelete => 'CASCADE',
1347         },
1348         {    key => 'itemtype',
1349             foreigntable => 'itemtypes',
1350             foreignkey => 'itemtype',
1351             onUpdate => 'CASCADE',
1352             onDelete => 'CASCADE',
1353         },
1354     ],
1355     issues => [    # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
1356     # for stat purposes
1357         {    key => 'borrowernumber',
1358             foreigntable => 'borrowers',
1359             foreignkey => 'borrowernumber',
1360             onUpdate => 'SET NULL',
1361             onDelete => 'SET NULL',
1362         },
1363         {    key => 'itemnumber',
1364             foreigntable => 'items',
1365             foreignkey => 'itemnumber',
1366             onUpdate => 'SET NULL',
1367             onDelete => 'SET NULL',
1368         },
1369     ],
1370     reserves => [
1371         {    key => 'borrowernumber',
1372             foreigntable => 'borrowers',
1373             foreignkey => 'borrowernumber',
1374             onUpdate => 'CASCADE',
1375             onDelete => 'CASCADE',
1376         },
1377         {    key => 'biblionumber',
1378             foreigntable => 'biblio',
1379             foreignkey => 'biblionumber',
1380             onUpdate => 'CASCADE',
1381             onDelete => 'CASCADE',
1382         },
1383         {    key => 'itemnumber',
1384             foreigntable => 'items',
1385             foreignkey => 'itemnumber',
1386             onUpdate => 'CASCADE',
1387             onDelete => 'CASCADE',
1388         },
1389         {    key => 'branchcode',
1390             foreigntable => 'branches',
1391             foreignkey => 'branchcode',
1392             onUpdate => 'CASCADE',
1393             onDelete => 'CASCADE',
1394         },
1395     ],
1396     borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1397     # but prevent deleting a branch as soon as it has 1 borrower !
1398         {    key => 'categorycode',
1399             foreigntable => 'categories',
1400             foreignkey => 'categorycode',
1401             onUpdate => 'RESTRICT',
1402             onDelete => 'RESTRICT',
1403         },
1404         {    key => 'branchcode',
1405             foreigntable => 'branches',
1406             foreignkey => 'branchcode',
1407             onUpdate => 'RESTRICT',
1408             onDelete => 'RESTRICT',
1409         },
1410     ],
1411     deletedborrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1412     # but prevent deleting a branch as soon as it has 1 borrower !
1413         {    key => 'categorycode',
1414             foreigntable => 'categories',
1415             foreignkey => 'categorycode',
1416             onUpdate => 'RESTRICT',
1417             onDelete => 'RESTRICT',
1418         },
1419         {    key => 'branchcode',
1420             foreigntable => 'branches',
1421             foreignkey => 'branchcode',
1422             onUpdate => 'RESTRICT',
1423             onDelete => 'RESTRICT',
1424         },
1425     ],
1426     accountlines => [
1427         {    key => 'borrowernumber',
1428             foreigntable => 'borrowers',
1429             foreignkey => 'borrowernumber',
1430             onUpdate => 'CASCADE',
1431             onDelete => 'CASCADE',
1432         },
1433         {    key => 'itemnumber',
1434             foreigntable => 'items',
1435             foreignkey => 'itemnumber',
1436             onUpdate => 'SET NULL',
1437             onDelete => 'SET NULL',
1438         },
1439     ],
1440     auth_tag_structure => [
1441         {    key => 'authtypecode',
1442             foreigntable => 'auth_types',
1443             foreignkey => 'authtypecode',
1444             onUpdate => 'CASCADE',
1445             onDelete => 'CASCADE',
1446         },
1447     ],
1448     # FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
1449 );
1450
1451
1452 # column changes
1453 my %column_change = (
1454     # table
1455     borrowers => [
1456                 {
1457                     from => 'emailaddress',
1458                     to => 'email',
1459                     after => 'city',
1460                 },
1461                 {
1462                     from => 'streetaddress',
1463                     to => 'address',
1464                     after => 'initials',
1465                 },
1466                 {
1467                     from => 'faxnumber',
1468                     to => 'fax',
1469                     after => 'phone',
1470                 },
1471                 {
1472                     from => 'textmessaging',
1473                     to => 'opacnote',
1474                     after => 'userid',
1475                 },
1476                 {
1477                     from => 'altnotes',
1478                     to => 'contactnote',
1479                     after => 'opacnote',
1480                 },
1481                 {
1482                     from => 'physstreet',
1483                     to => 'B_address',
1484                     after => 'fax',
1485                 },
1486                 {
1487                     from => 'streetcity',
1488                     to => 'B_city',
1489                     after => 'B_address',
1490                 },
1491                 {
1492                     from => 'phoneday',
1493                     to => 'mobile',
1494                     after => 'phone',
1495                 },
1496                 {
1497                     from => 'zipcode',
1498                     to => 'zipcode',
1499                     after => 'city',
1500                 },
1501                 {
1502                     from => 'homezipcode',
1503                     to => 'B_zipcode',
1504                     after => 'B_city',
1505                 },
1506                 {
1507                     from => 'altphone',
1508                     to => 'B_phone',
1509                     after => 'B_zipcode',
1510                 },
1511                 {
1512                     from => 'expiry',
1513                     to => 'dateexpiry',
1514                     after => 'dateenrolled',
1515                 },
1516                 {
1517                     from => 'guarantor',
1518                     to => 'guarantorid',
1519                     after => 'contactname',
1520                 },
1521                 {
1522                     from => 'altrelationship',
1523                     to => 'relationship',
1524                     after => 'borrowernotes',
1525                 },
1526             ],
1527
1528     deletedborrowers => [
1529                 {
1530                     from => 'emailaddress',
1531                     to => 'email',
1532                     after => 'city',
1533                 },
1534                 {
1535                     from => 'streetaddress',
1536                     to => 'address',
1537                     after => 'initials',
1538                 },
1539                 {
1540                     from => 'faxnumber',
1541                     to => 'fax',
1542                     after => 'phone',
1543                 },
1544                 {
1545                     from => 'textmessaging',
1546                     to => 'opacnote',
1547                     after => 'userid',
1548                 },
1549                 {
1550                     from => 'altnotes',
1551                     to => 'contactnote',
1552                     after => 'opacnote',
1553                 },
1554                 {
1555                     from => 'physstreet',
1556                     to => 'B_address',
1557                     after => 'fax',
1558                 },
1559                 {
1560                     from => 'streetcity',
1561                     to => 'B_city',
1562                     after => 'B_address',
1563                 },
1564                 {
1565                     from => 'phoneday',
1566                     to => 'mobile',
1567                     after => 'phone',
1568                 },
1569                 {
1570                     from => 'zipcode',
1571                     to => 'zipcode',
1572                     after => 'city',
1573                 },
1574                 {
1575                     from => 'homezipcode',
1576                     to => 'B_zipcode',
1577                     after => 'B_city',
1578                 },
1579                 {
1580                     from => 'altphone',
1581                     to => 'B_phone',
1582                     after => 'B_zipcode',
1583                 },
1584                 {
1585                     from => 'expiry',
1586                     to => 'dateexpiry',
1587                     after => 'dateenrolled',
1588                 },
1589                 {
1590                     from => 'guarantor',
1591                     to => 'guarantorid',
1592                     after => 'contactname',
1593                 },
1594                 {
1595                     from => 'altrelationship',
1596                     to => 'relationship',
1597                     after => 'borrowernotes',
1598                 },
1599             ],
1600         );
1601     
1602
1603 # MOVE all tables TO UTF-8 and innoDB
1604 $sth = $dbh->prepare("show table status");
1605 $sth->execute;
1606 while ( my $table = $sth->fetchrow_hashref ) {
1607     next if $table->{Name} eq 'marc_word';
1608     next if $table->{Name} eq 'marc_subfield_table';
1609     next if $table->{Name} eq 'auth_word';
1610     next if $table->{Name} eq 'auth_subfield_table';
1611      if ($table->{Engine} ne 'InnoDB') {
1612          $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
1613          print "moving $table->{Name} to InnoDB\n";
1614      }
1615     unless ($table->{Collation} =~ /^utf8/) {
1616          print "moving $table->{Name} to utf8\n";
1617         $dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
1618         $dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
1619         # FIXME : maybe a ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8 would be better, def char set seems to work fine. If any problem encountered, let's try with convert !
1620     } else {
1621     }
1622 }
1623
1624
1625 foreach my $table (keys %column_change) {
1626     $sth = $dbh->prepare("show columns from $table");
1627     $sth->execute();
1628     undef %types;
1629     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1630     {
1631         $types{$column}->{type} ="$type";
1632         $types{$column}->{null} = "$null";
1633         $types{$column}->{key} = "$key";
1634         $types{$column}->{default} = "$default";
1635         $types{$column}->{extra} = "$extra";
1636     }    # while
1637     my $tablerows = $column_change{$table};
1638     foreach my $row ( @$tablerows ) {
1639         if ($types{$row->{from}}->{type}) {
1640             print "altering $table $row->{from} to $row->{to}\n";
1641             # ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
1642 #             alter table `borrowers` change `faxnumber` `fax` type text  null after phone
1643             my $sql =
1644                 "alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
1645                 ($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
1646                 ($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
1647                 "$types{$row->{from}}->{extra} after $row->{after} ";
1648 #             print "$sql";
1649             $dbh->do($sql);
1650         }
1651     }
1652 }
1653
1654 # Enter here the field you want to delete from DB.
1655 # FIXME :: there is a %uselessfield before which seems doing the same things.
1656 my %fieldtodelete = (
1657     # tablename => [fieldname1,fieldname2,...]
1658
1659 ); # %fielddelete
1660
1661 print "removing some unused fields...\n";
1662 foreach my $table ( keys %fieldtodelete ) {
1663     foreach my $field ( @{$fieldtodelete{$table}} ){
1664         print "removing ".$field." from ".$table;
1665         my $sth = $dbh->prepare("ALTER TABLE $table DROP $field");
1666         $sth->execute;
1667         if ( $sth->err ) {
1668             print "Error : $sth->errstr \n";
1669         }
1670     }
1671 }
1672
1673 # Enter here the line you want to remove from DB.
1674 my %linetodelete = (
1675     # table name => where clause.
1676     userflags => "bit = 8", # delete the 'reserveforself' flags
1677     
1678 ); # %linetodelete
1679
1680 #-------------------
1681 # Initialize
1682
1683 # Start checking
1684
1685 # Get version of MySQL database engine.
1686 my $mysqlversion = `mysqld --version`;
1687 $mysqlversion =~ /Ver (\S*) /;
1688 $mysqlversion = $1;
1689 if ( $mysqlversion ge '3.23' ) {
1690     print "Could convert to MyISAM database tables...\n" unless $silent;
1691 }
1692
1693 #---------------------------------
1694 # Tables
1695
1696 # Collect all tables into a list
1697 $sth = $dbh->prepare("show tables");
1698 $sth->execute;
1699 while ( my ($table) = $sth->fetchrow ) {
1700     $existingtables{$table} = 1;
1701 }
1702
1703
1704 # Now add any missing tables
1705 foreach $table ( keys %requiretables ) {
1706     unless ( $existingtables{$table} ) {
1707     print "Adding $table table...\n" unless $silent;
1708         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
1709         $sth->execute;
1710         if ( $sth->err ) {
1711             print "Error : $sth->errstr \n";
1712             $sth->finish;
1713         }    # if error
1714     }    # unless exists
1715 }    # foreach
1716
1717 #---------------------------------
1718 # Columns
1719
1720 foreach $table ( keys %requirefields ) {
1721     print "Check table $table\n" if $debug and not $silent;
1722     $sth = $dbh->prepare("show columns from $table");
1723     $sth->execute();
1724     undef %types;
1725     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1726     {
1727         $types{$column} = $type;
1728     }    # while
1729     foreach $column ( keys %{ $requirefields{$table} } ) {
1730         print "  Check column $column  [$types{$column}]\n" if $debug and not $silent;
1731         if ( !$types{$column} ) {
1732
1733             # column doesn't exist
1734             print "Adding $column field to $table table...\n" unless $silent;
1735             $query = "alter table $table
1736             add column $column " . $requirefields{$table}->{$column};
1737             print "Execute: $query\n" if $debug;
1738             my $sti = $dbh->prepare($query);
1739             $sti->execute;
1740             if ( $sti->err ) {
1741                 print "**Error : $sti->errstr \n";
1742                 $sti->finish;
1743             }    # if error
1744         }    # if column
1745     }    # foreach column
1746 }    # foreach table
1747
1748 foreach $table ( keys %fielddefinitions ) {
1749     print "Check table $table\n" if $debug;
1750     $sth = $dbh->prepare("show columns from $table");
1751     $sth->execute();
1752     my $definitions;
1753     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1754     {
1755         $definitions->{$column}->{type}    = $type;
1756         $definitions->{$column}->{null}    = $null;
1757         $definitions->{$column}->{null}    = 'NULL' if $null eq 'YES';
1758         $definitions->{$column}->{key}     = $key;
1759         $definitions->{$column}->{default} = $default;
1760         $definitions->{$column}->{extra}   = $extra;
1761     }    # while
1762     my $fieldrow = $fielddefinitions{$table};
1763     foreach my $row (@$fieldrow) {
1764         my $field   = $row->{field};
1765         my $type    = $row->{type};
1766         my $null    = $row->{null};
1767 #         $null    = 'YES' if $row->{null} eq 'NULL';
1768         my $key     = $row->{key};
1769         my $default = $row->{default};
1770 #         $default="''" unless $default;
1771         my $extra   = $row->{extra};
1772         my $def     = $definitions->{$field};
1773         my $after    = ($row->{after}?" after ".$row->{after}:"");
1774
1775         unless ( $type eq $def->{type}
1776             && $null eq $def->{null}
1777             && $key eq $def->{key}
1778             && $extra eq $def->{extra} )
1779         {
1780             if ( $null eq '' ) {
1781                 $null = 'NOT NULL';
1782             }
1783             if ( $key eq 'PRI' ) {
1784                 $key = 'PRIMARY KEY';
1785             }
1786             unless ( $extra eq 'auto_increment' ) {
1787                 $extra = '';
1788             }
1789     
1790             # if it's a new column use "add", if it's an old one, use "change".
1791             my $action;
1792             if ($definitions->{$field}->{type}) {
1793                 $action="change $field"
1794             } else {
1795                 $action="add";
1796             }
1797 # if it's a primary key, drop the previous pk, before altering the table
1798             print "  alter or create $field in $table\n" unless $silent;
1799             my $query;
1800             if ($key ne 'PRIMARY KEY') {
1801 #                 warn "alter table $table $action $field $type $null $key $extra default $default $after";
1802                 $query = "alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1803             } else {
1804 #             warn "alter table $table drop primary key, $action $field $type $null $key $extra default $default $after";
1805                  # something strange : for indexes UNIQUE, they are reported as primary key here.
1806                  # but if you try to run with drop primary key, it fails.
1807                  # thus, we run the query twice, one will fail, one will succeed.
1808                  # strange...
1809                 $query="alter table $table drop primary key, $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1810                 $query="alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1811             }
1812             $dbh->do($query);
1813         }
1814     }
1815 }
1816
1817 print "removing some unused data...\n";
1818 foreach my $table ( keys %linetodelete ) {
1819     foreach my $where ( @{linetodelete{$table}} ){
1820         print "DELETE FROM ".$table." where ".$where;
1821         print "\n";
1822         my $sth = $dbh->prepare("DELETE FROM $table where $where");
1823         $sth->execute;
1824         if ( $sth->err ) {
1825             print "Error : $sth->errstr \n";
1826         }
1827     }
1828 }
1829
1830 # Populate tables with required data
1831
1832 # synch table and deletedtable.
1833 foreach my $table (('borrowers','items','biblio','biblioitems')) {
1834     my %deletedborrowers;
1835     print "synch'ing $table and deleted$table\n";
1836     $sth = $dbh->prepare("show columns from deleted$table");
1837     $sth->execute;
1838     while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1839         $deletedborrowers{$column}=1;
1840     }
1841     $sth = $dbh->prepare("show columns from $table");
1842     $sth->execute;
1843     my $previous;
1844     while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1845         unless ($deletedborrowers{$column}) {
1846             my $newcol="alter table deleted$table add $column $type";
1847             if ($null eq 'YES') {
1848                 $newcol .= " NULL ";
1849             } else {
1850                 $newcol .= " NOT NULL ";
1851             }
1852             $newcol .= "default ".$dbh->quote($default) if $default;
1853             $newcol .= " after $previous" if $previous;
1854             $previous=$column;
1855             print "creating column $column\n";
1856             $dbh->do($newcol);
1857         }
1858     }
1859 }
1860 #
1861 # update publisheddate 
1862 #
1863 $sth = $dbh->prepare("select count(*) from serial where publisheddate is NULL");
1864 $sth->execute;
1865 my ($emptypublished) = $sth->fetchrow;
1866 if ($emptypublished) {
1867     print "Updating publisheddate\n";
1868     $dbh->do("update serial set publisheddate=planneddate where publisheddate is NULL");
1869 }
1870 foreach my $table ( keys %tabledata ) {
1871     print "Checking for data required in table $table...\n" unless $silent;
1872     my $tablerows = $tabledata{$table};
1873     foreach my $row (@$tablerows) {
1874         my $uniquefieldrequired = $row->{uniquefieldrequired};
1875         my $uniquevalue         = $row->{$uniquefieldrequired};
1876         my $forceupdate         = $row->{forceupdate};
1877         my $sth                 =
1878           $dbh->prepare(
1879 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
1880         );
1881         $sth->execute($uniquevalue);
1882         if ($sth->rows) {
1883             foreach my $field (keys %$forceupdate) {
1884                 if ($forceupdate->{$field}) {
1885                     my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
1886                     $sth->execute($row->{$field}, $uniquevalue);
1887                 }
1888             }
1889         } else {
1890             print "Adding row to $table: " unless $silent;
1891             my @values;
1892             my $fieldlist;
1893             my $placeholders;
1894             foreach my $field ( keys %$row ) {
1895                 next if $field eq 'uniquefieldrequired';
1896                 next if $field eq 'forceupdate';
1897                 my $value = $row->{$field};
1898                 push @values, $value;
1899                 print "  $field => $value" unless $silent;
1900                 $fieldlist .= "$field,";
1901                 $placeholders .= "?,";
1902             }
1903             print "\n" unless $silent;
1904             $fieldlist    =~ s/,$//;
1905             $placeholders =~ s/,$//;
1906             print "insert into $table ($fieldlist) values ($placeholders)";
1907             my $sth =
1908             $dbh->prepare(
1909                 "insert into $table ($fieldlist) values ($placeholders)");
1910             $sth->execute(@values);
1911         }
1912     }
1913 }
1914
1915 #
1916 # check indexes and create them when needed
1917 #
1918 print "Checking for index required...\n" unless $silent;
1919 foreach my $table ( keys %indexes ) {
1920     #
1921     # read all indexes from $table
1922     #
1923     $sth = $dbh->prepare("show index from $table");
1924     $sth->execute;
1925     my %existingindexes;
1926     while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
1927         $existingindexes{$key_name} = 1;
1928     }
1929     # read indexes to check
1930     my $tablerows = $indexes{$table};
1931     foreach my $row (@$tablerows) {
1932         my $key_name=$row->{indexname};
1933         if ($existingindexes{$key_name} eq 1) {
1934 #             print "$key_name existing";
1935         } else {
1936             print "\tCreating index $key_name in $table\n";
1937             my $sql;
1938             if ($row->{indexname} eq 'PRIMARY') {
1939                 $sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
1940             } else {
1941                 $sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
1942             }
1943              $dbh->do($sql);
1944             print "Error $sql : $dbh->err \n" if $dbh->err;
1945         }
1946     }
1947 }
1948
1949 #
1950 # check foreign keys and create them when needed
1951 #
1952 print "Checking for foreign keys required...\n" unless $silent;
1953 foreach my $table ( keys %foreign_keys ) {
1954     #
1955     # read all indexes from $table
1956     #
1957     $sth = $dbh->prepare("show table status like '$table'");
1958     $sth->execute;
1959     my $stat = $sth->fetchrow_hashref;
1960     # read indexes to check
1961     my $tablerows = $foreign_keys{$table};
1962     foreach my $row (@$tablerows) {
1963         my $foreign_table=$row->{foreigntable};
1964         if ($stat->{'Comment'} =~/$foreign_table/) {
1965 #             print "$foreign_table existing\n";
1966         } else {
1967             print "\tCreating foreign key $foreign_table in $table\n";
1968             # first, drop any orphan value in child table
1969             if ($row->{onDelete} ne "RESTRICT") {
1970                 my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
1971                 $dbh->do($sql);
1972                 print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
1973             }
1974             my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
1975             $sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
1976             $sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
1977             $dbh->do($sql);
1978             if ($dbh->err) {
1979                 print "====================
1980 An error occured during :
1981 \t$sql
1982 It probably means there is something wrong in your DB : a row ($table.$row->{key}) refers to a value in $row->{foreigntable}.$row->{foreignkey} that does not exist. solve the problem and run updater again (or just the previous SQL statement).
1983 You can find those values with select
1984 \t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
1985 ====================\n
1986 ";
1987             }
1988         }
1989     }
1990 }
1991 # now drop useless tables
1992 foreach $table ( @TableToDelete ) {
1993     if ( $existingtables{$table} ) {
1994         print "Dropping unused table $table\n" if $debug and not $silent;
1995         $dbh->do("drop table $table");
1996         if ( $dbh->err ) {
1997             print "Error : $dbh->errstr \n";
1998         }
1999     }
2000 }
2001
2002 #
2003 # SPECIFIC STUFF
2004 #
2005 #
2006 # create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
2007 #
2008
2009 # 1st, get how many biblio we will have to do...
2010 $sth = $dbh->prepare('select count(*) from marc_biblio');
2011 $sth->execute;
2012 my ($totaltodo) = $sth->fetchrow;
2013
2014 $sth = $dbh->prepare("show columns from biblio");
2015 $sth->execute();
2016 my $definitions;
2017 my $bibliofwexist=0;
2018 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
2019     $bibliofwexist=1 if $column eq 'frameworkcode';
2020 }
2021 unless ($bibliofwexist) {
2022     print "moving biblioframework to biblio table\n";
2023     $dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
2024     $sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
2025     $sth->execute;
2026     my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
2027     my $totaldone=0;
2028     while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
2029         $sth_update->execute($frameworkcode,$biblionumber);
2030         $totaldone++;
2031         print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
2032     }
2033     print "\rdone\n";
2034 }
2035
2036 # at last, remove useless fields
2037 foreach $table ( keys %uselessfields ) {
2038     my @fields = split /,/,$uselessfields{$table};
2039     my $fields;
2040     my $exists;
2041     foreach my $fieldtodrop (@fields) {
2042         $fieldtodrop =~ s/\t//g;
2043         $fieldtodrop =~ s/\n//g;
2044         $exists =0;
2045         $sth = $dbh->prepare("show columns from $table");
2046         $sth->execute;
2047         while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
2048         {
2049             $exists =1 if ($column eq $fieldtodrop);
2050         }
2051         if ($exists) {
2052             print "deleting $fieldtodrop field in $table...\n" unless $silent;
2053             my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
2054             $sth->execute;
2055         }
2056     }
2057 }    # foreach
2058
2059 #
2060 # Changing aqbookfund's primary key 
2061 #
2062 $sth=$dbh->prepare("ALTER TABLE `aqbookfund` DROP PRIMARY KEY , ADD PRIMARY KEY ( `bookfundid` , `branchcode` ) ;");
2063 $sth->execute;
2064
2065 $sth->finish;
2066
2067 exit;
2068
2069 # $Log$
2070 # Revision 1.169  2007/06/26 09:23:26  tipaul
2071 # improving OpacBrowser systempref explanation
2072 #
2073 # Revision 1.168  2007/06/25 15:02:31  tipaul
2074 # missing field declaration
2075 #
2076 # Revision 1.167  2007/06/15 13:09:08  toins
2077 # bugfix : bibliotitems.dewey & deletedbiblioitems.dewey mustn't be double(8,6).
2078 #
2079 # Revision 1.166  2007/06/08 09:40:12  toins
2080 # bug fix : items.homebranch must be VARCHAR(10)
2081 #
2082 # Revision 1.165  2007/05/23 16:33:10  tipaul
2083 # skip move to innoDB for the 4 22 tables, that are used to store MARC records, are useless in Koha 3.0 The process is very very long, so the updatedatabase should speed up a lot (by long I mean 1 hour on my Dual core with SCSI disk, for a 50 000 biblios long table
2084 #
2085 # Revision 1.164  2007/05/04 16:24:09  tipaul
2086 # various bugfixes on parameters modules + adding default NoZebraIndexes systempreference if it's empty
2087 #
2088 # Revision 1.163  2007/05/02 16:44:31  tipaul
2089 # NoZebra SQL index management :
2090 # * adding 3 subs in Biblio.pm
2091 # - GetNoZebraIndexes, that get the index structure in a new systempreference (added with this commit)
2092 # - _DelBiblioNoZebra, that retrieve all index entries for a biblio and remove in a variable the biblio reference
2093 # - _AddBiblioNoZebra, that add index entries for a biblio.
2094 # Note that the 2 _Add and _Del subs work only in a hash variable, to speed up things in case of a modif (ie : delete+add). The effective SQL update is done in the ModZebra sub (that existed before, and dealed with zebra index).
2095 # I think the code has to be more deeply tested, but it works at least partially.
2096 #
2097 # Revision 1.162  2007/04/30 16:16:50  tipaul
2098 # bugfix for updatedatabase : when there is no default value (NULL fields) + removing bibliothesaurus table+adding NoZebra systempref (False by default)
2099 #
2100 # Revision 1.161  2007/04/13 16:27:55  hdl
2101 # Adding Version variable to systempreferences.
2102 #
2103 # Revision 1.160  2007/03/19 18:35:13  toins
2104 #  - adding default value in marc_subfield_structure.
2105 #  - now marc_subfields_structure displays subfields in tab view.
2106 #
2107 # Revision 1.159  2007/03/16 01:25:09  kados
2108 # Using my precrash CVS copy I did the following:
2109 #
2110 # cvs -z3 -d:ext:kados@cvs.savannah.nongnu.org:/sources/koha co -P koha
2111 # find koha.precrash -type d -name "CVS" -exec rm -v {} \;
2112 # cp -r koha.precrash/* koha/
2113 # cd koha/
2114 # cvs commit
2115 #
2116 # This should in theory put us right back where we were before the crash
2117 #
2118 # Revision 1.159  2007/03/12 17:52:30  rych
2119 # add pri key to userflags
2120 #
2121 # Revision 1.158  2007/03/09 15:14:57  tipaul
2122 # rel_3_0 moved to HEAD
2123 #
2124 # Revision 1.157.2.56  2007/01/31 16:22:54  btoumi
2125 # -add possibility to use isbn with length of 13 characters
2126 # for  Import datas in the reservoir.
2127 # -modify isbn field in marc_breeding table (varchar 13)
2128 # -add isbn filter (no - )when u read a notice from reservoir
2129 # -add filter to have right field 100
2130 #
2131 # Revision 1.157.2.55  2007/01/30 10:50:19  tipaul
2132 # adding 2 usefull indexes to biblioitems table
2133 #
2134 # Revision 1.157.2.54  2007/01/29 16:45:52  toins
2135 # * adding a new default authorised value : SUGGEST.
2136 # SUGGEST give some reasons to accept or reject a suggestion.
2137 #
2138 # * default value for borrowersMandatoryfield syspref is now "cardnumber|surname|adress"
2139 #
2140 # Revision 1.157.2.53  2007/01/26 20:48:37  hdl
2141 # Serials management : Bugfixes + improvements.
2142 # - Partial dates are now managed
2143 # - next Date Calculation with irregularity tested for 1 week and 1 month.
2144 # - manage if subscription is abouttoexpire or expired.
2145 # - Adding some information on serials pages about subscription.
2146 # - Managing irregularity with numbers.
2147 # - Adding Internal Notes in subscription management.
2148 # - Repeating Button above pages.
2149 #
2150 # Please run Updatedatabase to change irregularity and add internalnotes field  to subscription
2151 #
2152 # Revision 1.157.2.52  2007/01/24 13:57:26  tipaul
2153 # - setting supplierid to auto_increment (HDL : could you check that is works, i'm not 100% sure)
2154 # - removing 22 -> 30 marc_subfield_table -> marcxml stuff, it's now in misc/migration_tools/22_to_30/
2155 #
2156 # Revision 1.157.2.51  2007/01/18 09:58:45  tipaul
2157 # defaulting NOT NULL fields (to '')
2158 #
2159 # Revision 1.157.2.50  2007/01/18 09:39:21  tipaul
2160 # issuedate must be defaulted with ' '
2161 #
2162 # Revision 1.157.2.49  2007/01/18 09:37:30  tipaul
2163 # removing 2 field definitions that were here twice
2164 #
2165 # Revision 1.157.2.48  2007/01/15 09:55:40  toins
2166 # adding a new logging systempref : FinesLog.
2167 #
2168 # Revision 1.157.2.47  2007/01/12 18:09:49  toins
2169 # LetterLog added
2170 #
2171 # Revision 1.157.2.46  2007/01/11 14:35:39  tipaul
2172 # adding Opac Browser feature : the build_browser_and_cloud.pl script will :
2173 # - fill the browser table, that enable browsing, digit by digit of a given category, the catalogue. A complete dewey classification is provided in the script, active only for french libraries, of course (although, for instance, the script check that the catalogue is in english for developping convenience)
2174 # - fill the tags table, that contains the subject cloud.
2175 #
2176 # The cloud part is a copy of the previous build_tags.pl script that can be deleted : those 2 scripts require to parse all the catalogue to extract interesting data, so they are long. It's useless to parse the catalogue twice !
2177 #
2178 # The commit also add the systempreference to hide/show the OpacBrowse in database & in systempref management script.
2179 #
2180 # IMPROVEMENTS to do :
2181 # - the script that builds the tables can be improved to update only last week biblios (at the price of a small error in value links, but it's not a problem).
2182 # - add, in parameters section, a place to edit browser descriptions. The build script has to be updated to to avoid deleting existing browser descriptions.
2183 #
2184 # Revision 1.157.2.45  2007/01/10 16:52:52  toins
2185 # Value for Log Features syspref are set to 0 by default.
2186 #
2187 # Revision 1.157.2.44  2007/01/10 16:31:15  toins
2188 # new systems preferences :
2189 #  - CataloguingLog (log the update/creation/deletion of a notice if set to 1)
2190 #  - BorrowersLog ( idem for borrowers )
2191 #  - IssueLog (log all issue if set to 1)
2192 #  - ReturnLog (log all return if set to 1)
2193 #  - SusbcriptionLog (log all creation/deletion/update of a subcription)
2194 #
2195 # All of theses are in a new tab called 'LOGFeatures' in systempreferences.pl
2196 #
2197 # Revision 1.157.2.43  2007/01/10 14:13:17  toins
2198 # opac_news.displayed is replaced by opac_news.number.
2199 # This field say how are ordered the news on the template.
2200 #
2201 # Revision 1.157.2.42  2007/01/09 14:09:01  toins
2202 # 2 field added to opac_news.('expirationdate' and 'displayed').