Commit cf0742d1 authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 530994: Allow extensions to specify where their template directory is (which…

Bug 530994: Allow extensions to specify where their template directory is (which allows CPAN-distributed extensions to have templates) Patch by Max Kanat-Alexander <mkanat@bugzilla.org> (module owner) a=mkanat
parent ab577dc1
......@@ -24,7 +24,9 @@ use strict;
use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Install::Util qw(extension_code_files);
use Bugzilla::Install::Util qw(
extension_code_files extension_template_directory
extension_package_directory);
use File::Basename;
use File::Spec;
......@@ -69,14 +71,7 @@ sub load {
$package = "${class}::$name";
}
# This allows people to override modify_inc in Config.pm, if they
# want to.
if ($package->can('modify_inc')) {
$package->modify_inc($config_file);
}
else {
modify_inc($package, $config_file);
}
__do_call($package, 'modify_inc', $config_file);
}
if ($map and defined $map->{$extension_file}) {
......@@ -151,19 +146,15 @@ sub load_all {
# directory of the extension.
sub modify_inc {
my ($class, $file) = @_;
my $lib_dir = File::Spec->catdir(dirname($file), 'lib');
# Allow Config.pm to override my_inc, if it wants to.
if ($class->can('my_inc')) {
unshift(@INC, sub { $class->my_inc($lib_dir, @_); });
}
else {
unshift(@INC, sub { my_inc($class, $lib_dir, @_); });
}
__do_call($class, 'package_dir', $file);
unshift(@INC, sub { __do_call($class, 'my_inc', @_) });
}
# This is what gets put into @INC by modify_inc.
sub my_inc {
my ($class, $lib_dir, undef, $file) = @_;
my ($class, undef, $file) = @_;
my $lib_dir = __do_call($class, 'lib_dir');
my @class_parts = split('::', $class);
my ($vol, $dir, $file_name) = File::Spec->splitpath($file);
my @dir_parts = File::Spec->splitdir($dir);
......@@ -191,6 +182,36 @@ sub my_inc {
use constant enabled => 1;
sub lib_dir {
my $invocant = shift;
my $package_dir = __do_call($invocant, 'package_dir');
return File::Spec->catdir($package_dir, 'lib');
}
sub template_dir { return extension_template_directory(@_); }
sub package_dir { return extension_package_directory(@_); }
######################
# Helper Subroutines #
######################
# In order to not conflict with extensions' private subroutines, any helpers
# here should start with a double underscore.
# This is for methods that can optionally be overridden in Config.pm.
# It falls back to the local implementation if $class cannot do
# the method. This is necessary because Config.pm is not a subclass of
# Bugzilla::Extension.
sub __do_call {
my ($class, $method, @args) = @_;
if ($class->can($method)) {
return $class->$method(@args);
}
my $function_ref;
{ no strict 'refs'; $function_ref = \&{$method}; }
return $function_ref->($class, @args);
}
1;
__END__
......@@ -389,6 +410,16 @@ your extension is a single file named C<Foo.pm>.
If any of this is confusing, just look at the code of the Example extension.
It uses this method to specify requirements.
=head2 Templates
Extensions store templates in a C<template> subdirectory of the extension.
(Obviously, this isn't available for extensions that aren't a directory.)
The format of this directory is exactly like the normal layout of Bugzilla's
C<template> directory--in fact, your extension's C<template> directory
becomes part of Bugzilla's template "search path" as described in
L<Bugzilla::Install::Util/template_include_path>.
=head2 Libraries
Extensions often want to have their own Perl modules. Your extension
......@@ -452,7 +483,16 @@ F<Config.pm> file, because CPAN itself will handle installing
the prerequisites of your module, so Bugzilla doesn't have to
worry about it.
=head3 Using a module distributed on CPAN
=head3 Templates in extensions distributed on CPAN
If your extension is F</usr/lib/perl5/Bugzilla/Extension/Foo.pm>,
then Bugzilla will look for templates in the directory
F</usr/lib/perl5/Bugzilla/Extension/Foo/template/>.
You can change this behavior by overriding the L</template_dir>
or L</package_dir> methods described lower down in this document.
=head3 Using an extension distributed on CPAN
There is a file named F<data/extensions/additional> in Bugzilla.
This is a plain-text file. Each line is the name of a module,
......@@ -482,18 +522,56 @@ By default, this will be C<undef> if you don't define it.
In addition to hooks, there are a few methods that your extension can
define to modify its behavior, if you want:
=head2 C<enabled>
=head2 Class Methods
This should return C<1> if this extension's hook code should be run
by Bugzilla, and C<0> otherwise.
These methods are called on your extension's class. (Like
C<< Bugzilla::Extension::Foo->some_method >>).
=head2 C<new>
=head3 C<new>
Once every request, this method is called on your extension in order
to create an "instance" of it. (Extensions are treated like objects--they
are instantiated once per request in Bugzilla, and then methods are
called on the object.)
=head2 Instance Methods
These are called on an instantiated Extension object.
=head3 C<enabled>
This should return C<1> if this extension's hook code should be run
by Bugzilla, and C<0> otherwise.
=head3 C<package_dir>
This returns the directory that your extension is located in.
If this is an extension that was installed via CPAN, the directory will
be the path to F<Bugzilla/Extension/Foo/>, if C<Foo.pm> is the name of your
extension.
If you want to override this method, and you have a F<Config.pm>, you must
override this method in F<Config.pm>.
=head3 C<template_dir>
The directory that your package's templates are in.
This defaults to the C<template> subdirectory of the L</package_dir>.
If you want to override this method, and you have a F<Config.pm>, you must
override this method in F<Config.pm>.
=head3 C<lib_dir>
The directory where your extension's libraries are.
This defaults to the C<lib> subdirectory of the L</package_dir>.
If you want to override this method, and you have a F<Config.pm>, you must
override this method in F<Config.pm>.
=head1 BUGZILLA::EXTENSION CLASS METHODS
These are used internally by Bugzilla to load and set up extensions.
......
......@@ -38,7 +38,9 @@ our @EXPORT_OK = qw(
bin_loc
get_version_and_os
extension_code_files
extension_package_directory
extension_requirement_packages
extension_template_directory
indicate_progress
install_string
include_languages
......@@ -168,6 +170,12 @@ sub extension_requirement_packages {
{ file => $file, returned => $name });
}
my $package = "Bugzilla::Extension::$name";
if ($package->can('package_dir')) {
$package->package_dir($file);
}
else {
extension_package_directory($package, $file);
}
$package_map{$file} = $package;
push(@$packages, $package);
}
......@@ -183,6 +191,42 @@ sub extension_requirement_packages {
return $packages;
}
# Used in this file and in Bugzilla::Extension.
sub extension_template_directory {
my $extension = shift;
my $class = ref($extension) || $extension;
my $base_dir = extension_package_directory($class);
return "$base_dir/template";
}
# For extensions that are in the extensions/ dir, this both sets and fetches
# the name of the directory that stores an extension's "stuff". We need this
# when determining the template directory for extensions (or other things
# that are relative to the extension's base directory).
sub extension_package_directory {
my ($invocant, $file) = @_;
my $class = ref($invocant) || $invocant;
my $var;
{ no strict 'refs'; $var = \${"${class}::EXTENSION_PACKAGE_DIR"}; }
if ($file) {
$$var = dirname($file);
}
my $value = $$var;
# This is for extensions loaded from data/extensions/additional.
if (!$value) {
my $short_path = $class;
$short_path =~ s/::/\//g;
$short_path .= ".pm";
my $long_path = $INC{$short_path};
die "$short_path is not in \%INC" if !$long_path;
$value = $long_path;
$value =~ s/\.pm//;
}
return $value;
}
sub indicate_progress {
my ($params) = @_;
my $current = $params->{current};
......@@ -338,8 +382,29 @@ sub _template_base_directories {
# First, we add extension template directories, because extension templates
# override standard templates. Extensions may be localized in the same way
# that Bugzilla templates are localized.
my @extensions = grep { -d "$_/template" } _extension_paths();
my @template_dirs = map { "$_/template" } @extensions;
#
# We use extension_requirement_packages instead of Bugzilla->extensions
# because this fucntion is called during the requirements phase of
# installation (so Bugzilla->extensions isn't available).
my $extensions = extension_requirement_packages();
my @template_dirs;
foreach my $extension (@$extensions) {
my $dir;
# If there's a template_dir method available in the extension
# package, then call it. Note that this has to be defined in
# Config.pm for extensions that have a Config.pm, to be effective
# during the Requirements phase of checksetup.pl.
if ($extension->can('template_dir')) {
$dir = $extension->template_dir;
}
else {
$dir = extension_template_directory($extension);
}
if (-d $dir) {
push(@template_dirs, $dir);
}
}
push(@template_dirs, bz_locations()->{'templatedir'});
return \@template_dirs;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment