From 90e1688ef09332be00b31278f8d5ee7703ac81b2 Mon Sep 17 00:00:00 2001 From: "jocuri%softhome.net" <> Date: Tue, 28 Feb 2006 22:39:00 +0000 Subject: Patch for bug 298341: Implement code hook mechanism; patch by zach@zachlipton.com, r=timeless, a=justdave. --- Bugzilla/Config.pm | 3 +- Bugzilla/Hook.pm | 83 ++++++++++++++++++++++++++++++++++++++++ Bugzilla/Template.pm | 25 +++++++++--- Bugzilla/Template/Plugin/Hook.pm | 60 ++++++++++++++++++++++++++++- 4 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 Bugzilla/Hook.pm (limited to 'Bugzilla') diff --git a/Bugzilla/Config.pm b/Bugzilla/Config.pm index 935fc2c9e..33480f5f1 100644 --- a/Bugzilla/Config.pm +++ b/Bugzilla/Config.pm @@ -69,6 +69,7 @@ if ($ENV{'PROJECT'} && $ENV{'PROJECT'} =~ /^(\w+)$/) { } our $attachdir = "$datadir/attachments"; our $webdotdir = "$datadir/webdot"; +our $extensionsdir = "$libpath/extensions"; our @parampanels = (); @@ -87,7 +88,7 @@ our @parampanels = (); admin => [qw(UpdateParams SetParam WriteParams)], db => [qw($db_driver $db_host $db_port $db_name $db_user $db_pass $db_sock)], locations => [qw($libpath $localconfig $attachdir $datadir $templatedir - $webdotdir $project)], + $webdotdir $project $extensionsdir)], params => [qw(@parampanels)], ); Exporter::export_ok_tags('admin', 'db', 'locations', 'params'); diff --git a/Bugzilla/Hook.pm b/Bugzilla/Hook.pm new file mode 100644 index 000000000..91188e87b --- /dev/null +++ b/Bugzilla/Hook.pm @@ -0,0 +1,83 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# The contents of this file are subject to the Mozilla Public +# License Version 1.1 (the "License"); you may not use this file +# except in compliance with the License. You may obtain a copy of +# the License at http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS +# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or +# implied. See the License for the specific language governing +# rights and limitations under the License. +# +# The Original Code is the Bugzilla Bug Tracking System. +# +# The Initial Developer of the Original Code is Netscape Communications +# Corporation. Portions created by Netscape are +# Copyright (C) 1998 Netscape Communications Corporation. All +# Rights Reserved. +# +# Contributor(s): Zach Lipton +# + +package Bugzilla::Hook; + +use Bugzilla::Util; +use Bugzilla::Error; + +use strict; + +sub process { + my $name = shift; + trick_taint($name); + + # get a list of all extensions + my @extensions = glob($Bugzilla::Config::extensionsdir."/*"); + + # check each extension to see if it uses the hook + # if so, invoke the extension source file: + foreach my $extension (@extensions) { + # all of these variables come directly from code or directory names. + # If there's malicious data here, we have much bigger issues to + # worry about, so we can safely detaint them: + trick_taint($extension); + if (-e $extension.'/code/'.$name.'.pl') { + do($extension.'/code/'.$name.'.pl'); + ThrowCodeError("An error occured processing hook \"$name\" in ". + "Bugzilla extension \"$extension\": $@") if $@; + } + } + +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Hook - Extendible extension hooks for Bugzilla code + +=head1 SYNOPSIS + + use Bugzilla::Hook; + + Bugzilla::Hook::process("hookname"); + +=head1 DESCRIPTION + +Bugzilla allows extension modules to drop in and add routines at +arbitrary points in Bugzilla code. These points are refered to as +hooks. When a piece of standard Bugzilla code wants to allow an extension +to perform additional functions, it uses Bugzilla::Hook's process() +subroutine to invoke any extension code if installed. + +=item C + +Invoke any code hooks with a matching name from any installed extensions. +When this subroutine is called with hook name foo, Bugzilla will attempt +to invoke any source files in C. +See C in the Bugzilla Guide for more information on +Bugzilla's extension mechanism. + +=back diff --git a/Bugzilla/Template.pm b/Bugzilla/Template.pm index 6327a31a5..57d113ef7 100644 --- a/Bugzilla/Template.pm +++ b/Bugzilla/Template.pm @@ -100,6 +100,7 @@ sub sortAcceptLanguage { # Returns the path to the templates based on the Accept-Language # settings of the user and of the available languages # If no Accept-Language is present it uses the defined default +# Templates may also be found in the extensions/ tree sub getTemplateIncludePath { # Return cached value if available @@ -113,17 +114,14 @@ sub getTemplateIncludePath { $template_include_path = [ "$templatedir/$languages/$project", "$templatedir/$languages/custom", - "$templatedir/$languages/extension", "$templatedir/$languages/default" ]; } else { $template_include_path = [ "$templatedir/$languages/custom", - "$templatedir/$languages/extension", "$templatedir/$languages/default" ]; } - return $template_include_path; } my @languages = sortAcceptLanguage($languages); my @accept_language = sortAcceptLanguage($ENV{'HTTP_ACCEPT_LANGUAGE'} || "" ); @@ -144,7 +142,6 @@ sub getTemplateIncludePath { map(( "$templatedir/$_/$project", "$templatedir/$_/custom", - "$templatedir/$_/extension", "$templatedir/$_/default" ), @usedlanguages ) @@ -153,12 +150,30 @@ sub getTemplateIncludePath { $template_include_path = [ map(( "$templatedir/$_/custom", - "$templatedir/$_/extension", "$templatedir/$_/default" ), @usedlanguages ) ]; } + + # add in extension template directories: + my @extensions = glob($Bugzilla::Config::extensionsdir."/*"); + foreach my $extension (@extensions) { + trick_taint($extension); # since this comes right from the filesystem + # we have bigger issues if it is insecure + push(@$template_include_path, + map(( + $extension."/template/".$_), + @usedlanguages)); + } + + # remove duplicates since they keep popping up: + my @dirs; + foreach my $dir (@$template_include_path) { + push(@dirs, $dir) unless grep ($dir eq $_, @dirs); + } + $template_include_path = [@dirs]; + return $template_include_path; } diff --git a/Bugzilla/Template/Plugin/Hook.pm b/Bugzilla/Template/Plugin/Hook.pm index b189c5d26..bcfda1e5b 100644 --- a/Bugzilla/Template/Plugin/Hook.pm +++ b/Bugzilla/Template/Plugin/Hook.pm @@ -18,12 +18,19 @@ # Rights Reserved. # # Contributor(s): Myk Melez +# Zach Lipton # package Bugzilla::Template::Plugin::Hook; use strict; +use Bugzilla::Config; +use Bugzilla::Template; +use Bugzilla::Util; +use Bugzilla::Error; +use File::Spec; + use base qw(Template::Plugin); sub load { @@ -42,7 +49,38 @@ sub process { my $paths = $self->{_CONTEXT}->{LOAD_TEMPLATES}->[0]->paths; my $template = $self->{_CONTEXT}->stash->{component}->{name}; my @hooks = (); + + # sanity check: + if (!$template =~ /[\w\.\/\-_\\]+/) { + ThrowCodeError("Template with invalid file name found in hook call: $template"); + } + # also get extension hook files that live in extensions/: + # parse out the parts of the template name + my ($vol, $subpath, $filename) = File::Spec->splitpath($template); + $subpath = $subpath || ''; + $filename =~ m/(.*)\.(.*)\.tmpl/; + my $templatename = $1; + my $type = $2; + # munge the filename to create the extension hook filename: + my $extensiontemplate = $subpath.'/'.$templatename.'-'.$hook_name.'.'.$type.'.tmpl'; + my @extensions = glob($Bugzilla::Config::extensionsdir."/*"); + my @usedlanguages = getLanguages(); + foreach my $extension (@extensions) { + foreach my $language (@usedlanguages) { + my $file = $extension.'/template/'.$language.'/'.$extensiontemplate; + if (-e $file) { + # tt is stubborn and won't take a template file not in its + # include path, so we open a filehandle and give it to process() + # so the hook gets invoked: + open (my $fh, $file); + push(@hooks, $fh); + } + } + } + + # we keep this too since you can still put hook templates in + # template/en/custom/hook foreach my $path (@$paths) { my @files = glob("$path/hook/$template/$hook_name/*.tmpl"); @@ -65,6 +103,24 @@ sub process { return $output; } +# get a list of languages we accept so we can find the hook +# that corresponds to our desired languages: +sub getLanguages() { + my $languages = trim(Param('languages')); + if (not ($languages =~ /,/)) { # only one language + return $languages; + } + my @languages = Bugzilla::Template::sortAcceptLanguage($languages); + my @accept_language = Bugzilla::Template::sortAcceptLanguage($ENV{'HTTP_ACCEPT_LANGUAGE'} || "" ); + my @usedlanguages; + foreach my $lang (@accept_language) { + if(my @found = grep /^\Q$lang\E(-.+)?$/i, @languages) { + push (@usedlanguages, @found); + } + } + return @usedlanguages; +} + 1; __END__ @@ -79,5 +135,7 @@ Template Toolkit plugin to process hooks added into templates by extensions. =head1 SEE ALSO -L, +L +L L +L -- cgit v1.2.3-24-g4f1b