From 981633c7edae461d985e5cc256a50c7bda0d96f6 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 --- Koha/Plugins/Base.pm | 51 +++++++++++++++++++++++++++++++++++++++++++++++ t/db_dependent/Plugins.t | 19 ++++++++++++++++-- t/lib/Koha/Plugin/Test.pm | 5 +++++ 3 files changed, 73 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..e3d5c37991 100755 --- a/t/db_dependent/Plugins.t +++ b/t/db_dependent/Plugins.t @@ -2,17 +2,18 @@ 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 Koha::Database; use t::lib::Mocks; BEGIN { @@ -49,6 +50,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 +153,16 @@ 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 3452af9302..ae9bd18627 100644 --- a/t/lib/Koha/Plugin/Test.pm +++ b/t/lib/Koha/Plugin/Test.pm @@ -78,6 +78,11 @@ sub install { return "Koha::Plugin::Test::install"; } +sub upgrade { + my ( $self, $args ) = @_; + return "Koha::Plugin::Test::install"; +} + sub uninstall { my ( $self, $args ) = @_; return "Koha::Plugin::Test::uninstall"; -- 2.15.1 (Apple Git-101)