1 package Koha::Plugins::Base;
3 # Copyright 2012 Kyle Hall
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 use Cwd qw( abs_path );
23 use List::Util qw( max );
26 use base qw{Module::Bundled::Files};
29 use C4::Output qw( output_with_http_headers );
31 use Koha::Exceptions::Plugin;
32 use Koha::Cache::Memory::Lite;
36 Koha::Plugins::Base - Base Module for plugins
41 my ( $class, $args ) = @_;
43 return unless ( C4::Context->config("enable_plugins") || $args->{'enable_plugins'} );
45 $args->{'class'} = $class;
46 $args->{'template'} = Template->new( { ABSOLUTE => 1, ENCODING => 'UTF-8' } );
48 my $self = bless( $args, $class );
50 my $plugin_version = $self->get_metadata->{version};
51 my $database_version = $self->retrieve_data('__INSTALLED_VERSION__') || 0;
53 ## Run the installation method if it exists and hasn't been run before
54 if ( $self->can('install') && !$self->retrieve_data('__INSTALLED__') ) {
56 if ( $self->install() ) {
57 $self->store_data( { '__INSTALLED__' => 1, '__ENABLED__' => 1 } );
58 if ( my $version = $plugin_version ) {
59 $self->store_data({ '__INSTALLED_VERSION__' => $version });
62 warn "Plugin $class failed during installation!";
66 Koha::Exceptions::Plugin::InstallDied->throw( plugin_class => $class );
68 } elsif ( $self->can('upgrade') ) {
69 if ( _version_compare( $plugin_version, $database_version ) == 1 ) {
71 if ( $self->upgrade() ) {
72 $self->store_data({ '__INSTALLED_VERSION__' => $plugin_version });
74 warn "Plugin $class failed during upgrade!";
78 Koha::Exceptions::Plugin::UpgradeDied->throw( plugin_class => $class );
81 } elsif ( $plugin_version ne $database_version ) {
82 $self->store_data({ '__INSTALLED_VERSION__' => $plugin_version });
85 $self->{_bundle_path} = abs_path($self->mbf_dir);
92 store_data allows a plugin to store key value pairs in the database for future use.
94 usage: $self->store_data({ param1 => 'param1val', param2 => 'param2value' })
99 my ( $self, $data ) = @_;
101 my $dbh = C4::Context->dbh;
102 my $sql = "REPLACE INTO plugin_data SET plugin_class = ?, plugin_key = ?, plugin_value = ?";
103 my $sth = $dbh->prepare($sql);
105 foreach my $key ( keys %$data ) {
106 $sth->execute( $self->{'class'}, $key, $data->{$key} );
109 if (exists $data->{__ENABLED__}) {
110 Koha::Cache::Memory::Lite->clear_from_cache(Koha::Plugins->ENABLED_PLUGINS_CACHE_KEY);
116 retrieve_data allows a plugin to read the values that were previously saved with store_data
118 usage: my $value = $self->retrieve_data( $key );
123 my ( $self, $key ) = @_;
125 my $dbh = C4::Context->dbh;
126 my $sql = "SELECT plugin_value FROM plugin_data WHERE plugin_class = ? AND plugin_key = ?";
127 my $sth = $dbh->prepare($sql);
128 $sth->execute( $self->{'class'}, $key );
129 my $row = $sth->fetchrow_hashref();
131 return $row->{'plugin_value'};
136 get_template returns a Template object. Eventually this will probably be calling
137 C4:Template, but at the moment, it does not.
139 The returned template contains 3 variables that can be used in the plugin
146 The name of the plugin class.
150 Then name of the plugin method used. For example 'tool' or 'report'.
154 The URL path to the plugin. It can be used in templates in order to localize
155 ressources like images in html tags, or other templates.
159 The absolute pathname to the plugin directory. Necessary to include other
160 templates from a template with the [% INCLUDE %] directive.
168 my ( $self, $args ) = @_;
172 my $template_name = $args->{'file'} // '';
173 # if not absolute, call mbf_path, which dies if file does not exist
174 $template_name = $self->mbf_path( $template_name )
175 if $template_name !~ m/^\//;
176 my ( $template, $loggedinuser, $cookie ) = C4::Auth::get_template_and_user(
177 { template_name => $template_name,
178 query => $self->{'cgi'},
180 authnotrequired => 1,
184 CLASS => $self->{'class'},
185 METHOD => scalar $self->{'cgi'}->param('method'),
186 PLUGIN_PATH => $self->get_plugin_http_path(),
187 PLUGIN_DIR => $self->bundle_path(),
188 LANG => C4::Languages::getlanguage($self->{'cgi'}),
195 my ( $self, $args ) = @_;
197 #FIXME: Why another encoding issue? For metadata containing non latin characters.
198 my $metadata = $self->{metadata};
199 defined($metadata->{$_}) && utf8::decode($metadata->{$_}) for keys %$metadata;
203 =head2 get_qualified_table_name
205 To avoid naming conflict, each plugins tables should use a fully qualified namespace.
206 To avoid hardcoding and make plugins more flexible, this method will return the proper
207 fully qualified table name.
209 usage: my $table = $self->get_qualified_table_name( 'myTable' );
213 sub get_qualified_table_name {
214 my ( $self, $table_name ) = @_;
216 return lc( join( '_', split( '::', $self->{'class'} ), $table_name ) );
219 =head2 get_plugin_http_path
221 To access a plugin's own resources ( images, js files, css files, etc... )
222 a plugin will need to know what path to use in the template files. This
223 method returns that path.
225 usage: my $path = $self->get_plugin_http_path();
229 sub get_plugin_http_path {
232 return "/plugin/" . join( '/', split( '::', $self->{'class'} ) );
237 go_home is a quick redirect to the Koha plugins home page
242 my ( $self, $params ) = @_;
244 print $self->{'cgi'}->redirect("/cgi-bin/koha/plugins/plugins-home.pl");
249 $self->output_html( $data, $status, $extra_options );
251 Outputs $data setting the right headers for HTML content.
253 Note: this is a wrapper function for C4::Output::output_with_http_headers
258 my ( $self, $data, $status, $extra_options ) = @_;
259 output_with_http_headers( $self->{cgi}, undef, $data, 'html', $status, $extra_options );
264 my $bundle_path = $self->bundle_path
266 Returns the directory in which bundled files are.
273 return $self->{_bundle_path};
278 $self->output( $data, $content_type[, $status[, $extra_options]]);
280 Outputs $data with the appropriate HTTP headers,
281 the authentication cookie and a Content-Type specified in
284 $content_type is one of the following: 'html', 'js', 'json', 'xml', 'rss', or 'atom'.
286 $status is an HTTP status message, like '403 Authentication Required'. It defaults to '200 OK'.
288 $extra_options is hashref. If the key 'force_no_caching' is present and has
289 a true value, the HTTP headers include directives to force there to be no
292 Note: this is a wrapper function for C4::Output::output_with_http_headers
297 my ( $self, $data, $content_type, $status, $extra_options ) = @_;
298 output_with_http_headers( $self->{cgi}, undef, $data, $content_type, $status, $extra_options );
301 =head2 _version_compare
303 Utility method to compare two version numbers.
304 Returns 1 if the first argument is the higher version
305 Returns -1 if the first argument is the lower version
306 Returns 0 if both versions are equal
308 if ( _version_compare( '2.6.26', '2.6.0' ) == 1 ) {
309 print "2.6.26 is greater than 2.6.0\n";
314 sub _version_compare {
317 if ( $args[0]->isa('Koha::Plugins::Base') ) {
321 my $ver1 = shift @args || 0;
322 my $ver2 = shift @args || 0;
324 my @v1 = split /[.+:~-]/, $ver1;
325 my @v2 = split /[.+:~-]/, $ver2;
327 for ( my $i = 0 ; $i < max( scalar(@v1), scalar(@v2) ) ; $i++ ) {
329 # Add missing version parts if one string is shorter than the other
330 # i.e. 0 should be lt 0.2.1 and not equal, so we append .0
331 # 0.0.0 <=> 0.2.1 = -1
332 push( @v1, 0 ) unless defined( $v1[$i] );
333 push( @v2, 0 ) unless defined( $v2[$i] );
335 # Strip letters before comparing, supresses 'Argument "v1" isn't numeric in int' warning
339 if ( int( $v1[$i] ) > int( $v2[$i] ) ) {
342 elsif ( int( $v1[$i] ) < int( $v2[$i] ) ) {
351 Method that returns wether the plugin is enabled or not
360 return $self->retrieve_data( '__ENABLED__' );
365 Method for enabling plugin
374 $self->store_data( {'__ENABLED__' => 1} );
381 Method for disabling plugin
390 $self->store_data( {'__ENABLED__' => 0} );
400 Kyle M Hall <kyle.m.hall@gmail.com>