add security on borrowernumber,barcode for book issues
[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) default '' ,
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 );
250
251 my %uselessfields = (
252 # tablename => "field1,field2",
253     borrowers => "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
254     deletedborrowers=> "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
255     );
256 # the other hash contains other actions that can't be done elsewhere. they are done
257 # either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
258
259 # The tabledata hash contains data that should be in the tables.
260 # The uniquefieldrequired hash entry is used to determine which (if any) fields
261 # must not exist in the table for this row to be inserted.  If the
262 # uniquefieldrequired entry is already in the table, the existing data is not
263 # modified, unless the forceupdate hash entry is also set.  Fields in the
264 # anonymous "forceupdate" hash will be forced to be updated to the default
265 # values given in the %tabledata hash.
266
267 my %tabledata = (
268 # tablename => [
269 #    {    uniquefielrequired => 'fieldname', # the primary key in the table
270 #        fieldname => fieldvalue,
271 #        fieldname2 => fieldvalue2,
272 #    },
273 # ],
274     systempreferences => [
275         {
276             uniquefieldrequired => 'variable',
277             variable            => 'useDaysMode',
278             value               => 'Calendar',
279             forceupdate         => { 'explanation' => 1,
280                                      'type' => 1},
281             explanation            => 'How to calculate return dates : Calendar means holidays will be controled, Days means the return date don\'t depend on holidays',
282             type        => 'Choice',
283             options        => 'Calendar|Days'
284         },
285         {
286             uniquefieldrequired => 'variable',
287             variable            => 'BorrowersTitles',
288             value               => 'Mr|Mrs|Miss|Ms',
289             forceupdate         => { 'explanation' => 1,
290                                      'type' => 1},
291             explanation         => 'List all Titles for borrowers',
292             type                => 'free',
293         },
294         {
295             uniquefieldrequired => 'variable',
296             variable            => 'BorrowerMandatoryField',
297             value               => 'cardnumber|surname|address',
298             forceupdate         => { 'explanation' => 1,
299                                      'type' => 1},
300             explanation         => 'List all mandatory fields for borrowers',
301             type                => 'free',
302         },
303         {
304             uniquefieldrequired => 'variable',
305             variable            => 'borrowerRelationship',
306             value               => 'father|mother,grand-mother',
307             forceupdate         => { 'explanation' => 1,
308                                      'type' => 1},
309             explanation         => 'The relationships between a guarantor & a guarantee (separated by | or ,)',
310             type                => 'free',
311         },
312         {
313             uniquefieldrequired => 'variable',
314             variable            => 'ReservesMaxPickUpDelay',
315             value               => '10',
316             forceupdate         => { 'explanation' => 1,
317                                      'type' => 1},
318             explanation         => 'Maximum delay to pick up a reserved document',
319             type                => 'free',
320         },
321         {
322             uniquefieldrequired => 'variable',
323             variable            => 'TransfersMaxDaysWarning',
324             value               => '3',
325             forceupdate         => { 'explanation' => 1,
326                                      'type' => 1},
327             explanation         => 'Max delay before considering the transfer has potentialy a problem',
328             type                => 'free',
329         },
330         {
331             uniquefieldrequired => 'variable',
332             variable            => 'memberofinstitution',
333             value               => '0',
334             forceupdate         => { 'explanation' => 1,
335                                      'type' => 1},
336             explanation         => 'Are your patrons members of institutions',
337             type                => 'YesNo',
338         },
339     {
340             uniquefieldrequired => 'variable',
341             variable            => 'ReadingHistory',
342             value               => '0',
343             forceupdate         => { 'explanation' => 1,
344                                      'type' => 1},
345             explanation         => 'Allow reading record info retrievable from issues and oldissues tables',
346             type                => 'YesNo',
347         },
348     {
349             uniquefieldrequired => 'variable',
350             variable            => 'IssuingInProcess',
351             value               => '0',
352             forceupdate         => { 'explanation' => 1,
353                                      'type' => 1},
354             explanation         => 'Allow no debt alert if the patron is issuing item that accumulate debt',
355             type                => 'YesNo',
356         },
357     {
358             uniquefieldrequired => 'variable',
359             variable            => 'AutomaticItemReturn',
360             value               => '1',
361             forceupdate         => { 'explanation' => 1,
362                                      'type' => 1},
363             explanation         => 'This Variable allow or not to return automaticly to his homebranch',
364             type                => 'YesNo',
365         },
366     {
367             uniquefieldrequired => 'variable',
368             variable            => 'reviewson',
369             value               => '0',
370             forceupdate         => { 'explanation' => 1,
371                                      'type' => 1},
372             explanation         => 'Allows patrons to submit reviews from the opac',
373             type                => 'YesNo',
374         },
375     {
376             uniquefieldrequired => 'variable',
377             variable            => 'intranet_includes',
378             value               => 'includes',
379             forceupdate         => { 'explanation' => 1,
380                                      'type' => 1},
381             explanation         => 'The includes directory you want for specific look of Koha (includes or includes_npl for example)',
382             type                => 'Free',
383         },
384         {
385             uniquefieldrequired => 'variable',
386             variable            => 'AutoLocation',
387             value               => '0',
388             forceupdate         => { 'explanation' => 1,
389                                      'type' => 1},
390             explanation         => 'switch to activate or not Autolocation, if Yes, the Librarian can\'t change his location, it\'s defined by branchip',
391             type                => 'YesNo',
392         },
393         {
394             uniquefieldrequired => 'variable',
395             variable            => 'serialsadditems',
396             value               => '0',
397             forceupdate         => {
398                 'explanation' => 1,
399                 'type' => 1
400             },
401             explanation => 'If set, a new item will be automatically added when receiving an issue',
402             type => 'YesNo',
403         },
404         {
405             uniquefieldrequired => 'variable',
406             variable            => 'expandedSearchOption',
407             value               => '0',
408             forceupdate         => {
409                 'explanation' => 1,
410                 'type' => 1
411             },
412             explanation => 'search among marc field',
413             type => 'YesNo',
414         },
415        {
416             uniquefieldrequired => 'variable',
417             variable            => 'RequestOnOpac',
418             value               => '1',
419             forceupdate         => { 'explanation' => 1,
420                                      'type' => 1},
421             explanation         => 'option to allow reserves on opac',
422             type                => 'YesNo',
423         },
424        {
425             uniquefieldrequired => 'variable',
426             variable            => 'OpacCloud',
427             value               => '1',
428             forceupdate         => { 'explanation' => 1,
429                                      'type' => 1},
430             explanation         => 'Enable / Disable cloud link on OPAC',
431             type                => 'YesNo',
432         },
433        {
434             uniquefieldrequired => 'variable',
435             variable            => 'OpacBrowser',
436             value               => '1',
437             forceupdate         => { 'explanation' => 1,
438                                      'type' => 1},
439             explanation         => 'Enable/Disable browser link on OPAC (needs to set misc/cronjob/build_browser.pl)',
440             type                => 'YesNo',
441         },
442        {
443             uniquefieldrequired => 'variable',
444             variable            => 'OpacTopissue',
445             value               => '1',
446             forceupdate         => { 'explanation' => 1,
447                                      'type' => 1},
448             explanation         => 'Enable / Disable the top issue link on OPAC',
449             type                => 'YesNo',
450         },
451        {
452             uniquefieldrequired => 'variable',
453             variable            => 'OpacAuthorities',
454             value               => '1',
455             forceupdate         => { 'explanation' => 1,
456                                      'type' => 1},
457             explanation         => 'Enable / Disable the search authority link on OPAC',
458             type                => 'YesNo',
459         },
460         {
461             uniquefieldrequired => 'variable',
462             variable            => 'CataloguingLog',
463             value               => '0',
464             forceupdate         => {'explanation' => 1, 'type' => 1},
465             explanation         => 'Active this if you want to log cataloguing action.',
466             type                => 'YesNo',
467         },
468         {
469             uniquefieldrequired => 'variable',
470             variable            => 'BorrowersLog',
471             value               => '0',
472             forceupdate         => {'explanation' => 1, 'type' => 1},
473             explanation         => 'Active this if you want to log borrowers edition/creation/deletion...',
474             type                => 'YesNo',
475         },
476         {
477             uniquefieldrequired => 'variable',
478             variable            => 'SubscriptionLog',
479             value               => '0',
480             forceupdate         => {'explanation' => 1, 'type' => 1},
481             explanation         => 'Active this if you want to log Subscription action',
482             type                => 'YesNo',
483         },
484         {
485             uniquefieldrequired => 'variable',
486             variable            => 'IssueLog',
487             value               => '0',
488             forceupdate         => {'explanation' => 1, 'type' => 1},
489             explanation         => 'Active this if you want to log issue.',
490             type                => 'YesNo',
491         },
492         {
493             uniquefieldrequired => 'variable',
494             variable            => 'ReturnLog',
495             value               => '0',
496             forceupdate         => {'explanation' => 1, 'type' => 1},
497             explanation         => 'Active this if you want to log the circulation return',
498             type                => 'YesNo',
499         },
500         {   
501             uniquefieldrequired => 'variable',
502             variable            => 'LetterLog',
503             value               => '0',
504             forceupdate         => {'explanation' => 1, 'type' => 1},
505             explanation         => 'Active this if you want to log all the letter sent',
506             type                => 'YesNo',
507         },
508         {
509             uniquefieldrequired => 'variable',
510             variable            => 'FinesLog',
511             value               => '0',
512             forceupdate         => {'explanation' => 1, 'type' => 1},
513             explanation         => 'Active this if you want to log fines',
514             type                => 'YesNo',
515         },
516     ],
517     userflags => [
518         {
519             uniquefieldrequired => 'bit',
520             bit                 => '14',
521             flag                => 'editauthorities',
522             flagdesc            => 'allow to edit authorities',
523             defaulton           => '0',
524         },
525         {
526             uniquefieldrequired => 'bit',
527             bit                 => '15',
528             flag                 => 'serials',
529             flagdesc            => 'allow to manage serials subscriptions',
530             defaulton           => '0',
531         },
532         {
533             uniquefieldrequired => 'bit',
534             bit                 => '16',
535             flag                 => 'reports',
536             flagdesc            => 'allow to access to the reports module',
537             defaulton           => '0',
538         },
539     ],
540     authorised_values => [
541         {
542             uniquefieldrequired => 'id',
543             category            => 'SUGGEST',
544             authorised_value    => 'Not enoug budget',
545             lib                 => 'This book it too much expensive',
546         }
547     ]
548 );
549
550 my %fielddefinitions = (
551 # fieldname => [
552 #    {          field => 'fieldname',
553 #             type    => 'fieldtype',
554 #             null    => '',
555 #             key     => '',
556 #             default => ''
557 #         },
558 #     ],
559     aqbasket =>  [
560         {
561             field    => 'booksellerid',
562             type    => 'int(11)',
563             null    => 'NOT NULL',
564             key        => '',
565             default    => '1',
566             extra    => '',
567         },
568     ],
569     aqbooksellers =>  [
570         {
571             field    => 'id',
572             type    => 'int(11)',
573             null    => 'NOT NULL',
574             key        => '',
575             default    => '',
576             extra    => 'auto_increment',
577         },
578         {
579             field    => 'listprice',
580             type    => 'varchar(10)',
581             null    => 'NULL',
582             key        => '',
583             default    => '',
584             extra    => '',
585         },
586         {
587             field    => 'invoiceprice',
588             type    => 'varchar(10)',
589             null    => 'NULL',
590             key        => '',
591             default    => '',
592             extra    => '',
593         },
594     ],
595     
596     accountlines =>  [
597         {
598             field    => 'notify_id',
599             type    => 'int(11)',
600             null    => 'NOT NULL',
601             key        => '',
602             default    => '0',
603             extra    => '',
604         },
605         {
606             field    => 'notify_level',
607             type    => 'int(2)',
608             null    => 'NOT NULL',
609             key        => '',
610             default    => '0',
611             extra    => '',
612         },
613     
614     ],
615     
616     borrowers => [
617         {    field => 'firstname',
618              type => 'text',
619              null => 'NULL',
620          },
621         {    field => 'initials',
622              type => 'text',
623              null => 'NULL',
624          },
625         {    field => 'B_email',
626              type => 'text',
627              null => 'NULL',
628              after => 'B_zipcode',
629          },
630          {
631             field => 'streetnumber', # street number (hidden if streettable table is empty)
632             type => 'char(10)',
633             null => 'NULL',
634             after => 'initials',
635         },
636         {
637             field => 'streettype', # street table, list builded from a system table
638             type => 'char(50)',
639             null => 'NULL',
640             after => 'streetnumber',
641         },
642         {    field => 'phone',
643              type => 'text',
644              null => 'NULL',
645          },
646         {
647             field => 'B_streetnumber', # street number (hidden if streettable table is empty)
648             type => 'char(10)',
649             null => 'NULL',
650             after => 'fax',
651         },
652         {
653             field => 'B_streettype', # street table, list builded from a system table
654             type => 'char(50)',
655             null => 'NULL',
656             after => 'B_streetnumber',
657         },
658         {
659             field => 'phonepro',
660             type => 'text',
661             null => 'NULL',
662             after => 'fax',
663         },
664         {
665             field => 'address2', # complement address
666             type => 'text',
667             null => 'NULL',
668             after => 'address',
669         },
670         {
671             field => 'emailpro',
672             type => 'text',
673             null => 'NULL',
674             after => 'fax',
675         },
676         {
677             field => 'contactfirstname', # contact's firstname
678             type => 'text',
679             null => 'NULL',
680             after => 'contactname',
681         },
682         {
683             field => 'contacttitle', # contact's title
684             type => 'text',
685             null => 'NULL',
686             after => 'contactfirstname',
687         },
688         {
689             field => 'branchcode',
690             type  => 'varchar(10)',
691             null  => 'NOT NULL',
692             default    => '',
693             extra => '',
694         },
695         {
696             field => 'categorycode',
697             type  => 'varchar(10)',
698             null  => 'NOT NULL',
699             default    => '',
700             extra => '',
701         }
702     ],
703     
704     biblioitems =>  [
705         {
706             field    => 'lcsort',
707             type    => 'varchar(25)',
708             null    => 'NULL',
709             key        => '',
710             default    => '',
711             extra    => '',
712         },
713         {
714             field    => 'ccode',
715             type    => 'varchar(4)',
716             null    => 'NULL',
717             key        => '',
718             default    => '',
719             extra    => '',
720         },
721     ],
722     branches =>  [
723         {
724             field    => 'branchip',
725             type    => 'varchar(15)',
726             null    => 'NULL',
727             key        => '',
728             default    => '',
729             extra    => '',
730         },
731         {
732             field    => 'branchprinter',
733             type    => 'varchar(100)',
734             null    => 'NULL',
735             key        => '',
736             default    => '',
737             extra    => '',
738         },
739         {
740             field   => 'branchcode',
741             type    => 'varchar(10)',
742             null    => 'NOT NULL',
743             default => '',
744             extra   => '',
745         }
746     ],
747     branchtransfers =>[
748         {
749             field   => 'frombranch',
750             type    => 'VARCHAR(10)',
751             null    => 'NOT NULL',
752             key     => '',
753             default => '',
754             extra   => '',
755         },
756         {
757             field   => 'tobranch',
758             type    => 'VARCHAR(10)',
759             null    => 'NOT NULL',
760             key     => '',
761             default => '',
762         }
763     ],
764     
765     categories =>  [
766         {
767             field    => 'category_type',
768             type    => 'char(1)',
769             null    => 'NOT NULL',
770             key        => '',
771             default    => 'A',
772             extra    => '',
773         },
774         {
775             field   => 'categorycode',
776             type    => 'varchar(10)',
777             null    => 'NOT NULL',
778             key     => 'PRI',
779             default => '',
780             extra   => '',
781         },
782     ],
783     
784     deletedborrowers => [
785         {    field => 'firstname',
786              type => 'text',
787              null => 'NULL',
788          },
789         {    field => 'initials',
790              type => 'text',
791              null => 'NULL',
792          },
793         {    field => 'B_email',
794              type => 'text',
795              null => 'NULL',
796              after => 'B_zipcode',
797          },
798          {
799             field => 'streetnumber', # street number (hidden if streettable table is empty)
800             type => 'char(10)',
801             null => 'NULL',
802             after => 'initials',
803         },
804         {
805             field => 'streettype', # street table, list builded from a system table
806             type => 'char(50)',
807             null => 'NULL',
808             after => 'streetnumber',
809         },
810         {    field => 'phone',
811              type => 'text',
812              null => 'NULL',
813          },
814          {
815             field => 'B_streetnumber', # street number (hidden if streettable table is empty)
816             type => 'char(10)',
817             null => 'NULL',
818             after => 'fax',
819         },
820         {
821             field => 'B_streettype', # street table, list builded from a system table
822             type => 'char(50)',
823             null => 'NULL',
824             after => 'B_streetnumber',
825         },
826         {
827             field => 'phonepro',
828             type => 'text',
829             null => 'NULL',
830             after => 'fax',
831         },
832         {
833             field => 'address2', # complement address
834             type => 'text',
835             null => 'NULL',
836             after => 'address',
837         },
838         {
839             field => 'emailpro',
840             type => 'text',
841             null => 'NULL',
842             after => 'fax',
843         },
844         {
845             field => 'contactfirstname', # contact's firstname
846             type => 'text',
847             null => 'NULL',
848             after => 'contactname',
849         },
850         {
851             field => 'contacttitle', # contact's title
852             type => 'text',
853             null => 'NULL',
854             after => 'contactfirstname',
855         },
856     ],
857     
858     issues =>  [
859         {
860             field    => 'borrowernumber',
861             type    => 'int(11)',
862             null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
863             key        => '',
864             default    => '',
865             extra    => '',
866         },
867         {
868             field    => 'itemnumber',
869             type    => 'int(11)',
870             null    => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
871             key        => '',
872             default    => '',
873             extra    => '',
874         },
875         {
876             field   => 'branchcode',
877             type    => 'varchar(10)',
878             null    => 'NULL',
879             key     => '',
880             default => '',
881             extra   => '',
882         },
883         {
884             field   => 'issuedate',
885             type    => 'date',
886             null    => '',
887             key     => '',
888             default => '0000-00-00',
889             extra   => '',
890         },
891     ],
892     
893     items => [
894         {
895             field    => 'onloan',
896             type    => 'date',
897             null    => 'NULL',
898             key        => '',
899             default    => '0000-00-00',
900             extra    => '',
901         },
902         {
903             field    => 'cutterextra',
904             type    => 'varchar(45)',
905             null    => 'NULL',
906             key        => '',
907             default    => '',
908             extra    => '',
909         },
910         {
911             field    => 'issue_date',
912             type    => 'date',
913             null    => 'NULL',
914             key        => '',
915             default    => '',
916             extra    => '',
917         },
918         {
919             field    => 'holdingbranch',
920             type    => 'varchar(10)',
921             null    => 'NULL',
922             key        => '',
923             default    => '',
924             extra    => '',
925         },
926         {
927             field    => 'itype',
928             type    => 'varchar(10)',
929             null    => 'NULL',
930             key        => '',
931             default    => '',
932             extra    => '',
933         },
934     ],
935     itemtypes => [
936         {
937             field  => 'itemtype',
938             type   => 'varchar(10)',
939             default    => '',
940             null   => 'NOT NULL',
941             key    => 'PRI',
942             extra  => 'UNIQUE',
943         },
944         {
945             field  => 'summary',
946             type   => 'TEXT',
947             null   => 'NULL',
948             key    => '',
949             extra  => '',
950         },
951     ],
952     marc_subfield_structure => [
953         {
954             field => 'defaultvalue',
955             type  => 'TEXT',
956             null  => 'NULL',
957             key    => '',
958             extra  => '',
959         }
960     ],
961     opac_news => [
962         {
963             field  => 'expirationdate',
964             type   => 'date',
965             null   => 'null',
966             key    => '',
967             extra  => '',
968         },
969         {
970             field   => 'number',
971             type    => 'int(11)',
972             null    => 'NULL',
973             key     => '',
974             default => '0',
975             extra   => '',
976         },
977     ],
978     reserves =>  [
979         {
980             field    => 'waitingdate',
981             type    => 'date',
982             null    => 'NULL',
983             key        => '',
984             default    => '',
985             extra    => '',
986         },
987     ],
988     serial => [
989         {
990             field   => 'notes',
991             type    => 'TEXT',
992             null    => 'NULL',
993             key     => '',
994             default => '',
995             extra   => ''
996         },
997     ],
998     shelfcontents => [
999         {
1000             field => 'dateadded',
1001             type => 'timestamp',
1002             null    => 'NULL',
1003         },
1004     ],
1005     systempreferences =>  [
1006         {
1007             field    => 'value',
1008             type    => 'text',
1009             null    => 'NULL',
1010             key        => '',
1011             default    => '',
1012             extra    => '',
1013         },
1014         {
1015             field    => 'explanation',
1016             type    => 'text',
1017             null    => 'NULL',
1018             key        => '',
1019             default    => '',
1020             extra    => '',
1021         },
1022     ],
1023     suggestions => [
1024         {
1025             field   => 'reason',
1026             type    => 'text',
1027             null    => 'NULL',
1028             key     => ''  ,
1029             default => '',
1030             extra   =>    '',
1031         }
1032     ],
1033 );
1034
1035 my %indexes = (
1036 #    table => [
1037 #         {    indexname => 'index detail'
1038 #         }
1039 #    ],
1040     aqbooksellers => [
1041         {    indexname => 'PRIMARY',
1042             content => 'id',
1043             type => 'PRI',
1044         }
1045     ],
1046     aqbasket => [
1047         {    indexname => 'booksellerid',
1048             content => 'booksellerid',
1049         },
1050     ],
1051     aqorders => [
1052         {    indexname => 'basketno',
1053             content => 'basketno',
1054         },
1055     ],
1056     aqorderbreakdown => [
1057         {    indexname => 'ordernumber',
1058             content => 'ordernumber',
1059         },
1060         {    indexname => 'bookfundid',
1061             content => 'bookfundid',
1062         },
1063     ],
1064     biblioitems => [
1065         {    indexname => 'isbn',
1066             content => 'isbn',
1067         },
1068         {    indexname => 'publishercode',
1069             content => 'publishercode',
1070         },
1071     ],
1072     branches => [
1073         {
1074             indexname => 'branchcode',
1075             content   => 'branchcode',
1076     
1077         }
1078     ],
1079     currency => [
1080         {    indexname => 'PRIMARY',
1081             content => 'currency',
1082             type => 'PRI',
1083         }
1084     ],
1085     categories => [
1086         {
1087             indexname => 'categorycode',
1088             content   => 'categorycode',
1089         }
1090     ],
1091     items => [
1092         {    indexname => 'homebranch',
1093             content => 'homebranch',
1094         },
1095         {    indexname => 'holdingbranch',
1096             content => 'holdingbranch',
1097         }
1098     ],
1099     itemtypes => [
1100         {
1101             indexname => 'itemtype',
1102             content   => 'itemtype',
1103         }
1104     ],
1105     shelfcontents => [
1106         {    indexname => 'shelfnumber',
1107             content => 'shelfnumber',
1108         },
1109         {    indexname => 'itemnumber',
1110             content => 'itemnumber',
1111         }
1112     ],
1113         userflags => [
1114                 {       indexname => 'PRIMARY',
1115                         content => 'bit',
1116                         type => 'PRI',
1117                 }
1118         ]
1119 );
1120
1121 my %foreign_keys = (
1122 #    table => [
1123 #         {    key => 'the key in table' (must be indexed)
1124 #            foreigntable => 'the foreigntable name', # (the parent)
1125 #            foreignkey => 'the foreign key column(s)' # (in the parent)
1126 #            onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1127 #            onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1128 #         }
1129 #    ],
1130     shelfcontents => [
1131         {    key => 'shelfnumber',
1132             foreigntable => 'bookshelf',
1133             foreignkey => 'shelfnumber',
1134             onUpdate => 'CASCADE',
1135             onDelete => 'CASCADE',
1136         },
1137         {    key => 'itemnumber',
1138             foreigntable => 'items',
1139             foreignkey => 'itemnumber',
1140             onUpdate => 'CASCADE',
1141             onDelete => 'CASCADE',
1142         },
1143     ],
1144     # onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be
1145     # easily deleted, but branches/itemtype not too easy to empty...
1146     biblioitems => [
1147         {    key => 'biblionumber',
1148             foreigntable => 'biblio',
1149             foreignkey => 'biblionumber',
1150             onUpdate => 'CASCADE',
1151             onDelete => 'CASCADE',
1152         },
1153         {    key => 'itemtype',
1154             foreigntable => 'itemtypes',
1155             foreignkey => 'itemtype',
1156             onUpdate => 'CASCADE',
1157             onDelete => 'RESTRICT',
1158         },
1159     ],
1160     items => [
1161         {    key => 'biblioitemnumber',
1162             foreigntable => 'biblioitems',
1163             foreignkey => 'biblioitemnumber',
1164             onUpdate => 'CASCADE',
1165             onDelete => 'CASCADE',
1166         },
1167         {    key => 'homebranch',
1168             foreigntable => 'branches',
1169             foreignkey => 'branchcode',
1170             onUpdate => 'CASCADE',
1171             onDelete => 'RESTRICT',
1172         },
1173         {    key => 'holdingbranch',
1174             foreigntable => 'branches',
1175             foreignkey => 'branchcode',
1176             onUpdate => 'CASCADE',
1177             onDelete => 'RESTRICT',
1178         },
1179     ],
1180     aqbasket => [
1181         {    key => 'booksellerid',
1182             foreigntable => 'aqbooksellers',
1183             foreignkey => 'id',
1184             onUpdate => 'CASCADE',
1185             onDelete => 'RESTRICT',
1186         },
1187     ],
1188     aqorders => [
1189         {    key => 'basketno',
1190             foreigntable => 'aqbasket',
1191             foreignkey => 'basketno',
1192             onUpdate => 'CASCADE',
1193             onDelete => 'CASCADE',
1194         },
1195         {    key => 'biblionumber',
1196             foreigntable => 'biblio',
1197             foreignkey => 'biblionumber',
1198             onUpdate => 'SET NULL',
1199             onDelete => 'SET NULL',
1200         },
1201     ],
1202     aqbooksellers => [
1203         {    key => 'listprice',
1204             foreigntable => 'currency',
1205             foreignkey => 'currency',
1206             onUpdate => 'CASCADE',
1207             onDelete => 'CASCADE',
1208         },
1209         {    key => 'invoiceprice',
1210             foreigntable => 'currency',
1211             foreignkey => 'currency',
1212             onUpdate => 'CASCADE',
1213             onDelete => 'CASCADE',
1214         },
1215     ],
1216     aqorderbreakdown => [
1217         {    key => 'ordernumber',
1218             foreigntable => 'aqorders',
1219             foreignkey => 'ordernumber',
1220             onUpdate => 'CASCADE',
1221             onDelete => 'CASCADE',
1222         },
1223         {    key => 'bookfundid',
1224             foreigntable => 'aqbookfund',
1225             foreignkey => 'bookfundid',
1226             onUpdate => 'CASCADE',
1227             onDelete => 'CASCADE',
1228         },
1229     ],
1230     branchtransfers => [
1231         {    key => 'frombranch',
1232             foreigntable => 'branches',
1233             foreignkey => 'branchcode',
1234             onUpdate => 'CASCADE',
1235             onDelete => 'CASCADE',
1236         },
1237         {    key => 'tobranch',
1238             foreigntable => 'branches',
1239             foreignkey => 'branchcode',
1240             onUpdate => 'CASCADE',
1241             onDelete => 'CASCADE',
1242         },
1243         {    key => 'itemnumber',
1244             foreigntable => 'items',
1245             foreignkey => 'itemnumber',
1246             onUpdate => 'CASCADE',
1247             onDelete => 'CASCADE',
1248         },
1249     ],
1250     issuingrules => [
1251         {    key => 'categorycode',
1252             foreigntable => 'categories',
1253             foreignkey => 'categorycode',
1254             onUpdate => 'CASCADE',
1255             onDelete => 'CASCADE',
1256         },
1257         {    key => 'itemtype',
1258             foreigntable => 'itemtypes',
1259             foreignkey => 'itemtype',
1260             onUpdate => 'CASCADE',
1261             onDelete => 'CASCADE',
1262         },
1263     ],
1264     issues => [    # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
1265     # for stat purposes
1266         {    key => 'borrowernumber',
1267             foreigntable => 'borrowers',
1268             foreignkey => 'borrowernumber',
1269             onUpdate => 'SET NULL',
1270             onDelete => 'SET NULL',
1271         },
1272         {    key => 'itemnumber',
1273             foreigntable => 'items',
1274             foreignkey => 'itemnumber',
1275             onUpdate => 'SET NULL',
1276             onDelete => 'SET NULL',
1277         },
1278     ],
1279     reserves => [
1280         {    key => 'borrowernumber',
1281             foreigntable => 'borrowers',
1282             foreignkey => 'borrowernumber',
1283             onUpdate => 'CASCADE',
1284             onDelete => 'CASCADE',
1285         },
1286         {    key => 'biblionumber',
1287             foreigntable => 'biblio',
1288             foreignkey => 'biblionumber',
1289             onUpdate => 'CASCADE',
1290             onDelete => 'CASCADE',
1291         },
1292         {    key => 'itemnumber',
1293             foreigntable => 'items',
1294             foreignkey => 'itemnumber',
1295             onUpdate => 'CASCADE',
1296             onDelete => 'CASCADE',
1297         },
1298         {    key => 'branchcode',
1299             foreigntable => 'branches',
1300             foreignkey => 'branchcode',
1301             onUpdate => 'CASCADE',
1302             onDelete => 'CASCADE',
1303         },
1304     ],
1305     borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1306     # but prevent deleting a branch as soon as it has 1 borrower !
1307         {    key => 'categorycode',
1308             foreigntable => 'categories',
1309             foreignkey => 'categorycode',
1310             onUpdate => 'RESTRICT',
1311             onDelete => 'RESTRICT',
1312         },
1313         {    key => 'branchcode',
1314             foreigntable => 'branches',
1315             foreignkey => 'branchcode',
1316             onUpdate => 'RESTRICT',
1317             onDelete => 'RESTRICT',
1318         },
1319     ],
1320     deletedborrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1321     # but prevent deleting a branch as soon as it has 1 borrower !
1322         {    key => 'categorycode',
1323             foreigntable => 'categories',
1324             foreignkey => 'categorycode',
1325             onUpdate => 'RESTRICT',
1326             onDelete => 'RESTRICT',
1327         },
1328         {    key => 'branchcode',
1329             foreigntable => 'branches',
1330             foreignkey => 'branchcode',
1331             onUpdate => 'RESTRICT',
1332             onDelete => 'RESTRICT',
1333         },
1334     ],
1335     accountlines => [
1336         {    key => 'borrowernumber',
1337             foreigntable => 'borrowers',
1338             foreignkey => 'borrowernumber',
1339             onUpdate => 'CASCADE',
1340             onDelete => 'CASCADE',
1341         },
1342         {    key => 'itemnumber',
1343             foreigntable => 'items',
1344             foreignkey => 'itemnumber',
1345             onUpdate => 'SET NULL',
1346             onDelete => 'SET NULL',
1347         },
1348     ],
1349     auth_tag_structure => [
1350         {    key => 'authtypecode',
1351             foreigntable => 'auth_types',
1352             foreignkey => 'authtypecode',
1353             onUpdate => 'CASCADE',
1354             onDelete => 'CASCADE',
1355         },
1356     ],
1357     # FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
1358 );
1359
1360
1361 # column changes
1362 my %column_change = (
1363     # table
1364     borrowers => [
1365                 {
1366                     from => 'emailaddress',
1367                     to => 'email',
1368                     after => 'city',
1369                 },
1370                 {
1371                     from => 'streetaddress',
1372                     to => 'address',
1373                     after => 'initials',
1374                 },
1375                 {
1376                     from => 'faxnumber',
1377                     to => 'fax',
1378                     after => 'phone',
1379                 },
1380                 {
1381                     from => 'textmessaging',
1382                     to => 'opacnote',
1383                     after => 'userid',
1384                 },
1385                 {
1386                     from => 'altnotes',
1387                     to => 'contactnote',
1388                     after => 'opacnote',
1389                 },
1390                 {
1391                     from => 'physstreet',
1392                     to => 'B_address',
1393                     after => 'fax',
1394                 },
1395                 {
1396                     from => 'streetcity',
1397                     to => 'B_city',
1398                     after => 'B_address',
1399                 },
1400                 {
1401                     from => 'phoneday',
1402                     to => 'mobile',
1403                     after => 'phone',
1404                 },
1405                 {
1406                     from => 'zipcode',
1407                     to => 'zipcode',
1408                     after => 'city',
1409                 },
1410                 {
1411                     from => 'homezipcode',
1412                     to => 'B_zipcode',
1413                     after => 'B_city',
1414                 },
1415                 {
1416                     from => 'altphone',
1417                     to => 'B_phone',
1418                     after => 'B_zipcode',
1419                 },
1420                 {
1421                     from => 'expiry',
1422                     to => 'dateexpiry',
1423                     after => 'dateenrolled',
1424                 },
1425                 {
1426                     from => 'guarantor',
1427                     to => 'guarantorid',
1428                     after => 'contactname',
1429                 },
1430                 {
1431                     from => 'altrelationship',
1432                     to => 'relationship',
1433                     after => 'borrowernotes',
1434                 },
1435             ],
1436
1437     deletedborrowers => [
1438                 {
1439                     from => 'emailaddress',
1440                     to => 'email',
1441                     after => 'city',
1442                 },
1443                 {
1444                     from => 'streetaddress',
1445                     to => 'address',
1446                     after => 'initials',
1447                 },
1448                 {
1449                     from => 'faxnumber',
1450                     to => 'fax',
1451                     after => 'phone',
1452                 },
1453                 {
1454                     from => 'textmessaging',
1455                     to => 'opacnote',
1456                     after => 'userid',
1457                 },
1458                 {
1459                     from => 'altnotes',
1460                     to => 'contactnote',
1461                     after => 'opacnote',
1462                 },
1463                 {
1464                     from => 'physstreet',
1465                     to => 'B_address',
1466                     after => 'fax',
1467                 },
1468                 {
1469                     from => 'streetcity',
1470                     to => 'B_city',
1471                     after => 'B_address',
1472                 },
1473                 {
1474                     from => 'phoneday',
1475                     to => 'mobile',
1476                     after => 'phone',
1477                 },
1478                 {
1479                     from => 'zipcode',
1480                     to => 'zipcode',
1481                     after => 'city',
1482                 },
1483                 {
1484                     from => 'homezipcode',
1485                     to => 'B_zipcode',
1486                     after => 'B_city',
1487                 },
1488                 {
1489                     from => 'altphone',
1490                     to => 'B_phone',
1491                     after => 'B_zipcode',
1492                 },
1493                 {
1494                     from => 'expiry',
1495                     to => 'dateexpiry',
1496                     after => 'dateenrolled',
1497                 },
1498                 {
1499                     from => 'guarantor',
1500                     to => 'guarantorid',
1501                     after => 'contactname',
1502                 },
1503                 {
1504                     from => 'altrelationship',
1505                     to => 'relationship',
1506                     after => 'borrowernotes',
1507                 },
1508             ],
1509         );
1510     
1511
1512 # MOVE all tables TO UTF-8 and innoDB
1513 $sth = $dbh->prepare("show table status");
1514 $sth->execute;
1515 while ( my $table = $sth->fetchrow_hashref ) {
1516      if ($table->{Engine} ne 'InnoDB') {
1517          $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
1518          print "moving $table->{Name} to InnoDB\n";
1519      }
1520     next if $table->{Name} eq 'marc_word';
1521     next if $table->{Name} eq 'marc_subfield_table';
1522     next if $table->{Name} eq 'auth_word';
1523     next if $table->{Name} eq 'auth_subfield_table';
1524     unless ($table->{Collation} =~ /^utf8/) {
1525          print "moving $table->{Name} to utf8\n";
1526         $dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
1527         $dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
1528         # 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 !
1529     } else {
1530     }
1531 }
1532
1533
1534 foreach my $table (keys %column_change) {
1535     $sth = $dbh->prepare("show columns from $table");
1536     $sth->execute();
1537     undef %types;
1538     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1539     {
1540         $types{$column}->{type} ="$type";
1541         $types{$column}->{null} = "$null";
1542         $types{$column}->{key} = "$key";
1543         $types{$column}->{default} = "$default";
1544         $types{$column}->{extra} = "$extra";
1545     }    # while
1546     my $tablerows = $column_change{$table};
1547     foreach my $row ( @$tablerows ) {
1548         if ($types{$row->{from}}->{type}) {
1549             print "altering $table $row->{from} to $row->{to}\n";
1550             # ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
1551 #             alter table `borrowers` change `faxnumber` `fax` type text  null after phone
1552             my $sql =
1553                 "alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
1554                 ($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
1555                 ($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
1556                 "$types{$row->{from}}->{extra} after $row->{after} ";
1557 #             print "$sql";
1558             $dbh->do($sql);
1559         }
1560     }
1561 }
1562
1563 # Enter here the field you want to delete from DB.
1564 # FIXME :: there is a %uselessfield before which seems doing the same things.
1565 my %fieldtodelete = (
1566     # tablename => [fieldname1,fieldname2,...]
1567
1568 ); # %fielddelete
1569
1570 print "removing some unused fields...\n";
1571 foreach my $table ( keys %fieldtodelete ) {
1572     foreach my $field ( @{$fieldtodelete{$table}} ){
1573         print "removing ".$field." from ".$table;
1574         my $sth = $dbh->prepare("ALTER TABLE $table DROP $field");
1575         $sth->execute;
1576         if ( $sth->err ) {
1577             print "Error : $sth->errstr \n";
1578         }
1579     }
1580 }
1581
1582 # Enter here the line you want to remove from DB.
1583 my %linetodelete = (
1584     # table name => where clause.
1585     userflags => "bit = 8", # delete the 'reserveforself' flags
1586     
1587 ); # %linetodelete
1588
1589 #-------------------
1590 # Initialize
1591
1592 # Start checking
1593
1594 # Get version of MySQL database engine.
1595 my $mysqlversion = `mysqld --version`;
1596 $mysqlversion =~ /Ver (\S*) /;
1597 $mysqlversion = $1;
1598 if ( $mysqlversion ge '3.23' ) {
1599     print "Could convert to MyISAM database tables...\n" unless $silent;
1600 }
1601
1602 #---------------------------------
1603 # Tables
1604
1605 # Collect all tables into a list
1606 $sth = $dbh->prepare("show tables");
1607 $sth->execute;
1608 while ( my ($table) = $sth->fetchrow ) {
1609     $existingtables{$table} = 1;
1610 }
1611
1612
1613 # Now add any missing tables
1614 foreach $table ( keys %requiretables ) {
1615     unless ( $existingtables{$table} ) {
1616     print "Adding $table table...\n" unless $silent;
1617         my $sth = $dbh->prepare("create table $table $requiretables{$table}");
1618         $sth->execute;
1619         if ( $sth->err ) {
1620             print "Error : $sth->errstr \n";
1621             $sth->finish;
1622         }    # if error
1623     }    # unless exists
1624 }    # foreach
1625
1626 #---------------------------------
1627 # Columns
1628
1629 foreach $table ( keys %requirefields ) {
1630     print "Check table $table\n" if $debug and not $silent;
1631     $sth = $dbh->prepare("show columns from $table");
1632     $sth->execute();
1633     undef %types;
1634     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1635     {
1636         $types{$column} = $type;
1637     }    # while
1638     foreach $column ( keys %{ $requirefields{$table} } ) {
1639         print "  Check column $column  [$types{$column}]\n" if $debug and not $silent;
1640         if ( !$types{$column} ) {
1641
1642             # column doesn't exist
1643             print "Adding $column field to $table table...\n" unless $silent;
1644             $query = "alter table $table
1645             add column $column " . $requirefields{$table}->{$column};
1646             print "Execute: $query\n" if $debug;
1647             my $sti = $dbh->prepare($query);
1648             $sti->execute;
1649             if ( $sti->err ) {
1650                 print "**Error : $sti->errstr \n";
1651                 $sti->finish;
1652             }    # if error
1653         }    # if column
1654     }    # foreach column
1655 }    # foreach table
1656
1657 foreach $table ( keys %fielddefinitions ) {
1658     print "Check table $table\n" if $debug;
1659     $sth = $dbh->prepare("show columns from $table");
1660     $sth->execute();
1661     my $definitions;
1662     while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1663     {
1664         $definitions->{$column}->{type}    = $type;
1665         $definitions->{$column}->{null}    = $null;
1666         $definitions->{$column}->{null}    = 'NULL' if $null eq 'YES';
1667         $definitions->{$column}->{key}     = $key;
1668         $definitions->{$column}->{default} = $default;
1669         $definitions->{$column}->{extra}   = $extra;
1670     }    # while
1671     my $fieldrow = $fielddefinitions{$table};
1672     foreach my $row (@$fieldrow) {
1673         my $field   = $row->{field};
1674         my $type    = $row->{type};
1675         my $null    = $row->{null};
1676 #         $null    = 'YES' if $row->{null} eq 'NULL';
1677         my $key     = $row->{key};
1678         my $default = $row->{default};
1679 #         $default="''" unless $default;
1680         my $extra   = $row->{extra};
1681         my $def     = $definitions->{$field};
1682         my $after    = ($row->{after}?" after ".$row->{after}:"");
1683
1684         unless ( $type eq $def->{type}
1685             && $null eq $def->{null}
1686             && $key eq $def->{key}
1687             && $extra eq $def->{extra} )
1688         {
1689             if ( $null eq '' ) {
1690                 $null = 'NOT NULL';
1691             }
1692             if ( $key eq 'PRI' ) {
1693                 $key = 'PRIMARY KEY';
1694             }
1695             unless ( $extra eq 'auto_increment' ) {
1696                 $extra = '';
1697             }
1698     
1699             # if it's a new column use "add", if it's an old one, use "change".
1700             my $action;
1701             if ($definitions->{$field}->{type}) {
1702                 $action="change $field"
1703             } else {
1704                 $action="add";
1705             }
1706 # if it's a primary key, drop the previous pk, before altering the table
1707             my $sth;
1708             if ($key ne 'PRIMARY KEY') {
1709 #                 warn "alter table $table $action $field $type $null $key $extra default $default $after";
1710                 $sth =$dbh->prepare("alter table $table $action $field $type $null $key $extra default ? $after");
1711             } else {
1712 #             warn "alter table $table drop primary key, $action $field $type $null $key $extra default $default $after";
1713                  # something strange : for indexes UNIQUE, they are reported as primary key here.
1714                  # but if you try to run with drop primary key, it fails.
1715                  # thus, we run the query twice, one will fail, one will succeed.
1716                  # strange...
1717                 $sth =$dbh->prepare("alter table $table drop primary key, $action $field $type $null $key $extra default ? $after");
1718                 $sth =$dbh->prepare("alter table $table $action $field $type $null $key $extra default ? $after");
1719             }
1720 # ALTER TABLE `borrowers` CHANGE `branchcode` `branchcode` VARCHAR( 10 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL 
1721             print "  alter or create $field in $table\n" unless $silent;
1722             $sth->execute($default);
1723         }
1724     }
1725 }
1726
1727 print "removing some unused data...\n";
1728 foreach my $table ( keys %linetodelete ) {
1729     foreach my $where ( @{linetodelete{$table}} ){
1730         print "DELETE FROM ".$table." where ".$where;
1731         print "\n";
1732         my $sth = $dbh->prepare("DELETE FROM $table where $where");
1733         $sth->execute;
1734         if ( $sth->err ) {
1735             print "Error : $sth->errstr \n";
1736         }
1737     }
1738 }
1739
1740 # Populate tables with required data
1741
1742 # synch table and deletedtable.
1743 foreach my $table (('borrowers','items','biblio','biblioitems')) {
1744     my %deletedborrowers;
1745     print "synch'ing $table and deleted$table\n";
1746     $sth = $dbh->prepare("show columns from deleted$table");
1747     $sth->execute;
1748     while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1749         $deletedborrowers{$column}=1;
1750     }
1751     $sth = $dbh->prepare("show columns from $table");
1752     $sth->execute;
1753     my $previous;
1754     while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1755         unless ($deletedborrowers{$column}) {
1756             my $newcol="alter table deleted$table add $column $type";
1757             if ($null eq 'YES') {
1758                 $newcol .= " NULL ";
1759             } else {
1760                 $newcol .= " NOT NULL ";
1761             }
1762             $newcol .= "default ".$dbh->quote($default) if $default;
1763             $newcol .= " after $previous" if $previous;
1764             $previous=$column;
1765             print "creating column $column\n";
1766             $dbh->do($newcol);
1767         }
1768     }
1769 }
1770 #
1771 # update publisheddate 
1772 #
1773 $sth = $dbh->prepare("select count(*) from serial where publisheddate is NULL");
1774 $sth->execute;
1775 my ($emptypublished) = $sth->fetchrow;
1776 if ($emptypublished) {
1777     print "Updating publisheddate\n";
1778     $dbh->do("update serial set publisheddate=planneddate where publisheddate is NULL");
1779 }
1780 foreach my $table ( keys %tabledata ) {
1781     print "Checking for data required in table $table...\n" unless $silent;
1782     my $tablerows = $tabledata{$table};
1783     foreach my $row (@$tablerows) {
1784         my $uniquefieldrequired = $row->{uniquefieldrequired};
1785         my $uniquevalue         = $row->{$uniquefieldrequired};
1786         my $forceupdate         = $row->{forceupdate};
1787         my $sth                 =
1788           $dbh->prepare(
1789 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
1790         );
1791         $sth->execute($uniquevalue);
1792         if ($sth->rows) {
1793             foreach my $field (keys %$forceupdate) {
1794                 if ($forceupdate->{$field}) {
1795                     my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
1796                     $sth->execute($row->{$field}, $uniquevalue);
1797                 }
1798             }
1799         } else {
1800             print "Adding row to $table: " unless $silent;
1801             my @values;
1802             my $fieldlist;
1803             my $placeholders;
1804             foreach my $field ( keys %$row ) {
1805                 next if $field eq 'uniquefieldrequired';
1806                 next if $field eq 'forceupdate';
1807                 my $value = $row->{$field};
1808                 push @values, $value;
1809                 print "  $field => $value" unless $silent;
1810                 $fieldlist .= "$field,";
1811                 $placeholders .= "?,";
1812             }
1813             print "\n" unless $silent;
1814             $fieldlist    =~ s/,$//;
1815             $placeholders =~ s/,$//;
1816             print "insert into $table ($fieldlist) values ($placeholders)";
1817             my $sth =
1818             $dbh->prepare(
1819                 "insert into $table ($fieldlist) values ($placeholders)");
1820             $sth->execute(@values);
1821         }
1822     }
1823 }
1824
1825 #
1826 # check indexes and create them when needed
1827 #
1828 print "Checking for index required...\n" unless $silent;
1829 foreach my $table ( keys %indexes ) {
1830     #
1831     # read all indexes from $table
1832     #
1833     $sth = $dbh->prepare("show index from $table");
1834     $sth->execute;
1835     my %existingindexes;
1836     while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
1837         $existingindexes{$key_name} = 1;
1838     }
1839     # read indexes to check
1840     my $tablerows = $indexes{$table};
1841     foreach my $row (@$tablerows) {
1842         my $key_name=$row->{indexname};
1843         if ($existingindexes{$key_name} eq 1) {
1844 #             print "$key_name existing";
1845         } else {
1846             print "\tCreating index $key_name in $table\n";
1847             my $sql;
1848             if ($row->{indexname} eq 'PRIMARY') {
1849                 $sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
1850             } else {
1851                 $sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
1852             }
1853              $dbh->do($sql);
1854             print "Error $sql : $dbh->err \n" if $dbh->err;
1855         }
1856     }
1857 }
1858
1859 #
1860 # check foreign keys and create them when needed
1861 #
1862 print "Checking for foreign keys required...\n" unless $silent;
1863 foreach my $table ( keys %foreign_keys ) {
1864     #
1865     # read all indexes from $table
1866     #
1867     $sth = $dbh->prepare("show table status like '$table'");
1868     $sth->execute;
1869     my $stat = $sth->fetchrow_hashref;
1870     # read indexes to check
1871     my $tablerows = $foreign_keys{$table};
1872     foreach my $row (@$tablerows) {
1873         my $foreign_table=$row->{foreigntable};
1874         if ($stat->{'Comment'} =~/$foreign_table/) {
1875 #             print "$foreign_table existing\n";
1876         } else {
1877             print "\tCreating foreign key $foreign_table in $table\n";
1878             # first, drop any orphan value in child table
1879             if ($row->{onDelete} ne "RESTRICT") {
1880                 my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
1881                 $dbh->do($sql);
1882                 print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
1883             }
1884             my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
1885             $sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
1886             $sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
1887             $dbh->do($sql);
1888             if ($dbh->err) {
1889                 print "====================
1890 An error occured during :
1891 \t$sql
1892 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).
1893 You can find those values with select
1894 \t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
1895 ====================\n
1896 ";
1897             }
1898         }
1899     }
1900 }
1901 # now drop useless tables
1902 foreach $table ( @TableToDelete ) {
1903     if ( $existingtables{$table} ) {
1904         print "Dropping unused table $table\n" if $debug and not $silent;
1905         $dbh->do("drop table $table");
1906         if ( $dbh->err ) {
1907             print "Error : $dbh->errstr \n";
1908         }
1909     }
1910 }
1911
1912 #
1913 # SPECIFIC STUFF
1914 #
1915 #
1916 # create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
1917 #
1918
1919 # 1st, get how many biblio we will have to do...
1920 $sth = $dbh->prepare('select count(*) from marc_biblio');
1921 $sth->execute;
1922 my ($totaltodo) = $sth->fetchrow;
1923
1924 $sth = $dbh->prepare("show columns from biblio");
1925 $sth->execute();
1926 my $definitions;
1927 my $bibliofwexist=0;
1928 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
1929     $bibliofwexist=1 if $column eq 'frameworkcode';
1930 }
1931 unless ($bibliofwexist) {
1932     print "moving biblioframework to biblio table\n";
1933     $dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
1934     $sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
1935     $sth->execute;
1936     my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
1937     my $totaldone=0;
1938     while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
1939         $sth_update->execute($frameworkcode,$biblionumber);
1940         $totaldone++;
1941         print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
1942     }
1943     print "\rdone\n";
1944 }
1945
1946 # at last, remove useless fields
1947 foreach $table ( keys %uselessfields ) {
1948     my @fields = split /,/,$uselessfields{$table};
1949     my $fields;
1950     my $exists;
1951     foreach my $fieldtodrop (@fields) {
1952         $fieldtodrop =~ s/\t//g;
1953         $fieldtodrop =~ s/\n//g;
1954         $exists =0;
1955         $sth = $dbh->prepare("show columns from $table");
1956         $sth->execute;
1957         while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1958         {
1959             $exists =1 if ($column eq $fieldtodrop);
1960         }
1961         if ($exists) {
1962             print "deleting $fieldtodrop field in $table...\n" unless $silent;
1963             my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
1964             $sth->execute;
1965         }
1966     }
1967 }    # foreach
1968
1969 #
1970 # Changing aqbookfund's primary key 
1971 #
1972 $sth=$dbh->prepare("ALTER TABLE `aqbookfund` DROP PRIMARY KEY , ADD PRIMARY KEY ( `bookfundid` , `branchcode` ) ;");
1973 $sth->execute;
1974
1975 $sth->finish;
1976
1977 exit;
1978
1979 # $Log$
1980 # Revision 1.160  2007/03/19 18:35:13  toins
1981 #  - adding default value in marc_subfield_structure.
1982 #  - now marc_subfields_structure displays subfields in tab view.
1983 #
1984 # Revision 1.159  2007/03/16 01:25:09  kados
1985 # Using my precrash CVS copy I did the following:
1986 #
1987 # cvs -z3 -d:ext:kados@cvs.savannah.nongnu.org:/sources/koha co -P koha
1988 # find koha.precrash -type d -name "CVS" -exec rm -v {} \;
1989 # cp -r koha.precrash/* koha/
1990 # cd koha/
1991 # cvs commit
1992 #
1993 # This should in theory put us right back where we were before the crash
1994 #
1995 # Revision 1.159  2007/03/12 17:52:30  rych
1996 # add pri key to userflags
1997 #
1998 # Revision 1.158  2007/03/09 15:14:57  tipaul
1999 # rel_3_0 moved to HEAD
2000 #
2001 # Revision 1.157.2.56  2007/01/31 16:22:54  btoumi
2002 # -add possibility to use isbn with length of 13 characters
2003 # for  Import datas in the reservoir.
2004 # -modify isbn field in marc_breeding table (varchar 13)
2005 # -add isbn filter (no - )when u read a notice from reservoir
2006 # -add filter to have right field 100
2007 #
2008 # Revision 1.157.2.55  2007/01/30 10:50:19  tipaul
2009 # adding 2 usefull indexes to biblioitems table
2010 #
2011 # Revision 1.157.2.54  2007/01/29 16:45:52  toins
2012 # * adding a new default authorised value : SUGGEST.
2013 # SUGGEST give some reasons to accept or reject a suggestion.
2014 #
2015 # * default value for borrowersMandatoryfield syspref is now "cardnumber|surname|adress"
2016 #
2017 # Revision 1.157.2.53  2007/01/26 20:48:37  hdl
2018 # Serials management : Bugfixes + improvements.
2019 # - Partial dates are now managed
2020 # - next Date Calculation with irregularity tested for 1 week and 1 month.
2021 # - manage if subscription is abouttoexpire or expired.
2022 # - Adding some information on serials pages about subscription.
2023 # - Managing irregularity with numbers.
2024 # - Adding Internal Notes in subscription management.
2025 # - Repeating Button above pages.
2026 #
2027 # Please run Updatedatabase to change irregularity and add internalnotes field  to subscription
2028 #
2029 # Revision 1.157.2.52  2007/01/24 13:57:26  tipaul
2030 # - setting supplierid to auto_increment (HDL : could you check that is works, i'm not 100% sure)
2031 # - removing 22 -> 30 marc_subfield_table -> marcxml stuff, it's now in misc/migration_tools/22_to_30/
2032 #
2033 # Revision 1.157.2.51  2007/01/18 09:58:45  tipaul
2034 # defaulting NOT NULL fields (to '')
2035 #
2036 # Revision 1.157.2.50  2007/01/18 09:39:21  tipaul
2037 # issuedate must be defaulted with ' '
2038 #
2039 # Revision 1.157.2.49  2007/01/18 09:37:30  tipaul
2040 # removing 2 field definitions that were here twice
2041 #
2042 # Revision 1.157.2.48  2007/01/15 09:55:40  toins
2043 # adding a new logging systempref : FinesLog.
2044 #
2045 # Revision 1.157.2.47  2007/01/12 18:09:49  toins
2046 # LetterLog added
2047 #
2048 # Revision 1.157.2.46  2007/01/11 14:35:39  tipaul
2049 # adding Opac Browser feature : the build_browser_and_cloud.pl script will :
2050 # - 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)
2051 # - fill the tags table, that contains the subject cloud.
2052 #
2053 # 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 !
2054 #
2055 # The commit also add the systempreference to hide/show the OpacBrowse in database & in systempref management script.
2056 #
2057 # IMPROVEMENTS to do :
2058 # - 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).
2059 # - add, in parameters section, a place to edit browser descriptions. The build script has to be updated to to avoid deleting existing browser descriptions.
2060 #
2061 # Revision 1.157.2.45  2007/01/10 16:52:52  toins
2062 # Value for Log Features syspref are set to 0 by default.
2063 #
2064 # Revision 1.157.2.44  2007/01/10 16:31:15  toins
2065 # new systems preferences :
2066 #  - CataloguingLog (log the update/creation/deletion of a notice if set to 1)
2067 #  - BorrowersLog ( idem for borrowers )
2068 #  - IssueLog (log all issue if set to 1)
2069 #  - ReturnLog (log all return if set to 1)
2070 #  - SusbcriptionLog (log all creation/deletion/update of a subcription)
2071 #
2072 # All of theses are in a new tab called 'LOGFeatures' in systempreferences.pl
2073 #
2074 # Revision 1.157.2.43  2007/01/10 14:13:17  toins
2075 # opac_news.displayed is replaced by opac_news.number.
2076 # This field say how are ordered the news on the template.
2077 #
2078 # Revision 1.157.2.42  2007/01/09 14:09:01  toins
2079 # 2 field added to opac_news.('expirationdate' and 'displayed').
2080 #
2081 # Revision 1.157.2.41  2006/12/22 17:11:33  tipaul
2082 # adding 3 systempreferences for opac features & a new systempref tab where all systempreferences are located
2083 #
2084 # Revision 1.157.2.40  2006/12/20 16:45:59  tipaul
2085 # ZEBRA update :
2086 # - adding a new table : when a biblio is added/modified/ deleted, an entry is entered in this table
2087 # - the zebraqueue_start.pl script read it & does the stuff.
2088 #
2089 # code coming from head (tumer). it can be run every minut instead of once every day for dev_week code.
2090 #
2091 # I just have commented the previous code (=real time update) in Biblio.pm, we will be able to reactivate it once indexdata fixes zebra update bug !
2092 #
2093 # Revision 1.157.2.39  2006/12/20 11:42:17  toins
2094 # adding table "tags"
2095 #
2096 # Revision 1.157.2.38  2006/12/19 12:06:53  alaurin
2097 # adding a new system preference : RequestOnOpac ;
2098 #
2099 # adding update database
2100 #
2101 # Revision 1.157.2.37  2006/12/19 10:49:21  toins
2102 # fix a minor bug in syspref "expandedSearchOption" and adding it on updatedatabase.
2103 #
2104 # Revision 1.157.2.36  2006/12/13 19:48:09  hdl
2105 # Adding claimdate to serials.
2106 # (Needed to record claimdate
2107 #
2108 # Revision 1.157.2.35  2006/12/08 15:36:57  hdl
2109 # Adding issuedate to issues table.
2110 #
2111 # Revision 1.157.2.34  2006/12/07 16:00:41  hdl
2112 # Adding issuedate to table issues.
2113 # Modifying issuedate on issue (Circ2.pm)
2114 # Modifying report issue_avg_stats to take this change into account. (Need TESTING)
2115 #
2116 # Revision 1.157.2.33  2006/12/06 14:12:18  btoumi
2117 # add BorrowersTitles systempreferences to setup borrowers title
2118 #
2119 # Revision 1.157.2.32  2006/12/06 13:49:41  toins
2120 # deleting additionalauthors, bibliosubjects, bibliosubtitles.
2121 #
2122 # Revision 1.157.2.31  2006/12/05 15:07:16  tipaul
2123 # NEW FEATURE :
2124 # a column (itemtypes.summary) has been added.
2125 #
2126 # If it is empty, no changes at all.
2127 # In admin/itemtypes.pl, the librarian can go and define what (and how) the record appeard below the title.
2128 # The summary must be entered exactly as the authority summary.
2129 # An example is provided in admin/itemtypes.pl add/modify itemtype.
2130 #
2131 # This feature had been requested for a while by my librarians. The 2 uses we can imagine are :
2132 # - for websites => show the link directly in the result list, to avoid 1 clic (& there is no need for a size/editor/publicationyear for web sites)
2133 # - for serial publications => show some serial specific informations.
2134 #
2135 # This commit should do everything that is needed for this feature.
2136 #
2137 # Revision 1.157.2.30  2006/11/29 11:58:18  toins
2138 # re indenting with space.
2139 #
2140 # Revision 1.157.2.29  2006/11/24 21:58:35  kados
2141 # changing items.itemtype to items.itype to avoid problems with
2142 # joins with biblioitems. NOTE: I don't think updatedatabase will remove the
2143 # items.itemtype from your db so you must do that manually if you updated
2144 # from an earlier version of updatedatabase.
2145 #
2146 # Revision 1.157.2.28  2006/11/24 13:54:55  hdl
2147 # Adding serialsadditem
2148 #
2149 # Revision 1.157.2.27  2006/11/24 11:07:09  alaurin
2150 # bug sanop #74 :
2151 #
2152 # Add a new system prefence "AutoLocation"
2153 #  this fonction  switching activation or not Autolocation, if Yes, the Librarian can't change his location, it's defined by branchip,
2154 # if autolocation is setting to "NO", librarian can change his settings ....
2155 #
2156 #
2157 # warn, if autolocation is setting "on", on circulation.pl we don't have anymore the choice to change your library and branchprinter,
2158 # defined on branches : branchip and branchprinter ....
2159 #
2160 # this function could be improved
2161 #
2162 # Revision 1.157.2.26  2006/11/23 11:01:06  toins
2163 # branchtransfers.frombranch & branchtransfers.tobranch must be VARCHAR(10)
2164 #
2165 # Revision 1.157.2.25  2006/11/23 09:05:33  tipaul
2166 # reintroducing move to innoDB (as only innoDB supports extended features like foreign keys)
2167 #
2168 # Revision 1.157.2.24  2006/11/21 09:15:23  toins
2169 # better userflag description
2170 #
2171 # Revision 1.157.2.23  2006/11/21 08:51:01  toins
2172 # 2 new userflags: serials & reports.
2173 #
2174 # Revision 1.157.2.22  2006/11/20 16:59:09  toins
2175 # adding a userflags: 'editauthorities'.
2176 #
2177 # Revision 1.157.2.21  2006/11/17 10:53:04  hdl
2178 # Changing
2179 # - subscription detail :
2180 #     adding manual history (in subscription table)
2181 #     addind subscription summary.
2182 # - menu-serials.inc deleting old link
2183 # - adapting serials-collection.pl
2184 #
2185 # TODO List :
2186 # - Some values are hard coded in subscription-detail
2187 # - subscription-detail.pl relies on subscription-add for edition BUT subscription-add transmits back data to subscription-detail in order to save data back into database. This is a bit odd enough and should not occur.
2188 # - Some more tests on numberlength which doesnot seem to be kept.
2189 #
2190 # Revision 1.157.2.20  2006/11/15 15:15:50  hdl
2191 # Final First Version for New Facility for subscription management.
2192 #
2193 # Now
2194 # use serials-collection.pl for history display
2195 # and serials-edit.pl for serial edition
2196 # subscription add and detail adds a new branch information to help IndependantBranches Library to manage different subscriptions for a serial
2197 #
2198 # This is aimed at replacing serials-receive and statecollection.
2199 #
2200 # Revision 1.157.2.19  2006/11/14 16:28:01  rych
2201 # Adding itemtype field to items
2202 #
2203 # Revision 1.157.2.18  2006/11/14 16:16:58  rych
2204 # fix mysql syntax
2205 #
2206 # Revision 1.157.2.17  2006/11/14 14:39:31  toins
2207 # * delete the userflags "reservforself" which is unused.
2208 # * some new function not use at the moment.
2209 #
2210 # Revision 1.157.2.16  2006/11/02 09:27:30  toins
2211 # issue.branchcode must be varchar(10).
2212 #
2213 # Revision 1.157.2.15  2006/10/31 17:41:51  toins
2214 # items.holdingbranch must be varchar(10)
2215 #
2216 # Revision 1.157.2.14  2006/10/30 09:41:45  btoumi
2217 # remove auto increment for accountno in accountlines table
2218 #
2219 # Revision 1.157.2.13  2006/10/20 10:35:05  alaurin
2220 # new program : branchoverdues.pl
2221 #
2222 # with this program, the librararians will can check , and specify the method of notification of documents in overdue
2223 #
2224 # little explanation :
2225 #
2226 #     - At first, the datas come from accountlines, generated by accounlines (type 'FU')
2227 #     - There is three levels of notification (come from overduerules ....)
2228 #     - there is four methods of notification :
2229 #         - letter (for us, use an openoffice program ....)
2230 #         - Mail (use a batch program)
2231 #         - Phone (simple Method, if this method is selected, we consider that the borrower as been notified)
2232 #         - Considered Lost (For us the third level)
2233 #
2234 #     - At this time we have some parameters hardcoded (Must be improve later)
2235 #
2236 #     - the choice of methods is hardcoded :
2237 #         - for the first overduelevel : three methods : mail,letter,phone
2238 #         -For the second overduelevel :only one method : letter
2239 #         - For the Third Overdue level : only one method : Considered Lost
2240 #
2241 #
2242 # this program will be heavy tested next week ....
2243 #
2244 # Revision 1.157.2.12  2006/10/19 09:04:07  toins
2245 # itemtypes.itemtype is a primary key.
2246 #
2247 # Revision 1.157.2.11  2006/10/18 13:31:13  toins
2248 # Borrowers.categorycode must have 10 chars lenght & categories.categorycode must be a primary key.
2249 #
2250 # Revision 1.157.2.10  2006/10/17 16:18:14  hdl
2251 # Changing primary key in aqbookfund.
2252 # Making it branchcode+aqbookfundid rather than simple aqbookfundid.
2253 #
2254 # Revision 1.157.2.9  2006/10/16 14:23:47  toins
2255 # Borrowers.branchcode must be varchar(10) too.
2256 #
2257 # Revision 1.157.2.7  2006/10/11 15:22:23  tipaul
2258 # - adding some missing fields, coming from dev_week :
2259 # * lcsort & ccode in biblioitems table. lcsort is used for loc callnumbers & ccode is used to have a item level circulation rules. Ccode means C<irc>code
2260 # * onloan & issue_date in items table. They are filled by the misc/update_items.pl script, with circulation values. NOW, onloan & issue_date in items are NOT set by circulation, this decision speed up a lot the circulation rate (with the Date::Manip removal). The price for this is to have the status of the item not real time updated in zebra, but that's worth the price.
2261 # * cutterextra in items, that we should ask tumer to understand what it does  ;-)
2262 #
2263 # Revision 1.157.2.6  2006/10/10 11:25:40  btoumi
2264 # add two tables : notifys , charges
2265 # modify accountlines tables add two fields (notify_id and notify_level)
2266 #
2267 # Revision 1.157.2.5  2006/10/02 09:15:44  hdl
2268 # Serials :
2269 # * synching with NZ-devs on Serials.
2270 # * adding routing lists support,
2271 # * adding serialsadditems support
2272 # * adding publisheddate management
2273 #
2274 # TODO :
2275 # Management for seasonal serials should be fixed in order to be language independant.
2276 #
2277 # Revision 1.157.2.4  2006/09/19 07:44:13  btoumi
2278 # bug fix : modify wrong field name BorrowerMandatoryField
2279 #
2280 # Revision 1.157.2.3  2006/09/18 14:00:24  btoumi
2281 # bug fix :wrond field name for opacnote and contactnote
2282 #
2283 # Revision 1.157.2.2  2006/09/11 13:24:03  alaurin
2284 # marcxml should be a longtext, some biblios can be more than 65535 char long
2285 #
2286 # Revision 1.157.2.1  2006/09/04 08:39:14  toins
2287 # sync with rel_2_2.
2288 #
2289 # Revision 1.157  2006/08/11 10:03:13  tipaul
2290 # the new "includes" features, for personalized templates. Look at koha-devel, i'll write a mail here (& something on the wiki)
2291 #
2292 # Revision 1.152  2006/06/27 09:26:37  btoumi
2293 # modify (initials,phone ) fields property in borrowers and deletedborrowers table
2294 #
2295 # Revision 1.151  2006/06/22 10:33:14  btoumi
2296 # sorry i forget deletedborrowers table
2297 # modify firstname field from deletedborrowers table
2298 #
2299 # Revision 1.149  2006/06/20 22:35:47  rangi
2300 # Code to allow the associated borrowers to work
2301 #
2302 # Revision 1.148  2006/06/17 22:12:01  rangi
2303 # Adding id field to reviews table
2304 #
2305 # Revision 1.147  2006/06/17 03:36:41  rangi
2306 # Table definition for the reviews table
2307 #
2308 # Revision 1.146  2006/06/17 03:29:41  rangi
2309 # Variable to allow librarians to switch reviews on or off
2310 #
2311 # Revision 1.145  2006/06/16 09:45:02  btoumi
2312 # updatedatabase.pl: add change of borrowers table to deletedborrowers table
2313 # deletemem.pl: delete use of warn function
2314 #
2315 # Revision 1.144  2006/06/08 15:36:31  alaurin
2316 # Add a new system preference 'AutomaticItemReturn' :
2317 #
2318 # if this prefence is switched on: the document returned in another library than homebranch, the system automaticly transfer the document to his homebranch (with notification for librarian in returns.tmpl) .
2319 #
2320 # switch off : the document stay in the holdingbranch ...
2321 #
2322 # correcting bugs :
2323 # - comment C4::acquisition (not using in request.pl).
2324 # - correcting date in request.pl
2325 # -add the new call of function getbranches in request.pl
2326 #
2327 # Revision 1.143  2006/06/07 02:02:47  bob_lyon
2328 # merging katipo changes...
2329 #
2330 # adding new preference IssuingInProcess
2331 #
2332 # Revision 1.142  2006/06/06 23:42:46  bob_lyon
2333 # Merging Katipo changes...
2334 #
2335 # Adding new system pref where one can still retrieve a correct reading
2336 # record history if one has moved older data from issues to oldissues table
2337 # to speed up issues speed
2338 #
2339 # Revision 1.141  2006/06/01 03:18:11  rangi
2340 # Adding a new column to the statistics table
2341 #
2342 # Revision 1.140  2006/05/22 22:40:45  rangi
2343 # Adding new systempreference allowing for the library to add borrowers to institutions (rest homes, parishes, schools, classes etc).
2344 #
2345 # Revision 1.139  2006/05/19 19:31:29  tgarip1957
2346 # Added new fields to auth_header and auth_subfield_table to allow ZEBRA use of authorities and new MARC framework like structure.
2347 # Authority tables are modified to be compatible with new MARC frameworks. This change is part of Authority Linking & Zebra authorities. Requires change in Mysql database. It will break head unless all changes regarding this is implemented. This warning will take place on all commits regarding this
2348 #
2349 # Revision 1.138  2006/05/19 16:51:44  alaurin
2350 # update database for :
2351 # - new feature ip and printer management
2352 # adding two fields in branches table (branchip,branchprinter)
2353 #
2354 # - waiting date : adding one field in reserves table(waiting date) to calculate the Maximum delay to pick up a reserved document when it's available
2355 #
2356 # new system preference :
2357 # - ReservesMaxPickUpDelay : Maximum delay to pick up a reserved document
2358 # TransfersMaxDaysWarning : Max delay before considering the transfer as potentialy a problem
2359 #
2360 # Revision 1.137  2006/04/18 09:36:36  plg
2361 # bug fixed: typo fixed in labels and labels_conf tables creation query.
2362 #
2363 # Revision 1.136  2006/04/17 21:55:33  sushi
2364 # Added 'labels' and 'labels_conf' tables, for spine lable tool.
2365 #
2366 # Revision 1.135  2006/04/15 02:37:03  tgarip1957
2367 # Marc record should be set to UTF-8 in leader.Force it.
2368 # XML should be with<record> wrappers
2369 #
2370 # Revision 1.134  2006/04/14 09:37:29  tipaul
2371 # improvements from SAN Ouest Provence :
2372 # * introducing a category_type into categories. It can be A (adult), C (children), P (Professionnal), I (institution/organisation).
2373 # * each category_type has it's own forms to create members.
2374 # * the borrowers table has been heavily modified (many fields changed), to get something more logic & readable
2375 # * reintroducing guarantor/guanrantee system that is now independant from hardcoded C/A for categories
2376 # * updating templates to fit template rules
2377 #
2378 # (see mail feb, 17 on koha-devel "new features for borrowers" for more details)
2379 #
2380 # Revision 1.133  2006/04/13 08:36:42  plg
2381 # new: function C4::Date::get_date_format_string_for_DHTMLcalendar based on
2382 # the system preference prefered date format.
2383 #
2384 # improvement: book fund list and budget list screen redesigned. Filters on
2385 # each field. Columns are not sortable yet. Using DHTML Calendar to fill date
2386 # fields instead of manual filling. Pagination system. From the book fund
2387 # list, you can reach the budget list, filtered on a book fund, or not. A
2388 # budget can be added only from book fund list screen.
2389 #
2390 # bug fixed: branchcode was missing in table aqbudget.
2391 #
2392 # bug fixed: when setting a branchcode to a book fund, all associated budgets
2393 # move to this branchcode.
2394 #
2395 # modification: when adding/modifying budget/fund, MySQL specific "REPLACE..."
2396 # statements replaced by standard SQL compliant statement.
2397 #
2398 # bug fixed: when adding/modifying a budget, if the book fund is associated to
2399 # a branch, the branch selection is disabled and set to the book fund branch.
2400 #
2401 # Revision 1.132  2006/04/06 12:37:05  hdl
2402 # Bugfixing : aqbookfund needed a field.
2403 #
2404 # Revision 1.131  2006/03/03 17:02:22  tipaul
2405 # commit for holidays and news management.
2406 # (some forgotten files)
2407 #
2408 # Revision 1.130  2006/03/03 16:35:21  tipaul
2409 # commit for holidays and news management.
2410 #
2411 # Contrib from Tmer Garip (from Turkey) :
2412 # * holiday :
2413 # in /tools/ the holiday.pl script let you define holidays (days where the library is closed), branch by branch. You can define 3 types of holidays :
2414 # - single day : only this day is closed
2415 # - repet weekly (like "sunday") : the day is holiday every week
2416 # - repet yearly (like "July, 4") : this day is closed every year.
2417 #
2418 # You can also put exception :
2419 # - sunday is holiday, but "2006 March, 5th" the library will be open
2420 #
2421 # The holidays are used for return date calculation : the return date is set to the next date where the library is open. A systempreference (useDaysMode) set ON (Calendar) or OFF (Normal) the calendar calculation.
2422 #
2423 # Revision 1.129  2006/02/27 18:19:33  hdl
2424 # New table used in overduerules.pl tools page.
2425 #
2426 # Revision 1.128  2006/01/25 15:16:06  tipaul
2427 # updating DB :
2428 # * removing useless tables
2429 # * adding useful indexes
2430 # * altering some columns definitions
2431 # * The goal being to have updater working fine for foreign keys.
2432 #
2433 # For me it's done, let me know if it works for you. You can see an updated schema of the DB (with constraints) on the wiki
2434 #
2435 # Revision 1.127  2006/01/24 17:57:17  tipaul
2436 # DB improvements : adding foreign keys on some tables. partial stuff done.
2437 #
2438 # Revision 1.126  2006/01/06 16:39:42  tipaul
2439 # synch'ing head and rel_2_2 (from 2.2.5, including npl templates)
2440 # Seems not to break too many things, but i'm probably wrong here.
2441 # at least, new features/bugfixes from 2.2.5 are here (tested on some features on my head local copy)
2442 #
2443 # - removing useless directories (koha-html and koha-plucene)
2444 #
2445 # Revision 1.125  2006/01/04 15:54:55  tipaul
2446 # utf8 is a : go for beta test in HEAD.
2447 # some explanations :
2448 # - updater/updatedatabase => will transform all tables in innoDB (not related to utf8, just to warn you) AND collate them in utf8 / utf8_general_ci. The SQL command is : ALTER TABLE tablename DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci.
2449 # - *-top.inc will show the pages in utf8
2450 # - THE HARD THING : for me, mysql-client and mysql-server were set up to communicate in iso8859-1, whatever the mysql collation ! Thus, pages were improperly shown, as datas were transmitted in iso8859-1 format ! After a full day of investigation, someone on usenet pointed "set NAMES 'utf8'" to explain that I wanted utf8. I could put this in my.cnf, but if I do that, ALL databases will "speak" in utf8, that's not what we want. Thus, I added a line in Context.pm : everytime a DB handle is opened, the communication is set to utf8.
2451 # - using marcxml field and no more the iso2709 raw marc biblioitems.marc field.
2452 #
2453 # Revision 1.124  2005/10/27 12:09:05  tipaul
2454 # new features for serial module :
2455 # - the last 5 issues are now shown, and their status can be changed (but not reverted to "waited", as there can be only one "waited")
2456 # - the library can create a "distribution list". this paper contains a list of borrowers (selected from the borrower list, or manually entered), and print it for a given issue. once printed, the sheet can be put on the issue and distributed to every reader on the list (one by one).
2457 #
2458 # Revision 1.123  2005/10/26 09:13:37  tipaul
2459 # big commit, still breaking things...
2460 #
2461 # * synch with rel_2_2. Probably the last non manual synch, as rel_2_2 should not be modified deeply.
2462 # * code cleaning (cleaning warnings from perl -w) continued
2463 #
2464 # Revision 1.122  2005/09/02 14:18:38  tipaul
2465 # new feature : image for itemtypes.
2466 #
2467 # * run updater/updatedatabase to create imageurl field in itemtypes.
2468 # * go to Koha >> parameters >> itemtypes >> modify (or add) an itemtype. You will see around 20 nice images to choose between (thanks to owen). If you prefer your own image, you also can type a complete url (http://www.myserver.lib/path/to/my/image.gif)
2469 # * go to OPAC, and search something. In the result list, you now have the picture instead of the text itemtype.
2470 #
2471 # Revision 1.121  2005/08/24 08:49:03  hdl
2472 # Adding a note field in serial table.
2473 # This will allow librarian to mention a note on a peculiar waiting serial number.
2474 #
2475 # Revision 1.120  2005/08/09 14:10:32  tipaul
2476 # 1st commit to go to zebra.
2477 # don't update your cvs if you want to have a working head...
2478 #
2479 # this commit contains :
2480 # * updater/updatedatabase : get rid with marc_* tables, but DON'T remove them. As a lot of things uses them, it would not be a good idea for instance to drop them. If you really want to play, you can rename them to test head without them but being still able to reintroduce them...
2481 # * Biblio.pm : modify MARCgetbiblio to find the raw marc record in biblioitems.marc field, not from marc_subfield_table, modify MARCfindframeworkcode to find frameworkcode in biblio.frameworkcode, modify some other subs to use biblio.biblionumber & get rid of bibid.
2482 # * other files : get rid of bibid and use biblionumber instead.
2483 #
2484 # What is broken :
2485 # * does not do anything on zebra yet.
2486 # * if you rename marc_subfield_table, you can't search anymore.
2487 # * you can view a biblio & bibliodetails, go to MARC editor, but NOT save any modif.
2488 # * don't try to add a biblio, it would add data poorly... (don't try to delete either, it may work, but that would be a surprise ;-) )
2489 #
2490 # IMPORTANT NOTE : you need MARC::XML package (http://search.cpan.org/~esummers/MARC-XML-0.7/lib/MARC/File/XML.pm), that requires a recent version of MARC::Record
2491 # Updatedatabase stores the iso2709 data in biblioitems.marc field & an xml version in biblioitems.marcxml Not sure we will keep it when releasing the stable version, but I think it's a good idea to have something readable in sql, at least for development stage.
2492 #
2493 # Revision 1.119  2005/08/04 16:07:58  tipaul
2494 # Synch really broke this script...
2495 #
2496 # Revision 1.118  2005/08/04 16:02:55  tipaul
2497 # oops... error in synch between 2.2 and head
2498 #
2499 # Revision 1.117  2005/08/04 14:24:39  tipaul
2500 # synch'ing 2.2 and head
2501 #
2502 # Revision 1.116  2005/08/04 08:55:54  tipaul
2503 # Letters / alert system, continuing...
2504 #
2505 # * adding a package Letters.pm, that manages Letters & alerts.
2506 # * adding feature : it's now possible to define a "letter" for any subscription created. If a letter is defined, users in OPAC can put an alert on the subscription. When an issue is marked "arrived", all users in the alert will recieve a mail (as defined in the "letter"). This last part (= send the mail) is not yet developped. (Should be done this week)
2507 # * adding feature : it's now possible to "put to an alert" in OPAC, for any serial subscription. The alert is stored in a new table, called alert. An alert can be put only if the librarian has activated them in subscription (and they activate it just by choosing a "letter" to sent to borrowers on new issues)
2508 # * adding feature : librarian can see in borrower detail which alerts they have put, and a user can see in opac-detail which alert they have put too.
2509 #
2510 # Note that the system should be generic enough to manage any type of alert.
2511 # I plan to extend it soon to virtual shelves : a borrower will be able to put an alert on a virtual shelf, to be warned when something is changed in the virtual shelf (mail being sent once a day by cron, or manually by the shelf owner. Anyway, a mail won't be sent on every change, users would be spammed by Koha ;-) )
2512 #
2513 # Revision 1.115  2005/08/02 16:15:34  tipaul
2514 # adding 2 fields to letter system :
2515 # * module (acquisition, catalogue...) : it will be usefull to show the librarian only letters he may be interested by.
2516 # * title, that will be used as mail subject.
2517 #
2518 # Revision 1.114  2005/07/28 15:10:13  tipaul
2519 # Introducing new "Letters" system : Letters will be used everytime you want to sent something to someone (through mail or paper). For example, sending a mail for overdues use letter that you can put as parameters. Sending a mail to a borrower when a suggestion is validated uses a letter too.
2520 # the letter table contains 3 fields :
2521 # * code => the code of the letter
2522 # * name => the complete name of the letter
2523 # * content => the complete text. It's a TEXT field type, so has no limits.
2524 #
2525 # My next goal now is to work on point 2-I "serial issue alert"
2526 # With this feature, in serials, a user can subscribe the "issue alert". For every issue arrived/missing, a mail is sent to all subscribers of this list. The mail warns the user that the issue is arrive or missing. Will be in head.
2527 # (see mail on koha-devel, 2005/04/07)
2528 #
2529 # The "serial issue alert" will be the 1st to use this letter system that probably needs some tweaking ;-)
2530 #
2531 # Once it will be stabilised default letters (in any languages) could be added during installer to help the library begin with this new feature.
2532 #
2533 # Revision 1.113  2005/07/28 08:38:41  tipaul
2534 # For instance, the return date does not rely on the borrower expiration date. A systempref will be added in Koha, to modify return date calculation schema :
2535 # * ReturnBeforeExpiry = yes => return date can't be after expiry date
2536 # * ReturnBeforeExpiry = no  => return date can be after expiry date
2537 #
2538 # Revision 1.112  2005/07/26 08:19:47  hdl
2539 # Adding IndependantBranches System preference variable in order to manage Branch independancy.
2540 #
2541 # Revision 1.111  2005/07/25 15:35:38  tipaul
2542 # we have decided that moving to Koha 3.0 requires being already in Koha 2.2.x
2543 # So, the updatedatabase script can highly be cleaned (90% removed).
2544 # Let's play with the new Koha DB structure now ;-)
2545 #