2521 lines
82 KiB
Perl
Executable file
2521 lines
82 KiB
Perl
Executable file
#!/usr/bin/perl
|
||
|
||
# $Id$
|
||
|
||
# Database Updater
|
||
# This script checks for required updates to the database.
|
||
|
||
# Part of the Koha Library Software www.koha.org
|
||
# Licensed under the GPL.
|
||
|
||
# Bugs/ToDo:
|
||
# - Would also be a good idea to offer to do a backup at this time...
|
||
|
||
# NOTE: If you do something more than once in here, make it table driven.
|
||
use strict;
|
||
|
||
# CPAN modules
|
||
use DBI;
|
||
use Getopt::Long;
|
||
|
||
# Koha modules
|
||
use C4::Context;
|
||
|
||
use MARC::Record;
|
||
use MARC::File::XML ( BinaryEncoding => 'utf8' );
|
||
|
||
# FIXME - The user might be installing a new database, so can't rely
|
||
# on /etc/koha.conf anyway.
|
||
|
||
my $debug = 0;
|
||
|
||
my (
|
||
$sth, $sti,
|
||
$query,
|
||
%existingtables, # tables already in database
|
||
%types,
|
||
$table,
|
||
$column,
|
||
$type, $null, $key, $default, $extra,
|
||
$prefitem, # preference item in systempreferences table
|
||
);
|
||
|
||
my $silent;
|
||
GetOptions( 's' => \$silent );
|
||
my $dbh = C4::Context->dbh;
|
||
print "connected to your DB. Checking & modifying it\n" unless $silent;
|
||
$|=1; # flushes output
|
||
|
||
#-------------------
|
||
# Defines
|
||
|
||
# Tables to add if they don't exist
|
||
my %requiretables = (
|
||
categorytable => "(categorycode char(5) NOT NULL default '',
|
||
description text default '',
|
||
itemtypecodes text default '',
|
||
PRIMARY KEY (categorycode)
|
||
)",
|
||
subcategorytable => "(subcategorycode char(5) NOT NULL default '',
|
||
description text default '',
|
||
itemtypecodes text default '',
|
||
PRIMARY KEY (subcategorycode)
|
||
)",
|
||
mediatypetable => "(mediatypecode char(5) NOT NULL default '',
|
||
description text default '',
|
||
itemtypecodes text default '',
|
||
PRIMARY KEY (mediatypecode)
|
||
)",
|
||
action_logs => "(
|
||
`timestamp` TIMESTAMP NOT NULL ,
|
||
`user` INT( 11 ) NOT NULL ,
|
||
`module` TEXT default '',
|
||
`action` TEXT default '' ,
|
||
`object` INT(11) default '' ,
|
||
`info` TEXT default '' ,
|
||
PRIMARY KEY ( `timestamp` , `user` )
|
||
)",
|
||
letter => "(
|
||
module varchar(20) NOT NULL default '',
|
||
code varchar(20) NOT NULL default '',
|
||
name varchar(100) NOT NULL default '',
|
||
title varchar(200) NOT NULL default '',
|
||
content text,
|
||
PRIMARY KEY (module,code)
|
||
)",
|
||
alert =>"(
|
||
alertid int(11) NOT NULL auto_increment,
|
||
borrowernumber int(11) NOT NULL default '0',
|
||
type varchar(10) NOT NULL default '',
|
||
externalid varchar(20) NOT NULL default '',
|
||
PRIMARY KEY (alertid),
|
||
KEY borrowernumber (borrowernumber),
|
||
KEY type (type,externalid)
|
||
)",
|
||
opac_news => "(
|
||
`idnew` int(10) unsigned NOT NULL auto_increment,
|
||
`title` varchar(250) NOT NULL default '',
|
||
`new` text NOT NULL,
|
||
`lang` varchar(4) NOT NULL default '',
|
||
`timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (`idnew`)
|
||
)",
|
||
repeatable_holidays => "(
|
||
`id` int(11) NOT NULL auto_increment,
|
||
`branchcode` varchar(4) NOT NULL default '',
|
||
`weekday` smallint(6) default NULL,
|
||
`day` smallint(6) default NULL,
|
||
`month` smallint(6) default NULL,
|
||
`title` varchar(50) NOT NULL default '',
|
||
`description` text NOT NULL,
|
||
PRIMARY KEY (`id`)
|
||
)",
|
||
special_holidays => "(
|
||
`id` int(11) NOT NULL auto_increment,
|
||
`branchcode` varchar(4) NOT NULL default '',
|
||
`day` smallint(6) NOT NULL default '0',
|
||
`month` smallint(6) NOT NULL default '0',
|
||
`year` smallint(6) NOT NULL default '0',
|
||
`isexception` smallint(1) NOT NULL default '1',
|
||
`title` varchar(50) NOT NULL default '',
|
||
`description` text NOT NULL,
|
||
PRIMARY KEY (`id`)
|
||
)",
|
||
overduerules =>"(`branchcode` varchar(255) NOT NULL default '',
|
||
`categorycode` char(2) NOT NULL default '',
|
||
`delay1` int(4) default '0',
|
||
`letter1` varchar(20) default NULL,
|
||
`debarred1` char(1) default '0',
|
||
`delay2` int(4) default '0',
|
||
`debarred2` char(1) default '0',
|
||
`letter2` varchar(20) default NULL,
|
||
`delay3` int(4) default '0',
|
||
`letter3` varchar(20) default NULL,
|
||
`debarred3` int(1) default '0',
|
||
PRIMARY KEY (`branchcode`,`categorycode`)
|
||
)",
|
||
cities => "(`cityid` int auto_increment,
|
||
`city_name` char(100) NOT NULL,
|
||
`city_zipcode` char(20),
|
||
PRIMARY KEY (`cityid`)
|
||
)",
|
||
roadtype => "(`roadtypeid` int auto_increment,
|
||
`road_type` char(100) NOT NULL,
|
||
PRIMARY KEY (`roadtypeid`)
|
||
)",
|
||
|
||
labels => "(
|
||
labelid int(11) NOT NULL auto_increment,
|
||
itemnumber varchar(100) NOT NULL default '',
|
||
timestamp timestamp(14) NOT NULL,
|
||
PRIMARY KEY (labelid)
|
||
)",
|
||
|
||
labels_conf => "(
|
||
id int(4) NOT NULL auto_increment,
|
||
barcodetype char(100) default '',
|
||
title tinyint(1) default '0',
|
||
isbn tinyint(1) default '0',
|
||
itemtype tinyint(1) default '0',
|
||
barcode tinyint(1) default '0',
|
||
dewey tinyint(1) default '0',
|
||
class tinyint(1) default '0',
|
||
author tinyint(1) default '0',
|
||
papertype char(100) default '',
|
||
startrow int(2) default NULL,
|
||
PRIMARY KEY (id)
|
||
)",
|
||
reviews => "(
|
||
reviewid integer NOT NULL auto_increment,
|
||
borrowernumber integer,
|
||
biblionumber integer,
|
||
review text,
|
||
approved tinyint,
|
||
datereviewed datetime,
|
||
PRIMARY KEY (reviewid)
|
||
)",
|
||
borrowers_to_borrowers => "(
|
||
borrower1 integer,
|
||
borrower2 integer
|
||
)",
|
||
subscriptionroutinglist => "(
|
||
routingid int(11) NOT NULL auto_increment,
|
||
subscriptionid int(11) NOT NULL default '0',
|
||
borrowernumber int(11) NOT NULL default '0',
|
||
ranking int(11) NOT NULL default '0',
|
||
numberpattern varchar(100) NULL,
|
||
PRIMARY KEY (routingid)
|
||
)",
|
||
|
||
);
|
||
|
||
my %requirefields = (
|
||
subscription => { 'letter' => 'char(20) NULL', 'distributedto' => 'text NULL'},
|
||
itemtypes => { 'imageurl' => 'char(200) NULL'},
|
||
aqbookfund => { 'branchcode' => 'varchar(4) NULL'},
|
||
aqbudget => { 'branchcode' => 'varchar(4) NULL'},
|
||
auth_header => { 'marc' => 'BLOB NOT NULL', 'linkid' => 'BIGINT(20) NULL'},
|
||
auth_subfield_structure =>{ 'hidden' => 'TINYINT(3) NOT NULL UNSIGNED ZEROFILL', 'kohafield' => 'VARCHAR(45) NOT NULL', 'linkid' => 'TINYINT(1) NOT NULL UNSIGNED', 'isurl' => 'TINYINT(1) UNSIGNED'},
|
||
statistics => { 'associatedborrower' => 'integer'},
|
||
# tablename => { 'field' => 'fieldtype' },
|
||
);
|
||
|
||
my %dropable_table = (
|
||
sessionqueries => 'sessionqueries',
|
||
marcrecorddone => 'marcrecorddone',
|
||
users => 'users',
|
||
itemsprices => 'itemsprices',
|
||
biblioanalysis => 'biblioanalysis',
|
||
borexp => 'borexp',
|
||
# tablename => 'tablename',
|
||
);
|
||
|
||
my %uselessfields = (
|
||
# tablename => "field1,field2",
|
||
borrowers => "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
|
||
deletedborrowers=> "suburb,altstreetaddress,altsuburb,altcity,studentnumber,school,area,preferredcont,altcp",
|
||
);
|
||
# the other hash contains other actions that can't be done elsewhere. they are done
|
||
# either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
|
||
|
||
# The tabledata hash contains data that should be in the tables.
|
||
# The uniquefieldrequired hash entry is used to determine which (if any) fields
|
||
# must not exist in the table for this row to be inserted. If the
|
||
# uniquefieldrequired entry is already in the table, the existing data is not
|
||
# modified, unless the forceupdate hash entry is also set. Fields in the
|
||
# anonymous "forceupdate" hash will be forced to be updated to the default
|
||
# values given in the %tabledata hash.
|
||
|
||
my %tabledata = (
|
||
# tablename => [
|
||
# { uniquefielrequired => 'fieldname', # the primary key in the table
|
||
# fieldname => fieldvalue,
|
||
# fieldname2 => fieldvalue2,
|
||
# },
|
||
# ],
|
||
systempreferences => [
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'Activate_Log',
|
||
value => 'On',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Turn Log Actions on DB On an Off',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'IndependantBranches',
|
||
value => 0,
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Turn Branch independancy management On an Off',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'ReturnBeforeExpiry',
|
||
value => 'Off',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'If Yes, Returndate on issuing can\'t be after borrower card expiry',
|
||
type => 'YesNo',
|
||
},
|
||
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'opacstylesheet',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Enter a complete URL to use an alternate layout stylesheet in OPAC',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'opaccolorstylesheet',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Enter the name of the color stylesheet to use in the OPAC',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'opaclayoutstylesheet',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Enter the name of the layout stylesheet to use in the OPAC',
|
||
type => 'free',
|
||
},
|
||
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'opacreadinghistory',
|
||
value => '1',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Turn on/off display of Patron Reading History in OPAC',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'opaclanguagesdisplay',
|
||
value => '1',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Turn on/off display of Change Language feature on OPAC',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'patronimages',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Turn on/off display of patron images in Intranet and specify a file extension for images',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'intranetstylesheet',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Enter a complete URL to use an alternate layout stylesheet in Intranet',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'intranetcolorstylesheet',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Enter the name of the color stylesheet to use in Intranet',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'opacsmallimage',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Enter a complete URL to an image, will be on top/left instead of the Koha logo',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'opaclargeimage',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Enter a complete URL to an image, will be on the main page, instead of the Koha logo',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'delimiter',
|
||
value => ';',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation => 'separator for reports exported to spreadsheet',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'MIME',
|
||
value => 'OPENOFFICE.ORG',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1,
|
||
'options' => 1
|
||
},
|
||
explanation =>
|
||
'Define the default application for report exportations into files',
|
||
type => 'Choice',
|
||
options => 'EXCEL|OPENOFFICE.ORG'
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'Delimiter',
|
||
value => ';',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1,
|
||
'options' => 1
|
||
},
|
||
explanation =>
|
||
'Define the default separator character for report exportations into files',
|
||
type => 'Choice',
|
||
options => ';|tabulation|,|/|\|#'
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'SubscriptionHistory',
|
||
value => ';',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1,
|
||
'options' => 1
|
||
},
|
||
explanation =>
|
||
'Define the information level for serials history in OPAC',
|
||
type => 'Choice',
|
||
options => 'simplified|full'
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'hidelostitems',
|
||
value => 'No',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation => 'show or hide "lost" items in OPAC.',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'IndependantBranches',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation => 'Turn Branch independency management On and Off',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'ReturnBeforeExpiry',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'If Yes, Returndate on issuing can\'t be after borrower card expiry',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'Disable_Dictionary',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation => 'Disables Dictionary buttons if set to yes',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'hide_marc',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'hide marc specific datas like subfield code & indicators to library',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'NotifyBorrowerDeparture',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Delay before expiry where a notice is sent when issuing',
|
||
type => 'Integer',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'OpacPasswordChange',
|
||
value => '1',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Enable/Disable password change in OPAC (disable it when using LDAP auth)',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'OpacNav',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Use HTML tabs to add navigational links to the left-hand navigational bar in OPAC',
|
||
type => 'Textarea',
|
||
options => '70|10'
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'IntranetNav',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1
|
||
},
|
||
explanation =>
|
||
'Use HTML tabs to add navigational links to the left-hand navigational bar in Intranet',
|
||
type => 'Textarea',
|
||
options => '70|10'
|
||
},
|
||
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'AnonSuggestions',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'
|
||
type' => 1
|
||
},
|
||
explanation =>
|
||
'Set to anonymous borrowernumber to enable Anonymous suggestions',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'MARCOrgCode',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'
|
||
type' => 1
|
||
},
|
||
explanation =>
|
||
'Your MARC Organization Code - http://www.loc.gov/marc/organizations/orgshome.html',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'AmazonContent',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'
|
||
type' => 1
|
||
},
|
||
explanation =>
|
||
'Turn On Amazon Content - You MUST set AmazonDevKey and AmazonAssocTag if enabled',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'AmazonDevKey',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'
|
||
type' => 1
|
||
},
|
||
explanation =>
|
||
'see: aws-portal.amazon.com/gp/aws/developer/registration/index.html',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'AmazonAssocTag',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'
|
||
type' => 1
|
||
},
|
||
explanation =>
|
||
'see: associates.amazon.com/gp/flex/associates/apply-login.html',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'type' => 1,
|
||
'options' => 1
|
||
},
|
||
variable => 'TemplateEncoding',
|
||
value => 'iso-8859-1',
|
||
explanation => 'Specify the encoding to use in Templates',
|
||
type => 'Choice',
|
||
options => 'iso-8859-1|utf-8'
|
||
},
|
||
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'opaccredits',
|
||
value => '',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'
|
||
type' => 1
|
||
},
|
||
explanation =>
|
||
'Put any HTML Credits at the bottom of the OPAC page',
|
||
type => 'Textarea',
|
||
options => '70|10'
|
||
},
|
||
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'opacheader',
|
||
value => '',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Enter HTML to be included as a custom header in the OPAC',
|
||
type => 'Textarea',
|
||
options => '30|10'
|
||
},
|
||
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'IntranetBiblioDefaultView',
|
||
value => 'marc',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Define the default view of a biblio in the intranet. Can be either normal, marc, or ISBD',
|
||
type => 'Choice',
|
||
options => 'normal|marc|isbd'
|
||
},
|
||
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'opacbookbag',
|
||
value => '1',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Enable or disable display of biblio basket (book bag)',
|
||
type => 'YesNo'
|
||
},
|
||
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'opacuserlogin',
|
||
value => '1',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Enable or disable display of user login features',
|
||
type => 'YesNo'
|
||
},
|
||
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'serialsadditems',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'
|
||
type' => 1
|
||
},
|
||
explanation =>
|
||
'If set, a new item will be automatically added when receiving an issue',
|
||
type => 'YesNo',
|
||
},
|
||
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'RoutingSerials',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'
|
||
type' => 1
|
||
},
|
||
explanation =>
|
||
'If set, will use alternate serials and routing lists functionality',
|
||
type => 'YesNo',
|
||
},
|
||
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'advancedMARCeditor',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'
|
||
type' => 1
|
||
},
|
||
explanation =>
|
||
"If set, the MARC editor won't show you tag/subfields description",
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'z3950NormalizeAuthor',
|
||
value => '0',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'
|
||
type' => 1
|
||
},
|
||
explanation =>
|
||
"If set, Personnal Authorities will replace authors in biblio.author",
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'z3950AuthorAuthFields',
|
||
value => '701,702,700',
|
||
forceupdate => {
|
||
'explanation' => 1,
|
||
'
|
||
type' => 1
|
||
},
|
||
explanation =>
|
||
"contains the MARC biblio tags of person authorities to fill biblio.author with when importing biblio",
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'useDaysMode',
|
||
value => 'Calendar',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'How to calculate return dates : Calendar means holidays will be controled, Days means the return date don\'t depend on holidays',
|
||
type => 'Choice',
|
||
options => 'Calendar|Days'
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'borrowerMandatoryField',
|
||
value => 'zipcode|surname',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'List all mandatory fields for borrowers',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'borrowerRelationship',
|
||
value => 'father|mother,grand-mother',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'The relationships between a guarantor & a guarantee (separated by | or ,)',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'ReservesMaxPickUpDelay',
|
||
value => '10',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Maximum delay to pick up a reserved document',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'TransfersMaxDaysWarning',
|
||
value => '3',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Max delay before considering the transfer has potentialy a problem',
|
||
type => 'free',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'memberofinstitution',
|
||
value => '0',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Are your patrons members of institutions',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'ReadingHistory',
|
||
value => '0',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Allow reading record info retrievable from issues and oldissues tables',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'IssuingInProcess',
|
||
value => '0',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Allow no debt alert if the patron is issuing item that accumulate debt',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'AutomaticItemReturn',
|
||
value => '1',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'This Variable allow or not to return automaticly to his homebranch',
|
||
type => 'YesNo',
|
||
},
|
||
{
|
||
uniquefieldrequired => 'variable',
|
||
variable => 'reviewson',
|
||
value => '0',
|
||
forceupdate => { 'explanation' => 1,
|
||
'type' => 1},
|
||
explanation => 'Allows patrons to submit reviews from the opac',
|
||
type => 'YesNo',
|
||
},
|
||
],
|
||
|
||
);
|
||
|
||
my %fielddefinitions = (
|
||
# fieldname => [
|
||
# { field => 'fieldname',
|
||
# type => 'fieldtype',
|
||
# null => '',
|
||
# key => '',
|
||
# default => ''
|
||
# },
|
||
# ],
|
||
serial => [
|
||
{
|
||
field => 'notes',
|
||
type => 'TEXT',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '',
|
||
extra => ''
|
||
},
|
||
{
|
||
field => 'routingnotes',
|
||
type => 'TEXT',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '',
|
||
extra => ''
|
||
},
|
||
{
|
||
field => 'claimdate',
|
||
type => 'DATE',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '',
|
||
extra => ''
|
||
},
|
||
],
|
||
|
||
subscription => [
|
||
{
|
||
field => 'firstacquidate',
|
||
type => 'DATE',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '0000-00-00',
|
||
extra => ''
|
||
},
|
||
{
|
||
field => 'numberpattern',
|
||
type => 'int(4)',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '0',
|
||
extra => ''
|
||
},
|
||
{
|
||
field => 'irregularity',
|
||
type => 'varchar(255)',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '',
|
||
extra => ''
|
||
},
|
||
{
|
||
field => 'hemisphere',
|
||
type => 'int(1)',
|
||
null => '',
|
||
key => '',
|
||
default => '1',
|
||
extra => ''
|
||
},
|
||
{
|
||
field => 'callnumber',
|
||
type => 'varchar(100)',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '',
|
||
extra => ''
|
||
},
|
||
],
|
||
|
||
aqbasket => [
|
||
{
|
||
field => 'booksellerid',
|
||
type => 'int(11)',
|
||
null => 'NOT NULL',
|
||
key => '',
|
||
default => '1',
|
||
extra => '',
|
||
},
|
||
],
|
||
aqbooksellers => [
|
||
{
|
||
field => 'id',
|
||
type => 'int(11)',
|
||
null => 'NOT NULL',
|
||
key => '',
|
||
default => '',
|
||
extra => 'auto_increment',
|
||
},
|
||
{
|
||
field => 'listprice',
|
||
type => 'varchar(10)',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '',
|
||
extra => '',
|
||
},
|
||
{
|
||
field => 'invoiceprice',
|
||
type => 'varchar(10)',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '',
|
||
extra => '',
|
||
},
|
||
],
|
||
issues => [
|
||
{
|
||
field => 'borrowernumber',
|
||
type => 'int(11)',
|
||
null => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
|
||
key => '',
|
||
default => '',
|
||
extra => '',
|
||
},
|
||
{
|
||
field => 'itemnumber',
|
||
type => 'int(11)',
|
||
null => 'NULL', # can be null when a borrower is deleted and the foreign key rule executed
|
||
key => '',
|
||
default => '',
|
||
extra => '',
|
||
},
|
||
],
|
||
borrowers => [
|
||
{ field => 'firstname',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
},
|
||
{ field => 'initials',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
},
|
||
{ field => 'B_email',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'B_zipcode',
|
||
},
|
||
{
|
||
field => 'streetnumber', # street number (hidden if streettable table is empty)
|
||
type => 'char(10)',
|
||
null => 'NULL',
|
||
after => 'initials',
|
||
},
|
||
{
|
||
field => 'streettype', # street table, list builded from a system table
|
||
type => 'char(50)',
|
||
null => 'NULL',
|
||
after => 'streetnumber',
|
||
},
|
||
{ field => 'phone',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
},
|
||
{
|
||
field => 'B_streetnumber', # street number (hidden if streettable table is empty)
|
||
type => 'char(10)',
|
||
null => 'NULL',
|
||
after => 'fax',
|
||
},
|
||
{
|
||
field => 'B_streettype', # street table, list builded from a system table
|
||
type => 'char(50)',
|
||
null => 'NULL',
|
||
after => 'B_streetnumber',
|
||
},
|
||
{
|
||
field => 'phonepro',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'fax',
|
||
},
|
||
{
|
||
field => 'address2', # complement address
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'address',
|
||
},
|
||
{
|
||
field => 'emailpro',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'fax',
|
||
},
|
||
{
|
||
field => 'contactfirstname', # contact's firstname
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'contactname',
|
||
},
|
||
{
|
||
field => 'contacttitle', # contact's title
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'contactfirstname',
|
||
},
|
||
],
|
||
|
||
deletedborrowers => [
|
||
{ field => 'firstname',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
},
|
||
{ field => 'initials',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
},
|
||
{ field => 'B_email',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'B_zipcode',
|
||
},
|
||
{
|
||
field => 'streetnumber', # street number (hidden if streettable table is empty)
|
||
type => 'char(10)',
|
||
null => 'NULL',
|
||
after => 'initials',
|
||
},
|
||
{
|
||
field => 'streettype', # street table, list builded from a system table
|
||
type => 'char(50)',
|
||
null => 'NULL',
|
||
after => 'streetnumber',
|
||
},
|
||
{ field => 'phone',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
},
|
||
{
|
||
field => 'B_streetnumber', # street number (hidden if streettable table is empty)
|
||
type => 'char(10)',
|
||
null => 'NULL',
|
||
after => 'fax',
|
||
},
|
||
{
|
||
field => 'B_streettype', # street table, list builded from a system table
|
||
type => 'char(50)',
|
||
null => 'NULL',
|
||
after => 'B_streetnumber',
|
||
},
|
||
{
|
||
field => 'phonepro',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'fax',
|
||
},
|
||
{
|
||
field => 'address2', # complement address
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'address',
|
||
},
|
||
{
|
||
field => 'emailpro',
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'fax',
|
||
},
|
||
{
|
||
field => 'contactfirstname', # contact's firstname
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'contactname',
|
||
},
|
||
{
|
||
field => 'contacttitle', # contact's title
|
||
type => 'text',
|
||
null => 'NULL',
|
||
after => 'contactfirstname',
|
||
},
|
||
],
|
||
|
||
branches => [
|
||
{
|
||
field => 'branchip',
|
||
type => 'varchar(15)',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '',
|
||
extra => '',
|
||
},
|
||
{
|
||
field => 'branchprinter',
|
||
type => 'varchar(100)',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '',
|
||
extra => '',
|
||
},
|
||
],
|
||
categories => [
|
||
{
|
||
field => 'category_type',
|
||
type => 'char(1)',
|
||
null => 'NOT NULL',
|
||
key => '',
|
||
default => 'A',
|
||
extra => '',
|
||
},
|
||
],
|
||
reserves => [
|
||
{
|
||
field => 'waitingdate',
|
||
type => 'date',
|
||
null => 'NULL',
|
||
key => '',
|
||
default => '',
|
||
extra => '',
|
||
},
|
||
],
|
||
);
|
||
|
||
my %indexes = (
|
||
# table => [
|
||
# { indexname => 'index detail'
|
||
# }
|
||
# ],
|
||
shelfcontents => [
|
||
{ indexname => 'shelfnumber',
|
||
content => 'shelfnumber',
|
||
},
|
||
{ indexname => 'itemnumber',
|
||
content => 'itemnumber',
|
||
}
|
||
],
|
||
bibliosubject => [
|
||
{ indexname => 'biblionumber',
|
||
content => 'biblionumber',
|
||
}
|
||
],
|
||
items => [
|
||
{ indexname => 'homebranch',
|
||
content => 'homebranch',
|
||
},
|
||
{ indexname => 'holdingbranch',
|
||
content => 'holdingbranch',
|
||
}
|
||
],
|
||
aqbooksellers => [
|
||
{ indexname => 'PRIMARY',
|
||
content => 'id',
|
||
type => 'PRIMARY',
|
||
}
|
||
],
|
||
aqbasket => [
|
||
{ indexname => 'booksellerid',
|
||
content => 'booksellerid',
|
||
},
|
||
],
|
||
aqorders => [
|
||
{ indexname => 'basketno',
|
||
content => 'basketno',
|
||
},
|
||
],
|
||
aqorderbreakdown => [
|
||
{ indexname => 'ordernumber',
|
||
content => 'ordernumber',
|
||
},
|
||
{ indexname => 'bookfundid',
|
||
content => 'bookfundid',
|
||
},
|
||
],
|
||
currency => [
|
||
{ indexname => 'PRIMARY',
|
||
content => 'currency',
|
||
type => 'PRIMARY',
|
||
}
|
||
],
|
||
);
|
||
|
||
my %foreign_keys = (
|
||
# table => [
|
||
# { key => 'the key in table' (must be indexed)
|
||
# foreigntable => 'the foreigntable name', # (the parent)
|
||
# foreignkey => 'the foreign key column(s)' # (in the parent)
|
||
# onUpdate => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
|
||
# onDelete => 'CASCADE|SET NULL|NO ACTION| RESTRICT',
|
||
# }
|
||
# ],
|
||
shelfcontents => [
|
||
{ key => 'shelfnumber',
|
||
foreigntable => 'bookshelf',
|
||
foreignkey => 'shelfnumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'itemnumber',
|
||
foreigntable => 'items',
|
||
foreignkey => 'itemnumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
],
|
||
# onDelete is RESTRICT on reference tables (branches, itemtype) as we don't want items to be
|
||
# easily deleted, but branches/itemtype not too easy to empty...
|
||
biblioitems => [
|
||
{ key => 'biblionumber',
|
||
foreigntable => 'biblio',
|
||
foreignkey => 'biblionumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'itemtype',
|
||
foreigntable => 'itemtypes',
|
||
foreignkey => 'itemtype',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'RESTRICT',
|
||
},
|
||
],
|
||
items => [
|
||
{ key => 'biblioitemnumber',
|
||
foreigntable => 'biblioitems',
|
||
foreignkey => 'biblioitemnumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'homebranch',
|
||
foreigntable => 'branches',
|
||
foreignkey => 'branchcode',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'RESTRICT',
|
||
},
|
||
{ key => 'holdingbranch',
|
||
foreigntable => 'branches',
|
||
foreignkey => 'branchcode',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'RESTRICT',
|
||
},
|
||
],
|
||
additionalauthors => [
|
||
{ key => 'biblionumber',
|
||
foreigntable => 'biblio',
|
||
foreignkey => 'biblionumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
],
|
||
bibliosubject => [
|
||
{ key => 'biblionumber',
|
||
foreigntable => 'biblio',
|
||
foreignkey => 'biblionumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
],
|
||
aqbasket => [
|
||
{ key => 'booksellerid',
|
||
foreigntable => 'aqbooksellers',
|
||
foreignkey => 'id',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'RESTRICT',
|
||
},
|
||
],
|
||
aqorders => [
|
||
{ key => 'basketno',
|
||
foreigntable => 'aqbasket',
|
||
foreignkey => 'basketno',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'biblionumber',
|
||
foreigntable => 'biblio',
|
||
foreignkey => 'biblionumber',
|
||
onUpdate => 'SET NULL',
|
||
onDelete => 'SET NULL',
|
||
},
|
||
],
|
||
aqbooksellers => [
|
||
{ key => 'listprice',
|
||
foreigntable => 'currency',
|
||
foreignkey => 'currency',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'invoiceprice',
|
||
foreigntable => 'currency',
|
||
foreignkey => 'currency',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
],
|
||
aqorderbreakdown => [
|
||
{ key => 'ordernumber',
|
||
foreigntable => 'aqorders',
|
||
foreignkey => 'ordernumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'bookfundid',
|
||
foreigntable => 'aqbookfund',
|
||
foreignkey => 'bookfundid',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
],
|
||
branchtransfers => [
|
||
{ key => 'frombranch',
|
||
foreigntable => 'branches',
|
||
foreignkey => 'branchcode',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'tobranch',
|
||
foreigntable => 'branches',
|
||
foreignkey => 'branchcode',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'itemnumber',
|
||
foreigntable => 'items',
|
||
foreignkey => 'itemnumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
],
|
||
issuingrules => [
|
||
{ key => 'categorycode',
|
||
foreigntable => 'categories',
|
||
foreignkey => 'categorycode',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'itemtype',
|
||
foreigntable => 'itemtypes',
|
||
foreignkey => 'itemtype',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
],
|
||
issues => [ # constraint is SET NULL : when a borrower or an item is deleted, we keep the issuing record
|
||
# for stat purposes
|
||
{ key => 'borrowernumber',
|
||
foreigntable => 'borrowers',
|
||
foreignkey => 'borrowernumber',
|
||
onUpdate => 'SET NULL',
|
||
onDelete => 'SET NULL',
|
||
},
|
||
{ key => 'itemnumber',
|
||
foreigntable => 'items',
|
||
foreignkey => 'itemnumber',
|
||
onUpdate => 'SET NULL',
|
||
onDelete => 'SET NULL',
|
||
},
|
||
],
|
||
reserves => [
|
||
{ key => 'borrowernumber',
|
||
foreigntable => 'borrowers',
|
||
foreignkey => 'borrowernumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'biblionumber',
|
||
foreigntable => 'biblio',
|
||
foreignkey => 'biblionumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'itemnumber',
|
||
foreigntable => 'items',
|
||
foreignkey => 'itemnumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'branchcode',
|
||
foreigntable => 'branches',
|
||
foreignkey => 'branchcode',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
],
|
||
borrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
|
||
# but prevent deleting a branch as soon as it has 1 borrower !
|
||
{ key => 'categorycode',
|
||
foreigntable => 'categories',
|
||
foreignkey => 'categorycode',
|
||
onUpdate => 'RESTRICT',
|
||
onDelete => 'RESTRICT',
|
||
},
|
||
{ key => 'branchcode',
|
||
foreigntable => 'branches',
|
||
foreignkey => 'branchcode',
|
||
onUpdate => 'RESTRICT',
|
||
onDelete => 'RESTRICT',
|
||
},
|
||
],
|
||
deletedborrowers => [ # foreign keys are RESTRICT as we don't want to delete borrowers when a branch is deleted
|
||
# but prevent deleting a branch as soon as it has 1 borrower !
|
||
{ key => 'categorycode',
|
||
foreigntable => 'categories',
|
||
foreignkey => 'categorycode',
|
||
onUpdate => 'RESTRICT',
|
||
onDelete => 'RESTRICT',
|
||
},
|
||
{ key => 'branchcode',
|
||
foreigntable => 'branches',
|
||
foreignkey => 'branchcode',
|
||
onUpdate => 'RESTRICT',
|
||
onDelete => 'RESTRICT',
|
||
},
|
||
],
|
||
accountlines => [
|
||
{ key => 'borrowernumber',
|
||
foreigntable => 'borrowers',
|
||
foreignkey => 'borrowernumber',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
{ key => 'itemnumber',
|
||
foreigntable => 'items',
|
||
foreignkey => 'itemnumber',
|
||
onUpdate => 'SET NULL',
|
||
onDelete => 'SET NULL',
|
||
},
|
||
],
|
||
auth_tag_structure => [
|
||
{ key => 'authtypecode',
|
||
foreigntable => 'auth_types',
|
||
foreignkey => 'authtypecode',
|
||
onUpdate => 'CASCADE',
|
||
onDelete => 'CASCADE',
|
||
},
|
||
],
|
||
# FIXME : don't constraint auth_*_table and auth_word, as they may be replaced by zebra
|
||
);
|
||
|
||
|
||
# column changes
|
||
my %column_change = (
|
||
# table
|
||
borrowers => [
|
||
{
|
||
from => 'emailaddress',
|
||
to => 'email',
|
||
after => 'city',
|
||
},
|
||
{
|
||
from => 'streetaddress',
|
||
to => 'address',
|
||
after => 'initials',
|
||
},
|
||
{
|
||
from => 'faxnumber',
|
||
to => 'fax',
|
||
after => 'phone',
|
||
},
|
||
{
|
||
from => 'textmessaging',
|
||
to => 'opacnote',
|
||
after => 'userid',
|
||
},
|
||
{
|
||
from => 'altnotes',
|
||
to => 'contactnote',
|
||
after => 'opacnote',
|
||
},
|
||
{
|
||
from => 'physstreet',
|
||
to => 'B_address',
|
||
after => 'fax',
|
||
},
|
||
{
|
||
from => 'streetcity',
|
||
to => 'B_city',
|
||
after => 'B_address',
|
||
},
|
||
{
|
||
from => 'phoneday',
|
||
to => 'mobile',
|
||
after => 'phone',
|
||
},
|
||
{
|
||
from => 'zipcode',
|
||
to => 'zipcode',
|
||
after => 'city',
|
||
},
|
||
{
|
||
from => 'homezipcode',
|
||
to => 'B_zipcode',
|
||
after => 'B_city',
|
||
},
|
||
{
|
||
from => 'altphone',
|
||
to => 'B_phone',
|
||
after => 'B_zipcode',
|
||
},
|
||
{
|
||
from => 'expiry',
|
||
to => 'dateexpiry',
|
||
after => 'dateenrolled',
|
||
},
|
||
{
|
||
from => 'guarantor',
|
||
to => 'guarantorid',
|
||
after => 'contactname',
|
||
},
|
||
{
|
||
from => 'textmessaging',
|
||
to => 'opacnotes',
|
||
after => 'flags',
|
||
},
|
||
{
|
||
from => 'altnotes',
|
||
to => 'contactnotes',
|
||
after => 'opacnotes',
|
||
},
|
||
{
|
||
from => 'altrelationship',
|
||
to => 'relationship',
|
||
after => 'borrowernotes',
|
||
},
|
||
],
|
||
|
||
deletedborrowers => [
|
||
{
|
||
from => 'emailaddress',
|
||
to => 'email',
|
||
after => 'city',
|
||
},
|
||
{
|
||
from => 'streetaddress',
|
||
to => 'address',
|
||
after => 'initials',
|
||
},
|
||
{
|
||
from => 'faxnumber',
|
||
to => 'fax',
|
||
after => 'phone',
|
||
},
|
||
{
|
||
from => 'textmessaging',
|
||
to => 'opacnote',
|
||
after => 'userid',
|
||
},
|
||
{
|
||
from => 'altnotes',
|
||
to => 'contactnote',
|
||
after => 'opacnote',
|
||
},
|
||
{
|
||
from => 'physstreet',
|
||
to => 'B_address',
|
||
after => 'fax',
|
||
},
|
||
{
|
||
from => 'streetcity',
|
||
to => 'B_city',
|
||
after => 'B_address',
|
||
},
|
||
{
|
||
from => 'phoneday',
|
||
to => 'mobile',
|
||
after => 'phone',
|
||
},
|
||
{
|
||
from => 'zipcode',
|
||
to => 'zipcode',
|
||
after => 'city',
|
||
},
|
||
{
|
||
from => 'homezipcode',
|
||
to => 'B_zipcode',
|
||
after => 'B_city',
|
||
},
|
||
{
|
||
from => 'altphone',
|
||
to => 'B_phone',
|
||
after => 'B_zipcode',
|
||
},
|
||
{
|
||
from => 'expiry',
|
||
to => 'dateexpiry',
|
||
after => 'dateenrolled',
|
||
},
|
||
{
|
||
from => 'guarantor',
|
||
to => 'guarantorid',
|
||
after => 'contactname',
|
||
},
|
||
{
|
||
from => 'textmessaging',
|
||
to => 'opacnotes',
|
||
after => 'flags',
|
||
},
|
||
{
|
||
from => 'altnotes',
|
||
to => 'contactnotes',
|
||
after => 'opacnotes',
|
||
},
|
||
{
|
||
from => 'altrelationship',
|
||
to => 'relationship',
|
||
after => 'borrowernotes',
|
||
},
|
||
],
|
||
|
||
);
|
||
|
||
foreach my $table (keys %column_change) {
|
||
$sth = $dbh->prepare("show columns from $table");
|
||
$sth->execute();
|
||
undef %types;
|
||
while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
|
||
{
|
||
$types{$column}->{type} ="$type";
|
||
$types{$column}->{null} = "$null";
|
||
$types{$column}->{key} = "$key";
|
||
$types{$column}->{default} = "$default";
|
||
$types{$column}->{extra} = "$extra";
|
||
} # while
|
||
my $tablerows = $column_change{$table};
|
||
foreach my $row ( @$tablerows ) {
|
||
if ($types{$row->{from}}->{type}) {
|
||
print "altering $table $row->{from} to $row->{to}\n";
|
||
# ALTER TABLE `borrowers` CHANGE `faxnumber` `fax` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL
|
||
# alter table `borrowers` change `faxnumber` `fax` type text null after phone
|
||
my $sql =
|
||
"alter table `$table` change `$row->{from}` `$row->{to}` $types{$row->{from}}->{type} ".
|
||
($types{$row->{from}}->{null} eq 'YES'?" NULL":" NOT NULL").
|
||
($types{$row->{from}}->{default}?" default ".$types{$row->{from}}->{default}:"").
|
||
"$types{$row->{from}}->{extra} after $row->{after} ";
|
||
# print "$sql";
|
||
$dbh->do($sql);
|
||
}
|
||
}
|
||
}
|
||
|
||
#-------------------
|
||
# Initialize
|
||
|
||
# Start checking
|
||
|
||
# Get version of MySQL database engine.
|
||
my $mysqlversion = `mysqld --version`;
|
||
$mysqlversion =~ /Ver (\S*) /;
|
||
$mysqlversion = $1;
|
||
if ( $mysqlversion ge '3.23' ) {
|
||
print "Could convert to MyISAM database tables...\n" unless $silent;
|
||
}
|
||
|
||
#---------------------------------
|
||
# Tables
|
||
|
||
# Collect all tables into a list
|
||
$sth = $dbh->prepare("show tables");
|
||
$sth->execute;
|
||
while ( my ($table) = $sth->fetchrow ) {
|
||
$existingtables{$table} = 1;
|
||
}
|
||
|
||
# Now add any missing tables
|
||
foreach $table ( keys %requiretables ) {
|
||
unless ( $existingtables{$table} ) {
|
||
print "Adding $table table...\n" unless $silent;
|
||
my $sth = $dbh->prepare("create table $table $requiretables{$table}");
|
||
$sth->execute;
|
||
if ( $sth->err ) {
|
||
print "Error : $sth->errstr \n";
|
||
$sth->finish;
|
||
} # if error
|
||
} # unless exists
|
||
} # foreach
|
||
|
||
# now drop useless tables
|
||
foreach $table ( keys %dropable_table ) {
|
||
if ( $existingtables{$table} ) {
|
||
print "Dropping unused table $table\n" if $debug and not $silent;
|
||
$dbh->do("drop table $table");
|
||
if ( $dbh->err ) {
|
||
print "Error : $dbh->errstr \n";
|
||
}
|
||
}
|
||
}
|
||
|
||
#---------------------------------
|
||
# Columns
|
||
|
||
foreach $table ( keys %requirefields ) {
|
||
print "Check table $table\n" if $debug and not $silent;
|
||
$sth = $dbh->prepare("show columns from $table");
|
||
$sth->execute();
|
||
undef %types;
|
||
while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
|
||
{
|
||
$types{$column} = $type;
|
||
} # while
|
||
foreach $column ( keys %{ $requirefields{$table} } ) {
|
||
print " Check column $column [$types{$column}]\n"
|
||
if $debug and not $silent;
|
||
if ( !$types{$column} ) {
|
||
|
||
# column doesn't exist
|
||
print "Adding $column field to $table table...\n" unless $silent;
|
||
$query = "alter table $table
|
||
add column $column " . $requirefields{$table}->{$column};
|
||
print "Execute: $query\n" if $debug;
|
||
my $sti = $dbh->prepare($query);
|
||
$sti->execute;
|
||
if ( $sti->err ) {
|
||
print "**Error : $sti->errstr \n";
|
||
$sti->finish;
|
||
} # if error
|
||
} # if column
|
||
} # foreach column
|
||
} # foreach table
|
||
|
||
foreach $table ( keys %fielddefinitions ) {
|
||
print "Check table $table\n" if $debug;
|
||
$sth = $dbh->prepare("show columns from $table");
|
||
$sth->execute();
|
||
my $definitions;
|
||
while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
|
||
{
|
||
$definitions->{$column}->{type} = $type;
|
||
$definitions->{$column}->{null} = $null;
|
||
$definitions->{$column}->{null} = 'NULL' if $null eq 'YES';
|
||
$definitions->{$column}->{key} = $key;
|
||
$definitions->{$column}->{default} = $default;
|
||
$definitions->{$column}->{extra} = $extra;
|
||
} # while
|
||
my $fieldrow = $fielddefinitions{$table};
|
||
foreach my $row (@$fieldrow) {
|
||
my $field = $row->{field};
|
||
my $type = $row->{type};
|
||
my $null = $row->{null};
|
||
# $null = 'YES' if $row->{null} eq 'NULL';
|
||
my $key = $row->{key};
|
||
my $default = $row->{default};
|
||
my $null = $row->{null};
|
||
# $default="''" unless $default;
|
||
my $extra = $row->{extra};
|
||
my $def = $definitions->{$field};
|
||
my $after = ($row->{after}?" after ".$row->{after}:"");
|
||
|
||
unless ( $type eq $def->{type}
|
||
&& $null eq $def->{null}
|
||
&& $key eq $def->{key}
|
||
&& $extra eq $def->{extra} )
|
||
{
|
||
if ( $null eq '' ) {
|
||
$null = 'NOT NULL';
|
||
}
|
||
if ( $key eq 'PRI' ) {
|
||
$key = 'PRIMARY KEY';
|
||
}
|
||
unless ( $extra eq 'auto_increment' ) {
|
||
$extra = '';
|
||
}
|
||
|
||
# if it's a new column use "add", if it's an old one, use "change".
|
||
my $action;
|
||
if ($definitions->{$field}->{type}) {
|
||
$action="change $field"
|
||
} else {
|
||
$action="add";
|
||
}
|
||
# if it's a primary key, drop the previous pk, before altering the table
|
||
# my $sth;
|
||
my $request = "alter table $table ";
|
||
$request.=" drop primary key" if $key eq 'PRIMARY KEY';
|
||
$request.= " $action $field $type $null $key $extra ";
|
||
$request.= "default ".$dbh->quote($default) if $default;
|
||
$request.= " $after";
|
||
# print "REQ : $request";
|
||
$dbh->do($request);
|
||
print " alter or create $field in $table\n" unless $silent;
|
||
}
|
||
}
|
||
}
|
||
|
||
# Populate tables with required data
|
||
|
||
# synch table and deletedtable.
|
||
foreach my $table ( ( 'borrowers', 'items', 'biblio', 'biblioitems' ) ) {
|
||
my %deletedborrowers;
|
||
print "synch'ing $table\n";
|
||
$sth = $dbh->prepare("show columns from deleted$table");
|
||
$sth->execute;
|
||
while ( my ( $column, $type, $null, $key, $default, $extra ) =
|
||
$sth->fetchrow )
|
||
{
|
||
$deletedborrowers{$column} = 1;
|
||
}
|
||
$sth = $dbh->prepare("show columns from $table");
|
||
$sth->execute;
|
||
my $previous;
|
||
while ( my ( $column, $type, $null, $key, $default, $extra ) =
|
||
$sth->fetchrow )
|
||
{
|
||
unless ( $deletedborrowers{$column} ) {
|
||
my $newcol = "alter table deleted$table add $column $type";
|
||
if ( $null eq 'YES' ) {
|
||
$newcol .= " NULL ";
|
||
}
|
||
else {
|
||
$newcol .= " NOT NULL ";
|
||
}
|
||
$newcol .= "default $default" if $default;
|
||
$newcol .= " after $previous" if $previous;
|
||
$previous = $column;
|
||
print "creating column $column\n";
|
||
$dbh->do($newcol);
|
||
}
|
||
}
|
||
}
|
||
|
||
foreach my $table ( keys %tabledata ) {
|
||
print "Checking for data required in table $table...\n" unless $silent;
|
||
my $tablerows = $tabledata{$table};
|
||
foreach my $row (@$tablerows) {
|
||
my $uniquefieldrequired = $row->{uniquefieldrequired};
|
||
my $uniquevalue = $row->{$uniquefieldrequired};
|
||
my $forceupdate = $row->{forceupdate};
|
||
my $sth =
|
||
$dbh->prepare(
|
||
"select $uniquefieldrequired from $table where $uniquefieldrequired=?"
|
||
);
|
||
$sth->execute($uniquevalue);
|
||
if ($sth->rows) {
|
||
foreach my $field (keys %$forceupdate) {
|
||
if ($forceupdate->{$field}) {
|
||
my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
|
||
$sth->execute($row->{$field}, $uniquevalue);
|
||
}
|
||
}
|
||
} else {
|
||
print "Adding row to $table: " unless $silent;
|
||
my @values;
|
||
my $fieldlist;
|
||
my $placeholders;
|
||
foreach my $field ( keys %$row ) {
|
||
next if $field eq 'uniquefieldrequired';
|
||
next if $field eq 'forceupdate';
|
||
my $value = $row->{$field};
|
||
push @values, $value;
|
||
print " $field => $value" unless $silent;
|
||
$fieldlist .= "$field,";
|
||
$placeholders .= "?,";
|
||
}
|
||
print "\n" unless $silent;
|
||
$fieldlist =~ s/,$//;
|
||
$placeholders =~ s/,$//;
|
||
my $sth =
|
||
$dbh->prepare(
|
||
"insert into $table ($fieldlist) values ($placeholders)");
|
||
$sth->execute(@values);
|
||
}
|
||
}
|
||
}
|
||
|
||
#
|
||
# check indexes and create them when needed
|
||
#
|
||
print "Checking for index required...\n" unless $silent;
|
||
foreach my $table ( keys %indexes ) {
|
||
#
|
||
# read all indexes from $table
|
||
#
|
||
$sth = $dbh->prepare("show index from $table");
|
||
$sth->execute;
|
||
my %existingindexes;
|
||
while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow ) {
|
||
$existingindexes{$key_name} = 1;
|
||
}
|
||
# read indexes to check
|
||
my $tablerows = $indexes{$table};
|
||
foreach my $row (@$tablerows) {
|
||
my $key_name=$row->{indexname};
|
||
if ($existingindexes{$key_name} eq 1) {
|
||
# print "$key_name existing";
|
||
} else {
|
||
print "\tCreating index $key_name in $table\n";
|
||
my $sql;
|
||
if ($row->{indexname} eq 'PRIMARY') {
|
||
$sql = "alter table $table ADD PRIMARY KEY ($row->{content})";
|
||
} else {
|
||
$sql = "alter table $table ADD INDEX $key_name ($row->{content}) $row->{type}";
|
||
}
|
||
$dbh->do($sql);
|
||
print "Error $sql : $dbh->err \n" if $dbh->err;
|
||
}
|
||
}
|
||
}
|
||
|
||
#
|
||
# check foreign keys and create them when needed
|
||
#
|
||
print "Checking for foreign keys required...\n" unless $silent;
|
||
foreach my $table ( keys %foreign_keys ) {
|
||
#
|
||
# read all indexes from $table
|
||
#
|
||
$sth = $dbh->prepare("show table status like '$table'");
|
||
$sth->execute;
|
||
my $stat = $sth->fetchrow_hashref;
|
||
# read indexes to check
|
||
my $tablerows = $foreign_keys{$table};
|
||
foreach my $row (@$tablerows) {
|
||
my $foreign_table=$row->{foreigntable};
|
||
if ($stat->{'Comment'} =~/$foreign_table/) {
|
||
# print "$foreign_table existing\n";
|
||
} else {
|
||
print "\tCreating foreign key $foreign_table in $table\n";
|
||
# first, drop any orphan value in child table
|
||
if ($row->{onDelete} ne "RESTRICT") {
|
||
my $sql = "delete from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})";
|
||
$dbh->do($sql);
|
||
print "SQL ERROR: $sql : $dbh->err \n" if $dbh->err;
|
||
}
|
||
my $sql="alter table $table ADD FOREIGN KEY $row->{key} ($row->{key}) REFERENCES $row->{foreigntable} ($row->{foreignkey})";
|
||
$sql .= " on update ".$row->{onUpdate} if $row->{onUpdate};
|
||
$sql .= " on delete ".$row->{onDelete} if $row->{onDelete};
|
||
$dbh->do($sql);
|
||
if ($dbh->err) {
|
||
print "====================
|
||
An error occured during :
|
||
\t$sql
|
||
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).
|
||
You can find those values with select
|
||
\t$table.* from $table where $row->{key} not in (select $row->{foreignkey} from $row->{foreigntable})
|
||
====================\n
|
||
";
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#
|
||
# SPECIFIC STUFF
|
||
#
|
||
#
|
||
# create frameworkcode row in biblio table & fill it with marc_biblio.frameworkcode.
|
||
#
|
||
|
||
# 1st, get how many biblio we will have to do...
|
||
$sth = $dbh->prepare('select count(*) from marc_biblio');
|
||
$sth->execute;
|
||
my ($totaltodo) = $sth->fetchrow;
|
||
|
||
$sth = $dbh->prepare("show columns from biblio");
|
||
$sth->execute();
|
||
my $definitions;
|
||
my $bibliofwexist=0;
|
||
while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
|
||
$bibliofwexist=1 if $column eq 'frameworkcode';
|
||
}
|
||
unless ($bibliofwexist) {
|
||
print "moving biblioframework to biblio table\n";
|
||
$dbh->do('ALTER TABLE `biblio` ADD `frameworkcode` VARCHAR( 4 ) NOT NULL AFTER `biblionumber`');
|
||
$sth = $dbh->prepare('select biblionumber,frameworkcode from marc_biblio');
|
||
$sth->execute;
|
||
my $sth_update = $dbh->prepare('update biblio set frameworkcode=? where biblionumber=?');
|
||
my $totaldone=0;
|
||
while (my ($biblionumber,$frameworkcode) = $sth->fetchrow) {
|
||
$sth_update->execute($frameworkcode,$biblionumber);
|
||
$totaldone++;
|
||
print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
|
||
}
|
||
print "\rdone\n";
|
||
}
|
||
|
||
#
|
||
# moving MARC data from marc_subfield_table to biblioitems.marc
|
||
#
|
||
$sth = $dbh->prepare("show columns from biblioitems");
|
||
$sth->execute();
|
||
my $definitions;
|
||
my $marcdone=0;
|
||
while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow ){
|
||
$marcdone=1 if ($type eq 'blob' && $column eq 'marc') ;
|
||
}
|
||
unless ($marcdone) {
|
||
print "moving MARC record to biblioitems table\n";
|
||
# changing marc field type
|
||
$dbh->do('ALTER TABLE `biblioitems` CHANGE `marc` `marc` BLOB NULL DEFAULT NULL ');
|
||
# adding marc xml, just for convenience
|
||
$dbh->do('ALTER TABLE `biblioitems` ADD `marcxml` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL ');
|
||
# moving data from marc_subfield_value to biblio
|
||
$sth = $dbh->prepare('select bibid,biblionumber from marc_biblio');
|
||
$sth->execute;
|
||
my $sth_update = $dbh->prepare('update biblioitems set marc=?, marcxml=? where biblionumber=?');
|
||
my $totaldone=0;
|
||
while (my ($bibid,$biblionumber) = $sth->fetchrow) {
|
||
my $record = MARCgetbiblio($dbh,$bibid);
|
||
#Force UTF-8 in record leader
|
||
$record->encoding('UTF-8');
|
||
print $record->as_formatted if ($biblionumber==3902);
|
||
$sth_update->execute($record->as_usmarc(),$record->as_xml_record(),$biblionumber);
|
||
$totaldone++;
|
||
print "\r$totaldone / $totaltodo" unless ($totaldone % 100);
|
||
}
|
||
print "\rdone\n";
|
||
}
|
||
|
||
|
||
# at last, remove useless fields
|
||
foreach $table ( keys %uselessfields ) {
|
||
my @fields = split /,/, $uselessfields{$table};
|
||
my $fields;
|
||
my $exists;
|
||
foreach my $fieldtodrop (@fields) {
|
||
$fieldtodrop =~ s/\t//g;
|
||
$fieldtodrop =~ s/\n//g;
|
||
$exists = 0;
|
||
$sth = $dbh->prepare("show columns from $table");
|
||
$sth->execute;
|
||
while ( my ( $column, $type, $null, $key, $default, $extra ) =
|
||
$sth->fetchrow )
|
||
{
|
||
$exists = 1 if ( $column eq $fieldtodrop );
|
||
}
|
||
if ($exists) {
|
||
print "deleting $fieldtodrop field in $table...\n" unless $silent;
|
||
my $sth = $dbh->prepare("alter table $table drop $fieldtodrop");
|
||
$sth->execute;
|
||
}
|
||
}
|
||
} # foreach
|
||
|
||
|
||
# MOVE all tables TO UTF-8 and innoDB
|
||
$sth = $dbh->prepare("show table status");
|
||
$sth->execute;
|
||
while ( my $table = $sth->fetchrow_hashref ) {
|
||
# if ($table->{Engine} ne 'InnoDB') {
|
||
# $dbh->do("ALTER TABLE $table->{Name} TYPE = innodb");
|
||
# print "moving $table->{Name} to InnoDB\n";
|
||
# }
|
||
unless ($table->{Collation} =~ /^utf8/) {
|
||
$dbh->do("ALTER TABLE $table->{Name} CONVERT TO CHARACTER SET utf8");
|
||
$dbh->do("ALTER TABLE $table->{Name} DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
|
||
# 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 !
|
||
print "moving $table->{Name} to utf8\n";
|
||
} else {
|
||
}
|
||
}
|
||
|
||
$sth->finish;
|
||
|
||
#
|
||
# those 2 subs are a copy of Biblio.pm, version 2.2.4
|
||
# they are useful only once, for moving from 2.2 to 3.0
|
||
# the MARCgetbiblio & MARCgetitem subs in Biblio.pm
|
||
# are still here, but uses other tables
|
||
# (the ones that are filled by updatedatabase !)
|
||
#
|
||
|
||
sub MARCgetbiblio {
|
||
|
||
# Returns MARC::Record of the biblio passed in parameter.
|
||
my ( $dbh, $bibid ) = @_;
|
||
my $record = MARC::Record->new();
|
||
# warn "". $bidid;
|
||
|
||
my $sth =
|
||
$dbh->prepare(
|
||
"select bibid,subfieldid,tag,tagorder,tag_indicator,subfieldcode,subfieldorder,subfieldvalue,valuebloblink
|
||
from marc_subfield_table
|
||
where bibid=? order by tag,tagorder,subfieldorder
|
||
"
|
||
);
|
||
my $sth2 =
|
||
$dbh->prepare(
|
||
"select subfieldvalue from marc_blob_subfield where blobidlink=?");
|
||
$sth->execute($bibid);
|
||
my $prevtagorder = 1;
|
||
my $prevtag = 'XXX';
|
||
my $previndicator;
|
||
my $field; # for >=10 tags
|
||
my $prevvalue; # for <10 tags
|
||
while ( my $row = $sth->fetchrow_hashref ) {
|
||
|
||
if ( $row->{'valuebloblink'} ) { #---- search blob if there is one
|
||
$sth2->execute( $row->{'valuebloblink'} );
|
||
my $row2 = $sth2->fetchrow_hashref;
|
||
$sth2->finish;
|
||
$row->{'subfieldvalue'} = $row2->{'subfieldvalue'};
|
||
}
|
||
if ( $row->{tagorder} ne $prevtagorder || $row->{tag} ne $prevtag ) {
|
||
$previndicator .= " ";
|
||
if ( $prevtag < 10 ) {
|
||
if ($prevtag ne '000') {
|
||
$record->add_fields( ( sprintf "%03s", $prevtag ), $prevvalue ) unless $prevtag eq "XXX"; # ignore the 1st loop
|
||
} else {
|
||
$record->leader(sprintf("%24s",$prevvalue));
|
||
}
|
||
}
|
||
else {
|
||
$record->add_fields($field) unless $prevtag eq "XXX";
|
||
}
|
||
undef $field;
|
||
$prevtagorder = $row->{tagorder};
|
||
$prevtag = $row->{tag};
|
||
$previndicator = $row->{tag_indicator};
|
||
if ( $row->{tag} < 10 ) {
|
||
$prevvalue = $row->{subfieldvalue};
|
||
}
|
||
else {
|
||
$field = MARC::Field->new(
|
||
( sprintf "%03s", $prevtag ),
|
||
substr( $row->{tag_indicator} . ' ', 0, 1 ),
|
||
substr( $row->{tag_indicator} . ' ', 1, 1 ),
|
||
$row->{'subfieldcode'},
|
||
$row->{'subfieldvalue'}
|
||
);
|
||
}
|
||
}
|
||
else {
|
||
if ( $row->{tag} < 10 ) {
|
||
$record->add_fields( ( sprintf "%03s", $row->{tag} ),
|
||
$row->{'subfieldvalue'} );
|
||
}
|
||
else {
|
||
$field->add_subfields( $row->{'subfieldcode'},
|
||
$row->{'subfieldvalue'} );
|
||
}
|
||
$prevtag = $row->{tag};
|
||
$previndicator = $row->{tag_indicator};
|
||
}
|
||
}
|
||
|
||
# the last has not been included inside the loop... do it now !
|
||
if ( $prevtag ne "XXX" )
|
||
{ # check that we have found something. Otherwise, prevtag is still XXX and we
|
||
# must return an empty record, not make MARC::Record fail because we try to
|
||
# create a record with XXX as field :-(
|
||
if ( $prevtag < 10 ) {
|
||
$record->add_fields( $prevtag, $prevvalue );
|
||
}
|
||
else {
|
||
|
||
# my $field = MARC::Field->new( $prevtag, "", "", %subfieldlist);
|
||
$record->add_fields($field);
|
||
}
|
||
}
|
||
return $record;
|
||
}
|
||
|
||
sub MARCgetitem {
|
||
|
||
# Returns MARC::Record of the biblio passed in parameter.
|
||
my ( $dbh, $bibid, $itemnumber ) = @_;
|
||
my $record = MARC::Record->new();
|
||
|
||
# search MARC tagorder
|
||
my $sth2 =
|
||
$dbh->prepare(
|
||
"select tagorder from marc_subfield_table,marc_subfield_structure where marc_subfield_table.tag=marc_subfield_structure.tagfield and marc_subfield_table.subfieldcode=marc_subfield_structure.tagsubfield and bibid=? and kohafield='items.itemnumber' and subfieldvalue=?"
|
||
);
|
||
$sth2->execute( $bibid, $itemnumber );
|
||
my ($tagorder) = $sth2->fetchrow_array();
|
||
|
||
#---- TODO : the leader is missing
|
||
my $sth =
|
||
$dbh->prepare(
|
||
"select bibid,subfieldid,tag,tagorder,tag_indicator,subfieldcode,subfieldorder,subfieldvalue,valuebloblink
|
||
from marc_subfield_table
|
||
where bibid=? and tagorder=? order by subfieldcode,subfieldorder
|
||
"
|
||
);
|
||
$sth2 =
|
||
$dbh->prepare(
|
||
"select subfieldvalue from marc_blob_subfield where blobidlink=?");
|
||
$sth->execute( $bibid, $tagorder );
|
||
while ( my $row = $sth->fetchrow_hashref ) {
|
||
if ( $row->{'valuebloblink'} ) { #---- search blob if there is one
|
||
$sth2->execute( $row->{'valuebloblink'} );
|
||
my $row2 = $sth2->fetchrow_hashref;
|
||
$sth2->finish;
|
||
$row->{'subfieldvalue'} = $row2->{'subfieldvalue'};
|
||
}
|
||
if ( $record->field( $row->{'tag'} ) ) {
|
||
my $field;
|
||
|
||
#--- this test must stay as this, because of strange behaviour of mySQL/Perl DBI with char var containing a number...
|
||
#--- sometimes, eliminates 0 at beginning, sometimes no ;-\\\
|
||
if ( length( $row->{'tag'} ) < 3 ) {
|
||
$row->{'tag'} = "0" . $row->{'tag'};
|
||
}
|
||
$field = $record->field( $row->{'tag'} );
|
||
if ($field) {
|
||
my $x =
|
||
$field->add_subfields( $row->{'subfieldcode'},
|
||
$row->{'subfieldvalue'} );
|
||
$record->delete_field($field);
|
||
$record->add_fields($field);
|
||
}
|
||
}
|
||
else {
|
||
if ( length( $row->{'tag'} ) < 3 ) {
|
||
$row->{'tag'} = "0" . $row->{'tag'};
|
||
}
|
||
my $temp =
|
||
MARC::Field->new( $row->{'tag'}, " ", " ",
|
||
$row->{'subfieldcode'} => $row->{'subfieldvalue'} );
|
||
$record->add_fields($temp);
|
||
}
|
||
|
||
}
|
||
return $record;
|
||
}
|
||
|
||
|
||
exit;
|
||
|
||
# $Log$
|
||
# Revision 1.156 2006/07/20 04:36:01 bob_lyon
|
||
# Merging back in some katipo changes to serials
|
||
#
|
||
# Revision 1.155 2006/07/17 12:51:48 toins
|
||
# auto_increment id in aqbooksellers
|
||
#
|
||
# Revision 1.153 2006/07/04 14:36:52 toins
|
||
# Head & rel_2_2 merged
|
||
#
|
||
# Revision 1.152 2006/06/27 09:26:37 btoumi
|
||
# modify (initials,phone ) fields property in borrowers and deletedborrowers table
|
||
#
|
||
# Revision 1.151 2006/06/22 10:33:14 btoumi
|
||
# sorry i forget deletedborrowers table
|
||
# modify firstname field from deletedborrowers table
|
||
#
|
||
# Revision 1.149 2006/06/20 22:35:47 rangi
|
||
# Code to allow the associated borrowers to work
|
||
#
|
||
# Revision 1.148 2006/06/17 22:12:01 rangi
|
||
# Adding id field to reviews table
|
||
#
|
||
# Revision 1.147 2006/06/17 03:36:41 rangi
|
||
# Table definition for the reviews table
|
||
#
|
||
# Revision 1.146 2006/06/17 03:29:41 rangi
|
||
# Variable to allow librarians to switch reviews on or off
|
||
#
|
||
# Revision 1.145 2006/06/16 09:45:02 btoumi
|
||
# updatedatabase.pl: add change of borrowers table to deletedborrowers table
|
||
# deletemem.pl: delete use of warn function
|
||
#
|
||
# Revision 1.144 2006/06/08 15:36:31 alaurin
|
||
# Add a new system preference 'AutomaticItemReturn' :
|
||
#
|
||
# 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) .
|
||
#
|
||
# switch off : the document stay in the holdingbranch ...
|
||
#
|
||
# correcting bugs :
|
||
# - comment C4::acquisition (not using in request.pl).
|
||
# - correcting date in request.pl
|
||
# -add the new call of function getbranches in request.pl
|
||
#
|
||
# Revision 1.143 2006/06/07 02:02:47 bob_lyon
|
||
# merging katipo changes...
|
||
#
|
||
# adding new preference IssuingInProcess
|
||
#
|
||
# Revision 1.142 2006/06/06 23:42:46 bob_lyon
|
||
# Merging Katipo changes...
|
||
#
|
||
# Adding new system pref where one can still retrieve a correct reading
|
||
# record history if one has moved older data from issues to oldissues table
|
||
# to speed up issues speed
|
||
#
|
||
# Revision 1.141 2006/06/01 03:18:11 rangi
|
||
# Adding a new column to the statistics table
|
||
#
|
||
# Revision 1.140 2006/05/22 22:40:45 rangi
|
||
# Adding new systempreference allowing for the library to add borrowers to institutions (rest homes, parishes, schools, classes etc).
|
||
#
|
||
# Revision 1.139 2006/05/19 19:31:29 tgarip1957
|
||
# Added new fields to auth_header and auth_subfield_table to allow ZEBRA use of authorities and new MARC framework like structure.
|
||
# 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
|
||
#
|
||
# Revision 1.138 2006/05/19 16:51:44 alaurin
|
||
# update database for :
|
||
# - new feature ip and printer management
|
||
# adding two fields in branches table (branchip,branchprinter)
|
||
#
|
||
# - 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
|
||
#
|
||
# new system preference :
|
||
# - ReservesMaxPickUpDelay : Maximum delay to pick up a reserved document
|
||
# TransfersMaxDaysWarning : Max delay before considering the transfer as potentialy a problem
|
||
#
|
||
# Revision 1.137 2006/04/18 09:36:36 plg
|
||
# bug fixed: typo fixed in labels and labels_conf tables creation query.
|
||
#
|
||
# Revision 1.136 2006/04/17 21:55:33 sushi
|
||
# Added 'labels' and 'labels_conf' tables, for spine lable tool.
|
||
#
|
||
# Revision 1.135 2006/04/15 02:37:03 tgarip1957
|
||
# Marc record should be set to UTF-8 in leader.Force it.
|
||
# XML should be with<record> wrappers
|
||
#
|
||
# Revision 1.134 2006/04/14 09:37:29 tipaul
|
||
# improvements from SAN Ouest Provence :
|
||
# * introducing a category_type into categories. It can be A (adult), C (children), P (Professionnal), I (institution/organisation).
|
||
# * each category_type has it's own forms to create members.
|
||
# * the borrowers table has been heavily modified (many fields changed), to get something more logic & readable
|
||
# * reintroducing guarantor/guanrantee system that is now independant from hardcoded C/A for categories
|
||
# * updating templates to fit template rules
|
||
#
|
||
# (see mail feb, 17 on koha-devel "new features for borrowers" for more details)
|
||
#
|
||
# Revision 1.133 2006/04/13 08:36:42 plg
|
||
# new: function C4::Date::get_date_format_string_for_DHTMLcalendar based on
|
||
# the system preference prefered date format.
|
||
#
|
||
# improvement: book fund list and budget list screen redesigned. Filters on
|
||
# each field. Columns are not sortable yet. Using DHTML Calendar to fill date
|
||
# fields instead of manual filling. Pagination system. From the book fund
|
||
# list, you can reach the budget list, filtered on a book fund, or not. A
|
||
# budget can be added only from book fund list screen.
|
||
#
|
||
# bug fixed: branchcode was missing in table aqbudget.
|
||
#
|
||
# bug fixed: when setting a branchcode to a book fund, all associated budgets
|
||
# move to this branchcode.
|
||
#
|
||
# modification: when adding/modifying budget/fund, MySQL specific "REPLACE..."
|
||
# statements replaced by standard SQL compliant statement.
|
||
#
|
||
# bug fixed: when adding/modifying a budget, if the book fund is associated to
|
||
# a branch, the branch selection is disabled and set to the book fund branch.
|
||
#
|
||
# Revision 1.132 2006/04/06 12:37:05 hdl
|
||
# Bugfixing : aqbookfund needed a field.
|
||
#
|
||
# Revision 1.131 2006/03/03 17:02:22 tipaul
|
||
# commit for holidays and news management.
|
||
# (some forgotten files)
|
||
#
|
||
# Revision 1.130 2006/03/03 16:35:21 tipaul
|
||
# commit for holidays and news management.
|
||
#
|
||
# Contrib from T<>mer Garip (from Turkey) :
|
||
# * holiday :
|
||
# 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 :
|
||
# - single day : only this day is closed
|
||
# - repet weekly (like "sunday") : the day is holiday every week
|
||
# - repet yearly (like "July, 4") : this day is closed every year.
|
||
#
|
||
# You can also put exception :
|
||
# - sunday is holiday, but "2006 March, 5th" the library will be open
|
||
#
|
||
# 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.
|
||
#
|
||
# Revision 1.129 2006/02/27 18:19:33 hdl
|
||
# New table used in overduerules.pl tools page.
|
||
#
|
||
# Revision 1.128 2006/01/25 15:16:06 tipaul
|
||
# updating DB :
|
||
# * removing useless tables
|
||
# * adding useful indexes
|
||
# * altering some columns definitions
|
||
# * The goal being to have updater working fine for foreign keys.
|
||
#
|
||
# 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
|
||
#
|
||
# Revision 1.127 2006/01/24 17:57:17 tipaul
|
||
# DB improvements : adding foreign keys on some tables. partial stuff done.
|
||
#
|
||
# Revision 1.126 2006/01/06 16:39:42 tipaul
|
||
# synch'ing head and rel_2_2 (from 2.2.5, including npl templates)
|
||
# Seems not to break too many things, but i'm probably wrong here.
|
||
# at least, new features/bugfixes from 2.2.5 are here (tested on some features on my head local copy)
|
||
#
|
||
# - removing useless directories (koha-html and koha-plucene)
|
||
#
|
||
# Revision 1.125 2006/01/04 15:54:55 tipaul
|
||
# utf8 is a : go for beta test in HEAD.
|
||
# some explanations :
|
||
# - 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.
|
||
# - *-top.inc will show the pages in utf8
|
||
# - 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.
|
||
# - using marcxml field and no more the iso2709 raw marc biblioitems.marc field.
|
||
#
|
||
# Revision 1.124 2005/10/27 12:09:05 tipaul
|
||
# new features for serial module :
|
||
# - 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")
|
||
# - 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).
|
||
#
|
||
# Revision 1.123 2005/10/26 09:13:37 tipaul
|
||
# big commit, still breaking things...
|
||
#
|
||
# * synch with rel_2_2. Probably the last non manual synch, as rel_2_2 should not be modified deeply.
|
||
# * code cleaning (cleaning warnings from perl -w) continued
|
||
#
|
||
# Revision 1.122 2005/09/02 14:18:38 tipaul
|
||
# new feature : image for itemtypes.
|
||
#
|
||
# * run updater/updatedatabase to create imageurl field in itemtypes.
|
||
# * 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)
|
||
# * go to OPAC, and search something. In the result list, you now have the picture instead of the text itemtype.
|
||
#
|
||
# Revision 1.121 2005/08/24 08:49:03 hdl
|
||
# Adding a note field in serial table.
|
||
# This will allow librarian to mention a note on a peculiar waiting serial number.
|
||
#
|
||
# Revision 1.120 2005/08/09 14:10:32 tipaul
|
||
# 1st commit to go to zebra.
|
||
# don't update your cvs if you want to have a working head...
|
||
#
|
||
# this commit contains :
|
||
# * 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...
|
||
# * 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.
|
||
# * other files : get rid of bibid and use biblionumber instead.
|
||
#
|
||
# What is broken :
|
||
# * does not do anything on zebra yet.
|
||
# * if you rename marc_subfield_table, you can't search anymore.
|
||
# * you can view a biblio & bibliodetails, go to MARC editor, but NOT save any modif.
|
||
# * 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 ;-) )
|
||
#
|
||
# 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
|
||
# 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.
|
||
#
|
||
# Revision 1.119 2005/08/04 16:07:58 tipaul
|
||
# Synch really broke this script...
|
||
#
|
||
# Revision 1.118 2005/08/04 16:02:55 tipaul
|
||
# oops... error in synch between 2.2 and head
|
||
#
|
||
# Revision 1.117 2005/08/04 14:24:39 tipaul
|
||
# synch'ing 2.2 and head
|
||
#
|
||
# Revision 1.116 2005/08/04 08:55:54 tipaul
|
||
# Letters / alert system, continuing...
|
||
#
|
||
# * adding a package Letters.pm, that manages Letters & alerts.
|
||
# * 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)
|
||
# * 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)
|
||
# * 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.
|
||
#
|
||
# Note that the system should be generic enough to manage any type of alert.
|
||
# 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 ;-) )
|
||
#
|
||
# Revision 1.115 2005/08/02 16:15:34 tipaul
|
||
# adding 2 fields to letter system :
|
||
# * module (acquisition, catalogue...) : it will be usefull to show the librarian only letters he may be interested by.
|
||
# * title, that will be used as mail subject.
|
||
#
|
||
# Revision 1.114 2005/07/28 15:10:13 tipaul
|
||
# 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.
|
||
# the letter table contains 3 fields :
|
||
# * code => the code of the letter
|
||
# * name => the complete name of the letter
|
||
# * content => the complete text. It's a TEXT field type, so has no limits.
|
||
#
|
||
# My next goal now is to work on point 2-I "serial issue alert"
|
||
# 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.
|
||
# (see mail on koha-devel, 2005/04/07)
|
||
#
|
||
# The "serial issue alert" will be the 1st to use this letter system that probably needs some tweaking ;-)
|
||
#
|
||
# Once it will be stabilised default letters (in any languages) could be added during installer to help the library begin with this new feature.
|
||
#
|
||
# Revision 1.113 2005/07/28 08:38:41 tipaul
|
||
# 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 :
|
||
# * ReturnBeforeExpiry = yes => return date can't be after expiry date
|
||
# * ReturnBeforeExpiry = no => return date can be after expiry date
|
||
#
|
||
# Revision 1.112 2005/07/26 08:19:47 hdl
|
||
# Adding IndependantBranches System preference variable in order to manage Branch independancy.
|
||
#
|
||
# Revision 1.111 2005/07/25 15:35:38 tipaul
|
||
# we have decided that moving to Koha 3.0 requires being already in Koha 2.2.x
|
||
# So, the updatedatabase script can highly be cleaned (90% removed).
|
||
# Let's play with the new Koha DB structure now ;-)
|
||
#
|