add zipcode list
[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',
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 (needs to set misc/cronjob/build_browser.pl)',
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        => '',
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_subfield_structure => [
1002         {
1003             field => 'defaultvalue',
1004             type  => 'TEXT',
1005             null  => 'NULL',
1006             key    => '',
1007             extra  => '',
1008         }
1009     ],
1010     opac_news => [
1011         {
1012             field  => 'expirationdate',
1013             type   => 'date',
1014             null   => 'null',
1015             key    => '',
1016             extra  => '',
1017         },
1018         {
1019             field   => 'number',
1020             type    => 'int(11)',
1021             null    => 'NULL',
1022             key     => '',
1023             default => '0',
1024             extra   => '',
1025         },
1026     ],
1027     reserves =>  [
1028         {
1029             field    => 'waitingdate',
1030             type    => 'date',
1031             null    => 'NULL',
1032             key        => '',
1033             default    => '',
1034             extra    => '',
1035         },
1036     ],
1037     serial => [
1038         {
1039             field   => 'notes',
1040             type    => 'TEXT',
1041             null    => 'NULL',
1042             key     => '',
1043             default => '',
1044             extra   => ''
1045         },
1046     ],
1047     shelfcontents => [
1048         {
1049             field => 'dateadded',
1050             type => 'timestamp',
1051             null    => 'NULL',
1052         },
1053     ],
1054     systempreferences =>  [
1055         {
1056             field    => 'value',
1057             type    => 'text',
1058             null    => 'NULL',
1059             key        => '',
1060             default    => '',
1061             extra    => '',
1062         },
1063         {
1064             field    => 'explanation',
1065             type    => 'text',
1066             null    => 'NULL',
1067             key        => '',
1068             default    => '',
1069             extra    => '',
1070         },
1071     ],
1072     suggestions => [
1073         {
1074             field   => 'reason',
1075             type    => 'text',
1076             null    => 'NULL',
1077             key     => ''  ,
1078             default => '',
1079             extra   =>    '',
1080         }
1081     ],
1082 );
1083
1084 my %indexes = (
1085 #    table => [
1086 #         {    indexname => 'index detail'
1087 #         }
1088 #    ],
1089     aqbooksellers => [
1090         {    indexname => 'PRIMARY',
1091             content => 'id',
1092             type => 'PRI',
1093         }
1094     ],
1095     aqbasket => [
1096         {    indexname => 'booksellerid',
1097             content => 'booksellerid',
1098         },
1099     ],
1100     aqorders => [
1101         {    indexname => 'basketno',
1102             content => 'basketno',
1103         },
1104     ],
1105     aqorderbreakdown => [
1106         {    indexname => 'ordernumber',
1107             content => 'ordernumber',
1108         },
1109         {    indexname => 'bookfundid',
1110             content => 'bookfundid',
1111         },
1112     ],
1113     biblioitems => [
1114         {    indexname => 'isbn',
1115             content => 'isbn',
1116         },
1117         {    indexname => 'publishercode',
1118             content => 'publishercode',
1119         },
1120     ],
1121     branches => [
1122         {
1123             indexname => 'branchcode',
1124             content   => 'branchcode',
1125             type => 'PRI',
1126         }
1127     ],
1128     branchrelations => [
1129         {
1130             indexname => 'PRIMARY',
1131             content => 'categorycode',
1132             type => 'PRI',
1133         }
1134     ],
1135     branchrelations => [
1136         {    indexname => 'PRIMARY',
1137             content => 'branchcode,categorycode',
1138             type => 'PRI',
1139         },
1140         {    indexname => 'branchcode',
1141             content => 'branchcode',
1142         },
1143         {    indexname => 'categorycode',
1144             content => 'categorycode',
1145         }
1146     ],
1147     currency => [
1148         {    indexname => 'PRIMARY',
1149             content => 'currency',
1150             type => 'PRI',
1151         }
1152     ],
1153     categories => [
1154         {
1155             indexname => 'categorycode',
1156             content   => 'categorycode',
1157         }
1158     ],
1159     items => [
1160         {    indexname => 'homebranch',
1161             content => 'homebranch',
1162         },
1163         {    indexname => 'holdingbranch',
1164             content => 'holdingbranch',
1165         }
1166     ],
1167     itemtypes => [
1168         {
1169             indexname => 'itemtype',
1170             content   => 'itemtype',
1171         }
1172     ],
1173     shelfcontents => [
1174         {    indexname => 'shelfnumber',
1175             content => 'shelfnumber',
1176         },
1177         {    indexname => 'itemnumber',
1178             content => 'itemnumber',
1179         }
1180     ],
1181         userflags => [
1182                 {       indexname => 'PRIMARY',
1183                         content => 'bit',
1184                         type => 'PRI',
1185                 }
1186         ]
1187 );
1188
1189 my %foreign_keys = (
1190 #    table => [
1191 #         {    key => 'the key in table' (must be indexed)
1192 #            foreigntable => 'the foreigntable name', # (the parent)
1193 #            foreignkey => 'the foreign key column(s)' # (in the parent)
1194 #            onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1195 #            onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1196 #         }
1197 #    ],
1198     branchrelations => [
1199         {    key => 'branchcode',
1200             foreigntable => 'branches',
1201             foreignkey => 'branchcode',
1202             onUpdate => 'CASCADE',
1203             onDelete => 'CASCADE',
1204         },
1205         {    key => 'categorycode',
1206             foreigntable => 'branchcategories',
1207             foreignkey => 'categorycode',
1208             onUpdate => 'CASCADE',
1209             onDelete => 'CASCADE',
1210         },
1211     ],
1212     shelfcontents => [
1213         {    key => 'shelfnumber',
1214             foreigntable => 'bookshelf',
1215             foreignkey => 'shelfnumber',
1216             onUpdate => 'CASCADE',
1217             onDelete => 'CASCADE',
1218         },
1219         {    key => 'itemnumber',
1220             foreigntable => 'items',
1221             foreignkey => 'itemnumber',
1222             onUpdate => 'CASCADE',
1223             onDelete => 'CASCADE',
1224         },
1225     ],
1226     # onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be
1227     # easily deleted, but branches/itemtype not too easy to empty...
1228     biblioitems => [
1229         {    key => 'biblionumber',
1230             foreigntable => 'biblio',
1231             foreignkey => 'biblionumber',
1232             onUpdate => 'CASCADE',
1233             onDelete => 'CASCADE',
1234         },
1235         {    key => 'itemtype',
1236             foreigntable => 'itemtypes',
1237             foreignkey => 'itemtype',
1238             onUpdate => 'CASCADE',
1239             onDelete => 'RESTRICT',
1240         },
1241     ],
1242     items => [
1243         {    key => 'biblioitemnumber',
1244             foreigntable => 'biblioitems',
1245             foreignkey => 'biblioitemnumber',
1246             onUpdate => 'CASCADE',
1247             onDelete => 'CASCADE',
1248         },
1249         {    key => 'homebranch',
1250             foreigntable => 'branches',
1251             foreignkey => 'branchcode',
1252             onUpdate => 'CASCADE',
1253             onDelete => 'RESTRICT',
1254         },
1255         {    key => 'holdingbranch',
1256             foreigntable => 'branches',
1257             foreignkey => 'branchcode',
1258             onUpdate => 'CASCADE',
1259             onDelete => 'RESTRICT',
1260         },
1261     ],
1262     aqbasket => [
1263         {    key => 'booksellerid',
1264             foreigntable => 'aqbooksellers',
1265             foreignkey => 'id',
1266             onUpdate => 'CASCADE',
1267             onDelete => 'RESTRICT',
1268         },
1269     ],
1270     aqorders => [
1271         {    key => 'basketno',
1272             foreigntable => 'aqbasket',
1273             foreignkey => 'basketno',
1274             onUpdate => 'CASCADE',
1275             onDelete => 'CASCADE',
1276         },
1277         {    key => 'biblionumber',
1278             foreigntable => 'biblio',
1279             foreignkey => 'biblionumber',
1280             onUpdate => 'SET NULL',
1281             onDelete => 'SET NULL',
1282         },
1283     ],
1284     aqbooksellers => [
1285         {    key => 'listprice',
1286             foreigntable => 'currency',
1287             foreignkey => 'currency',
1288             onUpdate => 'CASCADE',
1289             onDelete => 'CASCADE',
1290         },
1291         {    key => 'invoiceprice',
1292             foreigntable => 'currency',
1293             foreignkey => 'currency',
1294             onUpdate => 'CASCADE',
1295             onDelete => 'CASCADE',
1296         },
1297     ],
1298     aqorderbreakdown => [
1299         {    key => 'ordernumber',
1300             foreigntable => 'aqorders',
1301             foreignkey => 'ordernumber',
1302             onUpdate => 'CASCADE',
1303             onDelete => 'CASCADE',
1304         },
1305         {    key => 'bookfundid',
1306             foreigntable => 'aqbookfund',
1307             foreignkey => 'bookfundid',
1308             onUpdate => 'CASCADE',
1309             onDelete => 'CASCADE',
1310         },
1311     ],
1312     branchtransfers => [
1313         {    key => 'frombranch',
1314             foreigntable => 'branches',
1315             foreignkey => 'branchcode',
1316             onUpdate => 'CASCADE',
1317             onDelete => 'CASCADE',
1318         },
1319         {    key => 'tobranch',
1320             foreigntable => 'branches',
1321             foreignkey => 'branchcode',
1322             onUpdate => 'CASCADE',
1323             onDelete => 'CASCADE',
1324         },
1325         {    key => 'itemnumber',
1326             foreigntable => 'items',
1327             foreignkey => 'itemnumber',
1328             onUpdate => 'CASCADE',
1329             onDelete => 'CASCADE',
1330         },
1331     ],
1332     issuingrules => [
1333         {    key => 'categorycode',
1334             foreigntable => 'categories',
1335             foreignkey => 'categorycode',
1336             onUpdate => 'CASCADE',
1337             onDelete => 'CASCADE',
1338         },
1339         {    key => 'itemtype',
1340             foreigntable => 'itemtypes',
1341             foreignkey => 'itemtype',
1342             onUpdate => 'CASCADE',
1343             onDelete => 'CASCADE',
1344         },
1345     ],
1346     issues => [    # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
1347     # for stat purposes
1348         {    key => 'borrowernumber',
1349             foreigntable => 'borrowers',
1350             foreignkey => 'borrowernumber',
1351             onUpdate => 'SET NULL',
1352             onDelete => 'SET NULL',
1353         },
1354         {    key => 'itemnumber',
1355             foreigntable => 'items',
1356             foreignkey => 'itemnumber',
1357             onUpdate => 'SET NULL',
1358             onDelete => 'SET NULL',
1359         },
1360     ],
1361     reserves => [
1362         {    key => 'borrowernumber',
1363             foreigntable => 'borrowers',
1364             foreignkey => 'borrowernumber',
1365             onUpdate => 'CASCADE',
1366             onDelete => 'CASCADE',
1367         },
1368         {    key => 'biblionumber',
1369             foreigntable => 'biblio',
1370             foreignkey => 'biblionumber',
1371             onUpdate => 'CASCADE',
1372             onDelete => 'CASCADE',
1373         },
1374         {    key => 'itemnumber',
1375             foreigntable => 'items',
1376             foreignkey => 'itemnumber',
1377             onUpdate => 'CASCADE',
1378             onDelete => 'CASCADE',
1379         },
1380         {    key => 'branchcode',
1381             foreigntable => 'branches',
1382             foreignkey => 'branchcode',
1383             onUpdate => 'CASCADE',
1384             onDelete => 'CASCADE',
1385         },
1386     ],
1387     borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1388     # but prevent deleting a branch as soon as it has 1 borrower !
1389         {    key => 'categorycode',
1390             foreigntable => 'categories',
1391             foreignkey => 'categorycode',
1392             onUpdate => 'RESTRICT',
1393             onDelete => 'RESTRICT',
1394         },
1395         {    key => 'branchcode',
1396             foreigntable => 'branches',
1397             foreignkey => 'branchcode',
1398             onUpdate => 'RESTRICT',
1399             onDelete => 'RESTRICT',
1400         },
1401     ],
1402     deletedborrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1403     # but prevent deleting a branch as soon as it has 1 borrower !
1404         {    key => 'categorycode',
1405             foreigntable => 'categories',
1406             foreignkey => 'categorycode',
1407             onUpdate => 'RESTRICT',
1408             onDelete => 'RESTRICT',
1409         },
1410         {    key => 'branchcode',
1411             foreigntable => 'branches',
1412             foreignkey => 'branchcode',
1413             onUpdate => 'RESTRICT',
1414             onDelete => 'RESTRICT',
1415         },
1416     ],
1417     accountlines => [
1418         {    key => 'borrowernumber',
1419             foreigntable => 'borrowers',
1420             foreignkey => 'borrowernumber',
1421             onUpdate => 'CASCADE',
1422             onDelete => 'CASCADE',
1423         },
1424         {    key => 'itemnumber',
1425             foreigntable => 'items',
1426             foreignkey => 'itemnumber',
1427             onUpdate => 'SET NULL',
1428             onDelete => 'SET NULL',
1429         },
1430     ],
1431     auth_tag_structure => [
1432         {    key => 'authtypecode',
1433             foreigntable => 'auth_types',
1434             foreignkey => 'authtypecode',
1435             onUpdate => 'CASCADE',
1436             onDelete => 'CASCADE',
1437         },
1438     ],
1439     # FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
1440 );
1441
1442
1443 # column changes
1444 my %column_change = (
1445     # table
1446     borrowers => [
1447                 {
1448                     from => 'emailaddress',
1449                     to => 'email',
1450                     after => 'city',
1451                 },
1452                 {
1453                     from => 'streetaddress',
1454                     to => 'address',
1455                     after => 'initials',
1456                 },
1457                 {
1458                     from => 'faxnumber',
1459                     to => 'fax',
1460                     after => 'phone',
1461                 },
1462                 {
1463                     from => 'textmessaging',
1464                     to => 'opacnote',
1465                     after => 'userid',
1466                 },
1467                 {
1468                     from => 'altnotes',
1469                     to => 'contactnote',
1470                     after => 'opacnote',
1471                 },
1472                 {
1473                     from => 'physstreet',
1474                     to => 'B_address',
1475                     after => 'fax',
1476                 },
1477                 {
1478                     from => 'streetcity',
1479                     to => 'B_city',
1480                     after => 'B_address',
1481                 },
1482                 {
1483                     from => 'phoneday',
1484                     to => 'mobile',
1485                     after => 'phone',
1486                 },
1487                 {
1488                     from => 'zipcode',
1489                     to => 'zipcode',
1490                     after => 'city',
1491                 },
1492                 {
1493                     from => 'homezipcode',
1494                     to => 'B_zipcode',
1495                     after => 'B_city',
1496                 },
1497                 {
1498                     from => 'altphone',
1499                     to => 'B_phone',
1500                     after => 'B_zipcode',
1501                 },
1502                 {
1503                     from => 'expiry',
1504                     to => 'dateexpiry',
1505                     after => 'dateenrolled',
1506                 },
1507                 {
1508                     from => 'guarantor',
1509                     to => 'guarantorid',
1510                     after => 'contactname',
1511                 },
1512                 {
1513                     from => 'altrelationship',
1514                     to => 'relationship',
1515                     after => 'borrowernotes',
1516                 },
1517             ],
1518
1519     deletedborrowers => [
1520                 {
1521                     from => 'emailaddress',
1522                     to => 'email',
1523                     after => 'city',
1524                 },
1525                 {
1526                     from => 'streetaddress',
1527                     to => 'address',
1528                     after => 'initials',
1529                 },
1530                 {
1531                     from => 'faxnumber',
1532                     to => 'fax',
1533                     after => 'phone',
1534                 },
1535                 {
1536                     from => 'textmessaging',
1537                     to => 'opacnote',
1538                     after => 'userid',
1539                 },
1540                 {
1541                     from => 'altnotes',
1542                     to => 'contactnote',
1543                     after => 'opacnote',
1544                 },
1545                 {
1546                     from => 'physstreet',
1547                     to => 'B_address',
1548                     after => 'fax',
1549                 },
1550                 {
1551                     from => 'streetcity',
1552                     to => 'B_city',
1553                     after => 'B_address',
1554                 },
1555                 {
1556                     from => 'phoneday',
1557                     to => 'mobile',
1558                     after => 'phone',
1559                 },
1560                 {
1561                     from => 'zipcode',
1562                     to => 'zipcode',
1563                     after => 'city',
1564                 },
1565                 {
1566                     from => 'homezipcode',
1567                     to => 'B_zipcode',
1568                     after => 'B_city',
1569                 },
1570                 {
1571                     from => 'altphone',
1572                     to => 'B_phone',
1573                     after => 'B_zipcode',
1574                 },
1575                 {
1576                     from => 'expiry',
1577                     to => 'dateexpiry',
1578                     after => 'dateenrolled',
1579                 },
1580                 {
1581                     from => 'guarantor',
1582                     to => 'guarantorid',
1583                     after => 'contactname',
1584                 },
1585                 {
1586                     from => 'altrelationship',
1587                     to => 'relationship',
1588                     after => 'borrowernotes',
1589                 },
1590             ],
1591         );
1592     
1593
1594 # MOVE all tables TO UTF-8 and innoDB
1595 $sth = $dbh->prepare("show table status");
1596 $sth->execute;
1597 while ( my $table = $sth->fetchrow_hashref ) {
1598     next if $table->{Name} eq 'marc_word';
1599     next if $table->{Name} eq 'marc_subfield_table';
1600     next if $table->{Name} eq 'auth_word';
1601     next if $table->{Name} eq 'auth_subfield_table';
1602      if ($table->{Engine} ne 'InnoDB') {
1603          $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
1604          print "moving $table->{Name} to InnoDB\n";
1605      }
1606     unless ($table->{Collation} =~ /^utf8/) {
1607          print "moving $table->{Name} to utf8\n";
1608         $dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
1609         $dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
1610         # 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 !
1611     } else {
1612     }
1613 }
1614
1615
1616 foreach my $table (keys %column_change) {
1617     $sth = $dbh->prepare("show columns from $table");
1618     $sth->execute();
1619     undef %types;
1620     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1621     {
1622         $types{$column}->{type} ="$type";
1623         $types{$column}->{null} = "$null";
1624         $types{$column}->{key} = "$key";
1625         $types{$column}->{default} = "$default";
1626         $types{$column}->{extra} = "$extra";
1627     }    # while
1628     my $tablerows = $column_change{$table};
1629     foreach my $row ( @$tablerows ) {
1630         if ($types{$row->{from}}->{type}) {
1631             print "altering $table $row->{from} to $row->{to}\n";
1632             # ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
1633 #             alter table `borrowers` change `faxnumber` `fax` type text  null after phone
1634             my $sql =
1635                 "alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
1636                 ($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
1637                 ($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
1638                 "$types{$row->{from}}->{extra} after $row->{after} ";
1639 #             print "$sql";
1640             $dbh->do($sql);
1641         }
1642     }
1643 }
1644
1645 # Enter here the field you want to delete from DB.
1646 # FIXME :: there is a %uselessfield before which seems doing the same things.
1647 my %fieldtodelete = (
1648     # tablename => [fieldname1,fieldname2,...]
1649
1650 ); # %fielddelete
1651
1652 print "removing some unused fields...\n";
1653 foreach my $table ( keys %fieldtodelete ) {
1654     foreach my $field ( @{$fieldtodelete{$table}} ){
1655         print "removing ".$field." from ".$table;
1656         my $sth = $dbh->prepare("ALTER TABLE $table DROP $field");
1657         $sth->execute;
1658         if ( $sth->err ) {
1659             print "Error : $sth->errstr \n";
1660         }
1661     }
1662 }
1663
1664 # Enter here the line you want to remove from DB.
1665 my %linetodelete = (
1666     # table name => where clause.
1667     userflags => "bit = 8", # delete the 'reserveforself' flags
1668     
1669 ); # %linetodelete
1670
1671 #-------------------
1672 # Initialize
1673
1674 # Start checking
1675
1676 # Get version of MySQL database engine.
1677 my $mysqlversion = `mysqld --version`;
1678 $mysqlversion =~ /Ver (\S*) /;
1679 $mysqlversion = $1;
1680 if ( $mysqlversion ge '3.23' ) {
1681     print "Could convert to MyISAM database tables...\n" unless $silent;
1682 }
1683
1684 #---------------------------------
1685 # Tables
1686
1687 # Collect all tables into a list
1688 $sth = $dbh->prepare("show tables");
1689 $sth->execute;
1690 while ( my ($table) = $sth->fetchrow ) {
1691     $existingtables{$table} = 1;
1692 }
1693
1694
1695 # Now add any missing tables
1696 foreach $table ( keys %requiretables ) {
1697     unless ( $existingtables{$table} ) {
1698     print "Adding $table table...\n" unless $silent;
1699         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
1700         $sth->execute;
1701         if ( $sth->err ) {
1702             print "Error : $sth->errstr \n";
1703             $sth->finish;
1704         }    # if error
1705     }    # unless exists
1706 }    # foreach
1707
1708 #---------------------------------
1709 # Columns
1710
1711 foreach $table ( keys %requirefields ) {
1712     print "Check table $table\n" if $debug and not $silent;
1713     $sth = $dbh->prepare("show columns from $table");
1714     $sth->execute();
1715     undef %types;
1716     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1717     {
1718         $types{$column} = $type;
1719     }    # while
1720     foreach $column ( keys %{ $requirefields{$table} } ) {
1721         print "  Check column $column  [$types{$column}]\n" if $debug and not $silent;
1722         if ( !$types{$column} ) {
1723
1724             # column doesn't exist
1725             print "Adding $column field to $table table...\n" unless $silent;
1726             $query = "alter table $table
1727             add column $column " . $requirefields{$table}->{$column};
1728             print "Execute: $query\n" if $debug;
1729             my $sti = $dbh->prepare($query);
1730             $sti->execute;
1731             if ( $sti->err ) {
1732                 print "**Error : $sti->errstr \n";
1733                 $sti->finish;
1734             }    # if error
1735         }    # if column
1736     }    # foreach column
1737 }    # foreach table
1738
1739 foreach $table ( keys %fielddefinitions ) {
1740     print "Check table $table\n" if $debug;
1741     $sth = $dbh->prepare("show columns from $table");
1742     $sth->execute();
1743     my $definitions;
1744     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1745     {
1746         $definitions->{$column}->{type}    = $type;
1747         $definitions->{$column}->{null}    = $null;
1748         $definitions->{$column}->{null}    = 'NULL' if $null eq 'YES';
1749         $definitions->{$column}->{key}     = $key;
1750         $definitions->{$column}->{default} = $default;
1751         $definitions->{$column}->{extra}   = $extra;
1752     }    # while
1753     my $fieldrow = $fielddefinitions{$table};
1754     foreach my $row (@$fieldrow) {
1755         my $field   = $row->{field};
1756         my $type    = $row->{type};
1757         my $null    = $row->{null};
1758 #         $null    = 'YES' if $row->{null} eq 'NULL';
1759         my $key     = $row->{key};
1760         my $default = $row->{default};
1761 #         $default="''" unless $default;
1762         my $extra   = $row->{extra};
1763         my $def     = $definitions->{$field};
1764         my $after    = ($row->{after}?" after ".$row->{after}:"");
1765
1766         unless ( $type eq $def->{type}
1767             && $null eq $def->{null}
1768             && $key eq $def->{key}
1769             && $extra eq $def->{extra} )
1770         {
1771             if ( $null eq '' ) {
1772                 $null = 'NOT NULL';
1773             }
1774             if ( $key eq 'PRI' ) {
1775                 $key = 'PRIMARY KEY';
1776             }
1777             unless ( $extra eq 'auto_increment' ) {
1778                 $extra = '';
1779             }
1780     
1781             # if it's a new column use "add", if it's an old one, use "change".
1782             my $action;
1783             if ($definitions->{$field}->{type}) {
1784                 $action="change $field"
1785             } else {
1786                 $action="add";
1787             }
1788 # if it's a primary key, drop the previous pk, before altering the table
1789             print "  alter or create $field in $table\n" unless $silent;
1790             my $query;
1791             if ($key ne 'PRIMARY KEY') {
1792 #                 warn "alter table $table $action $field $type $null $key $extra default $default $after";
1793                 $query = "alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1794             } else {
1795 #             warn "alter table $table drop primary key, $action $field $type $null $key $extra default $default $after";
1796                  # something strange : for indexes UNIQUE, they are reported as primary key here.
1797                  # but if you try to run with drop primary key, it fails.
1798                  # thus, we run the query twice, one will fail, one will succeed.
1799                  # strange...
1800                 $query="alter table $table drop primary key, $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1801                 $query="alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1802             }
1803             $dbh->do($query);
1804         }
1805     }
1806 }
1807
1808 print "removing some unused data...\n";
1809 foreach my $table ( keys %linetodelete ) {
1810     foreach my $where ( @{linetodelete{$table}} ){
1811         print "DELETE FROM ".$table." where ".$where;
1812         print "\n";
1813         my $sth = $dbh->prepare("DELETE FROM $table where $where");
1814         $sth->execute;
1815         if ( $sth->err ) {
1816             print "Error : $sth->errstr \n";
1817         }
1818     }
1819 }
1820
1821 # Populate tables with required data
1822
1823 # synch table and deletedtable.
1824 foreach my $table (('borrowers','items','biblio','biblioitems')) {
1825     my %deletedborrowers;
1826     print "synch'ing $table and deleted$table\n";
1827     $sth = $dbh->prepare("show columns from deleted$table");
1828     $sth->execute;
1829     while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1830         $deletedborrowers{$column}=1;
1831     }
1832     $sth = $dbh->prepare("show columns from $table");
1833     $sth->execute;
1834     my $previous;
1835     while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1836         unless ($deletedborrowers{$column}) {
1837             my $newcol="alter table deleted$table add $column $type";
1838             if ($null eq 'YES') {
1839                 $newcol .= " NULL ";
1840             } else {
1841                 $newcol .= " NOT NULL ";
1842             }
1843             $newcol .= "default ".$dbh->quote($default) if $default;
1844             $newcol .= " after $previous" if $previous;
1845             $previous=$column;
1846             print "creating column $column\n";
1847             $dbh->do($newcol);
1848         }
1849     }
1850 }
1851 #
1852 # update publisheddate 
1853 #
1854 $sth = $dbh->prepare("select count(*) from serial where publisheddate is NULL");
1855 $sth->execute;
1856 my ($emptypublished) = $sth->fetchrow;
1857 if ($emptypublished) {
1858     print "Updating publisheddate\n";
1859     $dbh->do("update serial set publisheddate=planneddate where publisheddate is NULL");
1860 }
1861 foreach my $table ( keys %tabledata ) {
1862     print "Checking for data required in table $table...\n" unless $silent;
1863     my $tablerows = $tabledata{$table};
1864     foreach my $row (@$tablerows) {
1865         my $uniquefieldrequired = $row->{uniquefieldrequired};
1866         my $uniquevalue         = $row->{$uniquefieldrequired};
1867         my $forceupdate         = $row->{forceupdate};
1868         my $sth                 =
1869           $dbh->prepare(
1870 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
1871         );
1872         $sth->execute($uniquevalue);
1873         if ($sth->rows) {
1874             foreach my $field (keys %$forceupdate) {
1875                 if ($forceupdate->{$field}) {
1876                     my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
1877                     $sth->execute($row->{$field}, $uniquevalue);
1878                 }
1879             }
1880         } else {
1881             print "Adding row to $table: " unless $silent;
1882             my @values;
1883             my $fieldlist;
1884             my $placeholders;
1885             foreach my $field ( keys %$row ) {
1886                 next if $field eq 'uniquefieldrequired';
1887                 next if $field eq 'forceupdate';
1888                 my $value = $row->{$field};
1889                 push @values, $value;
1890                 print "  $field => $value" unless $silent;
1891                 $fieldlist .= "$field,";
1892                 $placeholders .= "?,";
1893             }
1894             print "\n" unless $silent;
1895             $fieldlist    =~ s/,$//;
1896             $placeholders =~ s/,$//;
1897             print "insert into $table ($fieldlist) values ($placeholders)";
1898             my $sth =
1899             $dbh->prepare(
1900                 "insert into $table ($fieldlist) values ($placeholders)");
1901             $sth->execute(@values);
1902         }
1903     }
1904 }
1905
1906 #
1907 # check indexes and create them when needed
1908 #
1909 print "Checking for index required...\n" unless $silent;
1910 foreach my $table ( keys %indexes ) {
1911     #
1912     # read all indexes from $table
1913     #
1914     $sth = $dbh->prepare("show index from $table");
1915     $sth->execute;
1916     my %existingindexes;
1917     while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
1918         $existingindexes{$key_name} = 1;
1919     }
1920     # read indexes to check
1921     my $tablerows = $indexes{$table};
1922     foreach my $row (@$tablerows) {
1923         my $key_name=$row->{indexname};
1924         if ($existingindexes{$key_name} eq 1) {
1925 #             print "$key_name existing";
1926         } else {
1927             print "\tCreating index $key_name in $table\n";
1928             my $sql;
1929             if ($row->{indexname} eq 'PRIMARY') {
1930                 $sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
1931             } else {
1932                 $sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
1933             }
1934              $dbh->do($sql);
1935             print "Error $sql : $dbh->err \n" if $dbh->err;
1936         }
1937     }
1938 }
1939
1940 #
1941 # check foreign keys and create them when needed
1942 #
1943 print "Checking for foreign keys required...\n" unless $silent;
1944 foreach my $table ( keys %foreign_keys ) {
1945     #
1946     # read all indexes from $table
1947     #
1948     $sth = $dbh->prepare("show table status like '$table'");
1949     $sth->execute;
1950     my $stat = $sth->fetchrow_hashref;
1951     # read indexes to check
1952     my $tablerows = $foreign_keys{$table};
1953     foreach my $row (@$tablerows) {
1954         my $foreign_table=$row->{foreigntable};
1955         if ($stat->{'Comment'} =~/$foreign_table/) {
1956 #             print "$foreign_table existing\n";
1957         } else {
1958             print "\tCreating foreign key $foreign_table in $table\n";
1959             # first, drop any orphan value in child table
1960             if ($row->{onDelete} ne "RESTRICT") {
1961                 my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
1962                 $dbh->do($sql);
1963                 print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
1964             }
1965             my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
1966             $sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
1967             $sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
1968             $dbh->do($sql);
1969             if ($dbh->err) {
1970                 print "====================
1971 An error occured during :
1972 \t$sql
1973 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).
1974 You can find those values with select
1975 \t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
1976 ====================\n
1977 ";
1978             }
1979         }
1980     }
1981 }
1982 # now drop useless tables
1983 foreach $table ( @TableToDelete ) {
1984     if ( $existingtables{$table} ) {
1985         print "Dropping unused table $table\n" if $debug and not $silent;
1986         $dbh->do("drop table $table");
1987         if ( $dbh->err ) {
1988             print "Error : $dbh->errstr \n";
1989         }
1990     }
1991 }
1992
1993 #
1994 # SPECIFIC STUFF
1995 #
1996 #
1997 # create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
1998 #
1999
2000 # 1st, get how many biblio we will have to do...
2001 $sth = $dbh->prepare('select count(*) from marc_biblio');
2002 $sth->execute;
2003 my ($totaltodo) = $sth->fetchrow;
2004
2005 $sth = $dbh->prepare("show columns from biblio");
2006 $sth->execute();
2007 my $definitions;
2008 my $bibliofwexist=0;
2009 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
2010     $bibliofwexist=1 if $column eq 'frameworkcode';
2011 }
2012 unless ($bibliofwexist) {
2013     print "moving biblioframework to biblio table\n";
2014     $dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
2015     $sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
2016     $sth->execute;
2017     my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
2018     my $totaldone=0;
2019     while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
2020         $sth_update->execute($frameworkcode,$biblionumber);
2021         $totaldone++;
2022         print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
2023     }
2024     print "\rdone\n";
2025 }
2026
2027 # at last, remove useless fields
2028 foreach $table ( keys %uselessfields ) {
2029     my @fields = split /,/,$uselessfields{$table};
2030     my $fields;
2031     my $exists;
2032     foreach my $fieldtodrop (@fields) {
2033         $fieldtodrop =~ s/\t//g;
2034         $fieldtodrop =~ s/\n//g;
2035         $exists =0;
2036         $sth = $dbh->prepare("show columns from $table");
2037         $sth->execute;
2038         while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
2039         {
2040             $exists =1 if ($column eq $fieldtodrop);
2041         }
2042         if ($exists) {
2043             print "deleting $fieldtodrop field in $table...\n" unless $silent;
2044             my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
2045             $sth->execute;
2046         }
2047     }
2048 }    # foreach
2049
2050 #
2051 # Changing aqbookfund's primary key 
2052 #
2053 $sth=$dbh->prepare("ALTER TABLE `aqbookfund` DROP PRIMARY KEY , ADD PRIMARY KEY ( `bookfundid` , `branchcode` ) ;");
2054 $sth->execute;
2055
2056 $sth->finish;
2057
2058 exit;
2059
2060 # $Log$
2061 # Revision 1.167  2007/06/15 13:09:08  toins
2062 # bugfix : bibliotitems.dewey & deletedbiblioitems.dewey mustn't be double(8,6).
2063 #
2064 # Revision 1.166  2007/06/08 09:40:12  toins
2065 # bug fix : items.homebranch must be VARCHAR(10)
2066 #
2067 # Revision 1.165  2007/05/23 16:33:10  tipaul
2068 # 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
2069 #
2070 # Revision 1.164  2007/05/04 16:24:09  tipaul
2071 # various bugfixes on parameters modules + adding default NoZebraIndexes systempreference if it's empty
2072 #
2073 # Revision 1.163  2007/05/02 16:44:31  tipaul
2074 # NoZebra SQL index management :
2075 # * adding 3 subs in Biblio.pm
2076 # - GetNoZebraIndexes, that get the index structure in a new systempreference (added with this commit)
2077 # - _DelBiblioNoZebra, that retrieve all index entries for a biblio and remove in a variable the biblio reference
2078 # - _AddBiblioNoZebra, that add index entries for a biblio.
2079 # 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).
2080 # I think the code has to be more deeply tested, but it works at least partially.
2081 #
2082 # Revision 1.162  2007/04/30 16:16:50  tipaul
2083 # bugfix for updatedatabase : when there is no default value (NULL fields) + removing bibliothesaurus table+adding NoZebra systempref (False by default)
2084 #
2085 # Revision 1.161  2007/04/13 16:27:55  hdl
2086 # Adding Version variable to systempreferences.
2087 #
2088 # Revision 1.160  2007/03/19 18:35:13  toins
2089 #  - adding default value in marc_subfield_structure.
2090 #  - now marc_subfields_structure displays subfields in tab view.
2091 #
2092 # Revision 1.159  2007/03/16 01:25:09  kados
2093 # Using my precrash CVS copy I did the following:
2094 #
2095 # cvs -z3 -d:ext:kados@cvs.savannah.nongnu.org:/sources/koha co -P koha
2096 # find koha.precrash -type d -name "CVS" -exec rm -v {} \;
2097 # cp -r koha.precrash/* koha/
2098 # cd koha/
2099 # cvs commit
2100 #
2101 # This should in theory put us right back where we were before the crash
2102 #
2103 # Revision 1.159  2007/03/12 17:52:30  rych
2104 # add pri key to userflags
2105 #
2106 # Revision 1.158  2007/03/09 15:14:57  tipaul
2107 # rel_3_0 moved to HEAD
2108 #
2109 # Revision 1.157.2.56  2007/01/31 16:22:54  btoumi
2110 # -add possibility to use isbn with length of 13 characters
2111 # for  Import datas in the reservoir.
2112 # -modify isbn field in marc_breeding table (varchar 13)
2113 # -add isbn filter (no - )when u read a notice from reservoir
2114 # -add filter to have right field 100
2115 #
2116 # Revision 1.157.2.55  2007/01/30 10:50:19  tipaul
2117 # adding 2 usefull indexes to biblioitems table
2118 #
2119 # Revision 1.157.2.54  2007/01/29 16:45:52  toins
2120 # * adding a new default authorised value : SUGGEST.
2121 # SUGGEST give some reasons to accept or reject a suggestion.
2122 #
2123 # * default value for borrowersMandatoryfield syspref is now "cardnumber|surname|adress"
2124 #
2125 # Revision 1.157.2.53  2007/01/26 20:48:37  hdl
2126 # Serials management : Bugfixes + improvements.
2127 # - Partial dates are now managed
2128 # - next Date Calculation with irregularity tested for 1 week and 1 month.
2129 # - manage if subscription is abouttoexpire or expired.
2130 # - Adding some information on serials pages about subscription.
2131 # - Managing irregularity with numbers.
2132 # - Adding Internal Notes in subscription management.
2133 # - Repeating Button above pages.
2134 #
2135 # Please run Updatedatabase to change irregularity and add internalnotes field  to subscription
2136 #
2137 # Revision 1.157.2.52  2007/01/24 13:57:26  tipaul
2138 # - setting supplierid to auto_increment (HDL : could you check that is works, i'm not 100% sure)
2139 # - removing 22 -> 30 marc_subfield_table -> marcxml stuff, it's now in misc/migration_tools/22_to_30/
2140 #
2141 # Revision 1.157.2.51  2007/01/18 09:58:45  tipaul
2142 # defaulting NOT NULL fields (to '')
2143 #
2144 # Revision 1.157.2.50  2007/01/18 09:39:21  tipaul
2145 # issuedate must be defaulted with ' '
2146 #
2147 # Revision 1.157.2.49  2007/01/18 09:37:30  tipaul
2148 # removing 2 field definitions that were here twice
2149 #
2150 # Revision 1.157.2.48  2007/01/15 09:55:40  toins
2151 # adding a new logging systempref : FinesLog.
2152 #
2153 # Revision 1.157.2.47  2007/01/12 18:09:49  toins
2154 # LetterLog added
2155 #
2156 # Revision 1.157.2.46  2007/01/11 14:35:39  tipaul
2157 # adding Opac Browser feature : the build_browser_and_cloud.pl script will :
2158 # - 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)
2159 # - fill the tags table, that contains the subject cloud.
2160 #
2161 # 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 !
2162 #
2163 # The commit also add the systempreference to hide/show the OpacBrowse in database & in systempref management script.
2164 #
2165 # IMPROVEMENTS to do :
2166 # - 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).
2167 # - add, in parameters section, a place to edit browser descriptions. The build script has to be updated to to avoid deleting existing browser descriptions.
2168 #
2169 # Revision 1.157.2.45  2007/01/10 16:52:52  toins
2170 # Value for Log Features syspref are set to 0 by default.
2171 #
2172 # Revision 1.157.2.44  2007/01/10 16:31:15  toins
2173 # new systems preferences :
2174 #  - CataloguingLog (log the update/creation/deletion of a notice if set to 1)
2175 #  - BorrowersLog ( idem for borrowers )
2176 #  - IssueLog (log all issue if set to 1)
2177 #  - ReturnLog (log all return if set to 1)
2178 #  - SusbcriptionLog (log all creation/deletion/update of a subcription)
2179 #
2180 # All of theses are in a new tab called 'LOGFeatures' in systempreferences.pl
2181 #
2182 # Revision 1.157.2.43  2007/01/10 14:13:17  toins
2183 # opac_news.displayed is replaced by opac_news.number.
2184 # This field say how are ordered the news on the template.
2185 #
2186 # Revision 1.157.2.42  2007/01/09 14:09:01  toins
2187 # 2 field added to opac_news.('expirationdate' and 'displayed').