From a882135c889cb9deedc95715af77458ddcc10a9c Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Fri, 18 Nov 2011 16:46:37 +0100 Subject: [PATCH] New version for updatedatabase This patch use DataTable, see BUG|BZ 6836 - css/datatables.css - lib/jquery/plugins/jquery.dataTables.min.js - js/datatables.js patch embedded : YAML Config file add a section /path/to/your/install/dir in your koha-conf.xml http://bugs.koha-community.org/show_bug.cgi?id=7167 Bug 7167 follow-up Major changes: * creating database tables for update on the fly, the 1st time the update script is called * adding missing file: C4/Config/File/YAML.pm * version is checked on mainpage.pl (and here only). If syspref Version differ from kohaversion.pl, the old updatedatabase is launched. If there are updates missing from new mechanism, the updatedatabase page is reached * kohaversion check on each page is now useless in Auth.pm, removed dead code * Updated installer: at the end of the process, retrieve all updates and automatically mark them "OK", as they're included in installer Minor changes: * adding copyright * adding poddoc * updating a warning, for better clarity * switching from $$var to $var-> * small TT glitch fixed in updatedatabase.tt * about.pl now returns the Version systempreference PLUS all the patches that have been applied Bug 7167 follow-up perlcritic & numbers display & partial apply depending on DEBUG * add use strict to updatedatabase, that is now perlcritic compliant * partial apply of DB revs is now managed by DEBUG env variable = if DEBUG=0, the user can just apply every DBrev. If DEBUG=1, we're in a dev env, the user know has the option to apply DBrevs one by one Display: * in updatedatabase, small spelling changes * in about.pl, remove 0 just after . (3.06.01 is displayed as 3.6.1) * improve the display of applied numbers on about.pl - before this patch, if you have N, N+1, N+2, N+3 and N+10 DB rev applied, about was displaying : , N+1 / N+2 / N+3 / N+10 - after this patch you have N......N+3 / N+10 * add ORDER BY into list_versions_already_knows to have number retrieved in the same order whatever the order they are applied http://bugs.koha-community.org/show_bug.cgi?id=6679 Signed-off-by: Chris Nighswonger Signed-off-by: Paul Poulain --- C4/Auth.pm | 81 +--- C4/Config/File/YAML.pm | 59 +++ C4/Installer.pm | 11 +- C4/Update/Database.pm | 502 ++++++++++++++++++++ about.pl | 40 +- admin/ajax-updatedb-getinfos.pl | 61 +++ admin/updatedatabase.pl | 103 ++++ etc/update/database/config.yaml | 1 + installer/data/mysql/kohastructure.sql | 21 + installer/data/mysql/update.pl | 28 ++ installer/data/mysql/versions/skeleton.pl | 17 + installer/data/mysql/versions/skeleton.sql | 3 + .../intranet-tmpl/prog/en/css/staff-global.css | 7 + .../prog/en/modules/admin/admin-home.tt | 7 + .../prog/en/modules/admin/updatedatabase.tt | 172 +++++++ mainpage.pl | 26 + 16 files changed, 1067 insertions(+), 72 deletions(-) create mode 100644 C4/Config/File/YAML.pm create mode 100644 C4/Update/Database.pm create mode 100755 admin/ajax-updatedb-getinfos.pl create mode 100755 admin/updatedatabase.pl create mode 100644 etc/update/database/config.yaml create mode 100644 installer/data/mysql/update.pl create mode 100755 installer/data/mysql/versions/skeleton.pl create mode 100644 installer/data/mysql/versions/skeleton.sql create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/updatedatabase.tt diff --git a/C4/Auth.pm b/C4/Auth.pm index ffee1bb..bc78f36 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -554,49 +554,6 @@ has authenticated. =cut -sub _version_check ($$) { - my $type = shift; - my $query = shift; - my $version; - # If Version syspref is unavailable, it means Koha is beeing installed, - # and so we must redirect to OPAC maintenance page or to the WebInstaller - # also, if OpacMaintenance is ON, OPAC should redirect to maintenance - if (C4::Context->preference('OpacMaintenance') && $type eq 'opac') { - warn "OPAC Install required, redirecting to maintenance"; - print $query->redirect("/cgi-bin/koha/maintenance.pl"); - } - unless ( $version = C4::Context->preference('Version') ) { # assignment, not comparison - if ( $type ne 'opac' ) { - warn "Install required, redirecting to Installer"; - print $query->redirect("/cgi-bin/koha/installer/install.pl"); - } else { - warn "OPAC Install required, redirecting to maintenance"; - print $query->redirect("/cgi-bin/koha/maintenance.pl"); - } - safe_exit; - } - - # check that database and koha version are the same - # there is no DB version, it's a fresh install, - # go to web installer - # there is a DB version, compare it to the code version - my $kohaversion=C4::Context::KOHAVERSION; - # remove the 3 last . to have a Perl number - $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/; - $debug and print STDERR "kohaversion : $kohaversion\n"; - if ($version < $kohaversion){ - my $warning = "Database update needed, redirecting to %s. Database is $version and Koha is $kohaversion"; - if ($type ne 'opac'){ - warn sprintf($warning, 'Installer'); - print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3"); - } else { - warn sprintf("OPAC: " . $warning, 'maintenance'); - print $query->redirect("/cgi-bin/koha/maintenance.pl"); - } - safe_exit; - } -} - sub _session_log { (@_) or return 0; open L, ">>/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog"; @@ -620,8 +577,18 @@ sub checkauth { $timeout = $1 * 86400; }; $timeout = 600 unless $timeout; + # check we have a Version. Otherwise => go to installer + unless ( C4::Context->preference('Version') ) { # assignment, not comparison + if ( $type ne 'opac' ) { + $debug && warn "Install required, redirecting to Installer"; + print $query->redirect("/cgi-bin/koha/installer/install.pl"); + } else { + $debug && warn "OPAC Install required, redirecting to maintenance"; + print $query->redirect("/cgi-bin/koha/maintenance.pl"); + } + safe_exit; + } - _version_check($type,$query); # state variables my $loggedin = 0; my %info; @@ -1076,19 +1043,6 @@ sub check_api_auth { my $timeout = C4::Context->preference('timeout'); $timeout = 600 unless $timeout; - unless (C4::Context->preference('Version')) { - # database has not been installed yet - return ("maintenance", undef, undef); - } - my $kohaversion=C4::Context::KOHAVERSION; - $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/; - if (C4::Context->preference('Version') < $kohaversion) { - # database in need of version update; assume that - # no API should be called while databsae is in - # this condition. - return ("maintenance", undef, undef); - } - # FIXME -- most of what follows is a copy-and-paste # of code from checkauth. There is an obvious need # for refactoring to separate the various parts of @@ -1309,19 +1263,6 @@ sub check_cookie_auth { my $timeout = C4::Context->preference('timeout'); $timeout = 600 unless $timeout; - unless (C4::Context->preference('Version')) { - # database has not been installed yet - return ("maintenance", undef); - } - my $kohaversion=C4::Context::KOHAVERSION; - $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/; - if (C4::Context->preference('Version') < $kohaversion) { - # database in need of version update; assume that - # no API should be called while databsae is in - # this condition. - return ("maintenance", undef); - } - # FIXME -- most of what follows is a copy-and-paste # of code from checkauth. There is an obvious need # for refactoring to separate the various parts of diff --git a/C4/Config/File/YAML.pm b/C4/Config/File/YAML.pm new file mode 100644 index 0000000..ee77ca2 --- /dev/null +++ b/C4/Config/File/YAML.pm @@ -0,0 +1,59 @@ +package C4::Config::File::YAML; +# Copyright 2011 BibLibre +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; +use YAML; + +sub _fileload{ + my ($filename)=shift; + return YAML::LoadFile($filename); +} + +sub _dirload{ + my $dirname=shift; + my $result; + opendir D, $dirname; + for my $name (readdir(D)){ + next if $name=~/^\./; + if (-d $name){ + $result->{$name}=_dirload($dirname."/".$name); + } + if ((-e $dirname."/".$name) and ($name=~/\.ya?ml/) ){ + $result=($result + ? [%$result,%{_fileload($dirname."/".$name)}] + : _fileload($dirname."/".$name)); + } + } + close D; + return $result; +} + + +sub new{ + my $config; + my $self=shift; + my $name=shift; + if (-f $name){ + $config=_fileload($name); + } + if (-d $name){ + $config->{$name}=_dirload($name); + } + return $config; +} +1; diff --git a/C4/Installer.pm b/C4/Installer.pm index 43fd724..f4faa4b 100755 --- a/C4/Installer.pm +++ b/C4/Installer.pm @@ -22,6 +22,7 @@ use strict; our $VERSION = 3.00; use C4::Context; +use C4::Update::Database; use C4::Installer::PerlModules 1.000000; =head1 NAME @@ -466,7 +467,15 @@ Koha software version. sub set_version_syspref { my $self = shift; - + # get all updatedatabase, and mark them as passed, as it's a fresh install + my $versions = C4::Update::Database::list_versions_availables(); + for my $v ( @$versions ) { + my $queries; + $queries->{queries} = ["initial setup"]; + $queries->{comments} = ["initial setup"]; + C4::Update::Database::set_infos($v,$queries,undef,undef); + } + # mark the "old" 3.6 version number my $kohaversion=C4::Context::KOHAVERSION; # remove the 3 last . to have a Perl number $kohaversion =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/; diff --git a/C4/Update/Database.pm b/C4/Update/Database.pm new file mode 100644 index 0000000..e4d137e --- /dev/null +++ b/C4/Update/Database.pm @@ -0,0 +1,502 @@ +package C4::Update::Database; + +# Copyright 2011 BibLibre +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use C4::Context; +use C4::Config::File::YAML; + +use File::Basename; +use Digest::MD5; +use List::MoreUtils qw/uniq/; + +=head1 NAME + +C4::Update::Database.pm + +=head1 SYNOPSIS + + use C4::Update::Database; + + This package is used by admin/updatedatabase.pl, to manage DB updates + +=head1 FUNCTIONS + +=cut +my $config = C4::Config::File::YAML->new( C4::Context->config("intranetdir") . qq{/etc/update/database/config.yaml} ); +my $VERSIONS_PATH = C4::Context->config('intranetdir') . '/' . $config->{versions_dir}; + +my $version; +my $list; + +my $dbh = C4::Context->dbh; + +=head2 + + my $file = get_filepath($version); + this sub will return the full path of a given DB update number + +=cut + +sub get_filepath { + my ( $version ) = @_; + my @files = <$VERSIONS_PATH/$version*>; + if ( scalar @files != 1 ) { + die "This version ($version) returned has ".scalar @files." corresponding, need only 1"; + } + + return $files[0]; +} + +=head2 get_md5 + + my $md5 = get_md5($filepath) + returns the md5sum of the selected file. + This is used to check consistency of updates + +=cut +sub get_md5 { + my ( $filepath ) = @_; + open(FILE, $filepath); + + my $ctx = Digest::MD5->new; + $ctx->addfile(*FILE); + my $md5 = $ctx->hexdigest; + close(FILE); + return $md5; +} + +=head2 execute_version + + $result = execute_version($version_number); + Execute an update. + This sub will detect if the number is made through a .pl or a .sql, and behave accordingly + if there is more than 1 file with the same number, an error will be issued + if you try to execute a version_number that has already be executed, then it will also issue an error + the sub return an result hash, with the version number and the result + +=cut + +sub execute_version { + my ( $version ) = @_; + my $report; + + my $filepath; + eval { + $filepath = get_filepath $version; + }; + if ( $@ ) { + return { $version => $@ }; + } + + my @file_infos = fileparse( $filepath, qr/\.[^.]*/ ); + my $extension = $file_infos[2]; + my $filename = $version . $extension; + + my $md5 = get_md5 $filepath; + my $r = md5_already_exists( $md5 ); + if ( scalar @$r ) { + my $p = @$r[0]; + $report->{$version} = "This file ( $filepath ) still already execute in version " . @$r[0]->{version} . " : same md5 (" . @$r[0]->{md5} . ")"; + return $report; + } + + my $queries; + given ( $extension ) { + when ( /.sql/ ) { + $queries = get_queries ( $filepath ); + } + when ( /.pl/ ) { + my $versions_dir = C4::Context->intranetdir . '/installer/data/mysql/versions/'; + my $version_file = $versions_dir . $filename; + if ( do $version_file ) { + $queries = _get_queries(); + } else { + $report->{$version} = "Load functions in $filename failed"; + } + } + default { + $report->{$version} = "This extension ($extension) is not take into account (only .pl or .sql)"; + } + } + + return $report + if ( defined $report->{$version} ); + + my $errors; + for my $query ( @{$queries->{queries}} ) { + eval { + check_coherency( $query ); + }; + if ( $@ ) { + push @$errors, $@ + } + } + + if ( $errors ) { + $_ =~ s/at [^ ]* line \d*\.$// for @$errors; + $report->{$version} = $errors; + return $report; + } + + $errors = execute ( $queries ); + $report->{$version} = scalar( @$errors ) ? $errors : "OK"; + set_infos ( $version, $queries, $errors, $md5 ); + return $report; +} + +=head2 list_versions_availables + + my @versions = list_versions_availables; + return an array with all version available + This list is retrieved from the directory defined in the etc/update/database/config.yaml, versions_dir parameter + +=cut + +sub list_versions_availables { + my @versions; + opendir DH, $VERSIONS_PATH or die "Cannot open directory ($!)"; + my @files = grep { !/^\.\.?$/ and /^.*\.(sql|pl)$/ and !/^skeleton/ } readdir DH; + for my $f ( @files ) { + my @file_infos = fileparse( $f, qr/\.[^.]*/ ); + push @versions, $file_infos[0]; + } + @versions = uniq @versions; + closedir DH; + return \@versions; +} + +=head2 list_versions_already_knows + + my @versions = list_versions_availables; + return an array with all version that have already been applied + This sub check first that the updatedb tables exist and create them if needed + +=cut + +sub list_versions_already_knows { + # 1st check if tables exist, otherwise create them + $dbh->do(qq{ + CREATE TABLE IF NOT EXISTS `updatedb_error` ( `version` varchar(32) DEFAULT NULL, `error` text ) ENGINE=InnoDB CHARSET=utf8; + }); + $dbh->do(qq{ + CREATE TABLE IF NOT EXISTS `updatedb_query` ( `version` varchar(32) DEFAULT NULL, `query` text ) ENGINE=InnoDB CHARSET=utf8; + }); + $dbh->do(qq{ + CREATE TABLE IF NOT EXISTS `updatedb_report` ( `version` text, `md5` varchar(50) DEFAULT NULL, `comment` text, `status` int(1) DEFAULT NULL ) ENGINE=InnoDB CHARSET=utf8; + }); + + my $query = qq/ SELECT version, comment, status FROM updatedb_report ORDER BY version/; + my $sth = $dbh->prepare( $query ); + $sth->execute; + my $versions = $sth->fetchall_arrayref( {} ); + map { + my $version = $_; + my @comments = defined $_->{comment} ? split '\\\n', $_->{comment} : ""; + push @{ $version->{comments} }, { comment => $_ } for @comments; + delete $version->{comment}; + } @$versions; + $sth->finish; + for my $version ( @$versions ) { + $query = qq/ SELECT query FROM updatedb_query WHERE version = ? ORDER BY version/; + $sth = $dbh->prepare( $query ); + $sth->execute( $version->{version} ); + $version->{queries} = $sth->fetchall_arrayref( {} ); + $sth->finish; + $query = qq/ SELECT error FROM updatedb_error WHERE version = ? ORDER BY version/; + $sth = $dbh->prepare( $query ); + $sth->execute( $version->{version} ); + $version->{errors} = $sth->fetchall_arrayref( {} ); + $sth->finish; + } + return $versions; +} + +=head2 execute + + my @errors = $execute(\@queries); + This sub will execute queries coming from an execute_version based on a .sql file + +=cut + +sub execute { + my ( $queries ) = @_; + my @errors; + for my $query ( @{$queries->{queries}} ) { + eval { + $dbh->do( $query ); + }; + push @errors, get_error(); + } + return \@errors; +} + +=head2 get_tables_name + + my $tables = get_tables_name; + return an array with all Koha mySQL table names + +=cut + +sub get_tables_name { + my $sth = $dbh->prepare("SHOW TABLES"); + $sth->execute(); + my @tables; + while ( my ( $table ) = $sth->fetchrow_array ) { + push @tables, $table; + } + return \@tables; +} +my $tables; + +=head2 check_coherency + + my $errors = check_coherency($query); + This sub will try to check if a SQL query is useless or no. + for queries that are CREATE TABLE, it will check if the table already exists + for queries that are ALTER TABLE, it will search if the modification has already been made + for queries that are INSERT, it will search if the insert has already been made if it's a syspref or a permission + + Those test cover 90% of the updatedatabases cases. That will help finding duplicate or inconsistencies + +=cut + +sub check_coherency { + my ( $query ) = @_; + $tables = get_tables_name() if not $tables; + + given ( $query ) { + when ( /CREATE TABLE(?:.*?)? `?(\w+)`?/ ) { + my $table_name = $1; + if ( grep { /$table_name/ } @$tables ) { + die "COHERENCY: Table $table_name already exists"; + } + } + + when ( /ALTER TABLE *`?(\w+)`? *ADD *(?:COLUMN)? `?(\w+)`?/ ) { + my $table_name = $1; + my $column_name = $2; + next if $column_name =~ /(UNIQUE|CONSTRAINT|INDEX|KEY|FOREIGN)/; + if ( not grep { /$table_name/ } @$tables ) { + return "COHERENCY: Table $table_name does not exist"; + } else { + my $sth = $dbh->prepare( "DESC $table_name $column_name" ); + my $rv = $sth->execute; + if ( $rv > 0 ) { + die "COHERENCY: Field $table_name.$column_name already exists"; + } + } + } + + when ( /INSERT INTO `?(\w+)`?.*?VALUES *\((.*?)\)/ ) { + my $table_name = $1; + my @values = split /,/, $2; + s/^ *'// foreach @values; + s/' *$// foreach @values; + given ( $table_name ) { + when ( /systempreferences/ ) { + my $syspref = $values[0]; + my $sth = $dbh->prepare( "SELECT COUNT(*) FROM systempreferences WHERE variable = ?" ); + $sth->execute( $syspref ); + if ( ( my $count = $sth->fetchrow_array ) > 0 ) { + die "COHERENCY: Syspref $syspref already exists"; + } + } + + when ( /permissions/){ + my $module_bit = $values[0]; + my $code = $values[1]; + my $sth = $dbh->prepare( "SELECT COUNT(*) FROM permissions WHERE module_bit = ? AND code = ?" ); + $sth->execute($module_bit, $code); + if ( ( my $count = $sth->fetchrow_array ) > 0 ) { + die "COHERENCY: Permission $code already exists"; + } + } + } + } + } + return 1; +} + +=head2 get_error + + my $errors = get_error() + This sub will return any mySQL error that occured during an update + +=cut + +sub get_error { + my @errors = $dbh->selectrow_array(qq{SHOW ERRORS}); # Get errors + my @warnings = $dbh->selectrow_array(qq{SHOW WARNINGS}); # Get warnings + if ( @errors ) { # Catch specifics errors + return qq{$errors[0] : $errors[1] => $errors[2]}; + } elsif ( @warnings ) { + return qq{$warnings[0] : $warnings[1] => $warnings[2]} + if $warnings[0] ne 'Note'; + } + return; +} + +=head2 + + my $result = get_queries($filepath); + this sub will return a hashref with 2 entries: + $result->{queries} is an array with all queries to execute + $result->{comments} is an array with all comments in the .sql file + +=cut + +sub get_queries { + my ( $filepath ) = @_; + open my $fh, "<", $filepath; + my @queries; + my @comments; + my $old_delimiter = $/; + while ( <$fh> ) { + my $line = $_; + chomp $line; + $line =~ s/^\s*//; + if ( $line =~ s/^--(.*)$// ) { + push @comments, $1; + next; + } + if ( $line =~ /^delimiter (.*)$/i ) { + $/ = $1; + next; + } + $line =~ s#$/##; + push @queries, $line if not $line =~ /^\s*$/; # Push if query is not empty + } + $/ = $old_delimiter; + close $fh; + + return { queries => \@queries, comments => \@comments }; +} + +=head2 md5_already_exists + + my $result = md5_already_exists($md5); + check if the md5 of an update has already been applied on the database. + If yes, it will return a hash with the version related to this md5 + +=cut + +sub md5_already_exists { + my ( $md5 ) = @_; + my $query = qq/SELECT version, md5 FROM updatedb_report WHERE md5 = ?/; + my $sth = $dbh->prepare( $query ); + $sth->execute( $md5 ); + my @r; + while ( my ( $version, $md5 ) = $sth->fetchrow ) { + push @r, { version => $version, md5 => $md5 }; + } + $sth->finish; + return \@r; +} + +=head2 set_infos + + set_info($version,$queries, $error, $md5); + this sub will insert into the updatedb tables what has been made on the database (queries, errors, result) + +=cut +sub set_infos { + my ( $version, $queries, $errors, $md5 ) = @_; + #SetVersion($DBversion) if not -s $errors; + for my $query ( @{ $queries->{queries} } ) { + my $sth = $dbh->prepare("INSERT INTO updatedb_query(version, query) VALUES (?, ?)"); + $sth->execute( $version, $query ); + $sth->finish; + } + for my $error ( @$errors ) { + my $sth = $dbh->prepare("INSERT INTO updatedb_error(version, error) VALUES (?, ?)"); + $sth->execute( $version, $error ); + } + my $sth = $dbh->prepare("INSERT INTO updatedb_report(version, md5, comment, status) VALUES (?, ?, ?, ?)"); + $sth->execute( + $version, + $md5, + join ('\n', @{ $queries->{comments} }), + ( @$errors > 0 ) ? 0 : 1 + ); +} + +=head2 mark_as_ok + + $mark_as_ok($version); + this sub will force to mark as "OK" an update that has failed + once this has been made, the status will look as "forced OK", and appear in green like versions that have been applied without any problem + +=cut +sub mark_as_ok { + my ( $version ) = @_; + my $sth = $dbh->prepare( "UPDATE updatedb_report SET status = 2 WHERE version=?" ); + my $affected = $sth->execute( $version ); + if ( $affected < 1 ) { + # For "Coherency" + my $filepath = get_filepath $version; + my $queries = get_queries $filepath; + my $errors; + for my $query ( @{$queries->{queries}} ) { + eval { + check_coherency( $query ); + }; + if ( $@ ) { + push @$errors, $@ + } + } + + $_ =~ s/at [^ ]* line \d*\.$// for @$errors; + my $md5 = get_md5 $filepath; + set_infos $version, $queries, $errors, $md5; + + $sth->execute( $version ); + } + $sth->finish; +} + +=head2 TransformToNum + + Transform the Koha version from a 4 parts string + to a number, with just 1 . (ie: it's a number) + +=cut + +sub TransformToNum { + my $version = shift; + + # remove the 3 last . to have a Perl number + $version =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/; + return $version; +} + +sub SetVersion { + my $kohaversion = TransformToNum(shift); + if ( C4::Context->preference('Version') ) { + my $finish = $dbh->prepare("UPDATE systempreferences SET value=? WHERE variable='Version'"); + $finish->execute($kohaversion); + } else { + my $finish = $dbh->prepare( +"INSERT IGNORE INTO systempreferences (variable,value,explanation) values ('Version',?,'The Koha database version. WARNING: Do not change this value manually, it is maintained by the webinstaller')" + ); + $finish->execute($kohaversion); + } +} + +1; diff --git a/about.pl b/about.pl index 7189a22..b0364d3 100755 --- a/about.pl +++ b/about.pl @@ -32,6 +32,7 @@ use C4::Output; use C4::Auth; use C4::Context; use C4::Installer; +use C4::Update::Database; #use Smart::Comments '####'; @@ -47,7 +48,44 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( } ); -my $kohaVersion = C4::Context::KOHAVERSION; +my $kohaVersion = C4::Context->preference("Version"); +# restore ., for display consistency +$kohaVersion =~ /(.)\.(..)(..)(...)/; +$kohaVersion = "$1.$2.$3.$4"; +# the $kohaVersion is duplicated since 3.7: the 3.6 (that uses the old mechanism) and the 3.7 (new mechanism). +# Both versions reflects how the database has been upgraded +my $already_knows = C4::Update::Database::list_versions_already_knows(); +# $last_known contains the previous DBrev applied number (all . removed). It's used to have a . instead of a number in case of continuous updates +my $last_known=0; +# $last_known_sep contains the previous DBrev applied with the separator (used for display) +my $last_known_sep=""; +for my $v ( @$already_knows ) { + my $current = $v->{version}; + $current =~s/\.//g; + # if the current number is the previous one +1, then just add a ., for a better display N.........N+10, for example + # (instead of N / N+1 / N+2 / ...) + if ($current==$last_known+1) { + $kohaVersion.="."; + } else { # we're not N+1, start a new range + # if version don't end by a ., no need to add the current loop number + # this avoid having N...N (in case of an isolated BDrev number) + if ($last_known & $kohaVersion =~ /\.$/) { + $kohaVersion .= "...".$last_known_sep; + } + # start a new range + $kohaVersion .= " / ".$v->{version}; + } + $last_known= $current; + $last_known_sep=$v->{version}; +} +# add the last DB rev number, we don't want to end with "..." +if ($kohaVersion =~ /\.$/) { + $kohaVersion .= "...".$last_known_sep; +} + +# remove any 0 just after a . for better readability (3.06.02.001 will become 3.6.2.1) +$kohaVersion =~ s/\.0+/\./g; + my $osVersion = `uname -a`; my $perl_path = $^X; if ($^O ne 'VMS') { diff --git a/admin/ajax-updatedb-getinfos.pl b/admin/ajax-updatedb-getinfos.pl new file mode 100755 index 0000000..25ceda5 --- /dev/null +++ b/admin/ajax-updatedb-getinfos.pl @@ -0,0 +1,61 @@ +#!/usr/bin/perl + +# Copyright 2011 BibLibre +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=head1 NAME + +ajax-updatedb-getinfos.pl + +=head1 DESCRIPTION +this script returns comments for a updatedatabase version + +=back + +=cut + +use Modern::Perl; +use CGI; +use C4::Update::Database; + +my $input = new CGI; +my $version = $input->param('version'); + +binmode STDOUT, ":utf8"; +print $input->header(-type => 'text/plain', -charset => 'UTF-8'); +my $filepath; +my $queries; +eval { + $filepath = C4::Update::Database::get_filepath( $version ); + $queries = C4::Update::Database::get_queries( $filepath ); +}; +if ( $@ ){ + print $@; + exit; +} + +if ( @{ $$queries{comments} } ) { + print "Comments :
" . join ( "
", @{ $$queries{comments} } ); +} else { + print "No comment
"; +} + +if ( @{ $$queries{queries} } ) { + print "

