Browse Source

Bug 7804 - Add Koha Plugin System

Adds support for custom plugins. At the moment the Plugins
feature supports two types of plugins, reports and tools.

Plugins are installed by uploading KPZ ( Koha Plugin Zip )
packages. A KPZ file is just a zip file containing the
perl files, template files, and any other files neccessary
to make the plugin work.

Test plan:
1) Apply patch
2) Run updatedatabase.pl
3) Create the directory /var/lib/koha/plugins
4) Add the lines
      <pluginsdir>/var/lib/koha/plugins</pluginsdir>
      <enable_plugins>1</enable_plugins>"
   to your koha-conf.xml file
5) Add the line
       Alias /plugin/ "/var/lib/koha/plugins/"
   to your koha-httpd.conf file
6) Restart your webserver
7) Access the plugins system from the "More" pulldown
8) Upload the example plugin file provided here
9) Try it out!

Signed-off-by: Bernardo Gonzalez Kriegel <bgkriegel@gmail.com>

Signed-off-by: Jonathan Druart <jonathan.druart@biblibre.com>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Jared Camins-Esakov <jcamins@cpbibliography.com>
3.12.x
Kyle M Hall 14 years ago
committed by Jared Camins-Esakov
parent
commit
5eabc672fd
  1. 4
      C4/Auth.pm
  2. 40
      C4/Installer/PerlDependencies.pm
  3. 4
      C4/Templates.pm
  4. 87
      Koha/Plugins.pm
  5. 190
      Koha/Plugins/Base.pm
  6. 100
      Koha/Plugins/Handler.pm
  7. 10
      Makefile.PL
  8. 2
      debian/templates/koha-conf-site.xml.in
  9. 1
      etc/koha-conf.xml
  10. 1
      etc/koha-httpd.conf
  11. 11
      installer/data/mysql/kohastructure.sql
  12. 1
      installer/data/mysql/sysprefs.sql
  13. 24
      installer/data/mysql/updatedatabase.pl
  14. 3
      koha-tmpl/intranet-tmpl/prog/en/includes/header.inc
  15. 7
      koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref
  16. 116
      koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-home.tt
  17. 55
      koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-upload.tt
  18. 11
      koha-tmpl/intranet-tmpl/prog/en/modules/reports/reports-home.tt
  19. 5
      koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
  20. 57
      plugins/plugins-home.pl
  21. 52
      plugins/plugins-uninstall.pl
  22. 98
      plugins/plugins-upload.pl
  23. 50
      plugins/run.pl
  24. 3
      rewrite-config.PL
  25. 1
      skel/var/lib/koha/plugins/README

4
C4/Auth.pm

