@@ -, +, @@
- 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
- 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
---
C4/Auth.pm | 82 +---
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 | 3 +
.../prog/en/modules/admin/admin-home.tt | 7 +
.../prog/en/modules/admin/updatedatabase.tt | 172 +++++++
mainpage.pl | 26 +
16 files changed, 1063 insertions(+), 73 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
--- a/C4/Auth.pm
+++ a/C4/Auth.pm
@@ -42,7 +42,6 @@ BEGIN {
else { exit }
}
- $VERSION = 3.02; # set version for version checking
$debug = $ENV{DEBUG};
@ISA = qw(Exporter);
@EXPORT = qw(&checkauth &get_template_and_user &haspermission &get_user_subpermissions);
@@ -550,49 +549,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";
@@ -616,8 +572,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;
@@ -1060,19 +1026,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
@@ -1293,19 +1246,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
--- a/C4/Config/File/YAML.pm
+++ a/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;
--- a/C4/Installer.pm
+++ a/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/;
--- a/C4/Update/Database.pm
+++ a/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;
--- a/about.pl
+++ a/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') {
--- a/admin/ajax-updatedb-getinfos.pl
+++ a/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";
+}
--- a/admin/updatedatabase.pl
+++ a/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;
--- a/etc/update/database/config.yaml
+++ a/etc/update/database/config.yaml
@@ -0,0 +1, @@
+versions_dir: installer/data/mysql/versions
--- a/installer/data/mysql/kohastructure.sql
+++ a/installer/data/mysql/kohastructure.sql
@@ -1981,6 +1981,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`
--
--- a/installer/data/mysql/update.pl
+++ a/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;
+
+}
--- a/installer/data/mysql/versions/skeleton.pl
+++ a/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;
--- a/installer/data/mysql/versions/skeleton.sql
+++ a/installer/data/mysql/versions/skeleton.sql
@@ -0,0 +1,3 @@
+-- This is a comment
+DELIMITER ;
+INSERT INTO foo values("bar");
--- a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css
+++ a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css
@@ -2144,4 +2144,7 @@ a.localimage img {
}
div.pager p {
margin: 0;
+
+.underline {
+ text-decoration : underline;
}
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt
+++ a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt
@@ -98,6 +98,13 @@
+ [% 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 %]
+
+
+
+
+
+
DB revision
+
Comments
+
Status
+
Availability
+
Details
+
+
+
+ [% FOREACH v IN versions %]
+
+
[% 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 %]
+
+
+
+ [% IF v.available %]
+ Unknown
+ Get comments
+ [% ELSE %]
+
+ Queries :
+
+ [% FOREACH q IN v.queries %]
+
[% q.query %]
+ [% END %]
+
+
+ [% IF v.status == 1 %]
+
+ Status :
+ OK
+
+ [% ELSE %]
+
+ Status :
+ [% IF v.status == 2 %]
+ OK
+ [FORCED]
+ [% ELSE %]
+ Failed
+ [Mark as OK]
+ [% END %]
+