From c0117171874e3228abc125b12c25dbd436ebe7f7 Mon Sep 17 00:00:00 2001 From: "mkanat%bugzilla.org" <> Date: Mon, 23 Nov 2009 06:55:17 +0000 Subject: Bug 430010: Re-work the template hooks system so that template hooks always live in template//hook/, both for extensions and for the base Bugzilla template/ directory. Patch by Max Kanat-Alexander (module owner) a=mkanat --- Bugzilla/Install/Util.pm | 79 ++++++++------ Bugzilla/Template/Plugin/Hook.pm | 117 +++++++++++---------- .../admin/sanitycheck/messages-statuses.html.tmpl | 35 ++++++ .../en/hook/global/user-error-errors.html.tmpl | 12 +++ 4 files changed, 153 insertions(+), 90 deletions(-) create mode 100644 extensions/example/template/en/hook/admin/sanitycheck/messages-statuses.html.tmpl create mode 100644 extensions/example/template/en/hook/global/user-error-errors.html.tmpl diff --git a/Bugzilla/Install/Util.pm b/Bugzilla/Install/Util.pm index effb39ee8..d3fb4e5f8 100644 --- a/Bugzilla/Install/Util.pm +++ b/Bugzilla/Install/Util.pm @@ -41,6 +41,8 @@ our @EXPORT_OK = qw( install_string include_languages template_include_path + template_base_directories + template_lang_directories vers_cmp get_console_locale init_console @@ -206,28 +208,59 @@ sub include_languages { return @usedlanguages; } + +# Used by template_include_path and Bugzilla::Template::Plugin::Hook. +sub template_lang_directories { + my ($languages, $templatedir, $subdir_name) = @_; -sub template_include_path { - my @usedlanguages = include_languages(@_); - # Now, we add template directories in the order they will be searched: - + my @add; + my $project = bz_locations->{'project'}; + if ($subdir_name) { + @add = ("$subdir_name.custom", $subdir_name); + unshift(@add, "$subdir_name.$project") if $project; + } + else { + @add = ("custom", "default"); + unshift(@add, $project) if $project; + } + my @result; + foreach my $lang (@$languages) { + foreach my $dir (@add) { + my $full_dir = "$templatedir/$lang/$dir"; + if (-d $full_dir) { + trick_taint($full_dir); + push(@result, $full_dir); + } + } + } + return @result; +} + +# Used by template_include_path and Bugzilla::Template::Plugin::Hook. +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 @include_path; + my @template_dirs; my @extensions = glob(bz_locations()->{'extensionsdir'} . "/*"); foreach my $extension (@extensions) { - next if -e "$extension/disabled"; - foreach my $lang (@usedlanguages) { - _add_language_set(\@include_path, $lang, "$extension/template"); - } + next if (-e "$extension/disabled" or !-d "$extension/template"); + push(@template_dirs, "$extension/template"); } - - # Then, we add normal template directories, sorted by language. - foreach my $lang (@usedlanguages) { - _add_language_set(\@include_path, $lang); + push(@template_dirs, bz_locations()->{'templatedir'}); + return \@template_dirs; +} + +sub template_include_path { + my @used_languages = include_languages(@_); + # Now, we add template directories in the order they will be searched: + my $template_dirs = template_base_directories(); + + my @include_path; + foreach my $template_dir (@$template_dirs) { + push(@include_path, + template_lang_directories(\@used_languages, $template_dir)); } - return \@include_path; } @@ -289,24 +322,6 @@ sub _get_string_from_file { return $strings{$string_id}; } -# Used by template_include_path. -sub _add_language_set { - my ($array, $lang, $templatedir) = @_; - - $templatedir ||= bz_locations()->{'templatedir'}; - my @add = ("$templatedir/$lang/custom", "$templatedir/$lang/default"); - - my $project = bz_locations->{'project'}; - unshift(@add, "$templatedir/$lang/$project") if $project; - - foreach my $dir (@add) { - if (-d $dir) { - trick_taint($dir); - push(@$array, $dir); - } - } -} - # Make an ordered list out of a HTTP Accept-Language header (see RFC 2616, 14.4) # We ignore '*' and ;q=0 # For languages with the same priority q the order remains unchanged. diff --git a/Bugzilla/Template/Plugin/Hook.pm b/Bugzilla/Template/Plugin/Hook.pm index 99ece0880..e74363461 100644 --- a/Bugzilla/Template/Plugin/Hook.pm +++ b/Bugzilla/Template/Plugin/Hook.pm @@ -20,93 +20,98 @@ # Contributor(s): Myk Melez # Zach Lipton # Elliotte Martin -# +# Max Kanat-Alexander package Bugzilla::Template::Plugin::Hook; - use strict; +use base qw(Template::Plugin); use Bugzilla::Constants; -use Bugzilla::Install::Util qw(include_languages); -use Bugzilla::Template; +use Bugzilla::Install::Util qw(include_languages template_base_directories + template_lang_directories); use Bugzilla::Util; use Bugzilla::Error; use File::Spec; -use base qw(Template::Plugin); - -sub load { - my ($class, $context) = @_; - return $class; -} sub new { my ($class, $context) = @_; return bless { _CONTEXT => $context }, $class; } +sub _context { return $_[0]->{_CONTEXT} } + sub process { my ($self, $hook_name, $template) = @_; - $template ||= $self->{_CONTEXT}->stash->{component}->{name}; - - my @hooks; + my $context = $self->_context(); + $template ||= $context->stash->{component}->{name}; # sanity check: if (!$template =~ /[\w\.\/\-_\\]+/) { - ThrowCodeError('template_invalid', { name => $template}); + ThrowCodeError('template_invalid', { name => $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 (undef, $path, $filename) = File::Spec->splitpath($template); + $path ||= ''; + $filename =~ m/(.+)\.(.+)\.tmpl$/; + my $template_name = $1; my $type = $2; - # munge the filename to create the extension hook filename: - my $extensiontemplate = $subpath.'/'.$templatename.'-'.$hook_name.'.'.$type.'.tmpl'; - my @extensions = glob(bz_locations()->{'extensionsdir'} . "/*"); - my @usedlanguages = include_languages({use_languages => Bugzilla->languages}); - foreach my $extension (@extensions) { - next if -e "$extension/disabled"; - foreach my $language (@usedlanguages) { - my $file = $extension.'/template/'.$language.'/'.$extensiontemplate; + + # Munge the filename to create the extension hook filename + my $extension_template = "$path/$template_name-$hook_name.$type.tmpl"; + + my $template_sets = _template_hook_include_path(); + + my @hooks; + foreach my $dir_set (@$template_sets) { + foreach my $lang_dir (@$dir_set) { + my $file = File::Spec->catdir($lang_dir, $extension_template); 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); + # TT won't take a template file not in its include path, + # so we open a filehandle and give it to process() + # instead of the file name. + open (my $fh, '<', $file) or die "$file: $!"; push(@hooks, $fh); + # Don't run the hook for more than one language. + last; } } } - my $paths = $self->{_CONTEXT}->{LOAD_TEMPLATES}->[0]->paths; - - # 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"); - - # Have to remove the templates path (INCLUDE_PATH) from the - # file path since the template processor auto-adds it back. - @files = map($_ =~ /^$path\/(.*)$/ ? $1 : {}, @files); - - # Add found files to the list of hooks, but removing duplicates, - # which can happen when there are identical hooks or duplicate - # directories in the INCLUDE_PATH (the latter probably being a TT bug). - foreach my $file (@files) { - push(@hooks, $file) unless grep($file eq $_, @hooks); - } - } - my $output; - foreach my $hook (@hooks) { - $output .= $self->{_CONTEXT}->process($hook); + foreach my $hook_fh (@hooks) { + $output .= $context->process($hook_fh); + close($hook_fh); } return $output; } +sub _template_hook_include_path { + my $cache = Bugzilla->request_cache; + my $language = $cache->{language} || ''; + my $cache_key = "template_plugin_hook_include_path_$language"; + return $cache->{$cache_key} if defined $cache->{$cache_key}; + + my @used_languages = include_languages({ + use_languages => Bugzilla->languages, + only_language => $language }); + my $template_dirs = template_base_directories(); + + # We create an array of arrayrefs, with each arrayref being a single + # extension's "language" directories. In addition to the extensions/ + # directory, this also includes a set for the base template/ directory. + my @template_sets; + foreach my $template_dir (@$template_dirs) { + my @language_dirs = template_lang_directories(\@used_languages, + $template_dir, 'hook'); + if (scalar @language_dirs) { + push(@template_sets, \@language_dirs); + } + } + $cache->{$cache_key} = \@template_sets; + return $cache->{$cache_key}; +} + 1; __END__ @@ -165,8 +170,4 @@ Output from processing template extension. L -L - -L - -L +L diff --git a/extensions/example/template/en/hook/admin/sanitycheck/messages-statuses.html.tmpl b/extensions/example/template/en/hook/admin/sanitycheck/messages-statuses.html.tmpl new file mode 100644 index 000000000..8a825e57c --- /dev/null +++ b/extensions/example/template/en/hook/admin/sanitycheck/messages-statuses.html.tmpl @@ -0,0 +1,35 @@ +[%# -*- 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 Example Plugin. + # + # The Initial Developer of the Original Code is ITA Software + # Portions created by the Initial Developer are Copyright (C) 2009 + # the Initial Developer. All Rights Reserved. + # + # Contributor(s): Bradley Baetz + #%] + +[% IF san_tag == "example_check_au_user" %] + EXAMPLE PLUGIN - Checking for non-Australian users. +[% ELSIF san_tag == "example_check_au_user_alert" %] + User <[% login FILTER html %]> isn't Australian. + [% IF user.in_group('editusers') %] + Edit this user. + [% END %] +[% ELSIF san_tag == "example_check_au_user_prompt" %] + Fix these users. +[% ELSIF san_tag == "example_repair_au_user_start" %] + EXAMPLE PLUGIN - OK, would now make users Australian. +[% ELSIF san_tag == "example_repair_au_user_end" %] + EXAMPLE PLUGIN - Users would now be Australian. +[% END %] diff --git a/extensions/example/template/en/hook/global/user-error-errors.html.tmpl b/extensions/example/template/en/hook/global/user-error-errors.html.tmpl new file mode 100644 index 000000000..df5a203dd --- /dev/null +++ b/extensions/example/template/en/hook/global/user-error-errors.html.tmpl @@ -0,0 +1,12 @@ +[%# Note that error messages should generally be indented four spaces, like + # below, because when Bugzilla translates an error message into plain + # text, it takes four spaces off the beginning of the lines. + # + # Note also that I prefixed my error name with "example", the name of my + # extension, so that I wouldn't conflict with other error names in + # Bugzilla or other extensions. + #%] +[% IF error == "example_my_error" %] + [% title = "Example Error Title" %] + This is the error message! It contains some html. +[% END %] -- cgit v1.2.3-24-g4f1b