From 1f043c443816c74cb1ef1c204ef4827a2252905d Mon Sep 17 00:00:00 2001
From: Kyle M Hall <kyle.m.hall@gmail.com>
Date: Fri, 22 Jan 2010 16:34:52 +0000
Subject: [PATCH] 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 line <pluginsdir>/var/lib/koha/plugins</pluginsdir>
5) Access the plugins system from the "More" pulldown
6) Upload the example plugin file provided here
7) Try it out!

Things that still need to be done:
1) Add some authentication so that only authorized users can use tools and/or reports
2) Add another permission for configuring plugins
3) Add a way to uninstall plugins
4) Give the plugin templates access to the standard Koha template include files
5) Give the plugin templates a way to access their own images, css files, js files, etc.
---
 C4/Auth.pm                                         |    2 +
 C4/Installer/PerlDependencies.pm                   |   30 ++++
 Koha/Plugins.pm                                    |   93 ++++++++++++
 Koha/Plugins/Base.pm                               |  158 ++++++++++++++++++++
 Koha/Plugins/Handler.pm                            |   89 +++++++++++
 Makefile.PL                                        |   10 +-
 debian/templates/koha-conf-site.xml.in             |    2 +
 etc/koha-conf.xml                                  |    1 +
 installer/data/mysql/kohastructure.sql             |   11 ++
 installer/data/mysql/sysprefs.sql                  |    1 +
 installer/data/mysql/updatedatabase.pl             |   19 +++
 .../intranet-tmpl/prog/en/includes/header.inc      |    3 +
 .../admin/preferences/enhanced_content.pref        |    7 +
 .../prog/en/modules/plugins/plugins-home.tt        |   93 ++++++++++++
 .../prog/en/modules/plugins/plugins-upload.tt      |   55 +++++++
 .../prog/en/modules/reports/report-plugins.tt      |   54 +++++++
 .../prog/en/modules/reports/reports-home.tt        |   11 ++-
 .../prog/en/modules/tools/tool-plugins.tt          |   54 +++++++
 .../prog/en/modules/tools/tools-home.tt            |    5 +
 plugins/plugins-home.pl                            |   51 +++++++
 plugins/plugins-upload.pl                          |   97 ++++++++++++
 plugins/run.pl                                     |   49 ++++++
 reports/report-plugins.pl                          |   47 ++++++
 rewrite-config.PL                                  |    3 +-
 skel/var/lib/koha/plugins/.htaccess                |    2 +
 skel/var/lib/koha/plugins/README                   |    1 +
 tools/tool-plugins.pl                              |   47 ++++++
 27 files changed, 992 insertions(+), 3 deletions(-)
 create mode 100644 Koha/Plugins.pm
 create mode 100644 Koha/Plugins/Base.pm
 create mode 100644 Koha/Plugins/Handler.pm
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-home.tt
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-upload.tt
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/reports/report-plugins.tt
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/tool-plugins.tt
 create mode 100755 plugins/plugins-home.pl
 create mode 100755 plugins/plugins-upload.pl
 create mode 100755 plugins/run.pl
 create mode 100755 reports/report-plugins.pl
 create mode 100644 skel/var/lib/koha/plugins/.htaccess
 create mode 100644 skel/var/lib/koha/plugins/README
 create mode 100755 tools/tool-plugins.pl

diff --git a/C4/Auth.pm b/C4/Auth.pm
index 2eb63f5..b89beea 100644
--- a/C4/Auth.pm
+++ b/C4/Auth.pm
@@ -203,6 +203,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 );
@@ -364,6 +365,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 {
diff --git a/C4/Installer/PerlDependencies.pm b/C4/Installer/PerlDependencies.pm
index 41fa8db..fa2928c 100644
--- a/C4/Installer/PerlDependencies.pm
+++ b/C4/Installer/PerlDependencies.pm
@@ -634,6 +634,36 @@ our $PERL_DEPS = {
         'required' => '0',
         'min_ver'  => '0.22',
     },
+    'File::Temp' => {
+        'usage'    => 'Plugins',
+        'required' => '0',
+        'min_ver'  => '0.22',
+    },
+    'File::Copy' => {
+        'usage'    => 'Plugins',
+        'required' => '0',
+        'min_ver'  => '2.08',
+    },
+    '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::Pluggable' => {
+        'usage'    => 'Plugins',
+        'required' => '0',
+        'min_ver'  => '3.9',
+    },
 };
 
 1;
