Created
May 18, 2020 13:37
-
-
Save dylanwh/adf0575ce9acf10f5cb81677fb6acdf7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- bugzilla-5.2/Bugzilla/Extension.pm 2020-03-28 15:20:01.000000000 -0400 | |
+++ harmony/Bugzilla/Extension.pm 2020-05-18 09:35:52.000000000 -0400 | |
@@ -13,22 +13,51 @@ | |
use Bugzilla::Constants; | |
use Bugzilla::Error; | |
-use Bugzilla::Install::Util qw( | |
- extension_code_files extension_template_directory | |
- extension_package_directory extension_web_directory); | |
+use Bugzilla::Install::Util qw( extension_code_files ); | |
use File::Basename; | |
use File::Spec; | |
+use List::Util qw(any); | |
+use Scalar::Util qw(refaddr); | |
-#################### | |
-# Subclass Methods # | |
-#################### | |
+BEGIN { push @INC, \&INC_HOOK } | |
-sub new { | |
- my ($class, $params) = @_; | |
- $params ||= {}; | |
- bless $params, $class; | |
- return $params; | |
+sub INC_HOOK { | |
+ my (undef, $fake_file) = @_; | |
+ state $bz_locations = bz_locations(); | |
+ my ($vol, $dir, $file) = File::Spec->splitpath($fake_file); | |
+ my @dirs = grep { length $_ } File::Spec->splitdir($dir); | |
+ | |
+ if (@dirs > 2 && $dirs[0] eq 'Bugzilla' && $dirs[1] eq 'Extension') { | |
+ my $extension = $dirs[2]; | |
+ splice @dirs, 0, 3, File::Spec->splitdir($bz_locations->{extensionsdir}), | |
+ $extension, "lib"; | |
+ my $real_file | |
+ = Cwd::realpath(File::Spec->catpath($vol, File::Spec->catdir(@dirs), $file)); | |
+ | |
+ my $first = 1; | |
+ $INC{$fake_file} = $real_file; | |
+ my $found = open my $fh, '<', $real_file; | |
+ unless ($found) { | |
+ require Carp; | |
+ Carp::croak | |
+ "Can't locate $fake_file while looking for $real_file in \@INC (\@INC contains: @INC)"; | |
+ } | |
+ return sub { | |
+ no warnings; | |
+ if (!$first) { | |
+ return 0 if eof $fh; | |
+ $_ = readline $fh or return 0; | |
+ return 1; | |
+ } | |
+ else { | |
+ $_ = qq{# line 1 "$real_file"\n}; | |
+ $first = 0; | |
+ return 1; | |
+ } | |
+ }; | |
+ } | |
+ return; | |
} | |
####################################### | |
@@ -58,13 +87,10 @@ | |
} | |
$package = "${class}::$name"; | |
} | |
- | |
- __do_call($package, 'modify_inc', $config_file); | |
} | |
if ($map and defined $map->{$extension_file}) { | |
$package = $map->{$extension_file}; | |
- $package->modify_inc($extension_file) if !$config_file; | |
} | |
else { | |
my $name = require $extension_file; | |
@@ -73,7 +99,6 @@ | |
{extension => $extension_file, returned => $name}); | |
} | |
$package = "${class}::$name"; | |
- $package->modify_inc($extension_file) if !$config_file; | |
} | |
$class->_validate_package($package, $extension_file); | |
@@ -104,83 +129,18 @@ | |
sub load_all { | |
my $class = shift; | |
- my ($file_sets, $extra_packages) = extension_code_files(); | |
- my @packages; | |
+ state $EXTENSIONS = []; | |
+ return $EXTENSIONS if @$EXTENSIONS; | |
+ | |
+ my ($file_sets) = extension_code_files(); | |
foreach my $file_set (@$file_sets) { | |
my $package = $class->load(@$file_set); | |
- push(@packages, $package); | |
+ push(@$EXTENSIONS, $package); | |
} | |
- # Extensions from data/extensions/additional | |
- foreach my $package (@$extra_packages) { | |
- | |
- # Don't load an "additional" extension if we already have an extension | |
- # loaded with that name. | |
- next if grep($_ eq $package, @packages); | |
- | |
- # Untaint the package name | |
- $package =~ /([\w:]+)/; | |
- $package = $1; | |
- eval("require $package") || die $@; | |
- $package->_validate_package($package); | |
- push(@packages, $package); | |
- } | |
- | |
- return \@packages; | |
-} | |
- | |
-# Modifies @INC so that extensions can use modules like | |
-# "use Bugzilla::Extension::Foo::Bar", when Bar.pm is in the lib/ | |
-# directory of the extension. | |
-sub modify_inc { | |
- my ($class, $file) = @_; | |
- | |
- # Note that this package_dir call is necessary to set things up | |
- # for my_inc, even if we didn't take its return value. | |
- my $package_dir = __do_call($class, 'package_dir', $file); | |
- | |
- # Don't modify @INC for extensions that are just files in the extensions/ | |
- # directory. We don't want Bugzilla's base lib/CGI.pm being loaded as | |
- # Bugzilla::Extension::Foo::CGI or any other confusing thing like that. | |
- return if $package_dir eq bz_locations->{'extensionsdir'}; | |
- unshift(@INC, sub { __do_call($class, 'my_inc', @_) }); | |
-} | |
- | |
-# This is what gets put into @INC by modify_inc. | |
-sub my_inc { | |
- my ($class, undef, $file) = @_; | |
- | |
- # This avoids infinite recursion in case anything inside of this function | |
- # does a "require". (I know for sure that File::Spec->case_tolerant does | |
- # a "require" on Windows, for example.) | |
- return if $file !~ /^Bugzilla/; | |
- | |
- 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); | |
- | |
- # File::Spec::Win32 (any maybe other OSes) add an empty directory at the | |
- # end of @dir_parts. | |
- @dir_parts = grep { $_ ne '' } @dir_parts; | |
- | |
- # Validate that this is a sub-package of Bugzilla::Extension::Foo ($class). | |
- for (my $i = 0; $i < scalar(@class_parts); $i++) { | |
- return if !@dir_parts; | |
- if (File::Spec->case_tolerant) { | |
- return if lc($class_parts[$i]) ne lc($dir_parts[0]); | |
- } | |
- else { | |
- return if $class_parts[$i] ne $dir_parts[0]; | |
- } | |
- shift(@dir_parts); | |
- } | |
+ Bugzilla::Hook::finalize($EXTENSIONS); | |
- # For Bugzilla::Extension::Foo::Bar, this would look something like | |
- # extensions/Example/lib/Bar.pm | |
- my $resolved_path = File::Spec->catfile($lib_dir, @dir_parts, $file_name); | |
- open(my $fh, '<', $resolved_path); | |
- return $fh; | |
+ return $EXTENSIONS; | |
} | |
#################### | |
@@ -189,45 +149,21 @@ | |
use constant enabled => 1; | |
-sub lib_dir { | |
- my $invocant = shift; | |
- my $package_dir = __do_call($invocant, 'package_dir'); | |
- | |
- # For extensions that are just files in the extensions/ directory, | |
- # use the base lib/ dir as our "lib_dir". Note that Bugzilla never | |
- # uses lib_dir in this case, though, because modify_inc is prevented | |
- # from modifying @INC when we're just a file in the extensions/ directory. | |
- # So this particular code block exists just to make lib_dir return | |
- # something right in case an extension needs it for some odd reason. | |
- if ($package_dir eq bz_locations()->{'extensionsdir'}) { | |
- return bz_locations->{'ext_libpath'}; | |
- } | |
- return File::Spec->catdir($package_dir, 'lib'); | |
+sub package_dir { | |
+ my ($class) = @_; | |
+ state $bz_locations = bz_locations(); | |
+ my (undef, undef, $name) = split(/::/, $class); | |
+ return File::Spec->catdir($bz_locations->{extensionsdir}, $name); | |
} | |
-sub template_dir { return extension_template_directory(@_); } | |
-sub package_dir { return extension_package_directory(@_); } | |
-sub web_dir { return extension_web_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); | |
+sub template_dir { | |
+ my ($class) = @_; | |
+ return File::Spec->catdir($class->package_dir, "template"); | |
+} | |
+ | |
+sub web_dir { | |
+ my ($class) = @_; | |
+ return File::Spec->catdir($class->package_dir, "web"); | |
} | |
1; | |
@@ -240,7 +176,7 @@ | |
=head1 SYNOPSIS | |
-The following would be in F<extensions/Foo/Extension.pm> or | |
+The following would be in F<extensions/Foo/Extension.pm> or | |
F<extensions/Foo.pm>: | |
package Bugzilla::Extension::Foo | |
@@ -255,7 +191,7 @@ | |
__PACKAGE__->NAME; | |
Custom templates would go into F<extensions/Foo/template/en/default/>. | |
-L<Template hooks|/Template Hooks> would go into | |
+L<Template hooks|/Template Hooks> would go into | |
F<extensions/Foo/template/en/default/hook/>. | |
=head1 DESCRIPTION | |
@@ -266,14 +202,14 @@ | |
The L</SYNOPSIS> above gives a pretty good overview of what's basically | |
required to write an extension. This section gives more information | |
-on exactly how extensions work and how you write them. There is also a | |
+on exactly how extensions work and how you write them. There is also a | |
L<wiki page|https://wiki.mozilla.org/Bugzilla:Extension_Notes> with additional HOWTOs, tips and tricks. | |
=head2 Using F<extensions/create.pl> | |
There is a script, L<extensions::create>, that will set up the framework | |
of a new extension for you. To use it, pick a name for your extension | |
-and, in the base bugzilla directory, do: | |
+and, in the base Bugzilla directory, do: | |
C<extensions/create.pl NAME> | |
@@ -300,7 +236,7 @@ | |
=item 1 | |
If your extension will have only code and no templates or other files, | |
-you can create a simple C<.pm> file in the F<extensions/> directory. | |
+you can create a simple C<.pm> file in the F<extensions/> directory. | |
For example, if you wanted to create an extension called "Foo" using this | |
method, you would put your code into a file called F<extensions/Foo.pm>. | |
@@ -375,14 +311,14 @@ | |
During your subroutine, you may want to know what values were passed | |
as CGI arguments to the current script, or what arguments were passed to | |
-the current WebService method. You can get that data via | |
+the current WebService method. You can get that data via | |
L<Bugzilla/input_params>. | |
=head3 Adding New Hooks To Bugzilla | |
If you need a new hook for your extension and you want that hook to be | |
-added to Bugzilla itself, see our development process at | |
-L<http://wiki.mozilla.org/Bugzilla:Developers>. | |
+added to Bugzilla itself, see our development process at | |
+L<https://wiki.mozilla.org/Bugzilla:Developers>. | |
In order for a new hook to be accepted into Bugzilla, it has to work, | |
it must have documentation in L<Bugzilla::Hook>, and it must have example | |
@@ -407,7 +343,7 @@ | |
If there are optional modules that add additional functionality to your | |
application, you can specify them in a constant called OPTIONAL_MODULES, | |
-which has the same format as | |
+which has the same format as | |
L<Bugzilla::Install::Requirements/OPTIONAL_MODULES>. | |
=head3 If Your Extension Needs Certain Modules In Order To Compile | |
@@ -442,11 +378,11 @@ | |
Note that it is I<not> a subclass of C<Bugzilla::Extension>, because | |
at the time that module requirements are being checked in L<checksetup>, | |
C<Bugzilla::Extension> cannot be loaded. Also, just like F<Extension.pm>, | |
-it ends with C<< __PACKAGE__->NAME; >>. Note also that it has the | |
+it ends with C<< __PACKAGE__->NAME; >>. Note also that it has the | |
B<exact same> C<package> name as F<Extension.pm>. | |
This file may not use any Perl modules other than L<Bugzilla::Constants>, | |
-L<Bugzilla::Install::Util>, L<Bugzilla::Install::Requirements>, and | |
+L<Bugzilla::Install::Util>, L<Bugzilla::Install::Requirements>, and | |
modules that ship with Perl itself. | |
If you want to define both C<REQUIRED_MODULES> and C<OPTIONAL_MODULES>, | |
@@ -458,7 +394,7 @@ | |
with an identical name in both files, or Perl may throw a warning that | |
you are redefining things. | |
-This method of setting C<REQUIRED_MODULES> is of course not available if | |
+This method of setting C<REQUIRED_MODULES> is of course not available if | |
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. | |
@@ -467,7 +403,7 @@ | |
=head2 Libraries | |
Extensions often want to have their own Perl modules. Your extension | |
-can load any Perl module in its F<lib/> directory. (So, if your extension is | |
+can load any Perl module in its F<lib/> directory. (So, if your extension is | |
F<extensions/Foo/>, then your Perl modules go into F<extensions/Foo/lib/>.) | |
However, the C<package> name of your libraries will not work quite | |
@@ -478,12 +414,12 @@ | |
name. | |
This allows any place in Bugzilla to load your modules, which is important | |
-for some hooks. It even allows other extensions to load your modules, and | |
+for some hooks. It even allows other extensions to load your modules, and | |
allows you to install your modules into the global Perl install | |
as F<Bugzilla/Extension/Foo/Bar.pm>, if you'd like, which helps allow CPAN | |
distribution of Bugzilla extensions. | |
-B<Note:> If you want to C<use> or C<require> a module that's in | |
+B<Note:> If you want to C<use> or C<require> a module that's in | |
F<extensions/Foo/lib/> at the top level of your F<Extension.pm>, | |
you must have a F<Config.pm> (see above) with at least the C<NAME> | |
constant defined in it. | |
@@ -519,13 +455,13 @@ | |
=head3 Which Templates Can Be Hooked | |
There is no list of template hooks like there is for standard code hooks. | |
-To find what places in the user interface can be hooked, search for the | |
-string C<Hook.process> in Bugzilla's templates (in the | |
-F<template/en/default/> directory). That will also give you the name of | |
+To find what places in the user interface can be hooked, search for the | |
+string C<Hook.process> in Bugzilla's templates (in the | |
+F<template/en/default/> directory). That will also give you the name of | |
the hooks--the first argument to C<Hook.process> is the name of the hook. | |
(A later section in this document explains how to use that name). | |
-For example, if you see C<Hook.process("additional_header")>, that means | |
+For example, if you see C<Hook.process("additional_header")>, that means | |
the name of the hook is C<additional_header>. | |
=head3 Where Template Hooks Go | |
@@ -542,10 +478,10 @@ | |
The files that go into this directory have a certain name, based on the | |
name of the template that is being hooked, and the name of the hook. | |
For example, let's imagine that you have an extension named "Foo", | |
-and you want to use the C<additional_header> hook in | |
+and you want to use the C<additional_header> hook in | |
F<template/en/default/global/header.html.tmpl>. Your code would go into | |
-F<extensions/Foo/template/en/default/hook/global/header-additional_header.html.tmpl>. Any code you put into that file will happen at the point that | |
-C<Hook.process("additional_header")> is called in | |
+F<extensions/Foo/template/en/default/hook/global/header-additional_header.html.tmpl>. Any code you put into that file will happen at the point that | |
+C<Hook.process("additional_header")> is called in | |
F<template/en/default/global/header.html.tmpl>. | |
As you can see, template extension file names follow a pattern. The | |
@@ -557,7 +493,7 @@ | |
=item <templates> | |
-This is the full path to the template directory, like | |
+This is the full path to the template directory, like | |
F<extensions/Foo/template/en/default>. This works much like normal templates | |
do, in the sense that template extensions in C<custom> override template | |
extensions in C<default> for your extension, templates for different languages | |
@@ -569,13 +505,13 @@ | |
just takes the first one it finds and stops searching. So while a template | |
extension in the C<custom> directory may override the same-named template | |
extension in the C<default> directory I<within your Bugzilla extension>, | |
-it will not override the same-named template extension in the C<default> | |
+it will not override the same-named template extension in the C<default> | |
directory of another Bugzilla extension. | |
=item <template path> | |
-This is the part of the path (excluding the filename) that comes after | |
-F<template/en/default/> in a template's path. So, for | |
+This is the part of the path (excluding the filename) that comes after | |
+F<template/en/default/> in a template's path. So, for | |
F<template/en/default/global/header.html.tmpl>, this would simply be | |
C<global>. | |
@@ -588,7 +524,7 @@ | |
=item <hook name> | |
This is the name of the hook--what you saw in C<Hook.process> inside | |
-of the template you want to hook. In our example, this is | |
+of the template you want to hook. In our example, this is | |
C<additional_header>. | |
=item <template type> | |
@@ -602,7 +538,7 @@ | |
=head3 Adding New Template Hooks to Bugzilla | |
-Adding new template hooks is just like adding code hooks (see | |
+Adding new template hooks is just like adding code hooks (see | |
L</Adding New Hooks To Bugzilla>) except that you don't have to | |
document them, and including example code is optional. | |
@@ -613,11 +549,11 @@ | |
new template to Bugzilla for your extension to use. | |
To replace the F<template/en/default/global/banner.html.tmpl> template | |
-in an extension named "Foo", create a file called | |
+in an extension named "Foo", create a file called | |
F<extensions/Foo/template/en/default/global/banner.html.tmpl>. Note that this | |
is very similar to the path for a template hook, except that it excludes | |
F<hook/>, and the template is named I<exactly> like the standard Bugzilla | |
-template. | |
+template. | |
You can also use this method to add entirely new templates. If you have | |
an extension named "Foo", and you add a file named | |
@@ -649,27 +585,12 @@ | |
If you include CSS, JavaScript, and images in your extension that are | |
served directly to the user (that is, they're not read by a script and | |
then printed--they're just linked directly in your HTML), they should go | |
-into the F<web/> subdirectory of your extension. | |
+into the F<web/> subdirectory of your extension. | |
So, for example, if you had a CSS file called F<style.css> and your | |
-extension was called F<Foo>, your file would go into | |
+extension was called F<Foo>, your file would go into | |
F<extensions/Foo/web/style.css>. | |
-=head2 Documenting Extensions | |
- | |
-Documentation goes in F<extensions/Foo/docs/en/rst/>, if it's in English, or | |
-change "en" to something else if it's not. The user documentation index file | |
-must be called index-user.rst; the admin documentation must be called | |
-index-admin.rst. These will end up in the User Guide and the Administration | |
-Guide respectively. Both documentation types are optional. You can use various | |
-Sphinx constructs such as :toctree: or :include: to include further reST files | |
-if you need more than one page of docs. | |
- | |
-When documenting extensions to the Bugzilla API, if your extension provides | |
-them, the index file would be F<extensions/Foo/docs/en/rst/api/v1/index.rst>. | |
-When and if your API has more than one version, increment the version number. | |
-These docs will get included in the WebService API Reference. | |
- | |
=head2 Disabling Your Extension | |
If you want your extension to be totally ignored by Bugzilla (it will | |
@@ -689,21 +610,21 @@ | |
If you've made an extension and you want to publish it, the first | |
thing you'll want to do is package up your extension's code and | |
-then put a link to it in the appropriate section of | |
-L<http://wiki.mozilla.org/Bugzilla:Addons>. | |
+then put a link to it in the appropriate section of | |
+L<https://wiki.mozilla.org/Bugzilla:Addons>. | |
=head2 Distributing on CPAN | |
If you want a centralized distribution point that makes it easy | |
-for Bugzilla users to install your extension, it is possible to | |
+for Bugzilla users to install your extension, it is possible to | |
distribute your Bugzilla Extension through CPAN. | |
The details of making a standard CPAN module are too much to | |
go into here, but a lot of it is covered in L<perlmodlib> | |
-and on L<http://www.cpan.org/> among other places. | |
+and on L<https://www.cpan.org/> among other places. | |
When you distribute your extension via CPAN, your F<Extension.pm> | |
-should simply install itself as F<Bugzilla/Extension/Foo.pm>, | |
+should simply install itself as F<Bugzilla/Extension/Foo.pm>, | |
where C<Foo> is the name of your module. You do not need a separate | |
F<Config.pm> file, because CPAN itself will handle installing | |
the prerequisites of your module, so Bugzilla doesn't have to | |
@@ -731,7 +652,7 @@ | |
If you are an extension author and you'd like some assistance from other | |
extension authors or the Bugzilla development team, you can use the | |
-normal support channels described at L<http://www.bugzilla.org/support/>. | |
+normal support channels described at L<https://www.bugzilla.org/support/>. | |
=head1 ADDITIONAL CONSTANTS | |
@@ -777,9 +698,9 @@ | |
=head3 C<package_dir> | |
-This returns the directory that your extension is located in. | |
+This returns the directory that your extension is located in. | |
-If this is an extension that was installed via CPAN, the directory will | |
+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. | |
@@ -797,7 +718,7 @@ | |
=head3 C<web_dir> | |
-The directory that your package's web related files are in, such as css and javascript. | |
+The directory that your package's web related files are in, such as CSS and JavaScript. | |
This defaults to the C<web> subdirectory of the L</package_dir>. | |
@@ -829,13 +750,3 @@ | |
Calls L</load> for every enabled extension installed into Bugzilla, | |
and returns an arrayref of all the package names that were loaded. | |
- | |
-=head1 B<Methods in need of POD> | |
- | |
-=over | |
- | |
-=item modify_inc | |
- | |
-=item my_inc | |
- | |
-=back |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment