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 );
25 use base qw{Module::Bundled::Files};
28 use C4::Output qw( output_with_http_headers );
32 Koha::Plugins::Base - Base Module for plugins
37 my ( $class, $args ) = @_;
39 return unless ( C4::Context->config("enable_plugins") || $args->{'enable_plugins'} );
41 $args->{'class'} = $class;
42 $args->{'template'} = Template->new( { ABSOLUTE => 1, ENCODING => 'UTF-8' } );
44 my $self = bless( $args, $class );
46 my $plugin_version = $self->get_metadata->{version};
47 my $database_version = $self->retrieve_data('__INSTALLED_VERSION__') || 0;
49 ## Run the installation method if it exists and hasn't been run before
50 if ( $self->can('install') && !$self->retrieve_data('__INSTALLED__') ) {
51 if ( $self->install() ) {
52 $self->store_data( { '__INSTALLED__' => 1, '__ENABLED__' => 1 } );
53 if ( my $version = $plugin_version ) {
54 $self->store_data({ '__INSTALLED_VERSION__' => $version });
57 warn "Plugin $class failed during installation!";
59 } elsif ( $self->can('upgrade') ) {
60 if ( _version_compare( $plugin_version, $database_version ) == 1 ) {
61 if ( $self->upgrade() ) {
62 $self->store_data({ '__INSTALLED_VERSION__' => $plugin_version });
64 warn "Plugin $class failed during upgrade!";
67 } elsif ( $plugin_version ne $database_version ) {
68 $self->store_data({ '__INSTALLED_VERSION__' => $plugin_version });
71 $self->{_bundle_path} = abs_path($self->mbf_dir);
78 store_data allows a plugin to store key value pairs in the database for future use.
80 usage: $self->store_data({ param1 => 'param1val', param2 => 'param2value' })
85 my ( $self, $data ) = @_;
87 my $dbh = C4::Context->dbh;
88 my $sql = "REPLACE INTO plugin_data SET plugin_class = ?, plugin_key = ?, plugin_value = ?";
89 my $sth = $dbh->prepare($sql);
91 foreach my $key ( keys %$data ) {
92 $sth->execute( $self->{'class'}, $key, $data->{$key} );
98 retrieve_data allows a plugin to read the values that were previously saved with store_data
100 usage: my $value = $self->retrieve_data( $key );
105 my ( $self, $key ) = @_;
107 my $dbh = C4::Context->dbh;
108 my $sql = "SELECT plugin_value FROM plugin_data WHERE plugin_class = ? AND plugin_key = ?";
109 my $sth = $dbh->prepare($sql);
110 $sth->execute( $self->{'class'}, $key );
111 my $row = $sth->fetchrow_hashref();
113 return $row->{'plugin_value'};
118 get_template returns a Template object. Eventually this will probably be calling
119 C4:Template, but at the moment, it does not.
121 The returned template contains 3 variables that can be used in the plugin
128 The name of the plugin class.
132 Then name of the plugin method used. For example 'tool' or 'report'.
136 The URL path to the plugin. It can be used in templates in order to localize
137 ressources like images in html tags, or other templates.
141 The absolute pathname to the plugin directory. Necessary to include other
142 templates from a template with the [% INCLUDE %] directive.
150 my ( $self, $args ) = @_;
154 my $template_name = $args->{'file'} // '';
155 # if not absolute, call mbf_path, which dies if file does not exist
156 $template_name = $self->mbf_path( $template_name )
157 if $template_name !~ m/^\//;
158 my ( $template, $loggedinuser, $cookie ) = C4::Auth::get_template_and_user(
159 { template_name => $template_name,
160 query => $self->{'cgi'},
162 authnotrequired => 1,
166 CLASS => $self->{'class'},
167 METHOD => scalar $self->{'cgi'}->param('method'),
168 PLUGIN_PATH => $self->get_plugin_http_path(),
169 PLUGIN_DIR => $self->bundle_path(),
170 LANG => C4::Languages::getlanguage($self->{'cgi'}),
177 my ( $self, $args ) = @_;
179 #FIXME: Why another encoding issue? For metadata containing non latin characters.
180 my $metadata = $self->{metadata};
181 defined($metadata->{$_}) && utf8::decode($metadata->{$_}) for keys %$metadata;
185 =head2 get_qualified_table_name
187 To avoid naming conflict, each plugins tables should use a fully qualified namespace.
188 To avoid hardcoding and make plugins more flexible, this method will return the proper
189 fully qualified table name.
191 usage: my $table = $self->get_qualified_table_name( 'myTable' );
195 sub get_qualified_table_name {
196 my ( $self, $table_name ) = @_;
198 return lc( join( '_', split( '::', $self->{'class'} ), $table_name ) );
201 =head2 get_plugin_http_path
203 To access a plugin's own resources ( images, js files, css files, etc... )
204 a plugin will need to know what path to use in the template files. This
205 method returns that path.
207 usage: my $path = $self->get_plugin_http_path();
211 sub get_plugin_http_path {
214 return "/plugin/" . join( '/', split( '::', $self->{'class'} ) );
219 go_home is a quick redirect to the Koha plugins home page
224 my ( $self, $params ) = @_;
226 print $self->{'cgi'}->redirect("/cgi-bin/koha/plugins/plugins-home.pl");
231 $self->output_html( $data, $status, $extra_options );
233 Outputs $data setting the right headers for HTML content.
235 Note: this is a wrapper function for C4::Output::output_with_http_headers
240 my ( $self, $data, $status, $extra_options ) = @_;
241 output_with_http_headers( $self->{cgi}, undef, $data, 'html', $status, $extra_options );
246 my $bundle_path = $self->bundle_path
248 Returns the directory in which bundled files are.
255 return $self->{_bundle_path};
260 $self->output( $data, $content_type[, $status[, $extra_options]]);
262 Outputs $data with the appropriate HTTP headers,
263 the authentication cookie and a Content-Type specified in
266 $content_type is one of the following: 'html', 'js', 'json', 'xml', 'rss', or 'atom'.
268 $status is an HTTP status message, like '403 Authentication Required'. It defaults to '200 OK'.
270 $extra_options is hashref. If the key 'force_no_caching' is present and has
271 a true value, the HTTP headers include directives to force there to be no
274 Note: this is a wrapper function for C4::Output::output_with_http_headers
279 my ( $self, $data, $content_type, $status, $extra_options ) = @_;
280 output_with_http_headers( $self->{cgi}, undef, $data, $content_type, $status, $extra_options );
283 =head2 _version_compare
285 Utility method to compare two version numbers.
286 Returns 1 if the first argument is the higher version
287 Returns -1 if the first argument is the lower version
288 Returns 0 if both versions are equal
290 if ( _version_compare( '2.6.26', '2.6.0' ) == 1 ) {
291 print "2.6.26 is greater than 2.6.0\n";
296 sub _version_compare {
299 if ( $args[0]->isa('Koha::Plugins::Base') ) {
303 my $ver1 = shift @args || 0;
304 my $ver2 = shift @args || 0;
306 my @v1 = split /[.+:~-]/, $ver1;
307 my @v2 = split /[.+:~-]/, $ver2;
309 for ( my $i = 0 ; $i < max( scalar(@v1), scalar(@v2) ) ; $i++ ) {
311 # Add missing version parts if one string is shorter than the other
312 # i.e. 0 should be lt 0.2.1 and not equal, so we append .0
313 # 0.0.0 <=> 0.2.1 = -1
314 push( @v1, 0 ) unless defined( $v1[$i] );
315 push( @v2, 0 ) unless defined( $v2[$i] );
316 if ( int( $v1[$i] ) > int( $v2[$i] ) ) {
319 elsif ( int( $v1[$i] ) < int( $v2[$i] ) ) {
328 Method that returns wether the plugin is enabled or not
337 return $self->retrieve_data( '__ENABLED__' );
342 Method for enabling plugin
351 $self->store_data( {'__ENABLED__' => 1} );
358 Method for disabling plugin
367 $self->store_data( {'__ENABLED__' => 0} );
377 Kyle M Hall <kyle.m.hall@gmail.com>