| # This Source Code Form is subject to the terms of the Mozilla Public |
| # License, v. 2.0. If a copy of the MPL was not distributed with this |
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| # |
| # This Source Code Form is "Incompatible With Secondary Licenses", as |
| # defined by the Mozilla Public License, v. 2.0. |
| |
| package Bugzilla::Extension; |
| |
| use 5.10.1; |
| use strict; |
| use warnings; |
| |
| use Bugzilla::Constants; |
| use Bugzilla::Error; |
| use Bugzilla::Install::Util qw( |
| extension_code_files extension_template_directory |
| extension_package_directory extension_web_directory); |
| |
| use File::Basename; |
| use File::Spec; |
| |
| #################### |
| # Subclass Methods # |
| #################### |
| |
| sub new { |
| my ($class, $params) = @_; |
| $params ||= {}; |
| bless $params, $class; |
| return $params; |
| } |
| |
| ####################################### |
| # Class (Bugzilla::Extension) Methods # |
| ####################################### |
| |
| sub load { |
| my ($class, $extension_file, $config_file) = @_; |
| my $package; |
| |
| # This is needed during checksetup.pl, because Extension packages can |
| # only be loaded once (they return "1" the second time they're loaded, |
| # instead of their name). During checksetup.pl, extensions are loaded |
| # once by Bugzilla::Install::Requirements, and then later again via |
| # Bugzilla->extensions (because of hooks). |
| my $map = Bugzilla->request_cache->{extension_requirement_package_map}; |
| |
| if ($config_file) { |
| if ($map and defined $map->{$config_file}) { |
| $package = $map->{$config_file}; |
| } |
| else { |
| my $name = require $config_file; |
| if ($name =~ /^\d+$/) { |
| ThrowCodeError('extension_must_return_name', |
| { extension => $config_file, |
| returned => $name }); |
| } |
| $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; |
| if ($name =~ /^\d+$/) { |
| ThrowCodeError('extension_must_return_name', |
| { extension => $extension_file, returned => $name }); |
| } |
| $package = "${class}::$name"; |
| $package->modify_inc($extension_file) if !$config_file; |
| } |
| |
| $class->_validate_package($package, $extension_file); |
| return $package; |
| } |
| |
| sub _validate_package { |
| my ($class, $package, $extension_file) = @_; |
| |
| # For extensions from data/extensions/additional, we don't have a file |
| # name, so we fake it. |
| if (!$extension_file) { |
| $extension_file = $package; |
| $extension_file =~ s/::/\//g; |
| $extension_file .= '.pm'; |
| } |
| |
| if (!eval { $package->NAME }) { |
| ThrowCodeError('extension_no_name', |
| { filename => $extension_file, package => $package }); |
| } |
| |
| if (!$package->isa($class)) { |
| ThrowCodeError('extension_must_be_subclass', |
| { filename => $extension_file, |
| package => $package, |
| class => $class }); |
| } |
| } |
| |
| sub load_all { |
| my $class = shift; |
| my ($file_sets, $extra_packages) = extension_code_files(); |
| my @packages; |
| foreach my $file_set (@$file_sets) { |
| my $package = $class->load(@$file_set); |
| push(@packages, $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); |
| } |
| # 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; |
| } |
| |
| #################### |
| # Instance Methods # |
| #################### |
| |
| 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 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); |
| } |
| |
| 1; |
| |
| __END__ |
| |
| =head1 NAME |
| |
| Bugzilla::Extension - Base class for Bugzilla Extensions. |
| |
| =head1 SYNOPSIS |
| |
| The following would be in F<extensions/Foo/Extension.pm> or |
| F<extensions/Foo.pm>: |
| |
| package Bugzilla::Extension::Foo |
| use strict; |
| use parent qw(Bugzilla::Extension); |
| |
| our $VERSION = '0.02'; |
| use constant NAME => 'Foo'; |
| |
| sub some_hook_name { ... } |
| |
| __PACKAGE__->NAME; |
| |
| Custom templates would go into F<extensions/Foo/template/en/default/>. |
| L<Template hooks|/Template Hooks> would go into |
| F<extensions/Foo/template/en/default/hook/>. |
| |
| =head1 DESCRIPTION |
| |
| This is the base class for all Bugzilla extensions. |
| |
| =head1 WRITING EXTENSIONS |
| |
| 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 |
| 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: |
| |
| C<extensions/create.pl NAME> |
| |
| But replace C<NAME> with the name you picked for your extension. That |
| will create a new directory in the F<extensions/> directory with the name |
| of your extension. The directory will contain a full framework for |
| a new extension, with helpful comments in each file describing things |
| about them. |
| |
| =head2 Example Extension |
| |
| There is a sample extension in F<extensions/Example/> that demonstrates |
| most of the things described in this document, so if you find the |
| documentation confusing, try just reading the code instead. |
| |
| =head2 Where Extension Code Goes |
| |
| Extension code lives under the F<extensions/> directory in Bugzilla. |
| |
| There are two ways to write extensions: |
| |
| =over |
| |
| =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. |
| |
| 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>. |
| |
| =item 2 |
| |
| If you plan for your extension to have templates and other files, you |
| can create a whole directory for your extension, and the main extension |
| code would go into a file called F<Extension.pm> in that 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/Extension.pm>. |
| |
| =back |
| |
| =head2 The Extension C<NAME>. |
| |
| The "name" of an extension shows up in several places: |
| |
| =over |
| |
| =item 1 |
| |
| The name of the package: |
| |
| C<package Bugzilla::Extension::Foo;> |
| |
| =item 2 |
| |
| In a C<NAME> constant that B<must> be defined for every extension: |
| |
| C<< use constant NAME => 'Foo'; >> |
| |
| =item 3 |
| |
| At the very end of the file: |
| |
| C<< __PACKAGE__->NAME; >> |
| |
| You'll notice that though most Perl packages end with C<1;>, Bugzilla |
| Extensions must B<always> end with C<< __PACKAGE__->NAME; >>. |
| |
| =back |
| |
| The name must be identical in all of those locations. |
| |
| =head2 Hooks |
| |
| In L<Bugzilla::Hook>, there is a L<list of hooks|Bugzilla::Hook/HOOKS>. |
| These are the various areas of Bugzilla that an extension can "hook" into, |
| which allow your extension to perform code during that point in Bugzilla's |
| execution. |
| |
| If your extension wants to implement a hook, all you have to do is |
| write a subroutine in your hook package that has the same name as |
| the hook. The subroutine will be called as a method on your extension, |
| and it will get the arguments specified in the hook's documentation as |
| named parameters in a hashref. |
| |
| For example, here's an implementation of a hook named C<foo_start> |
| that gets an argument named C<bar>: |
| |
| sub foo_start { |
| my ($self, $args) = @_; |
| my $bar = $args->{bar}; |
| print "I got $bar!\n"; |
| } |
| |
| And that would go into your extension's code file--the file that was |
| described in the L</Where Extension Code Goes> section above. |
| |
| 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 |
| 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>. |
| |
| 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 |
| code in F<extensions/Example/Extension.pm>. |
| |
| One question that is often asked about new hooks is, "Is this the most |
| flexible way to implement this hook?" That is, the more power extension |
| authors get from a hook, the more likely it is to be accepted into Bugzilla. |
| Hooks that only hook a very specific part of Bugzilla will not be accepted |
| if their functionality can be accomplished equally well with a more generic |
| hook. |
| |
| =head2 If Your Extension Requires Certain Perl Modules |
| |
| If there are certain Perl modules that your extension requires in order |
| to run, there is a way you can tell Bugzilla this, and then L<checksetup> |
| will make sure that those modules are installed, when you run L<checksetup>. |
| |
| To do this, you need to specify a constant called C<REQUIRED_MODULES> |
| in your extension. This constant has the same format as |
| L<Bugzilla::Install::Requirements/REQUIRED_MODULES>. |
| |
| 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 |
| L<Bugzilla::Install::Requirements/OPTIONAL_MODULES>. |
| |
| =head3 If Your Extension Needs Certain Modules In Order To Compile |
| |
| If your extension needs a particular Perl module in order to |
| I<compile>, then you have a "chicken and egg" problem--in order to |
| read C<REQUIRED_MODULES>, we have to compile your extension. In order |
| to compile your extension, we need to already have the modules in |
| C<REQUIRED_MODULES>! |
| |
| To get around this problem, Bugzilla allows you to have an additional |
| file, besides F<Extension.pm>, called F<Config.pm>, that contains |
| just C<REQUIRED_MODULES>. If you have a F<Config.pm>, it must also |
| contain the C<NAME> constant, instead of your main F<Extension.pm> |
| containing the C<NAME> constant. |
| |
| The contents of the file would look something like this for an extension |
| named C<Foo>: |
| |
| package Bugzilla::Extension::Foo; |
| use strict; |
| use constant NAME => 'Foo'; |
| use constant REQUIRED_MODULES => [ |
| { |
| package => 'Some-Package', |
| module => 'Some::Module', |
| version => 0, |
| } |
| ]; |
| __PACKAGE__->NAME; |
| |
| 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 |
| 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 |
| modules that ship with Perl itself. |
| |
| If you want to define both C<REQUIRED_MODULES> and C<OPTIONAL_MODULES>, |
| they must both be in F<Config.pm> or both in F<Extension.pm>. |
| |
| Every time your extension is loaded by Bugzilla, F<Config.pm> will be |
| read and then F<Extension.pm> will be read, so your methods in F<Extension.pm> |
| will have access to everything in F<Config.pm>. Don't define anything |
| 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 |
| 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 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 |
| 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 |
| like normal Perl modules do. F<extensions/Foo/lib/Bar.pm> is |
| loaded as C<Bugzilla::Extension::Foo::Bar>. Or, to say it another way, |
| C<use Bugzilla::Extension::Foo::Bar;> loads F<extensions/Foo/lib/Bar.pm>, |
| which should have C<package Bugzilla::Extension::Foo::Bar;> as its package |
| 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 |
| 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 |
| 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. |
| |
| =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>. |
| |
| You can actually include templates in your extension without having any |
| C<.pm> files in your extension at all, if you want. (That is, it's entirely |
| valid to have an extension that's just template files and no code files.) |
| |
| Bugzilla's templates are written in a language called Template Toolkit. |
| You can find out more about Template Toolkit at L<http://template-toolkit.org>. |
| |
| There are two ways to extend or modify Bugzilla's templates: you can use |
| template hooks (described below) or you can override existing templates |
| entirely (described further down). |
| |
| =head2 Template Hooks |
| |
| Templates can be extended using a system of "hooks" that add new UI elements |
| to a particular area of Bugzilla without modifying the code of the existing |
| templates. This is the recommended way for extensions to modify the user |
| interface of Bugzilla. |
| |
| =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 |
| 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 |
| the name of the hook is C<additional_header>. |
| |
| =head3 Where Template Hooks Go |
| |
| To extend templates in your extension using template hooks, you put files into |
| the F<template/en/default/hook> directory of your extension. So, if you had an |
| extension called "Foo", your template extensions would go into |
| F<extensions/Foo/template/en/default/hook/>. |
| |
| (Note that the base F<template/en/default/hook> directory in Bugzilla itself |
| also works, although you would never use that for an extension that you |
| intended to distribute.) |
| |
| 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 |
| 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<template/en/default/global/header.html.tmpl>. |
| |
| As you can see, template extension file names follow a pattern. The |
| pattern looks like: |
| |
| <templates>/hook/<template path>/<template name>-<hook name>.<template type>.tmpl |
| |
| =over |
| |
| =item <templates> |
| |
| 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 |
| can be supplied, etc. Template extensions are searched for and run in the |
| order described in L<Bugzilla::Install::Util/template_include_path>. |
| |
| The difference between normal templates and template hooks is that hooks |
| will be run for I<every> extension, whereas for normal templates, Bugzilla |
| 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> |
| 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 |
| F<template/en/default/global/header.html.tmpl>, this would simply be |
| C<global>. |
| |
| =item <template name> |
| |
| This is the file name of the template, before the C<.html.tmpl> part. |
| So, for F<template/en/default/global/header.html.tmpl>, this would be |
| C<header>. |
| |
| =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 |
| C<additional_header>. |
| |
| =item <template type> |
| |
| This is what comes after the template name but before C<.tmpl> in the |
| template's path. In most cases this is C<html>, but sometimes it's |
| C<none>, C<txt>, C<js>, or various other formats, indicating what |
| type of output the template has. |
| |
| =back |
| |
| =head3 Adding New Template Hooks to Bugzilla |
| |
| 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. |
| |
| =head2 Overriding Existing Templates |
| |
| Sometimes you don't want to extend a template, you just want to replace |
| it entirely with your extension's template, or you want to add an entirely |
| 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 |
| 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. |
| |
| You can also use this method to add entirely new templates. If you have |
| an extension named "Foo", and you add a file named |
| F<extensions/Foo/template/en/default/foo/bar.html.tmpl>, you can load |
| that in your code using C<< $template->process('foo/bar.html.tmpl') >>. |
| |
| =head3 A Warning About Extensions That You Want To Distribute |
| |
| You should never override an existing Bugzilla template in an |
| extension that you plan to distribute to others, because only one extension |
| can override any given template, and which extension will "win" that war |
| if there are multiple extensions installed is totally undefined. |
| |
| However, adding new templates in an extension that you want to distribute |
| is fine, though you have to be careful about how you name them, because |
| any templates with an identical path and name (say, both called |
| F<global/stuff.html.tmpl>) will conflict. The usual way to work around |
| this is to put all your custom templates into a template path that's |
| named after your extension (since the name of your extension has to be |
| unique anyway). So if your extension was named Foo, your custom templates |
| would go into F<extensions/Foo/template/en/default/foo/>. The only |
| time that doesn't work is with the C<page_before_template> extension, in which |
| case your templates should probably be in a directory like |
| F<extensions/Foo/template/en/default/page/foo/> so as not to conflict with |
| other pages that other extensions might add. |
| |
| =head2 CSS, JavaScript, and Images |
| |
| 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. |
| |
| 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 |
| 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 |
| not be compiled or seen to exist at all), then create a file called |
| C<disabled> in your extension's directory. (If your extension is just |
| a file, like F<extensions/Foo.pm>, you cannot use this method to disable |
| your extension, and will just have to remove it from the directory if you |
| want to totally disable it.) Note that if you are running under mod_perl, |
| you may have to restart your web server for this to take effect. |
| |
| If you want your extension to be compiled and have L<checksetup> check |
| for its module pre-requisites, but you don't want the module to be used |
| by Bugzilla, then you should make your extension's L</enabled> method |
| return C<0> or some false value. |
| |
| =head1 DISTRIBUTING EXTENSIONS |
| |
| 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>. |
| |
| =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 |
| 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. |
| |
| When you distribute your extension via CPAN, your F<Extension.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 |
| worry about it. |
| |
| =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, |
| like C<Bugzilla::Extension::Foo>. In addition to the extensions |
| in the F<extensions/> directory, each module listed in this file |
| will be loaded as a Bugzilla Extension whenever Bugzilla loads or |
| uses extensions. |
| |
| =head1 GETTING HELP WITH WRITING EXTENSIONS |
| |
| 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/>. |
| |
| =head1 ADDITIONAL CONSTANTS |
| |
| In addition to C<NAME>, there are some other constants you might |
| want to define: |
| |
| =head2 C<$VERSION> |
| |
| This should be a string that describes what version of your extension |
| this is. Something like C<1.0>, C<1.3.4> or a similar string. |
| |
| There are no particular restrictions on the format of version numbers, |
| but you should probably keep them to just numbers and periods, in the |
| interest of other software that parses version numbers. |
| |
| By default, this will be C<undef> if you don't define it. |
| |
| =head1 SUBCLASS METHODS |
| |
| In addition to hooks, there are a few methods that your extension can |
| define to modify its behavior, if you want: |
| |
| =head2 Class Methods |
| |
| These methods are called on your extension's class. (Like |
| C<< Bugzilla::Extension::Foo->some_method >>). |
| |
| =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<web_dir> |
| |
| 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>. |
| |
| 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. |
| If you are an extension author, you don't need to care about these. |
| |
| =head2 C<load> |
| |
| Takes two arguments, the path to F<Extension.pm> and the path to F<Config.pm>, |
| for an extension. Loads the extension's code packages into memory using |
| C<require>, does some sanity-checking on the extension, and returns the |
| package name of the loaded extension. |
| |
| =head2 C<load_all> |
| |
| 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 |