From 69c3bbdd23ad2ac6c9643cad15de93254ea17488 Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Thu, 14 Jun 2012 11:46:57 +0200 Subject: [PATCH] Bug 7167: New version for updatedatabase Content-Type: text/plain; charset="utf-8" This patch use DataTable, see BUG|BZ 6836 - css/datatables.css - lib/jquery/plugins/jquery.dataTables.min.js - js/datatables.js 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 * 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 | 87 +--- C4/Installer.pm | 11 +- C4/Update/Database.pm | 545 ++++++++++++++++++++ about.pl | 42 +- admin/ajax-updatedb-getinfos.pl | 61 +++ admin/updatedatabase.pl | 103 ++++ installer/data/mysql/kohastructure.sql | 21 + installer/data/mysql/update.pl | 28 + .../intranet-tmpl/prog/en/css/staff-global.css | 7 + koha-tmpl/intranet-tmpl/prog/en/modules/about.tt | 2 +- .../prog/en/modules/admin/admin-home.tt | 7 + .../prog/en/modules/admin/updatedatabase.tt | 191 +++++++ mainpage.pl | 18 + 13 files changed, 1049 insertions(+), 74 deletions(-) create mode 100644 C4/Update/Database.pm create mode 100755 admin/ajax-updatedb-getinfos.pl create mode 100755 admin/updatedatabase.pl create mode 100644 installer/data/mysql/update.pl create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/updatedatabase.tt diff --git a/C4/Auth.pm b/C4/Auth.pm index 705eda0..e3d5ad8 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -547,50 +547,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"); - safe_exit; - } - 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 my $fh, '>>', "/tmp/sessionlog" or warn "ERROR: Cannot append to /tmp/sessionlog"; @@ -618,8 +574,23 @@ sub checkauth { my $dbh = C4::Context->dbh; my $timeout = _timeout_syspref(); + # days + if ($timeout =~ /(\d+)[dD]/) { + $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; @@ -1066,19 +1037,6 @@ sub check_api_auth { my $dbh = C4::Context->dbh; my $timeout = _timeout_syspref(); - 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 @@ -1298,19 +1256,6 @@ sub check_cookie_auth { my $dbh = C4::Context->dbh; my $timeout = _timeout_syspref(); - 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/Installer.pm b/C4/Installer.pm index 304698e..4b86f1c 100644 --- a/C4/Installer.pm +++ b/C4/Installer.pm @@ -23,6 +23,7 @@ use strict; our $VERSION = 3.07.00.049; use C4::Context; use C4::Installer::PerlModules; +use C4::Update::Database; =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..4e41694 --- /dev/null +++ b/C4/Update/Database.pm @@ -0,0 +1,545 @@ +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 File::Basename; +use File::Find::Rule; +use Digest::MD5; +use List::MoreUtils qw/uniq/; +use YAML; + +=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 $VERSIONS_PATH = C4::Context->config('intranetdir') . '/installer/data/mysql/versions'; + +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 = File::Find::Rule->file->name( "$version.sql", "$version.pl" ) ->in( ( $VERSIONS_PATH ) ); + + 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} = { + error => "ALREADY_EXISTS", + filepath => $filepath, + old_version => @$r[0]->{version}, + 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} = { + error => "LOAD_FUNCTIONS_FAILED", + filename => $filename, + }; + } + } + default { + $report->{$version} = { + error => "BAD_EXTENSION", + extension => $extension, + }; + } + } + + 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; + + my @files = File::Find::Rule->file->name( "*.sql", "*.pl" ) ->in( ( $VERSIONS_PATH ) ); + + for my $f ( @files ) { + my @file_infos = fileparse( $f, qr/\.[^.]*/ ); + push @versions, $file_infos[0]; + } + @versions = uniq @versions; + 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 is_uptodate + is_uptodate(); + return 1 if the database is up to date else 0. + The database is up to date if all versions are excecuted. +=cut +sub is_uptodate { + 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 ) { + return 0; + } + } + return 1; +} + +=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/; + $version =~ s/Bug(\d+)/$1/; + 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); + } +} + +=head2 TableExists($table) + +=cut +sub TableExists { + my $table = shift; + eval { + local $dbh->{PrintError} = 0; + local $dbh->{RaiseError} = 1; + $dbh->do(qq{SELECT * FROM $table WHERE 1 = 0 }); + }; + return 1 unless $@; + return 0; +} + +1; diff --git a/about.pl b/about.pl index 3c641b3..de949cb 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,45 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( } ); -my $kohaVersion = C4::Context::KOHAVERSION; +my $kohaVersion = C4::Context->preference("Version"); +# restore ., for display consistency +$kohaVersion =~ /(.)\.(..)(..)(...)/; +# transform digits to Perl number, to display 3.6.1.2 instead of 3.06.01.002 +$kohaVersion = ($1+0).".".($2+0).".".($3+0).".".($4+0); + +my $dbrev_applied=""; # the list of database revisions + +# 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) { + $dbrev_applied.="."; + } 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 & $dbrev_applied =~ /\.$/) { + $dbrev_applied .= "...".$last_known_sep; + } + # start a new range + $dbrev_applied .= " ".$v->{version}; + } + $last_known= $current; + $last_known_sep=$v->{version}; +} +# add the last DB rev number, we don't want to end with "..." +if ($dbrev_applied =~ /\.$/) { + $dbrev_applied .= "...".$last_known_sep; +} + my $osVersion = `uname -a`; my $perl_path = $^X; if ($^O ne 'VMS') { @@ -76,6 +115,7 @@ my $warnIsRootUser = (! $loggedinuser); $template->param( kohaVersion => $kohaVersion, + dbrev_applied => $dbrev_applied, osVersion => $osVersion, perlPath => $perl_path, perlVersion => $perlVersion, 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..5735451 --- /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 }, + } +); + +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/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index c0d9936..137fd83 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -2082,6 +2082,27 @@ CREATE TABLE `tags_index` ( -- a weighted list of all tags and where they are us 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/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css index 6f5a840..bbe0e49 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css +++ b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css @@ -2139,6 +2139,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/about.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt index 8b2d555..385f392 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt @@ -37,7 +37,7 @@ - + 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 cbf3b92..b2a3b69 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 @@ -110,6 +110,13 @@
Did you mean?
Choose which plugins to use to suggest searches to patrons and staff.
+ +

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..5962fdf --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/updatedatabase.tt @@ -0,0 +1,191 @@ +[% INCLUDE 'doc-head-open.inc' %] +Koha › Administration › Update Database +[% INCLUDE 'doc-head-close.inc' %] + + +[% INCLUDE 'datatables-strings.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 %] + [% IF r.error.error == "ALREADY_EXISTS" %] + This file ( [% r.error.filepath %] ) still already execute in version [% r.error.old_version %] : same md5 ([% r.error.md5 %]) + [Mark as OK] + [% ELSIF r.error.error == "LOAD_FUNCTIONS_FAILED" %] + Load functions in [% r.error.filename %] failed + [% ELSIF r.error.error == "BAD_EXTENSION" %] + This extension ([% r.error.extension %]) is not take into account (only .pl or .sql)"; + [% ELSE %] + [% r.error %]; + [% END %] + [% END %] + [% IF report_loo.coherency %] + [Mark as OK] + [% END %] +
  • + [% END %] +
+
+ [% END %] + + [% IF nb_availables %] + Your datebase is not up to date.
+ [% IF nb_availables == 1 %] + 1 update available [UPDATE [% availables.first.version %]] + [% ELSE %] + [% nb_availables %] updates available [UPDATE ALL]: + [% IF ( dev_mode ) %] +
    + [% FOREACH av IN availables %] +
  • [% av.version %] [UPDATE]
  • + [% END %] +
+ [% END %] + [% END %] + [% ELSE %] + Your database is up to date + [% END %] +
+ +
Server information
Koha version: [% kohaVersion |html %]
Koha version: [% kohaVersion |html %] with the following database revisionapplied: [% dbrev_applied|html %]
OS version ('uname -a'): [% osVersion |html %]
Perl interpreter: [% perlPath |html %]
Perl version: [% perlVersion |html %]
+ + + + + + + + + + + [% 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..3e7dd3a 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,22 @@ 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; +} +unless ( C4::Update::Database::is_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