@ -133,7 +133,7 @@ EOQ
sub get_template_and_user {
my $in = shift;
my $template =
C4::Templates::gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'} );
C4::Templates::gettemplate( $in->{'template_name'}, $in->{'type'}, $in->{'query'}, $in->{'is_plugin'} );
my ( $user, $cookie, $sessionID, $flags );
if ( $in->{'template_name'} !~m/maintenance/ ) {
( $user, $cookie, $sessionID, $flags ) = checkauth(
@ -202,6 +202,7 @@ sub get_template_and_user {
$template->param( CAN_user_serials => 1 );
$template->param( CAN_user_reports => 1 );
$template->param( CAN_user_staffaccess => 1 );
$template->param( CAN_user_plugins => 1 );
foreach my $module (keys %$all_perms) {
foreach my $subperm (keys %{ $all_perms->{$module} }) {
$template->param( "CAN_user_${module}_${subperm}" => 1 );
@ -366,6 +367,7 @@ sub get_template_and_user {
OPACLocalCoverImages => C4::Context->preference('OPACLocalCoverImages'),
AllowMultipleCovers => C4::Context->preference('AllowMultipleCovers'),
EnableBorrowerFiles => C4::Context->preference('EnableBorrowerFiles'),
UseKohaPlugins => C4::Context->preference('UseKohaPlugins'),
);
}
else {

40
C4/Installer/PerlDependencies.pm

@ -634,6 +634,46 @@ our $PERL_DEPS = {
'required' => '1',
'min_ver' => '0.22',
},
'File::Temp' => {
'usage' => 'Plugins',
'required' => '0',
'min_ver' => '0.22',
},
'File::Copy' => {
'usage' => 'Plugins',
'required' => '0',
'min_ver' => '2.08',
},
'File::Path' => {
'usage' => 'Plugins',
'required' => '0',
'min_ver' => '2.07',
},
'Archive::Extract' => {
'usage' => 'Plugins',
'required' => '0',
'min_ver' => '0.60',
},
'Archive::Zip' => {
'usage' => 'Plugins',
'required' => '0',
'min_ver' => '1.30',
},
'Module::Load::Conditional' => {
'usage' => 'Plugins',
'required' => '0',
'min_ver' => '0.38',
},
'Module::Bundled::Files' => {
'usage' => 'Plugins',
'required' => '0',
'min_ver' => '0.03',
},
'Module::Pluggable' => {
'usage' => 'Plugins',
'required' => '0',
'min_ver' => '3.9',
},
};
1;

4
C4/Templates.pm

@ -228,13 +228,15 @@ sub _get_template_file {
sub gettemplate {
my ( $tmplbase, $interface, $query ) = @_;
my ( $tmplbase, $interface, $query, $is_plugin ) = @_;
($query) or warn "no query in gettemplate";
my $path = C4::Context->preference('intranet_includes') || 'includes';
$tmplbase =~ s/\.tmpl$/.tt/;
my ($htdocs, $theme, $lang, $filename)
= _get_template_file($tmplbase, $interface, $query);
$filename = $tmplbase if ( $is_plugin );
my $template = C4::Templates->new($interface, $filename, $tmplbase, $query);
# NOTE: Commenting these out rather than deleting them so that those who need
# to know how we previously shimmed these directories will be able to understand.
# my $is_intranet = $interface eq 'intranet';

87
Koha/Plugins.pm

@ -0,0 +1,87 @@
package Koha::Plugins;
# Copyright 2012 Kyle Hall
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use Modern::Perl;
use Module::Load::Conditional qw(can_load);
use Module::Pluggable search_path => ['Koha::Plugin'];
use C4::Context;
use C4::Output;
BEGIN {
die('Plugins not enabled in config') unless ( C4::Context->config("enable_plugins") );
push @INC, C4::Context->config("pluginsdir");
}
=head1 NAME
Koha::Plugins - Module for loading and managing plugins.
=cut
sub new {
my ( $class, $args ) = @_;
$args->{'pluginsdir'} = C4::Context->config("pluginsdir");
return bless( $args, $class );
}
=head2 GetPlugins()
This will return a list of all the available plugins of the passed type.
Usage: my @plugins = C4::Plugins::GetPlugins( $method );
At the moment, the available types are 'report' and 'tool'.
=cut
sub GetPlugins {
my $self = shift;
my $method = shift;
my @plugin_classes = $self->plugins();
my @plugins;
foreach my $plugin_class (@plugin_classes) {
if ( can_load( modules => { $plugin_class => undef } ) ) {
my $plugin = $plugin_class->new();
if ($method) {
if ( $plugin->can($method) ) {
push( @plugins, $plugin );
}
} else {
push( @plugins, $plugin );
}
}
}
return @plugins;
}
1;
__END__
=head1 AUTHOR
Kyle M Hall <kyle.m.hall@gmail.com>
=cut

190
Koha/Plugins/Base.pm

@ -0,0 +1,190 @@
package Koha::Plugins::Base;
# Copyright 2012 Kyle Hall
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use Modern::Perl;
use Module::Pluggable require => 1;
use base qw{Module::Bundled::Files};
use C4::Context;
use C4::Auth;
BEGIN {
die('Plugins not enabled in config') unless ( C4::Context->config("enable_plugins") );
push @INC, C4::Context->config("pluginsdir");
}
=head1 NAME
C4::Plugins::Base - Base Module for plugins
=cut
sub new {
my ( $class, $args ) = @_;
$args->{'class'} = $class;
$args->{'template'} = Template->new( { ABSOLUTE => 1 } );
my $self = bless( $args, $class );
## 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 } );
} else {
warn "Plugin $class failed during installation!";
}
}
return $self;
}
=head2 store_data
set_data allows a plugin to store key value pairs in the database for future use.
usage: $self->set_data({ param1 => 'param1val', param2 => 'param2value' })
=cut
sub store_data {
my ( $self, $data ) = @_;
my $dbh = C4::Context->dbh;
my $sql = "REPLACE INTO plugin_data SET plugin_class = ?, plugin_key = ?, plugin_value = ?";
my $sth = $dbh->prepare($sql);
foreach my $key ( keys %$data ) {
$sth->execute( $self->{'class'}, $key, $data->{$key} );
}
}
=head2 retrieve_data
retrieve_data allows a plugin to read the values that were previously saved with store_data
usage: my $value = $self->retrieve_data( $key );
=cut
sub retrieve_data {
my ( $self, $key ) = @_;
my $dbh = C4::Context->dbh;
my $sql = "SELECT plugin_value FROM plugin_data WHERE plugin_class = ? AND plugin_key = ?";
my $sth = $dbh->prepare($sql);
$sth->execute( $self->{'class'}, $key );
my $row = $sth->fetchrow_hashref();
return $row->{'plugin_value'};
}
=head2 get_template
get_template returns a Template object. Eventually this will probably be calling
C4:Template, but at the moment, it does not.
=cut
sub get_template {
my ( $self, $args ) = @_;
# my $template =
# C4::Templates->new( my $interface = 'intranet', my $filename = $self->mbf_path( $args->{'file'} ), my $tmplbase = '', my $query = $self->{'cgi'} );
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
{ template_name => $self->mbf_path( $args->{'file'} ),
query => $self->{'cgi'},
type => "intranet",
authnotrequired => 1,
# flagsrequired => { tools => '*' },
is_plugin => 1,
}
);
$template->param(
CLASS => $self->{'class'},
METHOD => $self->{'cgi'}->param('method'),
PLUGIN_PATH => $self->get_plugin_http_path(),
);
return $template;
}
sub get_metadata {
my ( $self, $args ) = @_;
return $self->{'metadata'};
}
=head2 get_qualified_table_name
To avoid naming conflict, each plugins tables should use a fully qualified namespace.
To avoid hardcoding and make plugins more flexible, this method will return the proper
fully qualified table name.
usage: my $table = $self->get_qualified_table_name( 'myTable' );
=cut
sub get_qualified_table_name {
my ( $self, $table_name ) = @_;
return lc( join( '_', split( '::', $self->{'class'} ), $table_name ) );
}
=head2 get_plugin_http_path
To access a plugin's own resources ( images, js files, css files, etc... )
a plugin will need to know what path to use in the template files. This
method returns that path.
usage: my $path = $self->get_plugin_http_path();
=cut
sub get_plugin_http_path {
my ($self) = @_;
return "/plugin/" . join( '/', split( '::', $self->{'class'} ) );
}
=head2 go_home
go_home is a quick redirect to the Koha plugins home page
=cut
sub go_home {
my ( $self, $params ) = @_;
print $self->{'cgi'}->redirect("/cgi-bin/koha/plugins/plugins-home.pl");
}
1;
__END__
=head1 AUTHOR
Kyle M Hall <kyle.m.hall@gmail.com>
=cut

100
Koha/Plugins/Handler.pm

@ -0,0 +1,100 @@
package Koha::Plugins::Handler;
# Copyright 2012 Kyle Hall
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use Modern::Perl;
use File::Path qw(remove_tree);
use Module::Load::Conditional qw(can_load);
use C4::Context;
BEGIN {
die('Plugins not enabled in config') unless ( C4::Context->config("enable_plugins") );
push @INC, C4::Context->config("pluginsdir");
}
=head1 NAME
C4::Plugins::Handler - Handler Module for running plugins
=head1 SYNOPSIS
Koha::Plugins::Handler->run({ class => $class, method => $method, cgi => $cgi });
$p->run();
=over 2
=cut
=item run
Runs a plugin
=cut
sub run {
my ( $class, $args ) = @_;
my $plugin_class = $args->{'class'};
my $plugin_method = $args->{'method'};
my $cgi = $args->{'cgi'};
if ( can_load( modules => { $plugin_class => undef } ) ) {
my $plugin = $plugin_class->new( { cgi => $cgi } );
if ( $plugin->can($plugin_method) ) {
$plugin->$plugin_method();
} else {
warn "Plugin does not have method $plugin_method";
}
} else {
warn "Plugin $plugin_class cannot be loaded";
}
}
=item delete
Deletes a plugin
=cut
sub delete {
my ( $class, $args ) = @_;
my $plugin_class = $args->{'class'};
my $plugin_dir = C4::Context->config("pluginsdir");
my $plugin_path = "$plugin_dir/" . join( '/', split( '::', $args->{'class'} ) );
Koha::Plugins::Handler->run( { class => $plugin_class, method => 'uninstall' } );
C4::Context->dbh->do( "DELETE FROM plugin_data WHERE plugin_class = ?", undef, ($plugin_class) );
unlink("$plugin_path.pm");
remove_tree($plugin_path);
}
1;
__END__
=back
=head1 AUTHOR
Kyle M Hall <kyle.m.hall@gmail.com>
=cut

10
Makefile.PL

@ -230,6 +230,10 @@ Directory for Apache and Zebra logs produced by Koha.
Directory for backup files produced by Koha.
=item PLUGINS_DIR
Directory for external Koha plugins.
=item PAZPAR2_CONF_DIR
Directory for PazPar2 configuration files.
@ -310,6 +314,7 @@ my $target_map = {
'./skel/var/lib/koha/zebradb/biblios/register' => { target => 'ZEBRA_DATA_DIR', trimdir => 6 },
'./skel/var/lib/koha/zebradb/biblios/shadow' => { target => 'ZEBRA_DATA_DIR', trimdir => 6 },
'./skel/var/lib/koha/zebradb/biblios/tmp' => { target => 'ZEBRA_DATA_DIR', trimdir => 6 },
'./skel/var/lib/koha/plugins' => { target => 'PLUGINS_DIR', trimdir => 6 },
'./sms' => 'INTRANET_CGI_DIR',
'./suggestion' => 'INTRANET_CGI_DIR',
'./svc' => 'INTRANET_CGI_DIR',
@ -1235,6 +1240,7 @@ sub get_target_directories {
$dirmap{'ZEBRA_LOCK_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'lock', 'zebradb');
$dirmap{'LOG_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'log');
$dirmap{'BACKUP_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'spool');
$dirmap{'PLUGINS_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'lib', 'koha', 'plugins');
$dirmap{'ZEBRA_DATA_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'lib', 'zebradb');
$dirmap{'ZEBRA_RUN_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'run', 'zebradb');
} elsif ($mode eq 'dev') {
@ -1265,6 +1271,7 @@ sub get_target_directories {
$dirmap{'ZEBRA_LOCK_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'lock', 'zebradb');
$dirmap{'LOG_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'log');
$dirmap{'BACKUP_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'spool');
$dirmap{'PLUGINS_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'lib', 'plugins');
$dirmap{'ZEBRA_DATA_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'lib', 'zebradb');
$dirmap{'ZEBRA_RUN_DIR'} = File::Spec->catdir(@basedir, $package, 'var', 'run', 'zebradb');
} else {
@ -1287,6 +1294,7 @@ sub get_target_directories {
$dirmap{'ZEBRA_LOCK_DIR'} = File::Spec->catdir(File::Spec->rootdir(), 'var', 'lock', $package, 'zebradb');
$dirmap{'LOG_DIR'} = File::Spec->catdir(File::Spec->rootdir(), 'var', 'log', $package);
$dirmap{'BACKUP_DIR'} = File::Spec->catdir(File::Spec->rootdir(), 'var', 'spool', $package);
$dirmap{'PLUGINS_DIR'} = File::Spec->catdir(File::Spec->rootdir(), 'var', 'lib', $package, 'plugins');
$dirmap{'ZEBRA_DATA_DIR'} = File::Spec->catdir(File::Spec->rootdir(), 'var', 'lib', $package, 'zebradb');
$dirmap{'ZEBRA_RUN_DIR'} = File::Spec->catdir(File::Spec->rootdir(), 'var', 'run', $package, 'zebradb');
}
@ -1560,7 +1568,7 @@ make_upgrade_backup ::
\t\$(NOECHO) umask 022; \$(MOD_BACKUP) \\
/;
foreach my $key (qw/KOHA_CONF_DIR INTRANET_TMPL_DIR INTRANET_WWW_DIR OPAC_TMPL_DIR OPAC_WWW_DIR
PAZPAR2_CONF_DIR ZEBRA_CONF_DIR/) {
PAZPAR2_CONF_DIR ZEBRA_CONF_DIR PLUGINS_DIR/) {
$upgrade .= "\t\t\$(KOHA_INST_$key) \$(KOHA_DEST_$key) \\\n"
unless ($config{'INSTALL_ZEBRA'} ne "yes" and $key =~ /ZEBRA/) or
exists $skip_directories->{$key} or

2
debian/templates/koha-conf-site.xml.in

@ -257,6 +257,8 @@
<biblioservershadow>1</biblioservershadow>
<authorityserver>authorities</authorityserver>
<authorityservershadow>1</authorityservershadow>
<pluginsdir>__PLUGINS_DIR__</pluginsdir>
<enable_plugins>0</enable_plugins>
<intranetdir>/usr/share/koha/intranet/cgi-bin</intranetdir>
<opacdir>/usr/share/koha/opac/cgi-bin/opac</opacdir>
<opachtdocs>/usr/share/koha/opac/htdocs/opac-tmpl</opachtdocs>

1
etc/koha-conf.xml

@ -276,6 +276,7 @@ __PAZPAR2_TOGGLE_XML_POST__
<biblioservershadow>1</biblioservershadow>
<authorityserver>authorities</authorityserver>
<authorityservershadow>1</authorityservershadow>
<pluginsdir>__PLUGINS_DIR__</pluginsdir>
<intranetdir>__INTRANET_CGI_DIR__</intranetdir>
<opacdir>__OPAC_CGI_DIR__/opac</opacdir>
<opachtdocs>__OPAC_TMPL_DIR__</opachtdocs>

1
etc/koha-httpd.conf

@ -105,6 +105,7 @@
ScriptAlias /cgi-bin/koha/ "__INTRANET_CGI_DIR__/"
ScriptAlias /index.html "__INTRANET_CGI_DIR__/mainpage.pl"
ScriptAlias /search "__INTRANET_CGI_DIR__/search.pl"
Alias /plugin/ "__PLUGINS_DIR__/"
ErrorLog __LOG_DIR__/koha-error_log
# TransferLog __LOG_DIR__/koha-access.log
SetEnv KOHA_CONF "__KOHA_CONF_DIR__/koha-conf.xml"

11
installer/data/mysql/kohastructure.sql

@ -3063,6 +3063,17 @@ CREATE TABLE linktracker (
KEY dateidx (timeclicked)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Table structure for table 'plugin_data'
--
CREATE TABLE IF NOT EXISTS plugin_data (
plugin_class varchar(255) NOT NULL,
plugin_key varchar(255) NOT NULL,
plugin_value text,
PRIMARY KEY (plugin_class,plugin_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;

1
installer/data/mysql/sysprefs.sql

@ -418,3 +418,4 @@ INSERT IGNORE INTO systempreferences (variable,value,explanation,options,type) V
INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('UseQueryParser', '0', 'If enabled, try to use QueryParser for queries.', NULL, 'YesNo');
INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('FinesIncludeGracePeriod','1','If enabled, fines calculations will include the grace period.',NULL,'YesNo');
INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('UNIMARCAuthorsFacetsSeparator',', ', 'UNIMARC authors facets separator', NULL, 'short');
INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('UseKohaPlugins','1','Enable or disable the ability to use Koha Plugins.','','YesNo');

24
installer/data/mysql/updatedatabase.pl

@ -6631,6 +6631,30 @@ if ( CheckVersion($DBversion) ) {
}
$DBversion = "3.11.00.XXX";
if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
$dbh->do("INSERT INTO userflags (bit, flag, flagdesc, defaulton) VALUES ('19', 'plugins', 'Koha plugins', '0')");
$dbh->do("INSERT INTO permissions (module_bit, code, description) VALUES
('19', 'manage', 'Manage plugins ( install / uninstall )'),
('19', 'tool', 'Use tool plugins'),
('19', 'report', 'Use report plugins'),
('19', 'configure', 'Configure plugins')
");
$dbh->do("INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('UseKohaPlugins','1','Enable or disable the ability to use Koha Plugins.','','YesNo')");
$dbh->do("
CREATE TABLE IF NOT EXISTS plugin_data (
plugin_class varchar(255) NOT NULL,
plugin_key varchar(255) NOT NULL,
plugin_value text,
PRIMARY KEY (plugin_class,plugin_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
");
print "Upgrade to $DBversion done (Bug 7804: Added plugin system.)\n";
SetVersion($DBversion);
}
=head1 FUNCTIONS
=head2 TableExists($table)

3
koha-tmpl/intranet-tmpl/prog/en/includes/header.inc

@ -30,6 +30,9 @@
[% IF ( CAN_user_tools ) %]
<li><a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a></li>
[% END %]
[% IF ( UseKohaPlugins && CAN_user_plugins ) %]
<li><a href="/cgi-bin/koha/plugins/plugins-home.pl">Plugins</a></li>
[% END %]
[% IF ( CAN_user_parameters ) %]
<li><a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a></li>
[% END %]

7
koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref

@ -319,3 +319,10 @@ Enhanced Content:
- pref: HTML5MediaExtensions
class: multi
- (separated with |).
Plugins:
-
- pref: UseKohaPlugins
choices:
yes: Enable
no: "Don't enable"
- the ability to use Koha Plugins. Note, the plugin system must also be enabled in the Koha configuration file to be fully enabled.

116
koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-home.tt

@ -0,0 +1,116 @@
[% USE KohaDates %]
[% INCLUDE 'doc-head-open.inc' %]
<title>Koha &rsaquo; Tools &rsaquo; Plugins </title>
[% INCLUDE 'doc-head-close.inc' %]
[% INCLUDE 'calendar.inc' %]
</head>
<body>
[% INCLUDE 'header.inc' %]
[% INCLUDE 'circ-search.inc' %]
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a>
&rsaquo; Plugins
</div>
<div id="doc3" class="yui-t1">
<div id="bd">
<div id="yui-main">
<div class="yui-b">
<div class="details">
<h1>Plugins</h1>
[% UNLESS ( plugins ) %]
[% UNLESS ( method ) %]
<h3>No plugins installed</h3>
[% ELSE %]
[% IF method == 'tool' %]
<h3>No plugins that can be used as a tool are installed</h3>
[% ELSIF method == 'report' %]
<h3>No plugins that can create a report are installed</h3>
[% ELSE %]
<h3>Unknown plugin type <i>[% method %]</i>
[% END %]
[% END %]
[% ELSE %]
<table>
<tr>
<th>Name</th>
<th>&nbsp;</th>
<th>Description</th>
<th>Author</th>
<th>Plugin Version</th>
<th>Minimum Koha Version</th>
<th>Maximum Koha Version</th>
<th>Last Updated</th>
[% IF ( CAN_user_plugins_configure ) %]<th>Configure</th>[% END %]
[% IF ( CAN_user_plugins_manage ) %]<th>Uninstall</th>[% END %]
</tr>
[% FOREACH plugin IN plugins %]
<tr>
<td><strong>[% plugin.metadata.name %]</strong></td>
<td>
[% IF ( CAN_user_plugins_report ) %]
[% IF plugin.can('report') %]
<p style="white-space:nowrap"><a href="/cgi-bin/koha/plugins/run.pl?class=[% plugin.class %]&method=report">Run report</a></p>
[% END %]
[% END %]
[% IF ( CAN_user_plugins_tool ) %]
[% IF plugin.can('tool') %]
<p style="white-space:nowrap"><a href="/cgi-bin/koha/plugins/run.pl?class=[% plugin.class %]&method=tool">Run tool</a></p>
[% END %]
[% END %]
</td>
<td>
[% plugin.metadata.description %]
[% IF ( plugin.metadata.minimum_version && koha_version < plugin.metadata.minimum_version ) %]
<div class="error">Warning: This report was written for a newer version of Koha. Run at your own risk.</div>
[% END %]
[% IF ( plugin.metadata.maximum_version && koha_version > plugin.metadata.maximum_version ) %]
<div class="error">Warning: This report was written for an older version of Koha. Run at your own risk.</div>
[% END %]
</td>
<td>[% plugin.metadata.author %]</td>
<td>[% plugin.metadata.version %]</td>
<td>[% plugin.metadata.minimum_version %]</td>
<td>[% plugin.metadata.maximum_version %]</td>
<td>[% plugin.metadata.date_updated | $KohaDates %]</td>
[% IF ( CAN_user_plugins_configure ) %]
<td>
[% IF plugin.can('configure') %]
<a href="/cgi-bin/koha/plugins/run.pl?class=[% plugin.class %]&method=configure">Configure</a>
[% END %]
</td>
[% END %]
[% IF ( CAN_user_plugins_manage ) %]
<td>
[% IF plugin.can('uninstall') %]
<a href="/cgi-bin/koha/plugins/plugins-uninstall.pl?class=[% plugin.class %]" onclick="return confirm('Are you sure you want to uninstall the plugin [% plugin.metadata.name %]?')">Uninstall</a>
[% END %]
</td>
[% END %]
[% END %]
</table>
[% END %]
</div>
</div>
</div>
<div class="yui-b noprint">
<div id="navmenu">
<ul id="navmenulist">
[% IF ( CAN_user_plugins_manage ) %]
<li><a href="plugins-upload.pl">Upload a plugin</a></li>
[% END %]
</ul>
</div>
</div>
</div>
</div>
[% INCLUDE 'intranet-bottom.inc' %]

55
koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-upload.tt

@ -0,0 +1,55 @@
[% INCLUDE 'doc-head-open.inc' %]
<title>Koha &rsaquo; Tools &rsaquo; Plugins &rsaquo; Upload Plugin
</title>
[% INCLUDE 'doc-head-close.inc' %]
[% INCLUDE 'calendar.inc' %]
</head>
<body>
[% INCLUDE 'header.inc' %]
[% INCLUDE 'circ-search.inc' %]
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a>
&rsaquo; <a href="/cgi-bin/koha/plugins/plugins-home.pl">Plugins</a>
&rsaquo; Upload Plugins
</div>
<div id="doc3" class="yui-t2">
<div id="bd">
<div id="yui-main">
<div class="yui-b">
<div class="yui-g">
<div class="yui-u first">
<h1>Upload Koha Plugin</h1>
[% IF ( ERRORS ) %]
<div class="dialog alert">
[% FOREACH ERROR IN ERRORS %]
[% IF ( ERROR.NOTKPZ ) %]<li><b>The upload file does not appear to be a kpz file. The extention is not '.kpz'.</b></li>
[% ELSIF ( ERROR.NOWRITETEMP ) %]<li><b>This script is not able to create/write to the necessary temporary directory.</b></li>
[% ELSIF ( ERROR.EMPTYUPLOAD ) %]<li><b>The upload file appears to be empty.</b></li>
[% ELSIF ( ERROR.UZIPFAIL ) %]<li><b>[% ERROR.UZIPFAIL %] failed to unpack.<br />Please verify the integrity of the zip file and retry.</b></li>
[% ELSIF ( ERROR.NOWRITEPLUGINS ) %]<li><b>Cannot unpack file to the plugins directory.<br />Please verify that the Apache user can write to the plugins directory.</b></li>
[% ELSE %]<li><b>[% ERROR.CORERR %] An unknown error has occurred.<br />Please review the error log for more details.</b></li>[% END %]
[% END %]
</div>
[% END %]
<form method="post" action="/cgi-bin/koha/plugins/plugins-upload.pl" enctype="multipart/form-data">
<fieldset class="brief">
<div class="hint"><b>NOTE:</b> Only KPZ file format is supported.</div>
<ol>
<li>
<label for="uploadfile">Select the file to upload: </label><input type="file" id="uploadfile" name="uploadfile" />
</li>
</ol>
</fieldset>
<fieldset class="action">
<input type="hidden" name="op" value="Upload" />
<input type="submit" value="Upload" class="submit" />
</fieldset>
</form>
</div>
</div>
</div>
</div>
[% INCLUDE 'intranet-bottom.inc' %]

11
koha-tmpl/intranet-tmpl/prog/en/modules/reports/reports-home.tt

@ -35,7 +35,16 @@
<li><a href="/cgi-bin/koha/reports/issues_stats.pl">Circulation</a></li>
<li><a href="/cgi-bin/koha/reports/serials_stats.pl">Serials</a></li>
<li><a href="/cgi-bin/koha/reports/reserves_stats.pl">Holds</a></li>
</ul></div>
</ul>
[% IF UseKohaPlugins %]
<h2>Report Plugins</h2>
<ul>
<li><a href="/cgi-bin/koha/plugins/plugins-home.pl?method=report">Report Plugins</a></li>
</ul>
[% END %]
</div>
<div class="yui-u"><h2>Top lists</h2>
<ul>

5
koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt

@ -97,6 +97,11 @@
<dd>Quote editor for Quote-of-the-day feature in OPAC</dd>
[% END %]
[% IF ( UseKohaPlugins && CAN_user_plugins_tool ) %]
<dt><a href="/cgi-bin/koha/plugins/plugins-home.pl?method=tool">Tool Plugins</a></dt>
<dd>Use tool plugins</dd>
[% END %]
</dl>
</div>
<div class="yui-u">

57
plugins/plugins-home.pl

@ -0,0 +1,57 @@
#!/usr/bin/perl
# Copyright 2010 Kyle M Hall <kyle.m.hall@gmail.com>
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use strict;
use warnings;
use CGI;
use Koha::Plugins;
use C4::Auth;
use C4::Output;
use C4::Dates;
use C4::Debug;
use C4::Context;
die("Koha plugins are disabled!")
unless C4::Context->preference('UseKohaPlugins');
my $input = new CGI;
my $method = $input->param('method');
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
{ template_name => "plugins/plugins-home.tmpl",
query => $input,
type => "intranet",
authnotrequired => 0,
flagsrequired => { plugins => '*' },
debug => 1,
}
);
$template->param(
koha_version => C4::Context->preference("Version"),
method => $method,
);
my @plugins = Koha::Plugins->new()->GetPlugins($method);
$template->param( plugins => \@plugins );
output_html_with_http_headers( $input, $cookie, $template->output );

52
plugins/plugins-uninstall.pl

@ -0,0 +1,52 @@
#!/usr/bin/perl
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use strict;
use warnings;
use Archive::Extract;
use File::Temp;
use File::Copy;
use CGI;
use C4::Context;
use C4::Auth;
use C4::Output;
use C4::Members;
use C4::Debug;
use Koha::Plugins::Handler;
die("Koha plugins are disabled!")
unless C4::Context->preference('UseKohaPlugins');
my $input = new CGI;
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
{ template_name => "plugins/plugins-upload.tmpl",
query => $input,
type => "intranet",
authnotrequired => 0,
flagsrequired => { plugins => 'manage' },
debug => 1,
}
);
my $class = $input->param('class');
Koha::Plugins::Handler->delete( { class => $class } );
print $input->redirect("/cgi-bin/koha/plugins/plugins-home.pl");

98
plugins/plugins-upload.pl

@ -0,0 +1,98 @@
#!/usr/bin/perl
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use strict;
use warnings;
use Archive::Extract;
use File::Temp;
use File::Copy;
use CGI;
use C4::Context;
use C4::Auth;
use C4::Output;
use C4::Members;
use C4::Debug;
use Koha::Plugins;
die("Koha plugins are disabled!")
unless C4::Context->preference('UseKohaPlugins');
my $input = new CGI;
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
{ template_name => "plugins/plugins-upload.tmpl",
query => $input,
type => "intranet",
authnotrequired => 0,
flagsrequired => { plugins => 'manage' },
debug => 1,
}
);
my $uploadfilename = $input->param('uploadfile');
my $uploadfile = $input->upload('uploadfile');
my $op = $input->param('op');
my ( $total, $handled, @counts, $tempfile, $tfh );
my %errors;
if ( ( $op eq 'Upload' ) && $uploadfile ) {
my $plugins_dir = C4::Context->config("pluginsdir");
my $dirname = File::Temp::tempdir( CLEANUP => 1 );
$debug and warn "dirname = $dirname";
my $filesuffix;
$filesuffix = $1 if $uploadfilename =~ m/(\..+)$/i;
( $tfh, $tempfile ) = File::Temp::tempfile( SUFFIX => $filesuffix, UNLINK => 1 );
$debug and warn "tempfile = $tempfile";
$errors{'NOTKPZ'} = 1 if ( $uploadfilename !~ /\.kpz$/i );
$errors{'NOWRITETEMP'} = 1 unless ( -w $dirname );
$errors{'NOWRITEPLUGINS'} = 1 unless ( -w $plugins_dir );
$errors{'EMPTYUPLOAD'} = 1 unless ( length($uploadfile) > 0 );
if (%errors) {
$template->param( ERRORS => [ \%errors ] );
} else {
while (<$uploadfile>) {
print $tfh $_;
}
close $tfh;
my $ae = Archive::Extract->new( archive => $tempfile, type => 'zip' );
unless ( $ae->extract( to => $plugins_dir ) ) {
warn "ERROR: " . $ae->error;
$errors{'UZIPFAIL'} = $uploadfilename;
$template->param( ERRORS => [ \%errors ] );
output_html_with_http_headers $input, $cookie, $template->output;
exit;
}
}
} elsif ( ( $op eq 'Upload' ) && !$uploadfile ) {
warn "Problem uploading file or no file uploaded.";
}
if ( $uploadfile && !%errors && !$template->param('ERRORS') ) {
print $input->redirect("/cgi-bin/koha/plugins/plugins-home.pl");
} else {
output_html_with_http_headers $input, $cookie, $template->output;
}

50
plugins/run.pl

@ -0,0 +1,50 @@
#!/usr/bin/perl
# Copyright 2010 Kyle M Hall <kyle.m.hall@gmail.com>
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with Koha; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use strict;
use warnings;
use CGI;
use Koha::Plugins::Handler;
use C4::Auth;
use C4::Output;
use C4::Dates;
use C4::Debug;
use C4::Context;
die("Koha plugins are disabled!")
unless C4::Context->preference('UseKohaPlugins');
my $cgi = new CGI;
my $class = $cgi->param('class');
my $method = $cgi->param('method');
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
{ template_name => "plugins/plugins-home.tmpl",
query => $cgi,
type => "intranet",
authnotrequired => 0,
flagsrequired => { plugins => $method },
debug => 1,
}
);
my $plugin = Koha::Plugins::Handler->run( { class => $class, method => $method, cgi => $cgi } );

3
rewrite-config.PL

@ -43,7 +43,7 @@ guesses worked out by the script.
The following configuration keywords are available:
PREFIX,
BASE_DIR, CGI_DIR, LOG_DIR, INSTALL_BASE,
BASE_DIR, CGI_DIR, LOG_DIR, PLUGINS_DIR, INSTALL_BASE,
DB_TYPE, DB_HOST, DB_PORT, DB_NAME, DB_PASS, DB_USER, WEBMASTER_EMAIL, WEBSERVER_DOMAIN,
WEBSERVER_HOST, WEBSERVER_IP, WEBSERVER_PORT, WEBSERVER_PORT_LIBRARIAN, ZEBRA_PASS, ZEBRA_USER
@ -82,6 +82,7 @@ $prefix = $ENV{'INSTALL_BASE'} || "/usr";
%configuration = (
"__KOHA_INSTALLED_VERSION__" => "no_version_found",
"__LOG_DIR__" => "/var/log",
"__PLUGINS_DIR__" => "/var/lib/koha/plugins",
"__DB_TYPE__" => "mysql",
"__DB_NAME__" => "koha",
"__DB_HOST__" => $myhost,

1
skel/var/lib/koha/plugins/README

@ -0,0 +1 @@
plugins dir
Loading…
Cancel
Save