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

Bug 430010: Re-work the template hooks system so that template hooks always live…

Bug 430010: Re-work the template hooks system so that template hooks always live in template/<lang>/hook/, both for extensions and for the base Bugzilla template/ directory. Patch by Max Kanat-Alexander <mkanat@bugzilla.org> (module owner) a=mkanat
parent c4b313b4
...@@ -41,6 +41,8 @@ our @EXPORT_OK = qw( ...@@ -41,6 +41,8 @@ our @EXPORT_OK = qw(
install_string install_string
include_languages include_languages
template_include_path template_include_path
template_base_directories
template_lang_directories
vers_cmp vers_cmp
get_console_locale get_console_locale
init_console init_console
...@@ -206,28 +208,59 @@ sub include_languages { ...@@ -206,28 +208,59 @@ sub include_languages {
return @usedlanguages; 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 @add;
my @usedlanguages = include_languages(@_); my $project = bz_locations->{'project'};
# Now, we add template directories in the order they will be searched: 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 # First, we add extension template directories, because extension templates
# override standard templates. Extensions may be localized in the same way # override standard templates. Extensions may be localized in the same way
# that Bugzilla templates are localized. # that Bugzilla templates are localized.
my @include_path; my @template_dirs;
my @extensions = glob(bz_locations()->{'extensionsdir'} . "/*"); my @extensions = glob(bz_locations()->{'extensionsdir'} . "/*");
foreach my $extension (@extensions) { foreach my $extension (@extensions) {
next if -e "$extension/disabled"; next if (-e "$extension/disabled" or !-d "$extension/template");
foreach my $lang (@usedlanguages) { push(@template_dirs, "$extension/template");
_add_language_set(\@include_path, $lang, "$extension/template");
}
} }
push(@template_dirs, bz_locations()->{'templatedir'});
# Then, we add normal template directories, sorted by language. return \@template_dirs;
foreach my $lang (@usedlanguages) { }
_add_language_set(\@include_path, $lang);
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; return \@include_path;
} }
...@@ -289,24 +322,6 @@ sub _get_string_from_file { ...@@ -289,24 +322,6 @@ sub _get_string_from_file {
return $strings{$string_id}; 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) # Make an ordered list out of a HTTP Accept-Language header (see RFC 2616, 14.4)
# We ignore '*' and <language-range>;q=0 # We ignore '*' and <language-range>;q=0
# For languages with the same priority q the order remains unchanged. # For languages with the same priority q the order remains unchanged.
......
...@@ -20,93 +20,98 @@ ...@@ -20,93 +20,98 @@
# Contributor(s): Myk Melez <myk@mozilla.org> # Contributor(s): Myk Melez <myk@mozilla.org>
# Zach Lipton <zach@zachlipton.com> # Zach Lipton <zach@zachlipton.com>
# Elliotte Martin <everythingsolved.com> # Elliotte Martin <everythingsolved.com>
# # Max Kanat-Alexander <mkanat@bugzilla.org>
package Bugzilla::Template::Plugin::Hook; package Bugzilla::Template::Plugin::Hook;
use strict; use strict;
use base qw(Template::Plugin);
use Bugzilla::Constants; use Bugzilla::Constants;
use Bugzilla::Install::Util qw(include_languages); use Bugzilla::Install::Util qw(include_languages template_base_directories
use Bugzilla::Template; template_lang_directories);
use Bugzilla::Util; use Bugzilla::Util;
use Bugzilla::Error; use Bugzilla::Error;
use File::Spec; use File::Spec;
use base qw(Template::Plugin);
sub load {
my ($class, $context) = @_;
return $class;
}
sub new { sub new {
my ($class, $context) = @_; my ($class, $context) = @_;
return bless { _CONTEXT => $context }, $class; return bless { _CONTEXT => $context }, $class;
} }
sub _context { return $_[0]->{_CONTEXT} }
sub process { sub process {
my ($self, $hook_name, $template) = @_; my ($self, $hook_name, $template) = @_;
$template ||= $self->{_CONTEXT}->stash->{component}->{name}; my $context = $self->_context();
$template ||= $context->stash->{component}->{name};
my @hooks;
# sanity check: # sanity check:
if (!$template =~ /[\w\.\/\-_\\]+/) { if (!$template =~ /[\w\.\/\-_\\]+/) {
ThrowCodeError('template_invalid', { name => $template}); ThrowCodeError('template_invalid', { name => $template });
} }
# also get extension hook files that live in extensions/: my (undef, $path, $filename) = File::Spec->splitpath($template);
# parse out the parts of the template name $path ||= '';
my ($vol, $subpath, $filename) = File::Spec->splitpath($template); $filename =~ m/(.+)\.(.+)\.tmpl$/;
$subpath = $subpath || ''; my $template_name = $1;
$filename =~ m/(.*)\.(.*)\.tmpl/;
my $templatename = $1;
my $type = $2; my $type = $2;
# munge the filename to create the extension hook filename:
my $extensiontemplate = $subpath.'/'.$templatename.'-'.$hook_name.'.'.$type.'.tmpl'; # Munge the filename to create the extension hook filename
my @extensions = glob(bz_locations()->{'extensionsdir'} . "/*"); my $extension_template = "$path/$template_name-$hook_name.$type.tmpl";
my @usedlanguages = include_languages({use_languages => Bugzilla->languages});
foreach my $extension (@extensions) { my $template_sets = _template_hook_include_path();
next if -e "$extension/disabled";
foreach my $language (@usedlanguages) { my @hooks;
my $file = $extension.'/template/'.$language.'/'.$extensiontemplate; foreach my $dir_set (@$template_sets) {
foreach my $lang_dir (@$dir_set) {
my $file = File::Spec->catdir($lang_dir, $extension_template);
if (-e $file) { if (-e $file) {
# tt is stubborn and won't take a template file not in its # TT won't take a template file not in its include path,
# include path, so we open a filehandle and give it to process() # so we open a filehandle and give it to process()
# so the hook gets invoked: # instead of the file name.
open (my $fh, $file); open (my $fh, '<', $file) or die "$file: $!";
push(@hooks, $fh); 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; my $output;
foreach my $hook (@hooks) { foreach my $hook_fh (@hooks) {
$output .= $self->{_CONTEXT}->process($hook); $output .= $context->process($hook_fh);
close($hook_fh);
} }
return $output; 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; 1;
__END__ __END__
...@@ -165,8 +170,4 @@ Output from processing template extension. ...@@ -165,8 +170,4 @@ Output from processing template extension.
L<Template::Plugin> L<Template::Plugin>
L<http://www.bugzilla.org/docs/tip/html/customization.html> L<http://wiki.mozilla.org/Bugzilla:Writing_Extensions>
L<http://bugzilla.mozilla.org/show_bug.cgi?id=229658>
L<http://bugzilla.mozilla.org/show_bug.cgi?id=298341>
[%# -*- 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 <bbaetz@everythingsolved.com>
#%]
[% IF san_tag == "example_check_au_user" %]
<em>EXAMPLE PLUGIN</em> - Checking for non-Australian users.
[% ELSIF san_tag == "example_check_au_user_alert" %]
User &lt;[% login FILTER html %]&gt; isn't Australian.
[% IF user.in_group('editusers') %]
<a href="editusers.cgi?id=[% userid FILTER none %]">Edit this user</a>.
[% END %]
[% ELSIF san_tag == "example_check_au_user_prompt" %]
<a href="sanitycheck.cgi?example_repair_au_user=1">Fix these users</a>.
[% ELSIF san_tag == "example_repair_au_user_start" %]
<em>EXAMPLE PLUGIN</em> - OK, would now make users Australian.
[% ELSIF san_tag == "example_repair_au_user_end" %]
<em>EXAMPLE PLUGIN</em> - Users would now be Australian.
[% END %]
[%# 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 <em>some html</em>.
[% END %]
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