[Rt-commit] rt branch, runtime-plugin, created. rt-3.9.6-240-g46592f6

Chia-liang Kao clkao at bestpractical.com
Tue Nov 30 08:24:44 EST 2010


The branch, runtime-plugin has been created
        at  46592f62d9edb4fa1cefba69ce81b1e731de66db (commit)

- Log -----------------------------------------------------------------
commit ccd08815c4f513cea8adeeb250b86897c73dd5fe
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Mon Oct 18 15:44:35 2010 +0900

    basic plugin api tests

diff --git a/t/plugins/_plugins/Hello/html/NoAuth/hello b/t/plugins/_plugins/Hello/html/NoAuth/hello
new file mode 100644
index 0000000..e892a58
--- /dev/null
+++ b/t/plugins/_plugins/Hello/html/NoAuth/hello
@@ -0,0 +1 @@
+<% _("Hello") %>
\ No newline at end of file
diff --git a/t/plugins/_plugins/Hello/lib/Hello.pm b/t/plugins/_plugins/Hello/lib/Hello.pm
new file mode 100644
index 0000000..6bff916
--- /dev/null
+++ b/t/plugins/_plugins/Hello/lib/Hello.pm
@@ -0,0 +1,3 @@
+package Hello;
+
+1;
diff --git a/t/plugins/_plugins/Hello/po/zh_tw.po b/t/plugins/_plugins/Hello/po/zh_tw.po
new file mode 100644
index 0000000..4f78110
--- /dev/null
+++ b/t/plugins/_plugins/Hello/po/zh_tw.po
@@ -0,0 +1,13 @@
+msgid ""
+msgstr ""
+"PO-Revision-Date: 2008-10-20 17:11-0400\n"
+"Project-Id-Version: Hello\n"
+"Last-Translator: Chia-liang Kao <clkao at clkao.org>\n"
+"Language-Team: rt-devel <rt-devel at lists.bestpractical.com>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: NOT FOUND IN SOURCE
+msgid "Hello"
+msgstr "你好"
diff --git a/t/plugins/api.t b/t/plugins/api.t
new file mode 100644
index 0000000..5fef1bd
--- /dev/null
+++ b/t/plugins/api.t
@@ -0,0 +1,25 @@
+#!perl
+use Cwd qw(abs_path);
+use File::Basename qw(basename dirname);
+
+require RT;
+$RT::PluginPath = abs_path(dirname($0)).'/_plugins';
+
+use RT::Test nodb => 1, tests => 5;
+
+is_deeply([RT->PluginDirs('lib')], []);
+ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
+RT->Config->Set('Plugins',qw(Hello));
+RT->InitPluginPaths;
+
+ok(grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
+
+is_deeply([RT->PluginDirs('lib')], ["$RT::PluginPath/Hello/lib"]);
+
+require RT::Interface::Web::Handler;
+
+
+is_deeply({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1],
+          ['plugin-Hello', $RT::PluginPath.'/Hello/html']);
+
+

commit 98809c8b164a3b7c7f936acf6291c4bab8ef924a
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Mon Oct 18 19:19:33 2010 +0900

    Avoid calling initplugins directly

diff --git a/lib/RT.pm b/lib/RT.pm
index 28a8d62..a7a4706 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -146,7 +146,7 @@ sub Init {
     InitSystemObjects();
     InitClasses();
     InitLogging();
-    InitPlugins();
+    RT->Plugins();
     RT::I18N->Init;
     RT->Config->PostLoadCheck;
 
diff --git a/lib/RT/Test.pm b/lib/RT/Test.pm
index 1334c21..b8942b3 100644
--- a/lib/RT/Test.pm
+++ b/lib/RT/Test.pm
@@ -150,8 +150,6 @@ sub import {
 
     $class->bootstrap_plugins( %args );
 
-    RT->Plugins;
-    
     RT::I18N->Init();
     RT->Config->PostLoadCheck;
 
@@ -407,7 +405,9 @@ sub bootstrap_plugins {
     my $self = shift;
     my %args = @_;
 
-    return unless $args{'requires'};
+    unless ($args{'requires'}) {
+        return RT->Plugins;
+    }
 
     my @plugins = @{ $args{'requires'} };
     push @plugins, $args{'testing'}
@@ -445,8 +445,8 @@ sub bootstrap_plugins {
     ) if @plugins;
 
     require File::Spec;
-    foreach my $name ( @plugins ) {
-        my $plugin = RT::Plugin->new( name => $name );
+    foreach my $plugin ( @{ RT->Plugins } ) {
+        my $name = $plugin->Name;
         Test::More::diag( "Initializing DB for the $name plugin" )
             if $ENV{'TEST_VERBOSE'};
 
@@ -479,6 +479,7 @@ sub bootstrap_plugins {
         $RT::Handle->Connect; # XXX: strange but mysql can loose connection
     }
     $dba_dbh->disconnect if $dba_dbh;
+    return RT->Plugins;
 }
 
 sub _get_dbh {

commit b151359b19d401eb3435c3970e19864ec0d6bcaa
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Mon Oct 18 19:26:12 2010 +0900

    Make Plugins init only once when the plugin list is empty

diff --git a/lib/RT.pm b/lib/RT.pm
index a7a4706..189f097 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -547,14 +547,14 @@ You can define plugins by adding them to the @Plugins list in your RT_SiteConfig
 
 =cut
 
-our @PLUGINS = ();
+my $PLUGINS;
 sub Plugins {
     my $self = shift;
-    unless (@PLUGINS) {
+    unless ($PLUGINS) {
         $self->InitPluginPaths;
-        @PLUGINS = $self->InitPlugins;
+        $PLUGINS = [$self->InitPlugins];
     }
-    return \@PLUGINS;
+    return $PLUGINS;
 }
 
 =head2 PluginDirs

commit 3b71a46a10a300bf8466f75c3ed018d4bdff3abd
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Mon Oct 18 19:44:29 2010 +0900

    rework plugin path init.

diff --git a/lib/RT.pm b/lib/RT.pm
index 189f097..c9b9583 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -591,7 +591,17 @@ In case F<local/lib> isn't in @INC, append them to @INC
 sub InitPluginPaths {
     my $self = shift || __PACKAGE__;
 
-    my @lib_dirs = $self->PluginDirs('lib');
+    if ($PLUGINS) {
+        Carp::carp "reinitializing plugin paths";
+        $PLUGINS = undef;
+    }
+
+    my @lib_dirs;
+    foreach my $plugin_name (grep $_, RT->Config->Get('Plugins')) {
+        my $path = RT::Plugin->BasePathFor( $plugin_name ) . '/lib';
+        next unless -d $path;
+        push @lib_dirs, $path;
+    }
 
     my @tmp_inc;
     my $added;
diff --git a/lib/RT/Plugin.pm b/lib/RT/Plugin.pm
index 1705c47..41a3d13 100644
--- a/lib/RT/Plugin.pm
+++ b/lib/RT/Plugin.pm
@@ -104,7 +104,12 @@ sub Path {
 
 sub _BasePath {
     my $self = shift;
-    my $base = $self->{'name'};
+    $self->BasePathFor($self->{'name'});
+}
+
+sub BasePathFor {
+    my ($class, $base) = @_;
+
     $base =~ s/::/-/g;
     my $local_base = $RT::LocalPluginPath."/".$base;
     my $base_base = $RT::PluginPath."/".$base;
diff --git a/lib/RT/Test.pm b/lib/RT/Test.pm
index b8942b3..3d24c02 100644
--- a/lib/RT/Test.pm
+++ b/lib/RT/Test.pm
@@ -420,10 +420,10 @@ sub bootstrap_plugins {
         $cwd = Cwd::getcwd();
     }
 
-    my $old_func = \&RT::Plugin::_BasePath;
+    my $old_func = \&RT::Plugin::BasePathFor;
     no warnings 'redefine';
-    *RT::Plugin::_BasePath = sub {
-        my $name = $_[0]->{'name'};
+    *RT::Plugin::BasePathFor = sub {
+        my $name = $_[1];
 
         return $cwd if $args{'testing'} && $name eq $args{'testing'};
 
diff --git a/t/plugins/api.t b/t/plugins/api.t
index 5fef1bd..49d9061 100644
--- a/t/plugins/api.t
+++ b/t/plugins/api.t
@@ -14,12 +14,9 @@ RT->InitPluginPaths;
 
 ok(grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
 
-is_deeply([RT->PluginDirs('lib')], ["$RT::PluginPath/Hello/lib"]);
+is_deeply([RT->PluginDirs('lib')], ["$RT::PluginPath/Hello/lib"], 'plugin lib dir found');
 
 require RT::Interface::Web::Handler;
 
-
 is_deeply({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1],
           ['plugin-Hello', $RT::PluginPath.'/Hello/html']);
-
-

commit 992f57074a9c979ae181f1543984e636ba9853df
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Mon Oct 18 19:47:06 2010 +0900

    cleanup _BasePath

diff --git a/lib/RT/Plugin.pm b/lib/RT/Plugin.pm
index 41a3d13..f0358e9 100644
--- a/lib/RT/Plugin.pm
+++ b/lib/RT/Plugin.pm
@@ -97,22 +97,24 @@ See also L</ComponentRoot>, L</PoDir> and other shortcut methods.
 sub Path {
     my $self   = shift;
     my $subdir = shift;
-    my $res = $self->_BasePath;
+    my $res = $self->BasePathFor($self->{'name'});
     $res .= "/$subdir" if defined $subdir && length $subdir;
     return $res;
 }
 
-sub _BasePath {
-    my $self = shift;
-    $self->BasePathFor($self->{'name'});
-}
+=head2 $class->BasePathFor($name)
+
+Takes a name of a given plugin and return its base path.
+
+=cut
+
 
 sub BasePathFor {
-    my ($class, $base) = @_;
+    my ($class, $name) = @_;
 
-    $base =~ s/::/-/g;
-    my $local_base = $RT::LocalPluginPath."/".$base;
-    my $base_base = $RT::PluginPath."/".$base;
+    $name =~ s/::/-/g;
+    my $local_base = $RT::LocalPluginPath."/".$name;
+    my $base_base = $RT::PluginPath."/".$name;
 
     return -d $local_base ? $local_base : $base_base;
 }

commit a08b24e18c181c6d37e954bc5174eae7322d6c37
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Mon Oct 18 20:17:18 2010 +0900

    Make RT::Plugin instance responsible for maintaining @INC

diff --git a/lib/RT.pm b/lib/RT.pm
index c9b9583..9bb2c53 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -573,8 +573,8 @@ sub PluginDirs {
     require RT::Plugin;
 
     my @res;
-    foreach my $plugin (grep $_, RT->Config->Get('Plugins')) {
-        my $path = RT::Plugin->new( name => $plugin )->Path( $subdir );
+    foreach my $plugin (@{ RT->Plugins }) {
+        my $path = $plugin->Path($subdir);
         next unless -d $path;
         push @res, $path;
     }
@@ -595,30 +595,6 @@ sub InitPluginPaths {
         Carp::carp "reinitializing plugin paths";
         $PLUGINS = undef;
     }
-
-    my @lib_dirs;
-    foreach my $plugin_name (grep $_, RT->Config->Get('Plugins')) {
-        my $path = RT::Plugin->BasePathFor( $plugin_name ) . '/lib';
-        next unless -d $path;
-        push @lib_dirs, $path;
-    }
-
-    my @tmp_inc;
-    my $added;
-    for (@INC) {
-        if ( Cwd::realpath($_) eq $RT::LocalLibPath) {
-            push @tmp_inc, $_, @lib_dirs;
-            $added = 1;
-        } else {
-            push @tmp_inc, $_;
-        }
-    }
-
-    # append @lib_dirs in case $RT::LocalLibPath isn't in @INC
-    push @tmp_inc, @lib_dirs unless $added;
-
-    my %seen;
-    @INC = grep !$seen{$_}++, @tmp_inc;
 }
 
 =head2 InitPlugins
@@ -631,10 +607,11 @@ sub InitPlugins {
     my $self    = shift;
     my @plugins;
     require RT::Plugin;
-    foreach my $plugin (grep $_, RT->Config->Get('Plugins')) {
-        $plugin->require;
+    foreach my $plugin_name (grep $_, RT->Config->Get('Plugins')) {
+        my $plugin = RT::Plugin->new(name => $plugin_name);
+        $plugin_name->require;
         die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR);
-        push @plugins, RT::Plugin->new(name =>$plugin);
+        push @plugins, $plugin;
     }
     return @plugins;
 }
diff --git a/lib/RT/Plugin.pm b/lib/RT/Plugin.pm
index f0358e9..410d8fa 100644
--- a/lib/RT/Plugin.pm
+++ b/lib/RT/Plugin.pm
@@ -65,13 +65,29 @@ it cares about is 'name', the name of this plugin.
 
 =cut
 
+use List::MoreUtils qw(first_index);
+
 sub new {
     my $class = shift;
     my $args ={@_};
     my $self = bless $args, $class;
+
+    my $add = $self->Path("lib");
+    my $local_path = first_index { Cwd::realpath($_) eq $RT::LocalLibPath } @INC;
+    if ($local_path >= 0 ) {
+        splice(@INC, $local_path+1, 0, $add);
+    }
+    else {
+        push @INC, $add;
+    }
+
     return $self;
 }
 
+sub DESTROY {
+    my $self = shift;
+
+}
 
 =head2 Name
 
diff --git a/lib/RT/Test.pm b/lib/RT/Test.pm
index 3d24c02..31c98a1 100644
--- a/lib/RT/Test.pm
+++ b/lib/RT/Test.pm
@@ -140,8 +140,6 @@ sub import {
 
     $class->bootstrap_db( %args );
 
-    RT::InitPluginPaths();
-
     RT::ConnectToDatabase()
         unless $args{nodb};
 
diff --git a/t/plugins/api.t b/t/plugins/api.t
index 49d9061..b97940d 100644
--- a/t/plugins/api.t
+++ b/t/plugins/api.t
@@ -11,7 +11,7 @@ is_deeply([RT->PluginDirs('lib')], []);
 ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
 RT->Config->Set('Plugins',qw(Hello));
 RT->InitPluginPaths;
-
+RT->Plugins;
 ok(grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
 
 is_deeply([RT->PluginDirs('lib')], ["$RT::PluginPath/Hello/lib"], 'plugin lib dir found');

commit 2fe98c5b610e0cded32aeb1a90277dc3bcb24572
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Mon Oct 18 21:08:07 2010 +0900

    Don't need InitPluginPaths to be called in Init anymore

diff --git a/lib/RT.pm b/lib/RT.pm
index 9bb2c53..367182f 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -139,8 +139,6 @@ sub Init {
 
     CheckPerlRequirements();
 
-    InitPluginPaths();
-
     #Get a database connection
     ConnectToDatabase();
     InitSystemObjects();

commit 308f619953a45104ddc1ddce3b106160e7c2edf4
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Mon Oct 18 21:44:35 2010 +0900

    inc path maintenance

diff --git a/lib/RT/Plugin.pm b/lib/RT/Plugin.pm
index 410d8fa..d0f6fba 100644
--- a/lib/RT/Plugin.pm
+++ b/lib/RT/Plugin.pm
@@ -80,13 +80,17 @@ sub new {
     else {
         push @INC, $add;
     }
+    $self->{_added_inc_path} = $add;
 
     return $self;
 }
 
 sub DESTROY {
     my $self = shift;
-
+    my $inc_path = first_index { Cwd::realpath($_) eq $self->{_added_inc_path} } @INC;
+    if ($inc_path >= 0 ) {
+        splice(@INC, $inc_path, 1);
+    }
 }
 
 =head2 Name
diff --git a/t/plugins/api.t b/t/plugins/api.t
index b97940d..f666ee5 100644
--- a/t/plugins/api.t
+++ b/t/plugins/api.t
@@ -5,7 +5,7 @@ use File::Basename qw(basename dirname);
 require RT;
 $RT::PluginPath = abs_path(dirname($0)).'/_plugins';
 
-use RT::Test nodb => 1, tests => 5;
+use RT::Test nodb => 1, tests => 7;
 
 is_deeply([RT->PluginDirs('lib')], []);
 ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
@@ -20,3 +20,9 @@ require RT::Interface::Web::Handler;
 
 is_deeply({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1],
           ['plugin-Hello', $RT::PluginPath.'/Hello/html']);
+
+# reset
+RT->Config->Set('Plugins',qw());
+RT->InitPluginPaths;
+ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);
+is({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1][0],'standard');

commit 031cb91fd0354f889aed50b13fd3a559ad82e3c9
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Mon Oct 18 22:23:07 2010 +0900

    basic probe

diff --git a/lib/RT/Plugin.pm b/lib/RT/Plugin.pm
index d0f6fba..3bb31e3 100644
--- a/lib/RT/Plugin.pm
+++ b/lib/RT/Plugin.pm
@@ -128,7 +128,6 @@ Takes a name of a given plugin and return its base path.
 
 =cut
 
-
 sub BasePathFor {
     my ($class, $name) = @_;
 
@@ -139,6 +138,23 @@ sub BasePathFor {
     return -d $local_base ? $local_base : $base_base;
 }
 
+=head2 AvailablePlugins($plugin_path)
+
+=cut
+
+sub AvailablePlugins {
+    my ($class, $plugin_path) = @_;
+    my @res;
+    my @paths = $plugin_path ? ($plugin_path) : ($RT::LocalPluginPath, $RT::PluginPath);
+    for my $abs_path (map { <$_/*> } @paths) {
+        my ($dir, $name) = $abs_path =~ m|(.*)/([^/]+)$|;
+        # ensure no cascading
+        next if $class->BasePathFor($name) ne $abs_path;
+        push @res, $name;
+    }
+    return \@res;
+}
+
 =head2 ComponentRoot
 
 Returns the directory this plugin has installed its L<HTML::Mason> templates into
diff --git a/t/plugins/probe.t b/t/plugins/probe.t
new file mode 100644
index 0000000..4b5bd8c
--- /dev/null
+++ b/t/plugins/probe.t
@@ -0,0 +1,32 @@
+#!perl
+use Cwd qw(abs_path);
+use File::Basename qw(basename dirname);
+
+require RT;
+$RT::PluginPath = abs_path(dirname($0)).'/_plugins';
+$RT::LocalPluginPath = abs_path(dirname($0)).'/_plugins_null';
+
+use RT::Test nodb => 1, tests => 7;
+
+is_deeply( RT::Plugin->AvailablePlugins, ['Hello'] );
+__END__
+is_deeply([RT->AvailablePlugins]);
+
+ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
+RT->Config->Set('Plugins',qw(Hello));
+RT->InitPluginPaths;
+RT->Plugins;
+ok(grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
+
+is_deeply([RT->PluginDirs('lib')], ["$RT::PluginPath/Hello/lib"], 'plugin lib dir found');
+
+require RT::Interface::Web::Handler;
+
+is_deeply({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1],
+          ['plugin-Hello', $RT::PluginPath.'/Hello/html']);
+
+# reset
+RT->Config->Set('Plugins',qw());
+RT->InitPluginPaths;
+ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);
+is({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1][0],'standard');

commit 370cd0d1edbb264e3e566442e95b7f0db0ebd705
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Tue Oct 19 13:14:23 2010 +0900

    Do INC maintenance for enabled plugin only.

diff --git a/lib/RT.pm b/lib/RT.pm
index 367182f..b333f26 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -607,6 +607,7 @@ sub InitPlugins {
     require RT::Plugin;
     foreach my $plugin_name (grep $_, RT->Config->Get('Plugins')) {
         my $plugin = RT::Plugin->new(name => $plugin_name);
+        $plugin->Enable;
         $plugin_name->require;
         die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR);
         push @plugins, $plugin;
diff --git a/lib/RT/Plugin.pm b/lib/RT/Plugin.pm
index 3bb31e3..bdc37fc 100644
--- a/lib/RT/Plugin.pm
+++ b/lib/RT/Plugin.pm
@@ -51,6 +51,7 @@ use strict;
 
 package RT::Plugin;
 use File::ShareDir;
+use Class::Accessor "antlers";
 
 =head1 NAME
 
@@ -67,11 +68,18 @@ it cares about is 'name', the name of this plugin.
 
 use List::MoreUtils qw(first_index);
 
+has _added_inc_path => (is => "rw", isa => "Str");
+
 sub new {
     my $class = shift;
     my $args ={@_};
     my $self = bless $args, $class;
 
+    return $self;
+}
+
+sub Enable {
+    my $self = shift;
     my $add = $self->Path("lib");
     my $local_path = first_index { Cwd::realpath($_) eq $RT::LocalLibPath } @INC;
     if ($local_path >= 0 ) {
@@ -80,14 +88,13 @@ sub new {
     else {
         push @INC, $add;
     }
-    $self->{_added_inc_path} = $add;
-
-    return $self;
+    $self->_added_inc_path( $add );
 }
 
 sub DESTROY {
     my $self = shift;
-    my $inc_path = first_index { Cwd::realpath($_) eq $self->{_added_inc_path} } @INC;
+    my $added = $self->_added_inc_path or return;
+    my $inc_path = first_index { Cwd::realpath($_) eq $added } @INC;
     if ($inc_path >= 0 ) {
         splice(@INC, $inc_path, 1);
     }

commit 039b8989a626f4aa5ba86b39dbb023e1aa165965
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Tue Oct 19 13:16:46 2010 +0900

    Make Name an accessor

diff --git a/lib/RT.pm b/lib/RT.pm
index b333f26..c9f744a 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -606,7 +606,7 @@ sub InitPlugins {
     my @plugins;
     require RT::Plugin;
     foreach my $plugin_name (grep $_, RT->Config->Get('Plugins')) {
-        my $plugin = RT::Plugin->new(name => $plugin_name);
+        my $plugin = RT::Plugin->new(Name => $plugin_name);
         $plugin->Enable;
         $plugin_name->require;
         die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR);
diff --git a/lib/RT/Plugin.pm b/lib/RT/Plugin.pm
index bdc37fc..b844768 100644
--- a/lib/RT/Plugin.pm
+++ b/lib/RT/Plugin.pm
@@ -69,6 +69,8 @@ it cares about is 'name', the name of this plugin.
 use List::MoreUtils qw(first_index);
 
 has _added_inc_path => (is => "rw", isa => "Str");
+has Name => (is => "rw", isa => "Str");
+
 
 sub new {
     my $class = shift;
@@ -100,17 +102,6 @@ sub DESTROY {
     }
 }
 
-=head2 Name
-
-Returns a human-readable name for this plugin.
-
-=cut
-
-sub Name { 
-    my $self = shift;
-    return $self->{name};
-}
-
 =head2 Path
 
 Takes a name of sub directory and returns its full path, for example:
@@ -124,7 +115,7 @@ See also L</ComponentRoot>, L</PoDir> and other shortcut methods.
 sub Path {
     my $self   = shift;
     my $subdir = shift;
-    my $res = $self->BasePathFor($self->{'name'});
+    my $res = $self->BasePathFor($self->Name);
     $res .= "/$subdir" if defined $subdir && length $subdir;
     return $res;
 }

commit eaa8358a2ab8dfcfe49dc86adbccc05b92f2927b
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Tue Oct 19 14:30:05 2010 +0900

    Helpers for plugin probing.

diff --git a/lib/RT/Plugin.pm b/lib/RT/Plugin.pm
index b844768..b7bc099 100644
--- a/lib/RT/Plugin.pm
+++ b/lib/RT/Plugin.pm
@@ -52,6 +52,7 @@ use strict;
 package RT::Plugin;
 use File::ShareDir;
 use Class::Accessor "antlers";
+use Parse::CPAN::Meta;
 
 =head1 NAME
 
@@ -70,7 +71,8 @@ use List::MoreUtils qw(first_index);
 
 has _added_inc_path => (is => "rw", isa => "Str");
 has Name => (is => "rw", isa => "Str");
-
+has Description => (is => "rw", isa => "Str");
+has BasePath => (is => "rw", isa => "Str");
 
 sub new {
     my $class = shift;
@@ -115,7 +117,7 @@ See also L</ComponentRoot>, L</PoDir> and other shortcut methods.
 sub Path {
     my $self   = shift;
     my $subdir = shift;
-    my $res = $self->BasePathFor($self->Name);
+    my $res = $self->BasePath || $self->BasePathFor($self->Name);
     $res .= "/$subdir" if defined $subdir && length $subdir;
     return $res;
 }
@@ -148,9 +150,28 @@ sub AvailablePlugins {
         my ($dir, $name) = $abs_path =~ m|(.*)/([^/]+)$|;
         # ensure no cascading
         next if $class->BasePathFor($name) ne $abs_path;
-        push @res, $name;
+        push @res, $class->ProbePlugin($name);
     }
-    return \@res;
+
+    # XXX: look for collision and warn
+    my %seen;
+    return { map { $seen{$_->Name}++ ? () : ($_->Name => $_) } @res };
+}
+
+sub ProbePlugin {
+    my ($class, $name) = @_;
+    my $base_path = $class->BasePathFor($name);
+    my $meta;
+    if (-e "$base_path/META.yml") {
+        ($meta) = Parse::CPAN::Meta::LoadFile( "$base_path/META.yml" ) or return;
+    }
+    else {
+        $meta = { name => $name };
+    }
+
+    return $class->new(Name => $meta->{name},
+                       Description => $meta->{abstract},
+                       BasePath => $base_path);
 }
 
 =head2 ComponentRoot
diff --git a/sbin/rt-test-dependencies.in b/sbin/rt-test-dependencies.in
index f9aae9d..e4f9e15 100755
--- a/sbin/rt-test-dependencies.in
+++ b/sbin/rt-test-dependencies.in
@@ -213,6 +213,7 @@ List::MoreUtils
 Net::CIDR
 Regexp::Common::net::CIDR
 Regexp::IPv6
+Parse::CPAN::Meta
 .
 
 $deps{'MASON'} = [ text_to_hash( << '.') ];
diff --git a/t/plugins/probe.t b/t/plugins/probe.t
index 4b5bd8c..b349399 100644
--- a/t/plugins/probe.t
+++ b/t/plugins/probe.t
@@ -6,27 +6,9 @@ require RT;
 $RT::PluginPath = abs_path(dirname($0)).'/_plugins';
 $RT::LocalPluginPath = abs_path(dirname($0)).'/_plugins_null';
 
-use RT::Test nodb => 1, tests => 7;
+use RT::Test nodb => 1, tests => 2;
 
-is_deeply( RT::Plugin->AvailablePlugins, ['Hello'] );
-__END__
-is_deeply([RT->AvailablePlugins]);
-
-ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
-RT->Config->Set('Plugins',qw(Hello));
-RT->InitPluginPaths;
-RT->Plugins;
-ok(grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
-
-is_deeply([RT->PluginDirs('lib')], ["$RT::PluginPath/Hello/lib"], 'plugin lib dir found');
-
-require RT::Interface::Web::Handler;
-
-is_deeply({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1],
-          ['plugin-Hello', $RT::PluginPath.'/Hello/html']);
-
-# reset
-RT->Config->Set('Plugins',qw());
-RT->InitPluginPaths;
-ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);
-is({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1][0],'standard');
+my $plugins = RT::Plugin->AvailablePlugins;
+is_deeply( [ keys %$plugins ], [qw(Hello)]);
+my $hello = $plugins->{Hello};
+is($hello->BasePath, "$RT::PluginPath/Hello");

commit 54983fc93de298e7f22d9e223646589074d0d0c9
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Tue Oct 19 14:48:37 2010 +0900

    do plugin probing

diff --git a/lib/RT.pm b/lib/RT.pm
index c9f744a..5b87586 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -538,6 +538,22 @@ also L</InitSystemObjects>.
 
 sub Nobody { return $Nobody }
 
+my $PLUGINS;
+my $LOADED_PLUGINS;
+
+=head2 ProbePlugins($reprobe)
+
+Probe for available plugins.  By default RT caches the plugins found, use C<$reprobe> to override the behaviour.
+
+=cut
+
+sub ProbePlugins {
+    my $self = shift;
+    my $reprobe = shift;
+    undef $PLUGINS if $reprobe;
+    $PLUGINS ||= RT::Plugin->AvailablePlugins;
+}
+
 =head2 Plugins
 
 Returns a listref of all Plugins currently configured for this RT instance.
@@ -545,14 +561,14 @@ You can define plugins by adding them to the @Plugins list in your RT_SiteConfig
 
 =cut
 
-my $PLUGINS;
 sub Plugins {
     my $self = shift;
-    unless ($PLUGINS) {
-        $self->InitPluginPaths;
-        $PLUGINS = [$self->InitPlugins];
+    $self->ProbePlugins;
+
+    unless ($LOADED_PLUGINS) {
+        $LOADED_PLUGINS = [$self->InitPlugins];
     }
-    return $PLUGINS;
+    return $LOADED_PLUGINS;
 }
 
 =head2 PluginDirs
@@ -564,6 +580,7 @@ is loaded to load plugins' configs.
 
 =cut
 
+
 sub PluginDirs {
     my $self = shift;
     my $subdir = shift;
@@ -587,11 +604,13 @@ In case F<local/lib> isn't in @INC, append them to @INC
 =cut
 
 sub InitPluginPaths {
+    warn "DEPRECATED";
     my $self = shift || __PACKAGE__;
 
-    if ($PLUGINS) {
+    $self->ProbePlugins(1);
+    if ($LOADED_PLUGINS) {
         Carp::carp "reinitializing plugin paths";
-        $PLUGINS = undef;
+        $LOADED_PLUGINS = undef;
     }
 }
 
@@ -606,7 +625,11 @@ sub InitPlugins {
     my @plugins;
     require RT::Plugin;
     foreach my $plugin_name (grep $_, RT->Config->Get('Plugins')) {
-        my $plugin = RT::Plugin->new(Name => $plugin_name);
+        my $plugin = $PLUGINS->{$plugin_name};
+        unless ($plugin) {
+            # XXX: this is mostly for testing for rt plugin dists.
+            $PLUGINS->{$plugin_name} = $plugin = RT::Plugin->new(Name => $plugin_name);
+        }
         $plugin->Enable;
         $plugin_name->require;
         die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR);

commit f593fe54919e68edf7bc1637143467d908e1d79d
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Tue Oct 19 14:57:11 2010 +0900

    Make InitPlugins more standalone

diff --git a/lib/RT.pm b/lib/RT.pm
index 5b87586..7fdbd3b 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -566,7 +566,7 @@ sub Plugins {
     $self->ProbePlugins;
 
     unless ($LOADED_PLUGINS) {
-        $LOADED_PLUGINS = [$self->InitPlugins];
+        $self->InitPlugins;
     }
     return $LOADED_PLUGINS;
 }
@@ -622,7 +622,7 @@ Initialze all Plugins found in the RT configuration file, setting up their lib a
 
 sub InitPlugins {
     my $self    = shift;
-    my @plugins;
+    $LOADED_PLUGINS ||= [];
     require RT::Plugin;
     foreach my $plugin_name (grep $_, RT->Config->Get('Plugins')) {
         my $plugin = $PLUGINS->{$plugin_name};
@@ -630,12 +630,15 @@ sub InitPlugins {
             # XXX: this is mostly for testing for rt plugin dists.
             $PLUGINS->{$plugin_name} = $plugin = RT::Plugin->new(Name => $plugin_name);
         }
+        next if $plugin->Enabled;
+
         $plugin->Enable;
         $plugin_name->require;
         die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR);
-        push @plugins, $plugin;
+        push @$LOADED_PLUGINS, $plugin;
     }
-    return @plugins;
+
+    return @$LOADED_PLUGINS;
 }
 
 
diff --git a/lib/RT/Plugin.pm b/lib/RT/Plugin.pm
index b7bc099..41d6c9b 100644
--- a/lib/RT/Plugin.pm
+++ b/lib/RT/Plugin.pm
@@ -71,6 +71,7 @@ use List::MoreUtils qw(first_index);
 
 has _added_inc_path => (is => "rw", isa => "Str");
 has Name => (is => "rw", isa => "Str");
+has Enabled => (is => "rw", isa => "Bool");
 has Description => (is => "rw", isa => "Str");
 has BasePath => (is => "rw", isa => "Str");
 
@@ -92,6 +93,7 @@ sub Enable {
     else {
         push @INC, $add;
     }
+    $self->Enabled(1);
     $self->_added_inc_path( $add );
 }
 

commit a3acc03fdcb0a7dfe666f0b2b162480e01ffbe34
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Mon Nov 8 12:40:40 2010 +0800

    Avoid using InitPluginPaths, which is deprecated

diff --git a/lib/RT.pm b/lib/RT.pm
index 7fdbd3b..d70b90c 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -604,14 +604,11 @@ In case F<local/lib> isn't in @INC, append them to @INC
 =cut
 
 sub InitPluginPaths {
-    warn "DEPRECATED";
+    Carp::carp "DEPRECATED";
     my $self = shift || __PACKAGE__;
 
     $self->ProbePlugins(1);
-    if ($LOADED_PLUGINS) {
-        Carp::carp "reinitializing plugin paths";
-        $LOADED_PLUGINS = undef;
-    }
+    $self->UnloadPlugins();
 }
 
 =head2 InitPlugins
@@ -641,6 +638,10 @@ sub InitPlugins {
     return @$LOADED_PLUGINS;
 }
 
+sub UnloadPlugins {
+    my $self = shift;
+    $LOADED_PLUGINS = undef;
+}
 
 sub InstallMode {
     my $self = shift;
diff --git a/lib/RT/Test.pm b/lib/RT/Test.pm
index 31c98a1..7f331f3 100644
--- a/lib/RT/Test.pm
+++ b/lib/RT/Test.pm
@@ -434,7 +434,8 @@ sub bootstrap_plugins {
     };
 
     RT->Config->Set( Plugins => @plugins );
-    RT->InitPluginPaths();
+    RT->ProbePlugins(1);
+    RT->UnloadPlugins();
 
     my $dba_dbh;
     $dba_dbh = _get_dbh(
diff --git a/t/plugins/api.t b/t/plugins/api.t
index f666ee5..7fedd97 100644
--- a/t/plugins/api.t
+++ b/t/plugins/api.t
@@ -10,8 +10,11 @@ use RT::Test nodb => 1, tests => 7;
 is_deeply([RT->PluginDirs('lib')], []);
 ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
 RT->Config->Set('Plugins',qw(Hello));
-RT->InitPluginPaths;
+
+RT->ProbePlugins(1);
+RT->UnloadPlugins;
 RT->Plugins;
+
 ok(grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
 
 is_deeply([RT->PluginDirs('lib')], ["$RT::PluginPath/Hello/lib"], 'plugin lib dir found');
@@ -23,6 +26,8 @@ is_deeply({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1],
 
 # reset
 RT->Config->Set('Plugins',qw());
-RT->InitPluginPaths;
+RT->ProbePlugins(1);
+RT->UnloadPlugins;
+
 ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);
 is({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1][0],'standard');

commit 35d35fd1f549ceee85dd6dfa99713a29c6e83a52
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Tue Nov 30 20:36:39 2010 +0800

    basic server restart protocol

diff --git a/lib/RT.pm b/lib/RT.pm
index d70b90c..dfb9b7f 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -643,6 +643,30 @@ sub UnloadPlugins {
     $LOADED_PLUGINS = undef;
 }
 
+
+=head2 RestartRequired
+
+=cut
+
+sub RestartRequired {
+    my ($class, $arg) = @_;
+    my $restart_file = File::Spec->catdir( $VarPath, 'restart' );
+
+    if ($arg) {
+        my $atime = my $mtime = time;
+        if (-e $restart_file) {
+            utime $atime, $mtime, $restart_file;
+        }
+        else {
+            open my $fh, '>', $restart_file or die $!;
+            close $fh;
+        }
+        return 1;
+    }
+
+    return -e $restart_file && -M $restart_file < 0;
+}
+
 sub InstallMode {
     my $self = shift;
     if (@_) {
diff --git a/share/html/Admin/index.html b/share/html/Admin/index.html
index 0c941e4..8988f8f 100755
--- a/share/html/Admin/index.html
+++ b/share/html/Admin/index.html
@@ -47,6 +47,13 @@
 %# END BPS TAGGED BLOCK }}}
 <& /Admin/Elements/Header, Title => loc('RT Administration') &>
 <& /Elements/Tabs &>
+
+% if (RT->RestartRequired) {
+<&| /Widgets/TitleBox, title => loc('Server restart required') &>
+Some configuration that you changed requires the web server to be restarted
+</&>
+% }
+
 <& /Elements/ListMenu, items => $tabs &>
 % if (RT->Config->Get('ShowRTNews')) {
 <& /Admin/Elements/Newsbox &>

commit 352c64fcd471c1cb5ab4315fc522f458eb2af5fc
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Fri Nov 12 12:25:58 2010 +0800

    catch up with the new menu system

diff --git a/share/html/Admin/Global/index.html b/share/html/Admin/Global/Plugins.html
old mode 100755
new mode 100644
similarity index 55%
copy from share/html/Admin/Global/index.html
copy to share/html/Admin/Global/Plugins.html
index f7667cf..c7aafbc
--- a/share/html/Admin/Global/index.html
+++ b/share/html/Admin/Global/Plugins.html
@@ -45,40 +45,33 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<& /Admin/Elements/Header, Title => loc('Admin/Global configuration') &>
+<& /Admin/Elements/Header, Title => $title &>
 <& /Elements/Tabs &>
-    <& /Elements/ListMenu, items => $items &>
-<%INIT>
-  my $items = {
-                
-               A => { title => loc('Scrips'),
-                           text => loc('Modify scrips which apply to all queues'),
-                           path => '/Admin/Global/Scrips.html',
-                         },
-               B => { title => loc('Templates'),
-                        text => loc('Edit system templates'),
-                        path => '/Admin/Global/Templates.html',
-                      },
-              
-                F => { title => loc('Custom Fields'),
-                text => loc('Modify global custom fields'),
-                        path => '/Admin/Global/CustomFields/index.html',
-                        },
 
-                G => { title => loc('Group Rights'),
-                                text => loc('Modify global group rights'),
-                                path => '/Admin/Global/GroupRights.html',
-                      },
-                H => { title => loc('User Rights'),
-                                text => loc('Modify global user rights'),
-                                path => '/Admin/Global/UserRights.html',
-                      },
-                I => { title => loc('RT at a glance'),
-                                text => loc('Modify the default "RT at a glance" view'),
-                                path => '/Admin/Global/MyRT.html',
-                      },
-                J => { title => loc('Theme'),
-                                path => '/Admin/Global/Theme.html',
-                      },
-};
-</%INIT>
+<style type="text/css">
+li.plugin-enabled {
+  background: lightgreen;
+}
+</style>
+
+
+<& /Elements/ListActions, actions => \@results &>
+<h1>RT Features</h1>
+<ul>
+% for my $plugin (values %$plugins) {
+% my $enabled = $plugin->Enabled;
+<li class="plugin-<% $enabled ? 'enabled' : 'disabled'%>">
+<span><% $plugin->Name %></span>
+<span><% $plugin->Description || loc('(No Descrition') %></span>
+<span><% $enabled ? 'Enabled' : 'Not enabled' %></span>
+</li>
+% }
+
+</ul>
+
+<%init>
+my $title = loc("RT Features");
+my (@results);
+
+my $plugins = RT->ProbePlugins;
+</%init>
diff --git a/share/html/Admin/Global/index.html b/share/html/Admin/Global/index.html
index f7667cf..eaa2c42 100755
--- a/share/html/Admin/Global/index.html
+++ b/share/html/Admin/Global/index.html
@@ -80,5 +80,8 @@
                 J => { title => loc('Theme'),
                                 path => '/Admin/Global/Theme.html',
                       },
+                K => { title => loc('RT Features'),
+                                path => '/Admin/Global/Plugins.html',
+                      },
 };
 </%INIT>
diff --git a/share/html/Elements/Tabs b/share/html/Elements/Tabs
index 5152d55..b577558 100755
--- a/share/html/Elements/Tabs
+++ b/share/html/Elements/Tabs
@@ -172,6 +172,7 @@ if ( $request_path !~ qr{^/SelfService/} ) {
         $admin_global->child( 'user-rights' => title => loc('User Rights'), path => '/Admin/Global/UserRights.html', );
         $admin_global->child( 'my-rt' => title => loc('RT at a glance'), path => '/Admin/Global/MyRT.html', );
         $admin_global->child( theme => title => loc('Theme'), path => '/Admin/Global/Theme.html', );
+        $admin_global->child( 'rt-features' => title => loc('RT Features'), path => '/Admin/Global/Plugins.html', );
 
         my $admin_tools = $admin->child( tools => title => loc('Tools'), path => '/Admin/Tools/', );
         $admin_tools->child( configuration => title => loc('System Configuration'), path => '/Admin/Tools/Configuration.html', );

commit 887ee4654147c8038548b8137186daf07264f4fb
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Fri Nov 12 12:32:51 2010 +0800

    Move plugin pm file require to RT::Plugin->Enable

diff --git a/lib/RT.pm b/lib/RT.pm
index dfb9b7f..791097c 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -629,9 +629,15 @@ sub InitPlugins {
         }
         next if $plugin->Enabled;
 
-        $plugin->Enable;
-        $plugin_name->require;
-        die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR);
+        eval { $plugin->Enable; 1 }
+            or do {
+                # XXX: the rt bootstrapping sequence loads RT_Config
+                # first, which requires scanning plugin directories,
+                # so the very first initplugins calls is actually
+                # before initlogging.
+                warn "Unable to load plugin: $plugin_name: $@";
+                next;
+            };
         push @$LOADED_PLUGINS, $plugin;
     }
 
diff --git a/lib/RT/Plugin.pm b/lib/RT/Plugin.pm
index 41d6c9b..20e3ee1 100644
--- a/lib/RT/Plugin.pm
+++ b/lib/RT/Plugin.pm
@@ -53,6 +53,7 @@ package RT::Plugin;
 use File::ShareDir;
 use Class::Accessor "antlers";
 use Parse::CPAN::Meta;
+use UNIVERSAL::require;
 
 =head1 NAME
 
@@ -93,6 +94,10 @@ sub Enable {
     else {
         push @INC, $add;
     }
+    my $module = $self->Name;
+    $module =~ s/-/::/g;
+    $module->require;
+    die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR);
     $self->Enabled(1);
     $self->_added_inc_path( $add );
 }

commit 35b9da49f8dfab70d9eae5d7feecbe91aef5f923
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Fri Nov 12 14:22:54 2010 +0800

    Allow plugin enabling through .enabled file

diff --git a/lib/RT.pm b/lib/RT.pm
index 791097c..183e4dc 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -621,7 +621,9 @@ sub InitPlugins {
     my $self    = shift;
     $LOADED_PLUGINS ||= [];
     require RT::Plugin;
-    foreach my $plugin_name (grep $_, RT->Config->Get('Plugins')) {
+    my %explicit_plugins = map { $_ => 1 } grep $_, RT->Config->Get('Plugins');
+
+    foreach my $plugin_name (keys %$PLUGINS) {
         my $plugin = $PLUGINS->{$plugin_name};
         unless ($plugin) {
             # XXX: this is mostly for testing for rt plugin dists.
@@ -629,16 +631,18 @@ sub InitPlugins {
         }
         next if $plugin->Enabled;
 
-        eval { $plugin->Enable; 1 }
-            or do {
-                # XXX: the rt bootstrapping sequence loads RT_Config
-                # first, which requires scanning plugin directories,
-                # so the very first initplugins calls is actually
-                # before initlogging.
-                warn "Unable to load plugin: $plugin_name: $@";
-                next;
-            };
-        push @$LOADED_PLUGINS, $plugin;
+        if ( $explicit_plugins{$plugin_name} || -e $plugin->Path(".enabled")) {
+            eval { $plugin->Enable; 1 }
+                or do {
+                    # XXX: the rt bootstrapping sequence loads RT_Config
+                    # first, which requires scanning plugin directories,
+                    # so the very first initplugins calls is actually
+                    # before initlogging.
+                    warn "Unable to load plugin: $plugin_name: $@";
+                    next;
+                };
+            push @$LOADED_PLUGINS, $plugin;
+        }
     }
 
     return @$LOADED_PLUGINS;

commit b10146d01e21657d4444c0ab3939e0a17fc5dbbb
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Fri Nov 12 15:28:47 2010 +0800

    Fix explicit plugin loading

diff --git a/lib/RT.pm b/lib/RT.pm
index 183e4dc..28318e7 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -631,7 +631,9 @@ sub InitPlugins {
         }
         next if $plugin->Enabled;
 
-        if ( $explicit_plugins{$plugin_name} || -e $plugin->Path(".enabled")) {
+        my $plugin_module = $plugin_name;
+        $plugin_module =~ s/-/::/g;
+        if ( $explicit_plugins{$plugin_module} || -e $plugin->Path(".enabled")) {
             eval { $plugin->Enable; 1 }
                 or do {
                     # XXX: the rt bootstrapping sequence loads RT_Config

commit 545b938c40cbd5df268bc4541def26b6aa146a22
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Fri Nov 12 15:29:34 2010 +0800

    The plugin enabling and disabling UI.

diff --git a/lib/RT.pm b/lib/RT.pm
index 28318e7..145d485 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -623,6 +623,12 @@ sub InitPlugins {
     require RT::Plugin;
     my %explicit_plugins = map { $_ => 1 } grep $_, RT->Config->Get('Plugins');
 
+    for (keys %explicit_plugins) {
+        s/::/-/g;
+        warn "Unable to find plugin: $_, skipping"
+            unless $PLUGINS->{$_};
+    }
+
     foreach my $plugin_name (keys %$PLUGINS) {
         my $plugin = $PLUGINS->{$plugin_name};
         unless ($plugin) {
diff --git a/share/html/Admin/Global/Plugins.html b/share/html/Admin/Global/Plugins.html
index c7aafbc..236c8ba 100644
--- a/share/html/Admin/Global/Plugins.html
+++ b/share/html/Admin/Global/Plugins.html
@@ -58,12 +58,22 @@ li.plugin-enabled {
 <& /Elements/ListActions, actions => \@results &>
 <h1>RT Features</h1>
 <ul>
-% for my $plugin (values %$plugins) {
+% for my $plugin_name (sort keys %$plugins) {
+% my $plugin = $plugins->{$plugin_name};
 % my $enabled = $plugin->Enabled;
 <li class="plugin-<% $enabled ? 'enabled' : 'disabled'%>">
 <span><% $plugin->Name %></span>
-<span><% $plugin->Description || loc('(No Descrition') %></span>
-<span><% $enabled ? 'Enabled' : 'Not enabled' %></span>
+<span><% $plugin->Description || loc('(No Description)') %></span>
+  <form method="POST">
+    <input name="PluginName" value="<% $plugin->Name %>" type="hidden" />
+% if ($enabled) {
+    <input name="Disable" value="Disable" type="submit" />
+% }
+% else {
+    <input name="Enable" value="Enable" type="submit" />
+% }
+  </form>
+
 </li>
 % }
 
@@ -74,4 +84,57 @@ my $title = loc("RT Features");
 my (@results);
 
 my $plugins = RT->ProbePlugins;
+
+if ($PluginName) {
+    my $plugin = $plugins->{$PluginName};
+    unless ($plugin) {
+        push @results, loc("Plugin [_1] not found", $PluginName);
+        return;
+    }
+
+    my $plugin_enable = $plugin->Path(".enabled");
+    if ($Enable) {
+        if ( eval { die loc("You need to make the directory [_1] writable first.\n", $plugin->Path)
+                        unless -w $plugin->Path;
+                    $plugin->Enable;
+                    open my $fh, '>', $plugin_enable
+                        or die $!;
+                    close $fh;
+                    RT->RestartRequired(1);
+                    1 } ) {
+            push @results, loc("Plugin [_1] enabled.", $PluginName);
+            push @results, loc("Server restart required");
+        }
+        else {
+            push @results, loc("Failed to enable plugin [_1]: [_2].", $PluginName, $@);
+        }
+    }
+    elsif ($Disable) {
+        if (-e $plugin_enable) {
+            if ( eval { unlink($plugin_enable)
+                            or die loc("You need to make the directory [_1] writable first.", $plugin->Path);
+                        RT->RestartRequired(1);
+                        RT->UnloadPlugins;
+                        $plugins = RT->ProbePlugins(1);
+                        RT->InitPlugins;
+                        1 } ) {
+                push @results, loc("Plugin [_1] disabled.", $PluginName);
+                push @results, loc("Server restart required");
+            }
+            else {
+                push @results, loc("Failed to disable plugin [_1]: [_2].", $PluginName, $@);
+            }
+        }
+        else {
+            push @results, loc("Plugin [_1] is enabled through RT_SiteConfig.pm, please disable it there.",
+                               $PluginName);
+        }
+    }
+}
+
 </%init>
+<%args>
+$Enable => ''
+$Disable => ''
+$PluginName => ''
+</%args>

commit dc1798460111f5e65c019ebe5853b5e834096fea
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Fri Nov 12 15:31:38 2010 +0800

    the server restart infobox.

diff --git a/share/html/Admin/Elements/CheckRestart b/share/html/Admin/Elements/CheckRestart
new file mode 100644
index 0000000..700d980
--- /dev/null
+++ b/share/html/Admin/Elements/CheckRestart
@@ -0,0 +1,6 @@
+% if (RT->RestartRequired) {
+<&| /Widgets/TitleBox, title => loc('Server restart required') &>
+<% loc('Some configuration that you changed requires the web server to be restarted to be fully functional.') %>
+</&>
+% }
+
diff --git a/share/html/Admin/Global/Plugins.html b/share/html/Admin/Global/Plugins.html
index 236c8ba..42aaaa5 100644
--- a/share/html/Admin/Global/Plugins.html
+++ b/share/html/Admin/Global/Plugins.html
@@ -47,6 +47,7 @@
 %# END BPS TAGGED BLOCK }}}
 <& /Admin/Elements/Header, Title => $title &>
 <& /Elements/Tabs &>
+<& /Admin/Elements/CheckRestart &>
 
 <style type="text/css">
 li.plugin-enabled {
diff --git a/share/html/Admin/index.html b/share/html/Admin/index.html
index 8988f8f..8cb834b 100755
--- a/share/html/Admin/index.html
+++ b/share/html/Admin/index.html
@@ -47,13 +47,7 @@
 %# END BPS TAGGED BLOCK }}}
 <& /Admin/Elements/Header, Title => loc('RT Administration') &>
 <& /Elements/Tabs &>
-
-% if (RT->RestartRequired) {
-<&| /Widgets/TitleBox, title => loc('Server restart required') &>
-Some configuration that you changed requires the web server to be restarted
-</&>
-% }
-
+<& /Admin/Elements/CheckRestart &>
 <& /Elements/ListMenu, items => $tabs &>
 % if (RT->Config->Get('ShowRTNews')) {
 <& /Admin/Elements/Newsbox &>

commit 6fbd803766581c4df8554b5b1f9cff4b49518b9a
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Fri Nov 12 16:30:09 2010 +0800

    Maintain explicit plugin ordering if configured from siteconfig.

diff --git a/lib/RT.pm b/lib/RT.pm
index 145d485..6ad9311 100755
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -617,40 +617,42 @@ Initialze all Plugins found in the RT configuration file, setting up their lib a
 
 =cut
 
+sub _try_enable_plugin {
+    my ($self, $plugin_name, $explicit) = @_;
+
+    my $plugin = $PLUGINS->{$plugin_name};
+    unless ($plugin) {
+        # XXX: this is mostly for testing for rt plugin dists.
+        $PLUGINS->{$plugin_name} = $plugin = RT::Plugin->new(Name => $plugin_name);
+    }
+    return if $plugin->Enabled;
+
+    if ( $explicit || -e $plugin->Path(".enabled")) {
+        eval { $plugin->Enable; 1 }
+            or do {
+                # XXX: the rt bootstrapping sequence loads RT_Config
+                # first, which requires scanning plugin directories,
+                # so the very first initplugins calls is actually
+                # before initlogging.
+                warn "Unable to load plugin: $plugin_name: $@";
+                return;
+            };
+        push @$LOADED_PLUGINS, $plugin;
+    }
+}
+
 sub InitPlugins {
     my $self    = shift;
     $LOADED_PLUGINS ||= [];
     require RT::Plugin;
-    my %explicit_plugins = map { $_ => 1 } grep $_, RT->Config->Get('Plugins');
 
-    for (keys %explicit_plugins) {
+    for (grep $_, RT->Config->Get('Plugins')) {
         s/::/-/g;
-        warn "Unable to find plugin: $_, skipping"
-            unless $PLUGINS->{$_};
+        $self->_try_enable_plugin($_, 1);
     }
 
-    foreach my $plugin_name (keys %$PLUGINS) {
-        my $plugin = $PLUGINS->{$plugin_name};
-        unless ($plugin) {
-            # XXX: this is mostly for testing for rt plugin dists.
-            $PLUGINS->{$plugin_name} = $plugin = RT::Plugin->new(Name => $plugin_name);
-        }
-        next if $plugin->Enabled;
-
-        my $plugin_module = $plugin_name;
-        $plugin_module =~ s/-/::/g;
-        if ( $explicit_plugins{$plugin_module} || -e $plugin->Path(".enabled")) {
-            eval { $plugin->Enable; 1 }
-                or do {
-                    # XXX: the rt bootstrapping sequence loads RT_Config
-                    # first, which requires scanning plugin directories,
-                    # so the very first initplugins calls is actually
-                    # before initlogging.
-                    warn "Unable to load plugin: $plugin_name: $@";
-                    next;
-                };
-            push @$LOADED_PLUGINS, $plugin;
-        }
+    for (keys %$PLUGINS) {
+        $self->_try_enable_plugin($_);
     }
 
     return @$LOADED_PLUGINS;

commit 46592f62d9edb4fa1cefba69ce81b1e731de66db
Author: Chia-liang Kao <clkao at bestpractical.com>
Date:   Tue Nov 30 20:55:42 2010 +0800

    Fix plugin lib order in @INC.

diff --git a/lib/RT/Plugin.pm b/lib/RT/Plugin.pm
index 20e3ee1..af0c493 100644
--- a/lib/RT/Plugin.pm
+++ b/lib/RT/Plugin.pm
@@ -84,12 +84,20 @@ sub new {
     return $self;
 }
 
+# the @INC entry that plugins lib dirs should be pushed splice into.
+# it should be the one after local lib
+my $inc_anchor;
 sub Enable {
     my $self = shift;
     my $add = $self->Path("lib");
-    my $local_path = first_index { Cwd::realpath($_) eq $RT::LocalLibPath } @INC;
-    if ($local_path >= 0 ) {
-        splice(@INC, $local_path+1, 0, $add);
+    unless (defined $inc_anchor) {
+        my $anchor = first_index { Cwd::realpath($_) eq Cwd::realpath($RT::LocalLibPath) } @INC;
+        $inc_anchor = ($anchor == -1 || $anchor == $#INC) # not found or last
+            ? '' : Cwd::realpath($INC[$anchor+1]);
+    }
+    my $anchor_idx = first_index { Cwd::realpath($_) eq $inc_anchor } @INC;
+    if ($anchor_idx >= 0 ) {
+        splice(@INC, $anchor_idx, 0, $add);
     }
     else {
         push @INC, $add;
diff --git a/t/plugins/_plugins/World/html/NoAuth/hello b/t/plugins/_plugins/World/html/NoAuth/hello
new file mode 100644
index 0000000..216e97c
--- /dev/null
+++ b/t/plugins/_plugins/World/html/NoAuth/hello
@@ -0,0 +1 @@
+World
diff --git a/t/plugins/_plugins/World/lib/World.pm b/t/plugins/_plugins/World/lib/World.pm
new file mode 100644
index 0000000..1dfec57
--- /dev/null
+++ b/t/plugins/_plugins/World/lib/World.pm
@@ -0,0 +1,3 @@
+package World;
+
+1;
diff --git a/t/plugins/api.t b/t/plugins/api.t
index 7fedd97..c28a0b2 100644
--- a/t/plugins/api.t
+++ b/t/plugins/api.t
@@ -2,32 +2,61 @@
 use Cwd qw(abs_path);
 use File::Basename qw(basename dirname);
 
-require RT;
-$RT::PluginPath = abs_path(dirname($0)).'/_plugins';
+BEGIN {
+    require RT;
+    require RT::Generated;
+    unshift @INC, abs_path($RT::LocalLibPath);
+    $RT::LocalPluginPath = abs_path(dirname($0)).'/_plugins';
+}
 
-use RT::Test nodb => 1, tests => 7;
+use RT::Test nodb => 1, tests => 9;
 
 is_deeply([RT->PluginDirs('lib')], []);
-ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
+ok(!grep { $_ eq "$RT::LocalPluginPath/Hello/lib" } @INC);;
 RT->Config->Set('Plugins',qw(Hello));
 
 RT->ProbePlugins(1);
 RT->UnloadPlugins;
 RT->Plugins;
 
-ok(grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);;
+ok(grep { $_ eq "$RT::LocalPluginPath/Hello/lib" } @INC);;
 
-is_deeply([RT->PluginDirs('lib')], ["$RT::PluginPath/Hello/lib"], 'plugin lib dir found');
+is_deeply([RT->PluginDirs('lib')], ["$RT::LocalPluginPath/Hello/lib"], 'plugin lib dir found');
 
 require RT::Interface::Web::Handler;
 
 is_deeply({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1],
-          ['plugin-Hello', $RT::PluginPath.'/Hello/html']);
+          ['plugin-Hello', $RT::LocalPluginPath.'/Hello/html']);
 
 # reset
 RT->Config->Set('Plugins',qw());
 RT->ProbePlugins(1);
 RT->UnloadPlugins;
 
-ok(!grep { $_ eq "$RT::PluginPath/Hello/lib" } @INC);
-is({RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root}[1][0],'standard');
+ok(!grep { $_ eq "$RT::LocalPluginPath/Hello/lib" } @INC);
+is_deeply(
+    [map { $_->[0] }
+         @{ {RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root} }],
+    [qw(local standard)]
+);
+
+
+my %inc_seem = map { $_ => 1 } @INC;
+# reset
+RT->Config->Set('Plugins',qw(Hello World));
+RT->ProbePlugins(1);
+RT->UnloadPlugins;
+RT->Plugins;
+
+is_deeply([@INC[0..2]],
+          [map { abs_path($_) }
+               $RT::LocalLibPath,
+               "$RT::LocalPluginPath/Hello/lib",
+               "$RT::LocalPluginPath/World/lib"]);
+
+is_deeply(
+    [map { $_->[0] }
+         @{ {RT::Interface::Web::Handler->DefaultHandlerArgs}->{comp_root} }],
+    [qw(local plugin-Hello plugin-World standard)]
+);
+

-----------------------------------------------------------------------


More information about the Rt-commit mailing list