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