Queries :
" . join ( "
", @{ $$queries{queries} } ); +} else { + print "
No queries"; +} diff --git a/admin/updatedatabase.pl b/admin/updatedatabase.pl new file mode 100755 index 0000000..d43c3b3 --- /dev/null +++ b/admin/updatedatabase.pl @@ -0,0 +1,103 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; +use strict; +use CGI; +use C4::Auth; +use C4::Output; +use C4::Update::Database; + +my $query = new CGI; +my $op = $query->param('op') || 'list'; + +my ( $template, $borrowernumber, $cookie ) = get_template_and_user( + { template_name => "admin/updatedatabase.tmpl", + query => $query, + type => "intranet", + authnotrequired => 0, + flagsrequired => { parameters => 1 }, # FIXME Add a new flag + } +); + +if ( $op eq 'update' ) { + my @versions = $query->param('version'); + @versions = sort { + C4::Update::Database::TransformToNum( $a ) <=> C4::Update::Database::TransformToNum( $b ) + } @versions; + + my @reports; + for my $version ( @versions ) { + push @reports, C4::Update::Database::execute_version $version; + } + + my @report_loop = map { + my ( $v, $r ) = each %$_; + my @errors = ref ( $r ) eq 'ARRAY' + ? + map { + { error => $_ } + } @$r + : + { error => $r }; + { + version => $v, + report => \@errors, + coherency => ( ref ( $r ) eq 'ARRAY' + ? @$r[0] =~ /COHERENCY/ ? 1 : 0 + : $r =~ /COHERENCY/ ? 1 : 0 ) + } + } @reports; + $template->param( report_loop => \@report_loop ); + + $op = 'list'; +} + +if ( $op eq 'mark_as_ok' ) { + my @versions = $query->param('version'); + C4::Update::Database::mark_as_ok $_ for @versions; + $op = 'list'; +} + +if ( $op eq 'list' ) { + my $versions_availables = C4::Update::Database::list_versions_availables; + my $versions = C4::Update::Database::list_versions_already_knows; + + for my $v ( @$versions_availables ) { + if ( not grep { $v eq $$_{version} } @$versions ) { + push @$versions, { + version => $v, + available => 1 + }; + } + } + my @sorted = sort { + C4::Update::Database::TransformToNum( $$a{version} ) <=> C4::Update::Database::TransformToNum( $$b{version} ) + } @$versions; + + my @availables = grep { defined $$_{available} and $$_{available} == 1 } @sorted; + my @v_availables = map { {version => $$_{version}} } @availables; + + $template->param( + dev_mode => $ENV{DEBUG}, + versions => \@sorted, + nb_availables => scalar @availables, + availables => [ map { {version => $$_{version}} } @availables ], + ); +} + +output_html_with_http_headers $query, $cookie, $template->output; diff --git a/etc/update/database/config.yaml b/etc/update/database/config.yaml new file mode 100644 index 0000000..e04717a --- /dev/null +++ b/etc/update/database/config.yaml @@ -0,0 +1 @@ +versions_dir: installer/data/mysql/versions diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index 12ea9d1..6c4850a 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -2048,6 +2048,27 @@ CREATE TABLE `tags_index` ( REFERENCES `biblio` (`biblionumber`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Table structure for database updates +-- +CREATE TABLE`updatedb_error` ( + `version` varchar(32) DEFAULT NULL, + `error` text +) ENGINE=InnoDB CHARSET=utf8; + +CREATE TABLE `updatedb_query` ( + `version` varchar(32) DEFAULT NULL, + `query` text +) ENGINE=InnoDB CHARSET=utf8; + +CREATE TABLE `updatedb_report` ( + `version` text, + `md5` varchar(50) DEFAULT NULL, + `comment` text, + `status` int(1) DEFAULT NULL +) ENGINE=InnoDB CHARSET=utf8; + -- -- Table structure for table `userflags` -- diff --git a/installer/data/mysql/update.pl b/installer/data/mysql/update.pl new file mode 100644 index 0000000..92275c7 --- /dev/null +++ b/installer/data/mysql/update.pl @@ -0,0 +1,28 @@ +use Modern::Perl; + +use C4::Context; +use C4::Update::Database; +use Getopt::Long; + + +my $version; +my $list; + +GetOptions( + 'm:s' => \$version, + 'l' => \$list, +); + +if ( $version ) { + my $report = C4::Update::Database::execute_version($version); +} + +if ( $list ) { + my $versions = C4::Update::Database::list_versions_availables(); + say "Versions availables:"; + say "\t- $_" for @$versions; + $versions = C4::Update::Database::list_versions_already_knows(); + say "Versions already knows:"; + say "\t- $$_{version}" for @$versions; + +} diff --git a/installer/data/mysql/versions/skeleton.pl b/installer/data/mysql/versions/skeleton.pl new file mode 100755 index 0000000..27ecde5 --- /dev/null +++ b/installer/data/mysql/versions/skeleton.pl @@ -0,0 +1,17 @@ +#!/usr/bin/perl + +use Modern::Perl; +use C4::Update::Database; + +sub _get_queries { + my @queries = ( + qq{INSERT INTO foo VALUES ('bar1')}, + qq{INSERT INTO foo VALUES ('bar2')}, + ); + my @comments = ( + qq{This is a test}, + ); + return { queries => \@queries, comments => \@comments }; +} + +1; diff --git a/installer/data/mysql/versions/skeleton.sql b/installer/data/mysql/versions/skeleton.sql new file mode 100644 index 0000000..11d1cc9 --- /dev/null +++ b/installer/data/mysql/versions/skeleton.sql @@ -0,0 +1,3 @@ +-- This is a comment +DELIMITER ; +INSERT INTO foo values("bar"); diff --git a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css index dcd9540..08c0456 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css +++ b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css @@ -2222,6 +2222,13 @@ a.localimage img { } div.pager p { margin: 0; + +tr.dragClass td { + background-color: grey; + color: yellow; +} +.underline { + text-decoration : underline; } div#acqui_order_supplierlist > div.supplier { diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt index ef46ede..d5e9ff4 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt @@ -100,6 +100,13 @@
Z39.50 client targets
Define which servers to query for MARC data in the integrated Z39.50 client.
+ +

