6 # This script checks for required updates to the database.
8 # Part of the Koha Library Software www.koha.org
9 # Licensed under the GPL.
12 # - Would also be a good idea to offer to do a backup at this time...
14 # NOTE: If you do something more than once in here, make it table driven.
24 use MARC::File::XML ( BinaryEncoding => 'utf8' );
26 # FIXME - The user might be installing a new database, so can't rely
27 # on /etc/koha.conf anyway.
34 %existingtables, # tables already in database
38 $type, $null, $key, $default, $extra,
39 $prefitem, # preference item in systempreferences table
46 my $dbh = C4::Context->dbh;
47 print "connected to your DB. Checking & modifying it\n" unless $silent;
48 $|=1; # flushes output
53 # Tables to add if they don't exist
55 categorytable => "(categorycode char(5) NOT NULL default '',
56 description text default '',
57 itemtypecodes text default '',
58 PRIMARY KEY (categorycode)
60 subcategorytable => "(subcategorycode char(5) NOT NULL default '',
61 description text default '',
62 itemtypecodes text default '',
63 PRIMARY KEY (subcategorycode)
65 mediatypetable => "(mediatypecode char(5) NOT NULL default '',
66 description text default '',
67 itemtypecodes text default '',
68 PRIMARY KEY (mediatypecode)
71 `timestamp` TIMESTAMP NOT NULL ,
72 `user` INT( 11 ) NOT NULL ,
73 `module` TEXT default '',
74 `action` TEXT default '' ,
75 `object` INT(11) NULL ,
76 `info` TEXT default '' ,
77 PRIMARY KEY ( `timestamp` , `user` )
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 '',
85 PRIMARY KEY (module,code)
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)
97 `idnew` int(10) unsigned NOT NULL auto_increment,
98 `title` varchar(250) NOT NULL default '',
100 `lang` varchar(4) NOT NULL default '',
101 `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
102 PRIMARY KEY (`idnew`)
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,
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,
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`)
138 cities => "(`cityid` int auto_increment,
139 `city_name` char(100) NOT NULL,
140 `city_zipcode` char(20),
141 PRIMARY KEY (`cityid`)
143 roadtype => "(`roadtypeid` int auto_increment,
144 `road_type` char(100) NOT NULL,
145 PRIMARY KEY (`roadtypeid`)
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)
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,
170 reviewid integer NOT NULL auto_increment,
171 borrowernumber integer,
172 biblionumber integer,
175 datereviewed datetime,
176 PRIMARY KEY (reviewid)
178 borrowers_to_borrowers => "(
182 subscriptionroutinglist=>"(
183 routingid integer NOT NULL auto_increment,
184 borrowernumber integer,
186 subscriptionid integer,
187 PRIMARY KEY (routingid)
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 ''
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`)
210 `entry` varchar(255) NOT NULL default '',
211 `weight` bigint(20) NOT NULL default '0',
212 PRIMARY KEY (`entry`)
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 ,
221 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=1",
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'",
239 issues =>{ 'issuedate'=>"date NOT NULL default '0000-00-00'", },
241 # tablename => { 'field' => 'fieldtype' },
244 # Enter here the table to delete.
245 my @TableToDelete = qw(
252 my %uselessfields = (
253 # tablename => "field1,field2",
254 borrowers => "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
255 deletedborrowers=> "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
257 # the other hash contains other actions that can't be done elsewhere. they are done
258 # either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
260 # The tabledata hash contains data that should be in the tables.
261 # The uniquefieldrequired hash entry is used to determine which (if any) fields
262 # must not exist in the table for this row to be inserted. If the
263 # uniquefieldrequired entry is already in the table, the existing data is not
264 # modified, unless the forceupdate hash entry is also set. Fields in the
265 # anonymous "forceupdate" hash will be forced to be updated to the default
266 # values given in the %tabledata hash.
270 # { uniquefielrequired => 'fieldname', # the primary key in the table
271 # fieldname => fieldvalue,
272 # fieldname2 => fieldvalue2,
275 systempreferences => [
277 uniquefieldrequired => 'variable',
278 variable => 'useDaysMode',
280 forceupdate => { 'explanation' => 1,
282 explanation => 'How to calculate return dates : Calendar means holidays will be controled, Days means the return date don\'t depend on holidays',
284 options => 'Calendar|Days'
287 uniquefieldrequired => 'variable',
288 variable => 'BorrowersTitles',
289 value => 'Mr|Mrs|Miss|Ms',
290 forceupdate => { 'explanation' => 1,
292 explanation => 'List all Titles for borrowers',
296 uniquefieldrequired => 'variable',
297 variable => 'BorrowerMandatoryField',
298 value => 'cardnumber|surname|address',
299 forceupdate => { 'explanation' => 1,
301 explanation => 'List all mandatory fields for borrowers',
305 uniquefieldrequired => 'variable',
306 variable => 'borrowerRelationship',
307 value => 'father|mother,grand-mother',
308 forceupdate => { 'explanation' => 1,
310 explanation => 'The relationships between a guarantor & a guarantee (separated by | or ,)',
314 uniquefieldrequired => 'variable',
315 variable => 'ReservesMaxPickUpDelay',
317 forceupdate => { 'explanation' => 1,
319 explanation => 'Maximum delay to pick up a reserved document',
323 uniquefieldrequired => 'variable',
324 variable => 'TransfersMaxDaysWarning',
326 forceupdate => { 'explanation' => 1,
328 explanation => 'Max delay before considering the transfer has potentialy a problem',
332 uniquefieldrequired => 'variable',
333 variable => 'memberofinstitution',
335 forceupdate => { 'explanation' => 1,
337 explanation => 'Are your patrons members of institutions',
341 uniquefieldrequired => 'variable',
342 variable => 'ReadingHistory',
344 forceupdate => { 'explanation' => 1,
346 explanation => 'Allow reading record info retrievable from issues and oldissues tables',
350 uniquefieldrequired => 'variable',
351 variable => 'IssuingInProcess',
353 forceupdate => { 'explanation' => 1,
355 explanation => 'Allow no debt alert if the patron is issuing item that accumulate debt',
359 uniquefieldrequired => 'variable',
360 variable => 'AutomaticItemReturn',
362 forceupdate => { 'explanation' => 1,
364 explanation => 'This Variable allow or not to return automaticly to his homebranch',
368 uniquefieldrequired => 'variable',
369 variable => 'reviewson',
371 forceupdate => { 'explanation' => 1,
373 explanation => 'Allows patrons to submit reviews from the opac',
377 uniquefieldrequired => 'variable',
378 variable => 'intranet_includes',
380 forceupdate => { 'explanation' => 1,
382 explanation => 'The includes directory you want for specific look of Koha (includes or includes_npl for example)',
386 uniquefieldrequired => 'variable',
387 variable => 'AutoLocation',
389 forceupdate => { 'explanation' => 1,
391 explanation => 'switch to activate or not Autolocation, if Yes, the Librarian can\'t change his location, it\'s defined by branchip',
395 uniquefieldrequired => 'variable',
396 variable => 'serialsadditems',
402 explanation => 'If set, a new item will be automatically added when receiving an issue',
406 uniquefieldrequired => 'variable',
407 variable => 'expandedSearchOption',
413 explanation => 'search among marc field',
417 uniquefieldrequired => 'variable',
418 variable => 'RequestOnOpac',
420 forceupdate => { 'explanation' => 1,
422 explanation => 'option to allow reserves on opac',
426 uniquefieldrequired => 'variable',
427 variable => 'OpacCloud',
429 forceupdate => { 'explanation' => 1,
431 explanation => 'Enable / Disable cloud link on OPAC',
435 uniquefieldrequired => 'variable',
436 variable => 'OpacBrowser',
438 forceupdate => { 'explanation' => 1,
440 explanation => 'Enable/Disable browser link on OPAC (needs to set misc/cronjob/build_browser.pl)',
444 uniquefieldrequired => 'variable',
445 variable => 'OpacTopissue',
447 forceupdate => { 'explanation' => 1,
449 explanation => 'Enable / Disable the top issue link on OPAC',
453 uniquefieldrequired => 'variable',
454 variable => 'OpacAuthorities',
456 forceupdate => { 'explanation' => 1,
458 explanation => 'Enable / Disable the search authority link on OPAC',
462 uniquefieldrequired => 'variable',
463 variable => 'CataloguingLog',
465 forceupdate => {'explanation' => 1, 'type' => 1},
466 explanation => 'Active this if you want to log cataloguing action.',
470 uniquefieldrequired => 'variable',
471 variable => 'BorrowersLog',
473 forceupdate => {'explanation' => 1, 'type' => 1},
474 explanation => 'Active this if you want to log borrowers edition/creation/deletion...',
478 uniquefieldrequired => 'variable',
479 variable => 'SubscriptionLog',
481 forceupdate => {'explanation' => 1, 'type' => 1},
482 explanation => 'Active this if you want to log Subscription action',
486 uniquefieldrequired => 'variable',
487 variable => 'IssueLog',
489 forceupdate => {'explanation' => 1, 'type' => 1},
490 explanation => 'Active this if you want to log issue.',
494 uniquefieldrequired => 'variable',
495 variable => 'ReturnLog',
497 forceupdate => {'explanation' => 1, 'type' => 1},
498 explanation => 'Active this if you want to log the circulation return',
502 uniquefieldrequired => 'variable',
503 variable => 'Version',
505 forceupdate => {'explanation' => 1, 'type' => 1},
506 explanation => 'Koha Version',
510 uniquefieldrequired => 'variable',
511 variable => 'LetterLog',
513 forceupdate => {'explanation' => 1, 'type' => 1},
514 explanation => 'Active this if you want to log all the letter sent',
518 uniquefieldrequired => 'variable',
519 variable => 'FinesLog',
521 forceupdate => {'explanation' => 1, 'type' => 1},
522 explanation => 'Active this if you want to log fines',
526 uniquefieldrequired => 'variable',
527 variable => 'NoZebra',
529 forceupdate => {'explanation' => 1, 'type' => 1},
530 explanation => 'Active this if you want NOT to use zebra (large libraries should avoid this parameters)',
534 uniquefieldrequired => 'variable',
535 variable => 'NoZebraIndexes',
537 forceupdate => {'explanation' => 1, 'type' => 1},
538 explanation => "Enter a specific hash for NoZebra indexes. Enter : 'indexname' => '100a,245a,500*','index2' => '...'",
544 uniquefieldrequired => 'bit',
546 flag => 'editauthorities',
547 flagdesc => 'allow to edit authorities',
551 uniquefieldrequired => 'bit',
554 flagdesc => 'allow to manage serials subscriptions',
558 uniquefieldrequired => 'bit',
561 flagdesc => 'allow to access to the reports module',
565 authorised_values => [
567 uniquefieldrequired => 'id',
568 category => 'SUGGEST',
569 authorised_value => 'Not enoug budget',
570 lib => 'This book it too much expensive',
575 my %fielddefinitions = (
577 # { field => 'fieldname',
578 # type => 'fieldtype',
586 field => 'booksellerid',
601 extra => 'auto_increment',
604 field => 'listprice',
605 type => 'varchar(10)',
612 field => 'invoiceprice',
613 type => 'varchar(10)',
623 field => 'notify_id',
631 field => 'notify_level',
642 { field => 'firstname',
646 { field => 'initials',
650 { field => 'B_email',
653 after => 'B_zipcode',
656 field => 'streetnumber', # street number (hidden if streettable table is empty)
662 field => 'streettype', # street table, list builded from a system table
665 after => 'streetnumber',
672 field => 'B_streetnumber', # street number (hidden if streettable table is empty)
678 field => 'B_streettype', # street table, list builded from a system table
681 after => 'B_streetnumber',
690 field => 'address2', # complement address
702 field => 'contactfirstname', # contact's firstname
705 after => 'contactname',
708 field => 'contacttitle', # contact's title
711 after => 'contactfirstname',
714 field => 'branchcode',
715 type => 'varchar(10)',
721 field => 'categorycode',
722 type => 'varchar(10)',
732 type => 'varchar(25)',
740 type => 'varchar(4)',
750 type => 'varchar(15)',
757 field => 'branchprinter',
758 type => 'varchar(100)',
765 field => 'branchcode',
766 type => 'varchar(10)',
774 field => 'frombranch',
775 type => 'VARCHAR(10)',
783 type => 'VARCHAR(10)',
792 field => 'category_type',
800 field => 'categorycode',
801 type => 'varchar(10)',
809 deletedborrowers => [
810 { field => 'firstname',
814 { field => 'initials',
818 { field => 'B_email',
821 after => 'B_zipcode',
824 field => 'streetnumber', # street number (hidden if streettable table is empty)
830 field => 'streettype', # street table, list builded from a system table
833 after => 'streetnumber',
840 field => 'B_streetnumber', # street number (hidden if streettable table is empty)
846 field => 'B_streettype', # street table, list builded from a system table
849 after => 'B_streetnumber',
858 field => 'address2', # complement address
870 field => 'contactfirstname', # contact's firstname
873 after => 'contactname',
876 field => 'contacttitle', # contact's title
879 after => 'contactfirstname',
885 field => 'borrowernumber',
887 null => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
893 field => 'itemnumber',
895 null => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
901 field => 'branchcode',
902 type => 'varchar(10)',
909 field => 'issuedate',
913 default => '0000-00-00',
924 default => '0000-00-00',
928 field => 'cutterextra',
929 type => 'varchar(45)',
936 field => 'issue_date',
944 field => 'holdingbranch',
945 type => 'varchar(10)',
953 type => 'varchar(10)',
963 type => 'varchar(10)',
977 marc_subfield_structure => [
979 field => 'defaultvalue',
988 field => 'expirationdate',
1005 field => 'waitingdate',
1025 field => 'dateadded',
1026 type => 'timestamp',
1030 systempreferences => [
1040 field => 'explanation',
1062 # { indexname => 'index detail'
1066 { indexname => 'PRIMARY',
1072 { indexname => 'booksellerid',
1073 content => 'booksellerid',
1077 { indexname => 'basketno',
1078 content => 'basketno',
1081 aqorderbreakdown => [
1082 { indexname => 'ordernumber',
1083 content => 'ordernumber',
1085 { indexname => 'bookfundid',
1086 content => 'bookfundid',
1090 { indexname => 'isbn',
1093 { indexname => 'publishercode',
1094 content => 'publishercode',
1099 indexname => 'branchcode',
1100 content => 'branchcode',
1104 branchrelations => [
1106 indexname => 'PRIMARY',
1107 content => 'categorycode',
1111 branchrelations => [
1112 { indexname => 'PRIMARY',
1113 content => 'branchcode,categorycode',
1116 { indexname => 'branchcode',
1117 content => 'branchcode',
1119 { indexname => 'categorycode',
1120 content => 'categorycode',
1124 { indexname => 'PRIMARY',
1125 content => 'currency',
1131 indexname => 'categorycode',
1132 content => 'categorycode',
1136 { indexname => 'homebranch',
1137 content => 'homebranch',
1139 { indexname => 'holdingbranch',
1140 content => 'holdingbranch',
1145 indexname => 'itemtype',
1146 content => 'itemtype',
1150 { indexname => 'shelfnumber',
1151 content => 'shelfnumber',
1153 { indexname => 'itemnumber',
1154 content => 'itemnumber',
1158 { indexname => 'PRIMARY',
1165 my %foreign_keys = (
1167 # { key => 'the key in table' (must be indexed)
1168 # foreigntable => 'the foreigntable name', # (the parent)
1169 # foreignkey => 'the foreign key column(s)' # (in the parent)
1170 # onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1171 # onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
1174 branchrelations => [
1175 { key => 'branchcode',
1176 foreigntable => 'branches',
1177 foreignkey => 'branchcode',
1178 onUpdate => 'CASCADE',
1179 onDelete => 'CASCADE',
1181 { key => 'categorycode',
1182 foreigntable => 'branchcategories',
1183 foreignkey => 'categorycode',
1184 onUpdate => 'CASCADE',
1185 onDelete => 'CASCADE',
1189 { key => 'shelfnumber',
1190 foreigntable => 'bookshelf',
1191 foreignkey => 'shelfnumber',
1192 onUpdate => 'CASCADE',
1193 onDelete => 'CASCADE',
1195 { key => 'itemnumber',
1196 foreigntable => 'items',
1197 foreignkey => 'itemnumber',
1198 onUpdate => 'CASCADE',
1199 onDelete => 'CASCADE',
1202 # onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be
1203 # easily deleted, but branches/itemtype not too easy to empty...
1205 { key => 'biblionumber',
1206 foreigntable => 'biblio',
1207 foreignkey => 'biblionumber',
1208 onUpdate => 'CASCADE',
1209 onDelete => 'CASCADE',
1211 { key => 'itemtype',
1212 foreigntable => 'itemtypes',
1213 foreignkey => 'itemtype',
1214 onUpdate => 'CASCADE',
1215 onDelete => 'RESTRICT',
1219 { key => 'biblioitemnumber',
1220 foreigntable => 'biblioitems',
1221 foreignkey => 'biblioitemnumber',
1222 onUpdate => 'CASCADE',
1223 onDelete => 'CASCADE',
1225 { key => 'homebranch',
1226 foreigntable => 'branches',
1227 foreignkey => 'branchcode',
1228 onUpdate => 'CASCADE',
1229 onDelete => 'RESTRICT',
1231 { key => 'holdingbranch',
1232 foreigntable => 'branches',
1233 foreignkey => 'branchcode',
1234 onUpdate => 'CASCADE',
1235 onDelete => 'RESTRICT',
1239 { key => 'booksellerid',
1240 foreigntable => 'aqbooksellers',
1242 onUpdate => 'CASCADE',
1243 onDelete => 'RESTRICT',
1247 { key => 'basketno',
1248 foreigntable => 'aqbasket',
1249 foreignkey => 'basketno',
1250 onUpdate => 'CASCADE',
1251 onDelete => 'CASCADE',
1253 { key => 'biblionumber',
1254 foreigntable => 'biblio',
1255 foreignkey => 'biblionumber',
1256 onUpdate => 'SET NULL',
1257 onDelete => 'SET NULL',
1261 { key => 'listprice',
1262 foreigntable => 'currency',
1263 foreignkey => 'currency',
1264 onUpdate => 'CASCADE',
1265 onDelete => 'CASCADE',
1267 { key => 'invoiceprice',
1268 foreigntable => 'currency',
1269 foreignkey => 'currency',
1270 onUpdate => 'CASCADE',
1271 onDelete => 'CASCADE',
1274 aqorderbreakdown => [
1275 { key => 'ordernumber',
1276 foreigntable => 'aqorders',
1277 foreignkey => 'ordernumber',
1278 onUpdate => 'CASCADE',
1279 onDelete => 'CASCADE',
1281 { key => 'bookfundid',
1282 foreigntable => 'aqbookfund',
1283 foreignkey => 'bookfundid',
1284 onUpdate => 'CASCADE',
1285 onDelete => 'CASCADE',
1288 branchtransfers => [
1289 { key => 'frombranch',
1290 foreigntable => 'branches',
1291 foreignkey => 'branchcode',
1292 onUpdate => 'CASCADE',
1293 onDelete => 'CASCADE',
1295 { key => 'tobranch',
1296 foreigntable => 'branches',
1297 foreignkey => 'branchcode',
1298 onUpdate => 'CASCADE',
1299 onDelete => 'CASCADE',
1301 { key => 'itemnumber',
1302 foreigntable => 'items',
1303 foreignkey => 'itemnumber',
1304 onUpdate => 'CASCADE',
1305 onDelete => 'CASCADE',
1309 { key => 'categorycode',
1310 foreigntable => 'categories',
1311 foreignkey => 'categorycode',
1312 onUpdate => 'CASCADE',
1313 onDelete => 'CASCADE',
1315 { key => 'itemtype',
1316 foreigntable => 'itemtypes',
1317 foreignkey => 'itemtype',
1318 onUpdate => 'CASCADE',
1319 onDelete => 'CASCADE',
1322 issues => [ # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
1324 { key => 'borrowernumber',
1325 foreigntable => 'borrowers',
1326 foreignkey => 'borrowernumber',
1327 onUpdate => 'SET NULL',
1328 onDelete => 'SET NULL',
1330 { key => 'itemnumber',
1331 foreigntable => 'items',
1332 foreignkey => 'itemnumber',
1333 onUpdate => 'SET NULL',
1334 onDelete => 'SET NULL',
1338 { key => 'borrowernumber',
1339 foreigntable => 'borrowers',
1340 foreignkey => 'borrowernumber',
1341 onUpdate => 'CASCADE',
1342 onDelete => 'CASCADE',
1344 { key => 'biblionumber',
1345 foreigntable => 'biblio',
1346 foreignkey => 'biblionumber',
1347 onUpdate => 'CASCADE',
1348 onDelete => 'CASCADE',
1350 { key => 'itemnumber',
1351 foreigntable => 'items',
1352 foreignkey => 'itemnumber',
1353 onUpdate => 'CASCADE',
1354 onDelete => 'CASCADE',
1356 { key => 'branchcode',
1357 foreigntable => 'branches',
1358 foreignkey => 'branchcode',
1359 onUpdate => 'CASCADE',
1360 onDelete => 'CASCADE',
1363 borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1364 # but prevent deleting a branch as soon as it has 1 borrower !
1365 { key => 'categorycode',
1366 foreigntable => 'categories',
1367 foreignkey => 'categorycode',
1368 onUpdate => 'RESTRICT',
1369 onDelete => 'RESTRICT',
1371 { key => 'branchcode',
1372 foreigntable => 'branches',
1373 foreignkey => 'branchcode',
1374 onUpdate => 'RESTRICT',
1375 onDelete => 'RESTRICT',
1378 deletedborrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
1379 # but prevent deleting a branch as soon as it has 1 borrower !
1380 { key => 'categorycode',
1381 foreigntable => 'categories',
1382 foreignkey => 'categorycode',
1383 onUpdate => 'RESTRICT',
1384 onDelete => 'RESTRICT',
1386 { key => 'branchcode',
1387 foreigntable => 'branches',
1388 foreignkey => 'branchcode',
1389 onUpdate => 'RESTRICT',
1390 onDelete => 'RESTRICT',
1394 { key => 'borrowernumber',
1395 foreigntable => 'borrowers',
1396 foreignkey => 'borrowernumber',
1397 onUpdate => 'CASCADE',
1398 onDelete => 'CASCADE',
1400 { key => 'itemnumber',
1401 foreigntable => 'items',
1402 foreignkey => 'itemnumber',
1403 onUpdate => 'SET NULL',
1404 onDelete => 'SET NULL',
1407 auth_tag_structure => [
1408 { key => 'authtypecode',
1409 foreigntable => 'auth_types',
1410 foreignkey => 'authtypecode',
1411 onUpdate => 'CASCADE',
1412 onDelete => 'CASCADE',
1415 # FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
1420 my %column_change = (
1424 from => 'emailaddress',
1429 from => 'streetaddress',
1431 after => 'initials',
1434 from => 'faxnumber',
1439 from => 'textmessaging',
1445 to => 'contactnote',
1446 after => 'opacnote',
1449 from => 'physstreet',
1454 from => 'streetcity',
1456 after => 'B_address',
1469 from => 'homezipcode',
1476 after => 'B_zipcode',
1481 after => 'dateenrolled',
1484 from => 'guarantor',
1485 to => 'guarantorid',
1486 after => 'contactname',
1489 from => 'altrelationship',
1490 to => 'relationship',
1491 after => 'borrowernotes',
1495 deletedborrowers => [
1497 from => 'emailaddress',
1502 from => 'streetaddress',
1504 after => 'initials',
1507 from => 'faxnumber',
1512 from => 'textmessaging',
1518 to => 'contactnote',
1519 after => 'opacnote',
1522 from => 'physstreet',
1527 from => 'streetcity',
1529 after => 'B_address',
1542 from => 'homezipcode',
1549 after => 'B_zipcode',
1554 after => 'dateenrolled',
1557 from => 'guarantor',
1558 to => 'guarantorid',
1559 after => 'contactname',
1562 from => 'altrelationship',
1563 to => 'relationship',
1564 after => 'borrowernotes',
1570 # MOVE all tables TO UTF-8 and innoDB
1571 $sth = $dbh->prepare("show table status");
1573 while ( my $table = $sth->fetchrow_hashref ) {
1574 if ($table->{Engine} ne 'InnoDB') {
1575 $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
1576 print "moving $table->{Name} to InnoDB\n";
1578 next if $table->{Name} eq 'marc_word';
1579 next if $table->{Name} eq 'marc_subfield_table';
1580 next if $table->{Name} eq 'auth_word';
1581 next if $table->{Name} eq 'auth_subfield_table';
1582 unless ($table->{Collation} =~ /^utf8/) {
1583 print "moving $table->{Name} to utf8\n";
1584 $dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
1585 $dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
1586 # 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 !
1592 foreach my $table (keys %column_change) {
1593 $sth = $dbh->prepare("show columns from $table");
1596 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1598 $types{$column}->{type} ="$type";
1599 $types{$column}->{null} = "$null";
1600 $types{$column}->{key} = "$key";
1601 $types{$column}->{default} = "$default";
1602 $types{$column}->{extra} = "$extra";
1604 my $tablerows = $column_change{$table};
1605 foreach my $row ( @$tablerows ) {
1606 if ($types{$row->{from}}->{type}) {
1607 print "altering $table $row->{from} to $row->{to}\n";
1608 # ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
1609 # alter table `borrowers` change `faxnumber` `fax` type text null after phone
1611 "alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
1612 ($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
1613 ($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
1614 "$types{$row->{from}}->{extra} after $row->{after} ";
1621 # Enter here the field you want to delete from DB.
1622 # FIXME :: there is a %uselessfield before which seems doing the same things.
1623 my %fieldtodelete = (
1624 # tablename => [fieldname1,fieldname2,...]
1628 print "removing some unused fields...\n";
1629 foreach my $table ( keys %fieldtodelete ) {
1630 foreach my $field ( @{$fieldtodelete{$table}} ){
1631 print "removing ".$field." from ".$table;
1632 my $sth = $dbh->prepare("ALTER TABLE $table DROP $field");
1635 print "Error : $sth->errstr \n";
1640 # Enter here the line you want to remove from DB.
1641 my %linetodelete = (
1642 # table name => where clause.
1643 userflags => "bit = 8", # delete the 'reserveforself' flags
1647 #-------------------
1652 # Get version of MySQL database engine.
1653 my $mysqlversion = `mysqld --version`;
1654 $mysqlversion =~ /Ver (\S*) /;
1656 if ( $mysqlversion ge '3.23' ) {
1657 print "Could convert to MyISAM database tables...\n" unless $silent;
1660 #---------------------------------
1663 # Collect all tables into a list
1664 $sth = $dbh->prepare("show tables");
1666 while ( my ($table) = $sth->fetchrow ) {
1667 $existingtables{$table} = 1;
1671 # Now add any missing tables
1672 foreach $table ( keys %requiretables ) {
1673 unless ( $existingtables{$table} ) {
1674 print "Adding $table table...\n" unless $silent;
1675 my $sth = $dbh->prepare("create table $table $requiretables{$table}");
1678 print "Error : $sth->errstr \n";
1684 #---------------------------------
1687 foreach $table ( keys %requirefields ) {
1688 print "Check table $table\n" if $debug and not $silent;
1689 $sth = $dbh->prepare("show columns from $table");
1692 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1694 $types{$column} = $type;
1696 foreach $column ( keys %{ $requirefields{$table} } ) {
1697 print " Check column $column [$types{$column}]\n" if $debug and not $silent;
1698 if ( !$types{$column} ) {
1700 # column doesn't exist
1701 print "Adding $column field to $table table...\n" unless $silent;
1702 $query = "alter table $table
1703 add column $column " . $requirefields{$table}->{$column};
1704 print "Execute: $query\n" if $debug;
1705 my $sti = $dbh->prepare($query);
1708 print "**Error : $sti->errstr \n";
1715 foreach $table ( keys %fielddefinitions ) {
1716 print "Check table $table\n" if $debug;
1717 $sth = $dbh->prepare("show columns from $table");
1720 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
1722 $definitions->{$column}->{type} = $type;
1723 $definitions->{$column}->{null} = $null;
1724 $definitions->{$column}->{null} = 'NULL' if $null eq 'YES';
1725 $definitions->{$column}->{key} = $key;
1726 $definitions->{$column}->{default} = $default;
1727 $definitions->{$column}->{extra} = $extra;
1729 my $fieldrow = $fielddefinitions{$table};
1730 foreach my $row (@$fieldrow) {
1731 my $field = $row->{field};
1732 my $type = $row->{type};
1733 my $null = $row->{null};
1734 # $null = 'YES' if $row->{null} eq 'NULL';
1735 my $key = $row->{key};
1736 my $default = $row->{default};
1737 # $default="''" unless $default;
1738 my $extra = $row->{extra};
1739 my $def = $definitions->{$field};
1740 my $after = ($row->{after}?" after ".$row->{after}:"");
1742 unless ( $type eq $def->{type}
1743 && $null eq $def->{null}
1744 && $key eq $def->{key}
1745 && $extra eq $def->{extra} )
1747 if ( $null eq '' ) {
1750 if ( $key eq 'PRI' ) {
1751 $key = 'PRIMARY KEY';
1753 unless ( $extra eq 'auto_increment' ) {
1757 # if it's a new column use "add", if it's an old one, use "change".
1759 if ($definitions->{$field}->{type}) {
1760 $action="change $field"
1764 # if it's a primary key, drop the previous pk, before altering the table
1765 print " alter or create $field in $table\n" unless $silent;
1767 if ($key ne 'PRIMARY KEY') {
1768 # warn "alter table $table $action $field $type $null $key $extra default $default $after";
1769 $query = "alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1771 # warn "alter table $table drop primary key, $action $field $type $null $key $extra default $default $after";
1772 # something strange : for indexes UNIQUE, they are reported as primary key here.
1773 # but if you try to run with drop primary key, it fails.
1774 # thus, we run the query twice, one will fail, one will succeed.
1776 $query="alter table $table drop primary key, $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1777 $query="alter table $table $action $field $type $null $key $extra ".($default?"default ".$dbh->quote($default):"")." $after";
1784 print "removing some unused data...\n";
1785 foreach my $table ( keys %linetodelete ) {
1786 foreach my $where ( @{linetodelete{$table}} ){
1787 print "DELETE FROM ".$table." where ".$where;
1789 my $sth = $dbh->prepare("DELETE FROM $table where $where");
1792 print "Error : $sth->errstr \n";
1797 # Populate tables with required data
1799 # synch table and deletedtable.
1800 foreach my $table (('borrowers','items','biblio','biblioitems')) {
1801 my %deletedborrowers;
1802 print "synch'ing $table and deleted$table\n";
1803 $sth = $dbh->prepare("show columns from deleted$table");
1805 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1806 $deletedborrowers{$column}=1;
1808 $sth = $dbh->prepare("show columns from $table");
1811 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ) {
1812 unless ($deletedborrowers{$column}) {
1813 my $newcol="alter table deleted$table add $column $type";
1814 if ($null eq 'YES') {
1815 $newcol .= " NULL ";
1817 $newcol .= " NOT NULL ";
1819 $newcol .= "default ".$dbh->quote($default) if $default;
1820 $newcol .= " after $previous" if $previous;
1822 print "creating column $column\n";
1828 # update publisheddate
1830 $sth = $dbh->prepare("select count(*) from serial where publisheddate is NULL");
1832 my ($emptypublished) = $sth->fetchrow;
1833 if ($emptypublished) {
1834 print "Updating publisheddate\n";
1835 $dbh->do("update serial set publisheddate=planneddate where publisheddate is NULL");
1837 foreach my $table ( keys %tabledata ) {
1838 print "Checking for data required in table $table...\n" unless $silent;
1839 my $tablerows = $tabledata{$table};
1840 foreach my $row (@$tablerows) {
1841 my $uniquefieldrequired = $row->{uniquefieldrequired};
1842 my $uniquevalue = $row->{$uniquefieldrequired};
1843 my $forceupdate = $row->{forceupdate};
1846 "select $uniquefieldrequired from $table where $uniquefieldrequired=?"
1848 $sth->execute($uniquevalue);
1850 foreach my $field (keys %$forceupdate) {
1851 if ($forceupdate->{$field}) {
1852 my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
1853 $sth->execute($row->{$field}, $uniquevalue);
1857 print "Adding row to $table: " unless $silent;
1861 foreach my $field ( keys %$row ) {
1862 next if $field eq 'uniquefieldrequired';
1863 next if $field eq 'forceupdate';
1864 my $value = $row->{$field};
1865 push @values, $value;
1866 print " $field => $value" unless $silent;
1867 $fieldlist .= "$field,";
1868 $placeholders .= "?,";
1870 print "\n" unless $silent;
1871 $fieldlist =~ s/,$//;
1872 $placeholders =~ s/,$//;
1873 print "insert into $table ($fieldlist) values ($placeholders)";
1876 "insert into $table ($fieldlist) values ($placeholders)");
1877 $sth->execute(@values);
1883 # check indexes and create them when needed
1885 print "Checking for index required...\n" unless $silent;
1886 foreach my $table ( keys %indexes ) {
1888 # read all indexes from $table
1890 $sth = $dbh->prepare("show index from $table");
1892 my %existingindexes;
1893 while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
1894 $existingindexes{$key_name} = 1;
1896 # read indexes to check
1897 my $tablerows = $indexes{$table};
1898 foreach my $row (@$tablerows) {
1899 my $key_name=$row->{indexname};
1900 if ($existingindexes{$key_name} eq 1) {
1901 # print "$key_name existing";
1903 print "\tCreating index $key_name in $table\n";
1905 if ($row->{indexname} eq 'PRIMARY') {
1906 $sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
1908 $sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
1911 print "Error $sql : $dbh->err \n" if $dbh->err;
1917 # check foreign keys and create them when needed
1919 print "Checking for foreign keys required...\n" unless $silent;
1920 foreach my $table ( keys %foreign_keys ) {
1922 # read all indexes from $table
1924 $sth = $dbh->prepare("show table status like '$table'");
1926 my $stat = $sth->fetchrow_hashref;
1927 # read indexes to check
1928 my $tablerows = $foreign_keys{$table};
1929 foreach my $row (@$tablerows) {
1930 my $foreign_table=$row->{foreigntable};
1931 if ($stat->{'Comment'} =~/$foreign_table/) {
1932 # print "$foreign_table existing\n";
1934 print "\tCreating foreign key $foreign_table in $table\n";
1935 # first, drop any orphan value in child table
1936 if ($row->{onDelete} ne "RESTRICT") {
1937 my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
1939 print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
1941 my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
1942 $sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
1943 $sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
1946 print "====================
1947 An error occured during :
1949 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).
1950 You can find those values with select
1951 \t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
1952 ====================\n
1958 # now drop useless tables
1959 foreach $table ( @TableToDelete ) {
1960 if ( $existingtables{$table} ) {
1961 print "Dropping unused table $table\n" if $debug and not $silent;
1962 $dbh->do("drop table $table");
1964 print "Error : $dbh->errstr \n";
1973 # create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
1976 # 1st, get how many biblio we will have to do...
1977 $sth = $dbh->prepare('select count(*) from marc_biblio');
1979 my ($totaltodo) = $sth->fetchrow;
1981 $sth = $dbh->prepare("show columns from biblio");
1984 my $bibliofwexist=0;
1985 while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
1986 $bibliofwexist=1 if $column eq 'frameworkcode';
1988 unless ($bibliofwexist) {
1989 print "moving biblioframework to biblio table\n";
1990 $dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
1991 $sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
1993 my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
1995 while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
1996 $sth_update->execute($frameworkcode,$biblionumber);
1998 print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
2003 # at last, remove useless fields
2004 foreach $table ( keys %uselessfields ) {
2005 my @fields = split /,/,$uselessfields{$table};
2008 foreach my $fieldtodrop (@fields) {
2009 $fieldtodrop =~ s/\t//g;
2010 $fieldtodrop =~ s/\n//g;
2012 $sth = $dbh->prepare("show columns from $table");
2014 while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
2016 $exists =1 if ($column eq $fieldtodrop);
2019 print "deleting $fieldtodrop field in $table...\n" unless $silent;
2020 my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
2027 # Changing aqbookfund's primary key
2029 $sth=$dbh->prepare("ALTER TABLE `aqbookfund` DROP PRIMARY KEY , ADD PRIMARY KEY ( `bookfundid` , `branchcode` ) ;");
2037 # Revision 1.164 2007/05/04 16:24:09 tipaul
2038 # various bugfixes on parameters modules + adding default NoZebraIndexes systempreference if it's empty
2040 # Revision 1.163 2007/05/02 16:44:31 tipaul
2041 # NoZebra SQL index management :
2042 # * adding 3 subs in Biblio.pm
2043 # - GetNoZebraIndexes, that get the index structure in a new systempreference (added with this commit)
2044 # - _DelBiblioNoZebra, that retrieve all index entries for a biblio and remove in a variable the biblio reference
2045 # - _AddBiblioNoZebra, that add index entries for a biblio.
2046 # Note that the 2 _Add and _Del subs work only in a hash variable, to speed up things in case of a modif (ie : delete+add). The effective SQL update is done in the ModZebra sub (that existed before, and dealed with zebra index).
2047 # I think the code has to be more deeply tested, but it works at least partially.
2049 # Revision 1.162 2007/04/30 16:16:50 tipaul
2050 # bugfix for updatedatabase : when there is no default value (NULL fields) + removing bibliothesaurus table+adding NoZebra systempref (False by default)
2052 # Revision 1.161 2007/04/13 16:27:55 hdl
2053 # Adding Version variable to systempreferences.
2055 # Revision 1.160 2007/03/19 18:35:13 toins
2056 # - adding default value in marc_subfield_structure.
2057 # - now marc_subfields_structure displays subfields in tab view.
2059 # Revision 1.159 2007/03/16 01:25:09 kados
2060 # Using my precrash CVS copy I did the following:
2062 # cvs -z3 -d:ext:kados@cvs.savannah.nongnu.org:/sources/koha co -P koha
2063 # find koha.precrash -type d -name "CVS" -exec rm -v {} \;
2064 # cp -r koha.precrash/* koha/
2068 # This should in theory put us right back where we were before the crash
2070 # Revision 1.159 2007/03/12 17:52:30 rych
2071 # add pri key to userflags
2073 # Revision 1.158 2007/03/09 15:14:57 tipaul
2074 # rel_3_0 moved to HEAD
2076 # Revision 1.157.2.56 2007/01/31 16:22:54 btoumi
2077 # -add possibility to use isbn with length of 13 characters
2078 # for Import datas in the reservoir.
2079 # -modify isbn field in marc_breeding table (varchar 13)
2080 # -add isbn filter (no - )when u read a notice from reservoir
2081 # -add filter to have right field 100
2083 # Revision 1.157.2.55 2007/01/30 10:50:19 tipaul
2084 # adding 2 usefull indexes to biblioitems table
2086 # Revision 1.157.2.54 2007/01/29 16:45:52 toins
2087 # * adding a new default authorised value : SUGGEST.
2088 # SUGGEST give some reasons to accept or reject a suggestion.
2090 # * default value for borrowersMandatoryfield syspref is now "cardnumber|surname|adress"
2092 # Revision 1.157.2.53 2007/01/26 20:48:37 hdl
2093 # Serials management : Bugfixes + improvements.
2094 # - Partial dates are now managed
2095 # - next Date Calculation with irregularity tested for 1 week and 1 month.
2096 # - manage if subscription is abouttoexpire or expired.
2097 # - Adding some information on serials pages about subscription.
2098 # - Managing irregularity with numbers.
2099 # - Adding Internal Notes in subscription management.
2100 # - Repeating Button above pages.
2102 # Please run Updatedatabase to change irregularity and add internalnotes field to subscription
2104 # Revision 1.157.2.52 2007/01/24 13:57:26 tipaul
2105 # - setting supplierid to auto_increment (HDL : could you check that is works, i'm not 100% sure)
2106 # - removing 22 -> 30 marc_subfield_table -> marcxml stuff, it's now in misc/migration_tools/22_to_30/
2108 # Revision 1.157.2.51 2007/01/18 09:58:45 tipaul
2109 # defaulting NOT NULL fields (to '')
2111 # Revision 1.157.2.50 2007/01/18 09:39:21 tipaul
2112 # issuedate must be defaulted with ' '
2114 # Revision 1.157.2.49 2007/01/18 09:37:30 tipaul
2115 # removing 2 field definitions that were here twice
2117 # Revision 1.157.2.48 2007/01/15 09:55:40 toins
2118 # adding a new logging systempref : FinesLog.
2120 # Revision 1.157.2.47 2007/01/12 18:09:49 toins
2123 # Revision 1.157.2.46 2007/01/11 14:35:39 tipaul
2124 # adding Opac Browser feature : the build_browser_and_cloud.pl script will :
2125 # - 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)
2126 # - fill the tags table, that contains the subject cloud.
2128 # 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 !
2130 # The commit also add the systempreference to hide/show the OpacBrowse in database & in systempref management script.
2132 # IMPROVEMENTS to do :
2133 # - 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).
2134 # - add, in parameters section, a place to edit browser descriptions. The build script has to be updated to to avoid deleting existing browser descriptions.
2136 # Revision 1.157.2.45 2007/01/10 16:52:52 toins
2137 # Value for Log Features syspref are set to 0 by default.
2139 # Revision 1.157.2.44 2007/01/10 16:31:15 toins
2140 # new systems preferences :
2141 # - CataloguingLog (log the update/creation/deletion of a notice if set to 1)
2142 # - BorrowersLog ( idem for borrowers )
2143 # - IssueLog (log all issue if set to 1)
2144 # - ReturnLog (log all return if set to 1)
2145 # - SusbcriptionLog (log all creation/deletion/update of a subcription)
2147 # All of theses are in a new tab called 'LOGFeatures' in systempreferences.pl
2149 # Revision 1.157.2.43 2007/01/10 14:13:17 toins
2150 # opac_news.displayed is replaced by opac_news.number.
2151 # This field say how are ordered the news on the template.
2153 # Revision 1.157.2.42 2007/01/09 14:09:01 toins
2154 # 2 field added to opac_news.('expirationdate' and 'displayed').