From b3f2a13a4368d9f8bec74a6da920649d56732b71 Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Thu, 26 Apr 2018 15:07:07 -0400 Subject: [PATCH] Bug 20669: Add upgrade method to plugins It would be nice if plugins had an upgrade method to handle altering tables and such when a new version of a plugin is installed. Right now it must be done in a completely bespoke manner. Test Plan: 1) Apply this patch 2) Download the 2 most recent releases of the Kitchen Sink plugin https://github.com/bywatersolutions/koha-plugin-kitchen-sink/releases 3) Install the earlier release 4) Look at the configuration page, at the bottom it should say the plugin has never been upgraded 5) Install the newer release 6) Reload the configuration page, it should now say that the plugin was upgraded and give you the date and time of the upgrade Signed-off-by: Claire Gravely Signed-off-by: Tomas Cohen Arazi Signed-off-by: Nick Clemens (cherry picked from commit 100385196ecadb34669190caa241ed73ea2a5a1c) Signed-off-by: Martin Renvoize --- Koha/Plugins/Base.pm | 51 +++++++++++++++++++++++++++++++++++++++ t/db_dependent/Plugins.t | 17 +++++++++++-- t/lib/Koha/Plugin/Test.pm | 5 ++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/Koha/Plugins/Base.pm b/Koha/Plugins/Base.pm index c39a8c91bf..ed87253a02 100644 --- a/Koha/Plugins/Base.pm +++ b/Koha/Plugins/Base.pm @@ -21,6 +21,7 @@ use Modern::Perl; use Module::Pluggable require => 1; use Cwd qw(abs_path); +use List::Util qw(max); use base qw{Module::Bundled::Files}; @@ -43,13 +44,26 @@ sub new { my $self = bless( $args, $class ); + my $plugin_version = $self->get_metadata->{version}; + my $database_version = $self->retrieve_data('__INSTALLED_VERSION__'); + ## Run the installation method if it exists and hasn't been run before if ( $self->can('install') && !$self->retrieve_data('__INSTALLED__') ) { if ( $self->install() ) { $self->store_data( { '__INSTALLED__' => 1 } ); + if ( my $version = $plugin_version ) { + $self->store_data({ '__INSTALLED_VERSION__' => $version }); + } } else { warn "Plugin $class failed during installation!"; } + } elsif ( $self->can('upgrade') && $plugin_version && $database_version ) { + if ( _version_compare( $plugin_version, $database_version ) == 1 ) { + if ( $self->upgrade() ) { + $self->store_data({ '__INSTALLED_VERSION__' => $plugin_version }); + warn "Plugin $class failed during upgrade!"; + } + } } return $self; @@ -218,6 +232,43 @@ sub output { output_with_http_headers( $self->{cgi}, undef, $data, $content_type, $status, $extra_options ); } +=head2 _version_compare + +Utility method to compare two version numbers. +Returns 1 if the first argument is the higher version +Returns -1 if the first argument is the lower version +Returns 0 if both versions are equal + +if ( _version_compare( '2.6.26', '2.6.0' ) == 1 ) { + print "2.6.26 is greater than 2.6.0\n"; +} + +=cut + +sub _version_compare { + my $ver1 = shift || 0; + my $ver2 = shift || 0; + + my @v1 = split /[.+:~-]/, $ver1; + my @v2 = split /[.+:~-]/, $ver2; + + for ( my $i = 0 ; $i < max( scalar(@v1), scalar(@v2) ) ; $i++ ) { + + # Add missing version parts if one string is shorter than the other + # i.e. 0 should be lt 0.2.1 and not equal, so we append .0 + # 0.0.0 <=> 0.2.1 = -1 + push( @v1, 0 ) unless defined( $v1[$i] ); + push( @v2, 0 ) unless defined( $v2[$i] ); + if ( int( $v1[$i] ) > int( $v2[$i] ) ) { + return 1; + } + elsif ( int( $v1[$i] ) < int( $v2[$i] ) ) { + return -1; + } + } + return 0; +} + 1; __END__ diff --git a/t/db_dependent/Plugins.t b/t/db_dependent/Plugins.t index 9379f94dfd..e5d8ed96ff 100755 --- a/t/db_dependent/Plugins.t +++ b/t/db_dependent/Plugins.t @@ -2,15 +2,15 @@ use Modern::Perl; -use Test::More tests => 35; +use Archive::Extract; use CGI; use File::Basename; use File::Spec; use File::Temp qw( tempdir tempfile ); use FindBin qw($Bin); -use Archive::Extract; use Module::Load::Conditional qw(can_load); use Test::MockModule; +use Test::More tests => 37; use C4::Context; use t::lib::Mocks; @@ -49,6 +49,7 @@ ok( $plugin->can('opac_head'), 'Test plugin can opac_head' ); ok( $plugin->can('opac_js'), 'Test plugin can opac_js' ); ok( $plugin->can('configure'), 'Test plugin can configure' ); ok( $plugin->can('install'), 'Test plugin can install' ); +ok( $plugin->can('upgrade'), 'Test plugin can upgrade' ); ok( $plugin->can('uninstall'), 'Test plugin can install' ); is( Koha::Plugins::Handler->run({ class => "Koha::Plugin::Test", method => 'report', enable_plugins => 1 }), "Koha::Plugin::Test::report", 'Test run plugin report method' ); @@ -151,3 +152,15 @@ subtest 'output and output_html tests' => sub { like($stdout, qr{Content-Type: text/html; charset=UTF-8}, 'Correct content-type'); like($stdout, qr{¡Hola output_html!}, 'Correct data'); }; + +subtest 'Test _version_compare' => sub { + + plan tests => 6; + + is( Koha::Plugins::Base::_version_compare( '1.1.1', '2.2.2' ), -1, "1.1.1 is less then 2.2.2" ); + is( Koha::Plugins::Base::_version_compare( '2.2.2', '1.1.1' ), 1, "1.1.1 is greater then 2.2.2" ); + is( Koha::Plugins::Base::_version_compare( '1.1.1', '1.1.1' ), 0, "1.1.1 is equal to 1.1.1" ); + is( Koha::Plugins::Base::_version_compare( '1.01.001', '1.1.1' ), 0, "1.01.001 is equal to 1.1.1" ); + is( Koha::Plugins::Base::_version_compare( '1', '1.0.0' ), 0, "1 is equal to 1.0.0" ); + is( Koha::Plugins::Base::_version_compare( '1.0', '1.0.0' ), 0, "1.0 is equal to 1.0.0" ); +}; diff --git a/t/lib/Koha/Plugin/Test.pm b/t/lib/Koha/Plugin/Test.pm index f817438986..180df57915 100644 --- a/t/lib/Koha/Plugin/Test.pm +++ b/t/lib/Koha/Plugin/Test.pm @@ -80,6 +80,11 @@ sub install { return "Koha::Plugin::Test::install"; } +sub upgrade { + my ( $self, $args ) = @_; + return "Koha::Plugin::Test::upgrade"; +} + sub uninstall { my ( $self, $args ) = @_; return "Koha::Plugin::Test::uninstall"; -- 2.39.5