Update Database

+
+
Check your updates
+
Verify your database versions and execute new updates
+
+ diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/updatedatabase.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/updatedatabase.tt new file mode 100644 index 0000000..cc03be0 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/updatedatabase.tt @@ -0,0 +1,172 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Administration › Update Database + +[% INCLUDE 'doc-head-close.inc' %] + + + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +
+ +
+
+
+ +

Database update

+ [% IF report_loop %] +
+ Report : +
    + [% FOREACH report_loo IN report_loop %] +
  • + [% report_loo.version %] -- + [% FOREACH r IN report_loo.report %] + [% r.error %]; + [% END %] + [% IF report_loo.coherency %] + [Mark as OK] + [% END %] +
  • + [% END %] +
+
+ [% END %] + + [% IF nb_availables %] + [% nb_availables %] update(s) available [ UPDATE ] + [% ELSE %] + Your database is up to date + [% END %] + + + + + + + + + + + + + + [% FOREACH v IN versions %] + + + + + + + + [% END %] + +
DB revisionCommentsStatusAvailabilityDetails
[% v.version %] +
    + [% FOREACH c IN v.comments %] +
  • [% c.comment %]
  • + [% END %] +
+
+ [% IF v.available %] + Unknown + [% ELSE %] + [% IF v.status %] + OK + [% IF v.status == 2 %] + [FORCED] + [% END %] + [% ELSE %] + Failed + [% END %] + [% END %] + + [% IF v.available %] + Available + [% IF (dev_mode) %] + [Execute] + [% END %] + [% ELSE %] + Already executed + [% END %] + + + Show details +
+ +
+
+
+
+ diff --git a/mainpage.pl b/mainpage.pl index f834985..b39c1c2 100755 --- a/mainpage.pl +++ b/mainpage.pl @@ -28,6 +28,8 @@ use C4::NewsChannels; use C4::Review qw/numberofreviews/; use C4::Suggestions qw/CountSuggestion/; use C4::Tags qw/get_count_by_tag_status/; +use C4::Update::Database; + my $query = new CGI; my ( $template, $loggedinuser, $cookie ) = get_template_and_user( @@ -42,6 +44,30 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( } ); +# +# check if you're uptodate, and if you're not, head to updater +# +my $koha36= C4::Context::KOHAVERSION; +$koha36 =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/; + +if (C4::Context->preference('Version') < $koha36) { + print $query->redirect("/cgi-bin/koha/installer/install.pl?step=3"); + exit; +} +my $versions_availables = C4::Update::Database::list_versions_availables; +my $versions = C4::Update::Database::list_versions_already_knows; +my $uptodate=1; +for my $v ( @$versions_availables ) { + if ( not grep { $v eq $$_{version} } @$versions ) { + $uptodate=0; + } +} +unless ($uptodate) { + # not uptodate, redirect to updatedatabase page + print $query->redirect("/cgi-bin/koha/admin/updatedatabase.pl"); + exit; +} + my $all_koha_news = &GetNewsToDisplay("koha"); my $koha_news_count = scalar @$all_koha_news; -- 1.7.9.5