From 6ffb826b52613014b682cbdb7a0a07775b016f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tadeusz=20=E2=80=9Etadzik=E2=80=9D=20So=C5=9Bnierz?= Date: Thu, 15 Jan 2026 16:44:38 +0100 Subject: [PATCH] Bug 41651: Allow plugins do specify dependencies, abort installation if unmet This approach uses a new $metadata key, to minimize the scope of changes needed. The downside of that is the fact that if the base plugin package requires those dependencies in compile-time already, the code (and thus metadata itself) will fail to load in an unhandled way. plugins-upload.pl can't know for sure whether it's really the plugin being installed that doesn't have its dependencies met, which is a limitation to how it currently works. --- Koha/Plugins.pm | 32 +++++++++++++++++-- Koha/Plugins/Base.pm | 8 +++-- .../prog/en/modules/plugins/plugins-upload.tt | 4 +++ plugins/plugins-upload.pl | 10 +++++- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/Koha/Plugins.pm b/Koha/Plugins.pm index ee5ddde144..e34c9c599f 100644 --- a/Koha/Plugins.pm +++ b/Koha/Plugins.pm @@ -28,6 +28,7 @@ use Module::Pluggable search_path => ['Koha::Plugin'], except => qr/::Edifact(|::Line|::Message|::Order|::Segment|::Transport)$/; use Try::Tiny; use POSIX qw(getpid); +use version 0.77 qw(is_lax); use C4::Context; use C4::Output; @@ -306,6 +307,15 @@ sub InstallPlugins { my @plugin_classes = $self->plugins(); my @plugins; + my $fail = sub { + my $message = shift; + if ( $params->{fatal} ) { + die $message; + } else { + warn $message; + } + }; + Koha::Exceptions::BadParameter->throw("Only one of 'include' and 'exclude' can be passed") if ( $params->{exclude} && $params->{include} ); @@ -336,7 +346,7 @@ sub InstallPlugins { } } - foreach my $plugin_class (@plugin_classes) { + PLUGINS: foreach my $plugin_class (@plugin_classes) { if ( can_load( modules => { $plugin_class => undef }, verbose => $verbose, nocache => 1 ) ) { next unless $plugin_class->isa('Koha::Plugins::Base'); @@ -346,12 +356,30 @@ sub InstallPlugins { try { $plugin = $plugin_class->new( { enable_plugins => $self->{'enable_plugins'} } ); } catch { - warn "$_"; + $fail->("$_"); $failed_instantiation = 1; }; next if $failed_instantiation; + my $dependencies = $plugin->get_metadata->{requires}; + for my $module (keys %$dependencies) { + my $version_requirement = $dependencies->{$module}; + unless (is_lax($version_requirement)) { + $fail->("The dependency of $plugin_class, $module has an invalid version requirement: " . + "$version_requirement. Skipping"); + next PLUGINS; + } + + # https://metacpan.org/pod/CPAN::Meta::Spec#Comparing-Version-Numbers + unless ( eval "use $module $version_requirement (); 1" ) { ## no critic 'ProhibitStringyEval' + # empty string for '0' (any version) + my $version_description = $version_requirement ? " version $version_requirement" : ''; + $fail->("Dependency of $plugin_class, $module$version_description is not installed. Skipping"); + next PLUGINS; + } + } + Koha::Plugins::Methods->search( { plugin_class => $plugin_class } )->delete(); foreach my $method ( @{ Class::Inspector->methods( $plugin_class, 'public' ) } ) { diff --git a/Koha/Plugins/Base.pm b/Koha/Plugins/Base.pm index 011edc296f..7ba45ef19c 100644 --- a/Koha/Plugins/Base.pm +++ b/Koha/Plugins/Base.pm @@ -206,9 +206,13 @@ Missing POD for get_metadata. sub get_metadata { my ( $self, $args ) = @_; - #FIXME: Why another encoding issue? For metadata containing non latin characters. my $metadata = $self->{metadata}; - defined( $metadata->{$_} ) && utf8::decode( $metadata->{$_} ) for keys %$metadata; + #FIXME: Why another encoding issue? For metadata containing non latin characters. + for (keys %$metadata) { + if (defined( $metadata->{$_} ) && ref $metadata->{$_} eq '') { + utf8::decode( $metadata->{$_} ); + } + } return $metadata; } diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-upload.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-upload.tt index 63916b2d6f..5170463c32 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-upload.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-upload.tt @@ -55,6 +55,10 @@
  • Cannot install plugin from unknown source whilst plugins_restricted is enabled.
  • + [% ELSIF ( ERROR.INSTALLFAIL ) %] +
  • Plugin installation failed:
    [% ERROR.INSTALLFAIL | html %]
  • [% ELSE %]
  • [% ERROR.CORERR | html %] An unknown error has occurred.
    Please review the error log for more details.
  • new->InstallPlugins( { verbose => 0 } ); + try { + Koha::Plugins->new->InstallPlugins( { verbose => 0, fatal => 1 } ); + } catch { + $errors{'INSTALLFAIL'} = $_; + $template->param( ERRORS => [ \%errors ] ); + output_html_with_http_headers $input, $cookie, $template->output; + exit; + }; } } elsif ( ( $op eq 'cud-Upload' ) && !$uploadfile && !$uploadlocation ) { warn "Problem uploading file or no file uploaded."; -- 2.51.0