diff --git a/Koha/Plugins.pm b/Koha/Plugins.pm
new file mode 100644
index 0000000..f0385c1
--- /dev/null
+++ b/Koha/Plugins.pm
@@ -0,0 +1,93 @@
+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 ) = @_;
+    my $self = bless( {}, $class );
+
+    $args->{'pluginsdir'} = C4::Context->config("pluginsdir");
+
+    # TODO: Decide what metadata will be required ( $args->{metadata} ) and add a check for those keys
+
+    return bless( $args, $class );
+}
+
+=item 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 $version = C4::Context->preference("Version");
+
+    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 ( defined($method) && $plugin->can($method) ) {
+                push( @plugins, $plugin );
+            } else {
+                push( @plugins, $plugin );
+            }
+        }
+    }
+
+    return @plugins;
+}
+
+1;
+__END__
+
+=back
+
+=head1 AUTHOR
+
+Kyle M Hall <kyle.m.hall@gmail.com>
+
+=cut
diff --git a/Koha/Plugins/Base.pm b/Koha/Plugins/Base.pm
new file mode 100644
index 0000000..5bbe794
--- /dev/null
+++ b/Koha/Plugins/Base.pm
@@ -0,0 +1,158 @@
+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 Template;
+
+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::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 ) = @_;
+
+    return Template->new( { ABSOLUTE => 1 } );
+}
+
+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
+
+   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__
+
+=back
+
+=head1 AUTHOR
+
+Kyle M Hall <kyle.m.hall@gmail.com>
+
+=cut
diff --git a/Koha/Plugins/Handler.pm b/Koha/Plugins/Handler.pm
new file mode 100644
index 0000000..f5cdafd
--- /dev/null
+++ b/Koha/Plugins/Handler.pm
@@ -0,0 +1,89 @@
+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 vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+
+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
+
+#sub new {
+#    my ( $class, %args ) = @_;
+#    my $self = bless( {}, $class );
+#
+#    $self->{'pluginsdir'} = C4::Context->config("pluginsdir");
+#    $self->{'class'} = $args{'class'};
+#
+#    return $self;
+#}
+
+=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 {
+
+        #TODO: Add
+    }
+}
+
+1;
+__END__
+
+=back
+
+=head1 AUTHOR
+
+Kyle M Hall <kyle.m.hall@gmail.com>
+
+=cut
diff --git a/Makefile.PL b/Makefile.PL
index 6c6e186..c0d5b90 100644
--- a/Makefile.PL
+++ b/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.
@@ -309,6 +313,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
diff --git a/debian/templates/koha-conf-site.xml.in b/debian/templates/koha-conf-site.xml.in
index 0858059..f433487 100644
--- a/debian/templates/koha-conf-site.xml.in
+++ b/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>
diff --git a/etc/koha-conf.xml b/etc/koha-conf.xml
index 53d7ce8..71eb05b 100644
--- a/etc/koha-conf.xml
+++ b/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>
diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql
index ed673da..4da1fc3 100644
--- a/installer/data/mysql/kohastructure.sql
+++ b/installer/data/mysql/kohastructure.sql
@@ -3056,6 +3056,17 @@ CREATE TABLE IF NOT EXISTS `borrower_modifications` (
   KEY `borrowernumber` (`borrowernumber`)
 ) 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 */;
diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql
index d5d15d7..1cd5162 100644
--- a/installer/data/mysql/sysprefs.sql
+++ b/installer/data/mysql/sysprefs.sql
@@ -406,3 +406,4 @@ INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('
 INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('NotesBlacklist','','List of notes fields that should not appear in the title notes/description separator of details',NULL,'free');
 INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ('SCOUserCSS', '', NULL, 'Add CSS to be included in the SCO module in an embedded <style> tag.', 'free');
 INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ('SCOUserJS', '', NULL, 'Define custom javascript for inclusion in the SCO module', 'free');
+INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES('UseKohaPlugins','1','Enable or disable the ability to use Koha Plugins.','','YesNo');
diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl
index 77265f4..e472c32 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -6339,6 +6339,25 @@ if ( CheckVersion($DBversion) ) {
    SetVersion ($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'), ('19', 'tools', 'Use Tool Plugins'), ('19', 'reports', 'Use Report 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 (Added plugin system.)\n";
+    SetVersion($DBversion);
+}
+
 =head1 FUNCTIONS
 
 =head2 TableExists($table)
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc
index f4037ac..9f04541 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc
+++ b/koha-tmpl/intranet-tmpl/prog/en/includes/header.inc
@@ -34,6 +34,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 %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref
index 655ae34..ca26073 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/enhanced_content.pref
@@ -300,3 +300,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.
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-home.tt
new file mode 100644
index 0000000..c122870
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/plugins/plugins-home.tt
@@ -0,0 +1,93 @@
+[% 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 ) %]
+                        <h2>No Plugins Exist.</h2>
+                        <p>Please upload one or more Koha Plugin files (.kpz) before using.</p>
+                    [% 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>
+                                <th>Configure</th>
+                            </tr>
+
+                            [% FOREACH plugin IN plugins %]
+                                <tr>
+                                    <td><strong>[% plugin.metadata.name %]</strong></td>
+                                    <td>
+                                        [% 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 %]
+
+                                        [% 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 %]
+                                    </td>
+                                    <td>
+                                        [% plugin.metadata.description %]
+
+                                        <!-- FIXME: The version checking is borked at the moment -->
+                                        [% IF ( plugin.db_version_too_old ) %]
+                                            <div class="error">Warning: This report was written for a newer version of Koha. Run at your own risk.</div>
+                                        [% END %]
+
+                                        [% IF ( plugin.db_version_too_new ) %]
+                                            <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>
+                                    <td>
+                                        [% IF plugin.can('configure') %]
+                                            <a href="/cgi-bin/koha/plugins/run.pl?class=[% plugin.class %]&method=configure">Configure</a>
+                                        [% END %]
+                                    </td>
+
+                            [% END %]
+                        </table>
+                    [% END %]
+                </div>
+            </div>
+        </div>
+
+        <div class="yui-b noprint">
+            <div id="navmenu">
+                <ul id="navmenulist">
+                    <li><a href="plugins-upload.pl">Upload A Plugin</a></li>
+                </ul>
+            </div>
+        </div>
+    </div>
+</div>
+
+
+[% INCLUDE 'intranet-bottom.inc' %]
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
new file mode 100644
index 0000000..94d3373
--- /dev/null
+++ b/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' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reports/report-plugins.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/reports/report-plugins.tt
new file mode 100644
index 0000000..b7fd215
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/reports/report-plugins.tt
@@ -0,0 +1,54 @@
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha &rsaquo; Reports &rsaquo; Report 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; <a href="/cgi-bin/koha/reports/reports-home.pl">Reports</a>
+&rsaquo; Plugins
+</div>
+
+<div id="doc3" class="yui-t1">
+        <div id="bd">
+                <div id="yui-main">
+                        <div class="details">
+                                <h1>Report Plugins</h1>
+
+                                [% IF ( ERROR_NO_REPORTS ) %]
+                                        <h2>No Report Plugins Exist.</h2>
+                                [% ELSE %]
+                                        <table>
+                                                <tr>
+                                                        <th>Name</th>
+                                                        <th>Description</th>
+                                                        <th>Author</th>
+                                                        <th>Last Update</th>
+                                                </tr>
+
+                                                [% FOREACH REPORTS_LOO IN REPORTS_LOOP %]
+                                                        <tr>
+                                                                <td><a href="/cgi-bin/koha/plugins/run/[% REPORTS_LOO.start %]">[% REPORTS_LOO.name %]</a></td>
+                                                                <td>
+                                                                        [% REPORTS_LOO.description %]
+                                                                        [% IF ( REPORTS_LOO.db_version_too_old ) %]
+                                                                                <div class="error">Warning: This report was written for a newer version of Koha. Run at your own risk.</div>
+                                                                        [% END %]
+                                                                </td>
+                                                                <td>[% REPORTS_LOO.author %]</td>
+                                                                <td>[% REPORTS_LOO.date_updated %]</td>
+                                                [% END %]
+                                        </table>
+                                [% END %]
+                        </div>
+                </div>
+        </div>
+</div>
+
+
+
+[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/reports/reports-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/reports/reports-home.tt
index 32c1803..fff1c04 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/reports/reports-home.tt
+++ b/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/reports/report-plugins.pl">View Report Plugins</a></li>
+        </ul>
+        [% END %]
+
+    </div>
 
     <div class="yui-u"><h2>Top lists</h2>
 	<ul>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tool-plugins.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tool-plugins.tt
new file mode 100644
index 0000000..9adf810
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tool-plugins.tt
@@ -0,0 +1,54 @@
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha &rsaquo; Tools &rsaquo; Tool 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; <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a>
+&rsaquo; Plugins
+</div>
+
+<div id="doc3" class="yui-t1">
+        <div id="bd">
+                <div id="yui-main">
+                        <div class="details">
+                                <h1>Tool Plugins</h1>
+
+                                [% IF ( ERROR_NO_TOOLS ) %]
+                                        <h2>No Tool Plugins Exist.</h2>
+                                [% ELSE %]
+                                        <table>
+                                                <tr>
+                                                        <th>Name</th>
+                                                        <th>Description</th>
+                                                        <th>Author</th>
+                                                        <th>Last Update</th>
+                                                </tr>
+
+                                                [% FOREACH TOOLS_LOO IN TOOLS_LOOP %]
+                                                        <tr>
+                                                                <td><a href="/cgi-bin/koha/plugins/run/[% TOOLS_LOO.start %]">[% TOOLS_LOO.name %]</a></td>
+                                                                <td>
+                                                                        [% TOOLS_LOO.description %]
+                                                                        [% IF ( TOOLS_LOO.db_version_too_old ) %]
+                                                                                <div class="error">Warning: This report was written for a newer version of Koha. Run at your own risk.</div>
+                                                                        [% END %]
+                                                                </td>
+                                                                <td>[% TOOLS_LOO.author %]</td>
+                                                                <td>[% TOOLS_LOO.date_updated %]</td>
+                                                [% END %]
+                                        </table>
+                                [% END %]
+                        </div>
+                </div>
+        </div>
+</div>
+
+
+
+[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
index 52cc1e6..86dd81c 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
+++ b/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_tools ) %]
+    <dt><a href="/cgi-bin/koha/tools/tool-plugins.pl">Tool Plugins</a></dt>
+    <dd>Use tool plugins</dd>
+    [% END %]
+
 </dl>
 </div>
 <div class="yui-u">
diff --git a/plugins/plugins-home.pl b/plugins/plugins-home.pl
new file mode 100755
index 0000000..49af843
--- /dev/null
+++ b/plugins/plugins-home.pl
@@ -0,0 +1,51 @@
+#!/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 ( $template, $borrowernumber, $cookie ) = get_template_and_user(
+    {   template_name   => "plugins/plugins-home.tmpl",
+        query           => $input,
+        type            => "intranet",
+        authnotrequired => 0,
+        flagsrequired   => { plugins => "manage" },
+        debug           => 1,
+    }
+);
+
+my @plugins = Koha::Plugins->new()->GetPlugins();
+
+$template->param( plugins => \@plugins );
+
+output_html_with_http_headers( $input, $cookie, $template->output );
diff --git a/plugins/plugins-upload.pl b/plugins/plugins-upload.pl
new file mode 100755
index 0000000..605b52d
--- /dev/null
+++ b/plugins/plugins-upload.pl
@@ -0,0 +1,97 @@
+#!/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 = $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;
+}
diff --git a/plugins/run.pl b/plugins/run.pl
new file mode 100755
index 0000000..f6b19a5
--- /dev/null
+++ b/plugins/run.pl
@@ -0,0 +1,49 @@
+#!/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 ( $template, $borrowernumber, $cookie ) = get_template_and_user(
+    {   template_name   => "plugins/plugins-home.tmpl",
+        query           => $cgi,
+        type            => "intranet",
+        authnotrequired => 0,
+        flagsrequired   => { plugins => "reports" },
+        debug           => 1,
+    }
+);
+
+my $class  = $cgi->param('class');
+my $method = $cgi->param('method');
+my $plugin = Koha::Plugins::Handler->run( { class => $class, method => $method, cgi => $cgi } );
diff --git a/reports/report-plugins.pl b/reports/report-plugins.pl
new file mode 100755
index 0000000..5a314e6
--- /dev/null
+++ b/reports/report-plugins.pl
@@ -0,0 +1,47 @@
+#!/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;
+
+my $input = new CGI;
+
+my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
+    {   template_name   => "reports/report-plugins.tmpl",
+        query           => $input,
+        type            => "intranet",
+        authnotrequired => 0,
+        flagsrequired   => { plugins => "reports" },
+        debug           => 1,
+    }
+);
+
+my @plugins = Koha::Plugins->new()->GetPlugins('report');
+
+$template->param( plugins => \@plugins );
+
+output_html_with_http_headers( $input, $cookie, $template->output );
diff --git a/rewrite-config.PL b/rewrite-config.PL
index 02715ab..90fd5ff 100644
--- a/rewrite-config.PL
+++ b/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,
diff --git a/skel/var/lib/koha/plugins/.htaccess b/skel/var/lib/koha/plugins/.htaccess
new file mode 100644
index 0000000..4d7523a
--- /dev/null
+++ b/skel/var/lib/koha/plugins/.htaccess
@@ -0,0 +1,2 @@
+AddHandler cgi-script .pl
+AddHandler send-as-is .css .js .gif .jpg
\ No newline at end of file
diff --git a/skel/var/lib/koha/plugins/README b/skel/var/lib/koha/plugins/README
new file mode 100644
index 0000000..d4290ea
--- /dev/null
+++ b/skel/var/lib/koha/plugins/README
@@ -0,0 +1 @@
+plugins dir
\ No newline at end of file
diff --git a/tools/tool-plugins.pl b/tools/tool-plugins.pl
new file mode 100755
index 0000000..7a699b8
--- /dev/null
+++ b/tools/tool-plugins.pl
@@ -0,0 +1,47 @@
+#!/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;
+
+my $input = new CGI;
+
+my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
+    {   template_name   => "tools/tool-plugins.tmpl",
+        query           => $input,
+        type            => "intranet",
+        authnotrequired => 0,
+        flagsrequired   => { plugins => "tools" },
+        debug           => 1,
+    }
+);
+
+my @plugins = Koha::Plugins->new()->GetPlugins('tool');
+
+$template->param( plugins => \@plugins );
+
+output_html_with_http_headers( $input, $cookie, $template->output );
-- 
1.7.2.5