[Patches] [PATCH] New version for updatedatabase
koha-patchbot at kohaaloha.com
koha-patchbot at kohaaloha.com
Wed Dec 21 03:12:05 NZDT 2011
From: Jonathan Druart <jonathan.druart at biblibre.com>
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
<installdir>/path/to/your/install/dir</installdir>
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
---
C4/Auth.pm | 84 +---
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 | 8 +
.../prog/en/modules/admin/admin-home.tt | 7 +
.../prog/en/modules/admin/updatedatabase.tt | 172 +++++++
mainpage.pl | 26 +
16 files changed, 1069 insertions(+), 74 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 e360e10..521a9e7 100755
--- a/C4/Auth.pm
+++ b/C4/Auth.pm
@@ -35,7 +35,7 @@ use POSIX qw/strftime/;
use List::MoreUtils qw/ any /;
# use utf8;
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap $cas $caslogout $servers $memcached);
+use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap $cas $caslogout $servers $memcached);
BEGIN {
sub psgi_env { any { /^psgi\./ } keys %ENV }
@@ -44,7 +44,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);
@@ -580,49 +579,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";
@@ -646,8 +602,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' ) {
+ 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;
+ }
- _version_check($type,$query);
# state variables
my $loggedin = 0;
my %info;
@@ -1089,19 +1055,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
@@ -1322,19 +1275,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 a7eb1f6..0879fce 100644
--- 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 : <br/>" . join ( "<br/>", @{ $$queries{comments} } );
+} else {
+ print "No comment <br/>";
+}
+
+if ( @{ $$queries{queries} } ) {
+ print "<br/><br/>Queries : <br/>" . join ( "<br/>", @{ $$queries{queries} } );
+} else {
+ print "<br/>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 a03bd23..877544a 100644
--- a/installer/data/mysql/kohastructure.sql
+++ b/installer/data/mysql/kohastructure.sql
@@ -1957,6 +1957,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 9eb7fa2..fa7bd8a 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css
+++ b/koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css
@@ -2133,3 +2133,11 @@ div.pager input.pagedisplay {
font-weight: bold;
text-align : center;
}
+
+tr.dragClass td {
+ background-color: grey;
+ color: yellow;
+}
+.underline {
+ text-decoration : underline;
+}
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 fe76ae8..760d235 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
@@ -98,6 +98,13 @@
<dt><a href="/cgi-bin/koha/admin/z3950servers.pl">Z39.50 Client Targets</a></dt>
<dd>Define which servers to query for MARC data in the integrated Z39.50 client.</dd>
</dl>
+
+<h3>Update Database</h3>
+<dl>
+ <dt><a href="/cgi-bin/koha/admin/updatedatabase.pl">Check your updates</a></dt>
+ <dd>Verify your database versions and execute new updates</dd>
+</dl>
+
</div>
</div>
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' %]
+<title>Koha › Administration › Update Database</title>
+<link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" />
+[% INCLUDE 'doc-head-close.inc' %]
+<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.dataTables.min.js"></script>
+<script type="text/javascript" src="[% themelang %]/js/datatables.js"></script>
+<script type="text/javascript">
+ //<![CDATA[
+ $(document).ready(function() {
+ $("#versionst").dataTable({
+ 'bAutoWidth': false,
+ 'sPaginationType': 'full_numbers'
+ } );
+ } );
+ function see_details(a){
+ var div = $(a).siblings('div');
+ $(div).slideToggle("fast", function() {
+ var isVisible = $(div).is(":visible");
+ if ( isVisible ){$(a).text("Hide details");}else{$(a).text("Show details");}
+ } );
+ }
+ function get_infos(version, node){
+ $.ajax({
+ url: "/cgi-bin/koha/admin/ajax-updatedb-getinfos.pl",
+ data: {
+ version: version
+ },
+ success: function(data){
+ $(node).replaceWith(data);
+ },
+ });
+ }
+//]]>
+</script>
+</head>
+<body>
+[% INCLUDE 'header.inc' %]
+[% INCLUDE 'cat-search.inc' %]
+
+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> › <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> › Database update</div>
+
+<div id="doc3" class="yui-t2">
+
+ <div id="bd">
+ <div id="yui-main">
+ <div class="yui-b">
+
+ <h2>Database update</h2>
+ [% IF report_loop %]
+ <div class="report" style="display:block; margin:1em;">
+ Report :
+ <ul>
+ [% FOREACH report_loo IN report_loop %]
+ <li>
+ [% report_loo.version %] --
+ [% FOREACH r IN report_loo.report %]
+ [% r.error %];
+ [% END %]
+ [% IF report_loo.coherency %]
+ [<a href="/cgi-bin/koha/admin/updatedatabase.pl?op=mark_as_ok&version=[% report_loo.version %]">Mark as OK</a>]
+ [% END %]
+ </li>
+ [% END %]
+ </ul>
+ </div>
+ [% END %]
+ <span class="infos" style="display:block; margin:1em;">
+ [% IF nb_availables %]
+ [% nb_availables %] update(s) available [ <a href="/cgi-bin/koha/admin/updatedatabase.pl?op=update[% FOREACH av IN availables %]&version=[% av.version %][% END %]">UPDATE</a> ]
+ [% ELSE %]
+ Your database is up to date
+ [% END %]
+ </span>
+
+ <table id="versionst">
+ <thead>
+ <tr>
+ <th>DB revision</th>
+ <th>Comments</th>
+ <th>Status</th>
+ <th>Availability</th>
+ <th>Details</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOREACH v IN versions %]
+ <tr>
+ <td>[% v.version %]</td>
+ <td>
+ <ul class="comments">
+ [% FOREACH c IN v.comments %]
+ <li>[% c.comment %]</li>
+ [% END %]
+ </ul>
+ </td>
+ <td>
+ [% IF v.available %]
+ Unknown
+ [% ELSE %]
+ [% IF v.status %]
+ <span style="color:green;">OK</span>
+ [% IF v.status == 2 %]
+ [FORCED]
+ [% END %]
+ [% ELSE %]
+ <span style="color:red;">Failed</span>
+ [% END %]
+ [% END %]
+ </td>
+ <td>
+ [% IF v.available %]
+ Available
+ [% IF (dev_mode) %]
+ [<a href="/cgi-bin/koha/admin/updatedatabase.pl?op=update&version=[% v.version %]">Execute</a>]
+ [% END %]
+ [% ELSE %]
+ Already executed
+ [% END %]
+ </td>
+ <td>
+ <div class="details" style="display:none;">
+ [% IF v.available %]
+ Unknown
+ <span style="display:block;"><a href="#" onclick="get_infos('[% v.version %]', this); return false;">Get comments</a></span>
+ [% ELSE %]
+ <div class="queries" style="display:block;">
+ <span class="underline">Queries</span> :
+ <ul>
+ [% FOREACH q IN v.queries %]
+ <li>[% q.query %]</li>
+ [% END %]
+ </ul>
+ </div>
+ [% IF v.status == 1 %]
+ <div class="status" style="display:block;">
+ <span class="underline">Status</span> :
+ <span style="color:green;">OK</span>
+ </div>
+ [% ELSE %]
+ <div class="status" style="display:block;">
+ <span class="underline">Status</span> :
+ [% IF v.status == 2 %]
+ <span style="color:green;">OK</span>
+ [FORCED]
+ [% ELSE %]
+ <span style="color:red;">Failed</span>
+ [<a href="/cgi-bin/koha/admin/updatedatabase.pl?op=mark_as_ok&version=[% v.version %]">Mark as OK</a>]
+ [% END %]
+ </div>
+ <div class="errors" style="display:block;">
+ <span class="underline">Errors</span> :
+ <ul class="errors">
+ [% FOREACH e IN v.errors %]
+ <li>[% e.error %]</li>
+ [% END %]
+ </ul>
+ </div>
+ [% END %]
+ [% END %]
+ </div>
+ <a href="#" onclick="see_details(this);return false;">Show details</a>
+ </td>
+ </tr>
+ [% END %]
+ </tbody>
+ </table>
+
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
diff --git a/mainpage.pl b/mainpage.pl
index bd1c9e5..2261131 100755
--- a/mainpage.pl
+++ b/mainpage.pl
@@ -29,6 +29,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 $authtypes = getauthtypes;
my @authtypesloop;
@@ -57,6 +59,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;
+}
+
$template->param(
authtypesloop => \@authtypesloop
);
--
1.7.5.4
More information about the Patches
mailing list