3 # Copyright (C) 2008 LibLime
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it under the
8 # terms of the GNU General Public License as published by the Free Software
9 # Foundation; either version 2 of the License, or (at your option) any later
12 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License along
17 # with Koha; if not, write to the Free Software Foundation, Inc.,
18 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #use warnings; FIXME - Bug 2505
34 my $installer = C4::Installer->new();
36 my $all_languages = getAllLanguages();
38 my $error = $installer->load_db_schema();
40 my $list = $installer->sql_file_list('en', 'marc21', { optional => 1, mandatory => 1 });
42 my ($fwk_language, $error_list) = $installer->load_sql_in_order($all_languages, @$list);
44 $installer->set_version_syspref();
46 $installer->set_marcflavour_syspref('MARC21');
48 $installer->set_indexing_engine(0);
58 my $installer = C4::Installer->new();
62 Creates a new installer.
71 # get basic information from context
72 $self->{'dbname'} = C4
::Context
->config("database");
73 $self->{'dbms'} = C4
::Context
->config("db_scheme") ? C4
::Context
->config("db_scheme") : "mysql";
74 $self->{'hostname'} = C4
::Context
->config("hostname");
75 $self->{'port'} = C4
::Context
->config("port");
76 $self->{'user'} = C4
::Context
->config("user");
77 $self->{'password'} = C4
::Context
->config("pass");
78 $self->{'dbh'} = DBI
->connect("DBI:$self->{dbms}:dbname=$self->{dbname};host=$self->{hostname}" .
79 ( $self->{port
} ?
";port=$self->{port}" : "" ),
80 $self->{'user'}, $self->{'password'});
81 $self->{'language'} = undef;
82 $self->{'marcflavour'} = undef;
83 $self->{'dbh'}->do('set NAMES "utf8"');
84 $self->{'dbh'}->{'mysql_enable_utf8'}=1;
90 =head2 marcflavour_list
94 my ($marcflavours) = $installer->marcflavour_list($lang);
98 Return a arrayref of the MARC flavour sets available for the
99 specified language C<$lang>. Returns 'undef' if a directory
100 for the language does not exist.
104 sub marcflavour_list
{
108 my $dir = C4
::Context
->config('intranetdir') . "/installer/data/$self->{dbms}/$lang/marcflavour";
109 opendir(MYDIR
, $dir) or return;
110 my @list = grep { !/^\.|CVS/ && -d
"$dir/$_" } readdir(MYDIR
);
115 =head2 marc_framework_sql_list
119 my ($defaulted_to_en, $list) = $installer->marc_framework_sql_list($lang, $marcflavour);
123 Returns in C<$list> a structure listing the filename, description, section,
124 and mandatory/optional status of MARC framework scripts available for C<$lang>
127 If the C<$defaulted_to_en> return value is true, no scripts are available
128 for language C<$lang> and the 'en' ones are returned.
132 sub marc_framework_sql_list
{
135 my $marcflavour = shift;
137 my $defaulted_to_en = 0;
140 my $dir = C4
::Context
->config('intranetdir') . "/installer/data/$self->{dbms}/$lang/marcflavour/".lc($marcflavour);
141 unless (opendir( MYDIR
, $dir )) {
143 warn "cannot open MARC frameworks directory $dir";
145 # if no translated MARC framework is available,
147 $dir = C4
::Context
->config('intranetdir') . "/installer/data/$self->{dbms}/en/marcflavour/".lc($marcflavour);
148 opendir(MYDIR
, $dir) or warn "cannot open English MARC frameworks directory $dir";
149 $defaulted_to_en = 1;
152 my @listdir = sort grep { !/^\.|marcflavour/ && -d
"$dir/$_" } readdir(MYDIR
);
156 my $request = $self->{'dbh'}->prepare("SELECT value FROM systempreferences WHERE variable='FrameworksLoaded'");
158 my ($frameworksloaded) = $request->fetchrow;
159 $frameworksloaded = '' unless defined $frameworksloaded; # avoid warning
160 my %frameworksloaded;
161 foreach ( split( /\|/, $frameworksloaded ) ) {
162 $frameworksloaded{$_} = 1;
165 foreach my $requirelevel (@listdir) {
166 opendir( MYDIR
, "$dir/$requirelevel" );
167 my @listname = grep { !/^\./ && -f
"$dir/$requirelevel/$_" && $_ =~ m/\.sql$/ } readdir(MYDIR
);
172 my $name = substr( $_, 0, -4 );
173 open FILE
, "<:utf8","$dir/$requirelevel/$name.txt";
175 $lines =~ s/\n|\r/<br \/>/g
;
177 utf8
::encode
($lines) unless ( utf8
::is_utf8
($lines) );
178 my $mandatory = ($requirelevel =~ /(mandatory|requi|oblig|necess)/i);
182 'fwkfile' => "$dir/$requirelevel/$_",
183 'fwkdescription' => $lines,
184 'checked' => ( ( $frameworksloaded{$_} || $mandatory ) ?
1 : 0 ),
185 'mandatory' => $mandatory,
189 sort { $a->{'fwkname'} cmp $b->{'fwkname'} } @frameworklist;
191 $cell{"frameworks"} = \
@fwks;
192 $cell{"label"} = ucfirst($requirelevel);
193 $cell{"code"} = lc($requirelevel);
194 push @fwklist, \
%cell;
197 return ($defaulted_to_en, \
@fwklist);
200 =head2 sample_data_sql_list
204 my ($defaulted_to_en, $list) = $installer->sample_data_sql_list($lang);
208 Returns in C<$list> a structure listing the filename, description, section,
209 and mandatory/optional status of sample data scripts available for C<$lang>.
210 If the C<$defaulted_to_en> return value is true, no scripts are available
211 for language C<$lang> and the 'en' ones are returned.
215 sub sample_data_sql_list
{
219 my $defaulted_to_en = 0;
222 my $dir = C4
::Context
->config('intranetdir') . "/installer/data/$self->{dbms}/$lang";
223 unless (opendir( MYDIR
, $dir )) {
225 warn "cannot open sample data directory $dir";
227 # if no sample data is available,
229 $dir = C4
::Context
->config('intranetdir') . "/installer/data/$self->{dbms}/en";
230 opendir(MYDIR
, $dir) or warn "cannot open English sample data directory $dir";
231 $defaulted_to_en = 1;
234 my @listdir = sort grep { !/^\.|marcflavour/ && -d
"$dir/$_" } readdir(MYDIR
);
238 my $request = $self->{'dbh'}->prepare("SELECT value FROM systempreferences WHERE variable='FrameworksLoaded'");
240 my ($frameworksloaded) = $request->fetchrow;
241 $frameworksloaded = '' unless defined $frameworksloaded; # avoid warning
242 my %frameworksloaded;
243 foreach ( split( /\|/, $frameworksloaded ) ) {
244 $frameworksloaded{$_} = 1;
247 foreach my $requirelevel (@listdir) {
248 opendir( MYDIR
, "$dir/$requirelevel" );
249 my @listname = grep { !/^\./ && -f
"$dir/$requirelevel/$_" && $_ =~ m/\.sql$/ } readdir(MYDIR
);
254 my $name = substr( $_, 0, -4 );
255 open FILE
, "<:utf8","$dir/$requirelevel/$name.txt";
257 $lines =~ s/\n|\r/<br \/>/g
;
259 utf8
::encode
($lines) unless ( utf8
::is_utf8
($lines) );
260 my $mandatory = ($requirelevel =~ /(mandatory|requi|oblig|necess)/i);
264 'fwkfile' => "$dir/$requirelevel/$_",
265 'fwkdescription' => $lines,
266 'checked' => ( ( $frameworksloaded{$_} || $mandatory ) ?
1 : 0 ),
267 'mandatory' => $mandatory,
270 my @fwks = sort { $a->{'fwkname'} cmp $b->{'fwkname'} } @frameworklist;
272 $cell{"frameworks"} = \
@fwks;
273 $cell{"label"} = ucfirst($requirelevel);
274 $cell{"code"} = lc($requirelevel);
275 push @levellist, \
%cell;
278 return ($defaulted_to_en, \
@levellist);
285 my $list = $installer->sql_file_list($lang, $marcflavour, $subset_wanted);
289 Returns an arrayref containing the filepaths of installer SQL scripts
290 available for laod. The C<$lang> and C<$marcflavour> arguments
291 specify the desired language and MARC flavour. while C<$subset_wanted>
292 is a hashref containing possible named parameters 'mandatory' and 'optional'.
299 my $marcflavour = shift;
300 my $subset_wanted = shift;
302 my ($marc_defaulted_to_en, $marc_sql) = $self->marc_framework_sql_list($lang, $marcflavour);
303 my ($sample_defaulted_to_en, $sample_sql) = $self->sample_data_sql_list($lang);
308 if ($subset_wanted->{'mandatory'}) {
309 push @sql_list, $_->{'fwkfile'} if $_->{'mandatory'};
311 if ($subset_wanted->{'optional'}) {
312 push @sql_list, $_->{'fwkfile'} unless $_->{'mandatory'};
314 } @
{ $_->{'frameworks'} }
315 } (@
$marc_sql, @
$sample_sql);
320 =head2 load_db_schema
324 my $error = $installer->load_db_schema();
328 Loads the SQL script that creates Koha's tables and indexes. The
329 return value is a string containing error messages reported by the
337 my $datadir = C4
::Context
->config('intranetdir') . "/installer/data/$self->{dbms}";
338 my $error = $self->load_sql("$datadir/kohastructure.sql");
343 =head2 load_sql_in_order
347 my ($fwk_language, $list) = $installer->load_sql_in_order($all_languages, @sql_list);
351 Given a list of SQL scripts supplied in C<@sql_list>, loads each of them
352 into the database and sets the FrameworksLoaded system preference to names
353 of the scripts that were loaded.
355 The SQL files are loaded in alphabetical order by filename (not including
356 directory path). This means that dependencies among the scripts are to
357 be resolved by carefully naming them, keeping in mind that the directory name
358 does *not* currently count.
360 FIXME: this is a rather delicate way of dealing with dependencies between
363 The return value C<$list> is an arrayref containing a hashref for each
364 "level" or directory containing SQL scripts; the hashref in turns contains
365 a list of hashrefs containing a list of each script load and any error
366 messages associated with the loading of each script.
368 FIXME: The C<$fwk_language> code probably doesn't belong and needs to be
369 moved to a different method.
373 sub load_sql_in_order
{
375 my $all_languages = shift;
381 my @aa = split /\/|\\/, ($a);
382 my @bb = split /\/|\\/, ($b);
385 my $request = $self->{'dbh'}->prepare( "SELECT value FROM systempreferences WHERE variable='FrameworksLoaded'" );
387 my ($systempreference) = $request->fetchrow;
388 $systempreference = '' unless defined $systempreference; # avoid warning
389 foreach my $file (@fnames) {
392 my $error = $self->load_sql($file);
393 my @file = split qr
(\
/|\\), $file;
394 $lang = $file[ scalar(@file) - 3 ] unless ($lang);
395 my $level = $file[ scalar(@file) - 2 ];
397 $systempreference .= "$file[scalar(@file)-1]|"
398 unless ( index( $systempreference, $file[ scalar(@file) - 1 ] ) >= 0 );
401 #Bulding here a hierarchy to display files by level.
402 push @
{ $hashlevel{$level} },
403 { "fwkname" => $file[ scalar(@file) - 1 ], "error" => $error };
406 #systempreference contains an ending |
407 chop $systempreference;
409 map { push @list, { "level" => $_, "fwklist" => $hashlevel{$_} } } keys %hashlevel;
411 for my $each_language (@
$all_languages) {
413 # warn "CODE".$each_language->{'language_code'};
414 # warn "LANG:".$lang;
415 if ( $lang eq $each_language->{'language_code'} ) {
416 $fwk_language = $each_language->{language_locale_name
};
421 "UPDATE systempreferences set value=\"$systempreference\" where variable='FrameworksLoaded'"
424 unless ( $updateflag == 1 ) {
426 "INSERT INTO systempreferences (value, variable, explanation, type) VALUES (\"$systempreference\",'FrameworksLoaded','Frameworks loaded through webinstaller','choice')";
427 my $rq = $self->{'dbh'}->prepare($string);
430 return ($fwk_language, \
@list);
433 =head2 set_marcflavour_syspref
437 $installer->set_marcflavour_syspref($marcflavour);
441 Set the 'marcflavour' system preference. The incoming
442 C<$marcflavour> references to a subdirectory of
443 installer/data/$dbms/$lang/marcflavour, and is
444 normalized to MARC21 or UNIMARC.
446 FIXME: this method assumes that the MARC flavour will be either
451 sub set_marcflavour_syspref
{
453 my $marcflavour = shift;
455 # we can have some variants of marc flavour, by having different directories, like : unimarc_small and unimarc_full, for small and complete unimarc frameworks.
456 # marc_cleaned finds the marcflavour, without the variant.
457 my $marc_cleaned = 'MARC21';
458 $marc_cleaned = 'UNIMARC' if $marcflavour =~ /unimarc/i;
460 $self->{'dbh'}->prepare(
461 "INSERT IGNORE INTO `systempreferences` (variable,value,explanation,options,type) VALUES('marcflavour','$marc_cleaned','Define global MARC flavor (MARC21 or UNIMARC) used for character encoding','MARC21|UNIMARC','Choice');"
466 =head2 set_indexing_engine
470 $installer->set_indexing_engine($nozebra);
474 Sets system preferences related to the indexing
475 engine. The C<$nozebra> argument is a boolean;
476 if true, turn on NoZebra mode and turn off QueryFuzzy,
477 QueryWeightFields, and QueryStemming. If false, turn
478 off NoZebra mode (i.e., use the Zebra search engine).
482 sub set_indexing_engine
{
487 $self->{'dbh'}->do("UPDATE systempreferences SET value=1 WHERE variable='NoZebra'");
488 $self->{'dbh'}->do("UPDATE systempreferences SET value=0 WHERE variable in ('QueryFuzzy','QueryWeightFields','QueryStemming')");
490 $self->{'dbh'}->do("UPDATE systempreferences SET value=0 WHERE variable='NoZebra'");
495 =head2 set_version_syspref
499 $installer->set_version_syspref();
503 Set or update the 'Version' system preference to the current
504 Koha software version.
508 sub set_version_syspref
{
511 my $kohaversion=C4
::Context
::KOHAVERSION
;
512 # remove the 3 last . to have a Perl number
513 $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
514 if (C4
::Context
->preference('Version')) {
515 warn "UPDATE Version";
516 my $finish=$self->{'dbh'}->prepare("UPDATE systempreferences SET value=? WHERE variable='Version'");
517 $finish->execute($kohaversion);
519 warn "INSERT Version";
520 my $finish=$self->{'dbh'}->prepare("INSERT into systempreferences (variable,value,explanation) values ('Version',?,'The Koha database version. WARNING: Do not change this value manually, it is maintained by the webinstaller')");
521 $finish->execute($kohaversion);
523 C4
::Context
->clear_syspref_cache();
530 my $error = $installer->load_sql($filename);
534 Runs a the specified SQL using the DB's command-line
535 SQL tool, and returns any strings sent to STDERR
536 by the command-line tool.
538 FIXME: there has been a long-standing desire to
539 replace this with an SQL loader that goes
540 through DBI; partly for portability issues
541 and partly to improve error handling.
543 FIXME: even using the command-line loader, some more
544 basic error handling should be added - deal
545 with missing files, e.g.
551 my $filename = shift;
553 my $datadir = C4
::Context
->config('intranetdir') . "/installer/data/$self->{dbms}";
556 if ( $self->{dbms
} eq 'mysql' ) {
558 . ( $self->{hostname
} ?
" -h $self->{hostname} " : "" )
559 . ( $self->{port
} ?
" -P $self->{port} " : "" )
560 . ( $self->{user
} ?
" -u $self->{user} " : "" )
561 . ( $self->{password
} ?
" -p'$self->{password}'" : "" )
562 . " $self->{dbname} ";
563 $error = qx($strcmd --default-character
-set
=utf8
<$filename 2>&1 1>/dev/null
);
564 } elsif ( $self->{dbms
} eq 'Pg' ) {
566 . ( $self->{hostname
} ?
" -h $self->{hostname} " : "" )
567 . ( $self->{port
} ?
" -p $self->{port} " : "" )
568 . ( $self->{user
} ?
" -U $self->{user} " : "" )
569 # . ( $self->{password} ? " -W $self->{password}" : "" ) # psql will NOT accept a password, but prompts...
570 . " $self->{dbname} "; # Therefore, be sure to run 'trust' on localhost in pg_hba.conf -fbcit
571 $error = qx($strcmd -f
$filename 2>&1 1>/dev/null
);
572 # Be sure to set 'client_min_messages = error' in postgresql.conf
573 # so that only true errors are returned to stderr or else the installer will
574 # report the import a failure although it really succeded -fbcit
576 # errors thrown while loading installer data should be logged
577 warn "C4::Installer::load_sql returned the following errors while attempting to load $filename:\n";
582 =head2 get_file_path_from_name
586 my $filename = $installer->get_file_path_from_name('script_name');
590 searches through the set of known SQL scripts and finds the fully
591 qualified path name for the script that mathches the input.
593 returns undef if no match was found.
598 sub get_file_path_from_name
{
600 my $partialname = shift;
602 my $lang = 'en'; # FIXME: how do I know what language I want?
604 my ($defaulted_to_en, $list) = $self->sample_data_sql_list($lang);
605 # warn( Data::Dumper->Dump( [ $list ], [ 'list' ] ) );
608 foreach my $frameworklist ( @
$list ) {
609 push @found, grep { $_->{'fwkfile'} =~ /$partialname$/ } @
{$frameworklist->{'frameworks'}};
612 # warn( Data::Dumper->Dump( [ \@found ], [ 'found' ] ) );
613 if ( 0 == scalar @found ) {
615 } elsif ( 1 < scalar @found ) {
616 warn "multiple results found for $partialname";
619 return $found[0]->{'fwkfile'};
627 C4::Installer is a refactoring of logic originally from installer/installer.pl, which was
628 originally written by Henri-Damien Laurant.
630 Koha Developement team <info@koha.org>
632 Galen Charlton <galen.charlton@liblime.com>