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;
35 Koha::Plugins::Base - Base Module for plugins
40 my ( $class, $args ) = @_;
42 return unless ( C4::Context->config("enable_plugins") || $args->{'enable_plugins'} );
44 $args->{'class'} = $class;
45 $args->{'template'} = Template->new( { ABSOLUTE => 1, ENCODING => 'UTF-8' } );
47 my $self = bless( $args, $class );
49 my $plugin_version = $self->get_metadata->{version};
50 my $database_version = $self->retrieve_data('__INSTALLED_VERSION__') || 0;
52 ## Run the installation method if it exists and hasn't been run before
53 if ( $self->can('install') && !$self->retrieve_data('__INSTALLED__') ) {
55 if ( $self->install() ) {
56 $self->store_data( { '__INSTALLED__' => 1, '__ENABLED__' => 1 } );
57 if ( my $version = $plugin_version ) {
58 $self->store_data({ '__INSTALLED_VERSION__' => $version });
61 warn "Plugin $class failed during installation!";
65 Koha::Exceptions::Plugin::InstallDied->throw( plugin_class => $class );
67 } elsif ( $self->can('upgrade') ) {
68 if ( _version_compare( $plugin_version, $database_version ) == 1 ) {
70 if ( $self->upgrade() ) {
71 $self->store_data({ '__INSTALLED_VERSION__' => $plugin_version });
73 warn "Plugin $class failed during upgrade!";
77 Koha::Exceptions::Plugin::UpgradeDied->throw( plugin_class => $class );
80 } elsif ( $plugin_version ne $database_version ) {
81 $self->store_data({ '__INSTALLED_VERSION__' => $plugin_version });
84 $self->{_bundle_path} = abs_path($self->mbf_dir);
91 store_data allows a plugin to store key value pairs in the database for future use.
93 usage: $self->store_data({ param1 => 'param1val', param2 => 'param2value' })
98 my ( $self, $data ) = @_;
100 my $dbh = C4::Context->dbh;
101 my $sql = "REPLACE INTO plugin_data SET plugin_class = ?, plugin_key = ?, plugin_value = ?";
102 my $sth = $dbh->prepare($sql);
104 foreach my $key ( keys %$data ) {
105 $sth->execute( $self->{'class'}, $key, $data->{$key} );
111 retrieve_data allows a plugin to read the values that were previously saved with store_data
113 usage: my $value = $self->retrieve_data( $key );
118 my ( $self, $key ) = @_;
120 my $dbh = C4::Context->dbh;
121 my $sql = "SELECT plugin_value FROM plugin_data WHERE plugin_class = ? AND plugin_key = ?";
122 my $sth = $dbh->prepare($sql);
123 $sth->execute( $self->{'class'}, $key );
124 my $row = $sth->fetchrow_hashref();
126 return $row->{'plugin_value'};
131 get_template returns a Template object. Eventually this will probably be calling
132 C4:Template, but at the moment, it does not.
134 The returned template contains 3 variables that can be used in the plugin
141 The name of the plugin class.
145 Then name of the plugin method used. For example 'tool' or 'report'.
149 The URL path to the plugin. It can be used in templates in order to localize
150 ressources like images in html tags, or other templates.
154 The absolute pathname to the plugin directory. Necessary to include other
155 templates from a template with the [% INCLUDE %] directive.
163 my ( $self, $args ) = @_;
167 my $template_name = $args->{'file'} // '';
168 # if not absolute, call mbf_path, which dies if file does not exist
169 $template_name = $self->mbf_path( $template_name )
170 if $template_name !~ m/^\//;
171 my ( $template, $loggedinuser, $cookie ) = C4::Auth::get_template_and_user(
172 { template_name => $template_name,
173 query => $self->{'cgi'},
175 authnotrequired => 1,
179 CLASS => $self->{'class'},
180 METHOD => scalar $self->{'cgi'}->param('method'),
181 PLUGIN_PATH => $self->get_plugin_http_path(),
182 PLUGIN_DIR => $self->bundle_path(),
183 LANG => C4::Languages::getlanguage($self->{'cgi'}),
190 my ( $self, $args ) = @_;
192 #FIXME: Why another encoding issue? For metadata containing non latin characters.
193 my $metadata = $self->{metadata};
194 defined($metadata->{$_}) && utf8::decode($metadata->{$_}) for keys %$metadata;
198 =head2 get_qualified_table_name
200 To avoid naming conflict, each plugins tables should use a fully qualified namespace.
201 To avoid hardcoding and make plugins more flexible, this method will return the proper
202 fully qualified table name.
204 usage: my $table = $self->get_qualified_table_name( 'myTable' );
208 sub get_qualified_table_name {
209 my ( $self, $table_name ) = @_;
211 return lc( join( '_', split( '::', $self->{'class'} ), $table_name ) );
214 =head2 get_plugin_http_path
216 To access a plugin's own resources ( images, js files, css files, etc... )
217 a plugin will need to know what path to use in the template files. This
218 method returns that path.
220 usage: my $path = $self->get_plugin_http_path();
224 sub get_plugin_http_path {
227 return "/plugin/" . join( '/', split( '::', $self->{'class'} ) );
232 go_home is a quick redirect to the Koha plugins home page
237 my ( $self, $params ) = @_;
239 print $self->{'cgi'}->redirect("/cgi-bin/koha/plugins/plugins-home.pl");
244 $self->output_html( $data, $status, $extra_options );
246 Outputs $data setting the right headers for HTML content.
248 Note: this is a wrapper function for C4::Output::output_with_http_headers
253 my ( $self, $data, $status, $extra_options ) = @_;
254 output_with_http_headers( $self->{cgi}, undef, $data, 'html', $status, $extra_options );
259 my $bundle_path = $self->bundle_path
261 Returns the directory in which bundled files are.
268 return $self->{_bundle_path};
273 $self->output( $data, $content_type[, $status[, $extra_options]]);
275 Outputs $data with the appropriate HTTP headers,
276 the authentication cookie and a Content-Type specified in
279 $content_type is one of the following: 'html', 'js', 'json', 'xml', 'rss', or 'atom'.
281 $status is an HTTP status message, like '403 Authentication Required'. It defaults to '200 OK'.
283 $extra_options is hashref. If the key 'force_no_caching' is present and has
284 a true value, the HTTP headers include directives to force there to be no
287 Note: this is a wrapper function for C4::Output::output_with_http_headers
292 my ( $self, $data, $content_type, $status, $extra_options ) = @_;
293 output_with_http_headers( $self->{cgi}, undef, $data, $content_type, $status, $extra_options );
296 =head2 _version_compare
298 Utility method to compare two version numbers.
299 Returns 1 if the first argument is the higher version
300 Returns -1 if the first argument is the lower version
301 Returns 0 if both versions are equal
303 if ( _version_compare( '2.6.26', '2.6.0' ) == 1 ) {
304 print "2.6.26 is greater than 2.6.0\n";
309 sub _version_compare {
312 if ( $args[0]->isa('Koha::Plugins::Base') ) {
316 my $ver1 = shift @args || 0;
317 my $ver2 = shift @args || 0;
319 my @v1 = split /[.+:~-]/, $ver1;
320 my @v2 = split /[.+:~-]/, $ver2;
322 for ( my $i = 0 ; $i < max( scalar(@v1), scalar(@v2) ) ; $i++ ) {
324 # Add missing version parts if one string is shorter than the other
325 # i.e. 0 should be lt 0.2.1 and not equal, so we append .0
326 # 0.0.0 <=> 0.2.1 = -1
327 push( @v1, 0 ) unless defined( $v1[$i] );
328 push( @v2, 0 ) unless defined( $v2[$i] );
329 if ( int( $v1[$i] ) > int( $v2[$i] ) ) {
332 elsif ( int( $v1[$i] ) < int( $v2[$i] ) ) {
341 Method that returns wether the plugin is enabled or not
350 return $self->retrieve_data( '__ENABLED__' );
355 Method for enabling plugin
364 $self->store_data( {'__ENABLED__' => 1} );
371 Method for disabling plugin
380 $self->store_data( {'__ENABLED__' => 0} );
390 Kyle M Hall <kyle.m.hall@gmail.com>