custis-merge
Andrey Krasilnikov 2013-10-31 16:06:52 +04:00
commit 6ab4d164d0
65 changed files with 1801 additions and 1213 deletions

View File

@ -330,17 +330,17 @@ sub get_mail_result {
sub template {
my $class = shift;
$class->request_cache->{language} = "";
$class->request_cache->{template} ||= Bugzilla::Template->create();
return $class->request_cache->{template};
}
sub template_inner {
my ($class, $lang) = @_;
$lang = defined($lang) ? $lang : ($class->request_cache->{language} || "");
$class->request_cache->{language} = $lang;
my $cache = $class->request_cache;
my $current_lang = $cache->{template_current_lang}->[0];
$lang ||= $current_lang || '';
$class->request_cache->{"template_inner_$lang"}
||= Bugzilla::Template->create();
||= Bugzilla::Template->create(language => $lang);
return $class->request_cache->{"template_inner_$lang"};
}
@ -618,22 +618,7 @@ sub dbh_main {
sub languages {
my $class = shift;
return $class->request_cache->{languages}
if $class->request_cache->{languages};
my @files = glob(catdir(bz_locations->{'templatedir'}, '*'));
my @languages;
foreach my $dir_entry (@files) {
# It's a language directory only if it contains "default" or
# "custom". This auto-excludes CVS directories as well.
next unless (-d catdir($dir_entry, 'default')
|| -d catdir($dir_entry, 'custom'));
$dir_entry = basename($dir_entry);
# Check for language tag format conforming to RFC 1766.
next unless $dir_entry =~ /^[a-zA-Z]{1,8}(-[a-zA-Z]{1,8})?$/;
push(@languages, $dir_entry);
}
return $class->request_cache->{languages} = \@languages;
return Bugzilla::Install::Util::supported_languages();
}
sub error_mode {
@ -921,7 +906,7 @@ sub has_flags {
my $class = shift;
if (!defined $class->request_cache->{has_flags}) {
$class->request_cache->{has_flags} = Bugzilla::Flag::has_flags();
$class->request_cache->{has_flags} = Bugzilla::Flag->any_exist;
}
return $class->request_cache->{has_flags};
}

View File

@ -51,7 +51,7 @@ use Bugzilla::Group;
use Bugzilla::Status;
use Bugzilla::Comment;
use List::Util qw(min);
use List::Util qw(min first);
use Storable qw(dclone);
use URI;
use URI::QueryParam;
@ -2703,11 +2703,9 @@ sub add_group {
return if !$group->is_active or !$group->is_bug_group;
# Make sure that bugs in this product can actually be restricted
# to this group.
grep($group->id == $_->id, @{$self->product_obj->groups_valid})
# But during product change, verification happens anyway in update().
|| $self->{_old_product_name}
|| ThrowUserError('group_invalid_restriction',
# to this group by the current user.
$self->product_obj->group_is_settable($group)
|| ThrowUserError('group_invalid_restriction',
{ product => $self->product, group_id => $group->id });
# OtherControl people can add groups only during a product change,
@ -2734,12 +2732,14 @@ sub remove_group {
return unless $group;
# First, check if this is a valid group for this product.
# You can *always* remove a group that is not valid for this product, so
# we don't do any other checks if that's the case. (set_product does this.)
# You can *always* remove a group that is not valid for this product
# or that is not active, so we don't do any other checks if either of
# those are the case. (Users might remove inactive groups, and set_product
# removes groups that aren't valid for this product.)
#
# This particularly happens when isbuggroup is no longer 1, and we're
# moving a bug to a new product.
if (grep($_->id == $group->id, @{$self->product_obj->groups_valid})) {
if ($group->is_active and $self->product_obj->group_is_valid($group)) {
my $controls = $self->product_obj->group_controls->{$group->id};
# Nobody can ever remove a Mandatory group.
@ -2819,41 +2819,18 @@ sub add_see_also {
if ($uri->path =~ m|^/p/([^/]+)/issues/detail$|) {
$project_name = $1;
} else {
ThrowUserError('bug_url_invalid',
{ url => $input });
ThrowUserError('bug_url_invalid', { url => $input });
}
my $bug_id = $uri->query_param('id');
detaint_natural($bug_id);
if (!$bug_id) {
ThrowUserError('bug_url_invalid',
{ url => $input, reason => 'id' });
ThrowUserError('bug_url_invalid', { url => $input, reason => 'id' });
}
# While Google Code URLs can be either HTTP or HTTPS,
# always go with the HTTP scheme, as that's the default.
$result = "http://code.google.com/p/" . $project_name .
"/issues/detail?id=" . $bug_id;
}
# Debian BTS URLs
elsif ($uri->authority =~ /^bugs.debian.org$/i) {
# Debian BTS URLs can look like various things:
# http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1234
# http://bugs.debian.org/1234
my $bug_id;
if ($uri->path =~ m|^/(\d+)$|) {
$bug_id = $1;
}
elsif ($uri->path =~ /bugreport\.cgi$/) {
$bug_id = $uri->query_param('bug');
detaint_natural($bug_id);
}
if (!$bug_id) {
ThrowUserError('bug_url_invalid',
{ url => $input, reason => 'id' });
}
# This is the shortest standard URL form for Debian BTS URLs,
# and so we reduce all URLs to this.
$result = "http://bugs.debian.org/" . $bug_id;
}
# Bugzilla URLs
else {
if ($uri->path !~ /show_bug\.cgi$/) {
@ -3841,6 +3818,20 @@ sub check_can_change_field {
return 1;
}
my @priv_results;
Bugzilla::Hook::process('bug_check_can_change_field',
{ bug => $self, field => $field,
new_value => $newvalue, old_value => $oldvalue,
priv_results => \@priv_results });
if (my $priv_required = first { $_ > 0 } @priv_results) {
$$PrivilegesRequired = $priv_required;
return 0;
}
my $allow_found = first { $_ == 0 } @priv_results;
if (defined $allow_found) {
return 1;
}
# Allow anyone to change comments.
if ($field =~ /^longdesc/) {
return 1;
@ -3850,15 +3841,15 @@ sub check_can_change_field {
# We store the required permission set into the $PrivilegesRequired
# variable which gets passed to the error template.
#
# $PrivilegesRequired = 0 : no privileges required;
# $PrivilegesRequired = 1 : the reporter, assignee or an empowered user;
# $PrivilegesRequired = 2 : the assignee or an empowered user;
# $PrivilegesRequired = 3 : an empowered user.
# $PrivilegesRequired = PRIVILEGES_REQUIRED_NONE : no privileges required;
# $PrivilegesRequired = PRIVILEGES_REQUIRED_REPORTER : the reporter, assignee or an empowered user;
# $PrivilegesRequired = PRIVILEGES_REQUIRED_ASSIGNEE : the assignee or an empowered user;
# $PrivilegesRequired = PRIVILEGES_REQUIRED_EMPOWERED : an empowered user.
# Only users in the time-tracking group can change time-tracking fields.
if ( grep($_ eq $field, qw(deadline estimated_time remaining_time)) ) {
if (!$user->is_timetracker) {
$$PrivilegesRequired = 3;
$$PrivilegesRequired = PRIVILEGES_REQUIRED_EMPOWERED;
return 0;
}
}
@ -3870,7 +3861,7 @@ sub check_can_change_field {
# *Only* users with (product-specific) "canconfirm" privs can confirm bugs.
if ($self->_changes_everconfirmed($field, $oldvalue, $newvalue)) {
$$PrivilegesRequired = 3;
$$PrivilegesRequired = PRIVILEGES_REQUIRED_EMPOWERED;
return $user->in_group('canconfirm', $self->{'product_id'});
}
@ -3901,36 +3892,36 @@ sub check_can_change_field {
# in that case we will have already returned 1 above
# when checking for the assignee of the bug.
if ($field eq 'assigned_to') {
$$PrivilegesRequired = 2;
$$PrivilegesRequired = PRIVILEGES_REQUIRED_ASSIGNEE;
return 0;
}
# - change the QA contact
if ($field eq 'qa_contact') {
$$PrivilegesRequired = 2;
$$PrivilegesRequired = PRIVILEGES_REQUIRED_ASSIGNEE;
return 0;
}
# - change the target milestone
if ($field eq 'target_milestone') {
$$PrivilegesRequired = 2;
$$PrivilegesRequired = PRIVILEGES_REQUIRED_ASSIGNEE;
return 0;
}
# - change the priority (unless he could have set it originally)
if ($field eq 'priority'
&& !Bugzilla->params->{'letsubmitterchoosepriority'})
{
$$PrivilegesRequired = 2;
$$PrivilegesRequired = PRIVILEGES_REQUIRED_ASSIGNEE;
return 0;
}
# - unconfirm bugs (confirming them is handled above)
if ($field eq 'everconfirmed') {
$$PrivilegesRequired = 2;
$$PrivilegesRequired = PRIVILEGES_REQUIRED_ASSIGNEE;
return 0;
}
# - change the status from one open state to another
if ($field eq 'bug_status'
&& is_open_state($oldvalue) && is_open_state($newvalue))
{
$$PrivilegesRequired = 2;
$$PrivilegesRequired = PRIVILEGES_REQUIRED_ASSIGNEE;
return 0;
}
@ -3941,7 +3932,7 @@ sub check_can_change_field {
# If we haven't returned by this point, then the user doesn't
# have the necessary permissions to change this field.
$$PrivilegesRequired = 1;
$$PrivilegesRequired = PRIVILEGES_REQUIRED_REPORTER;
return 0;
}

View File

@ -659,12 +659,10 @@ sub sendMail
};
my $msg;
my $tmpl = '';
my $template = Bugzilla->template_inner($user->settings->{lang}->{value});
Bugzilla::Hook::process('bugmail-pre_template', { tmpl => \$tmpl, vars => $vars });
$tmpl = "email/newchangedmail.txt.tmpl" unless $template->template_exists($tmpl);
$template->process($tmpl, $vars, \$msg) || ThrowTemplateError($template->error());
$template->process("email/newchangedmail.txt.tmpl", $vars, \$msg)
|| ThrowTemplateError($template->error());
Bugzilla->template_inner("");
MessageToMTA($msg);

View File

@ -182,8 +182,10 @@ use Cwd qw(abs_path);
CGI_URI_LIMIT
LANG_ISO_FULL
LANG_FULL_ISO
PRIVILEGES_REQUIRED_NONE
PRIVILEGES_REQUIRED_REPORTER
PRIVILEGES_REQUIRED_ASSIGNEE
PRIVILEGES_REQUIRED_EMPOWERED
BUG_ID_ADD_TO_BLOCKED
BUG_ID_ADD_TO_DEPENDSON
@ -542,28 +544,14 @@ use constant PASSWORD_SALT_LENGTH => 8;
# can be safely done or not based on the web server's URI length setting.
use constant CGI_URI_LIMIT => 8000;
# Full language names corresponding to 2-letter ISO codes
# Used to select stemming language in fulltext search
use constant LANG_ISO_FULL => {
da => 'danish',
nl => 'dutch',
en => 'english',
fi => 'finnish',
fr => 'french',
de => 'german',
hu => 'hungarian',
it => 'italian',
no => 'norwegian',
pt => 'portuguese',
ro => 'romanian',
ru => 'russian',
es => 'spanish',
sv => 'swedish',
tr => 'turkish',
};
# If the user isn't allowed to change a field, we must tell him who can.
# We store the required permission set into the $PrivilegesRequired
# variable which gets passed to the error template.
# The reverse of LANG_ISO_FULL
use constant LANG_FULL_ISO => { reverse %{LANG_ISO_FULL()} };
use constant PRIVILEGES_REQUIRED_NONE => 0;
use constant PRIVILEGES_REQUIRED_REPORTER => 1;
use constant PRIVILEGES_REQUIRED_ASSIGNEE => 2;
use constant PRIVILEGES_REQUIRED_EMPOWERED => 3;
sub bz_locations {
# We know that Bugzilla/Constants.pm must be in %INC at this point.

View File

@ -792,10 +792,10 @@ sub bz_drop_fk {
}
sub bz_drop_related_fks {
sub bz_get_related_fks {
my ($self, $table, $column) = @_;
my @tables = $self->_bz_real_schema->get_table_list();
my @dropped;
my @related;
foreach my $check_table (@tables) {
my @columns = $self->bz_table_columns($check_table);
foreach my $check_column (@columns) {
@ -805,13 +805,22 @@ sub bz_drop_related_fks {
and (($fk->{TABLE} eq $table and $fk->{COLUMN} eq $column)
or ($check_column eq $column and $check_table eq $table)))
{
$self->bz_drop_fk($check_table, $check_column);
push(@dropped, [$check_table, $check_column, $fk]);
push(@related, [$check_table, $check_column, $fk]);
}
} # foreach $column
} # foreach $table
return \@dropped;
return \@related;
}
sub bz_drop_related_fks {
my $self = shift;
my $related = $self->bz_get_related_fks(@_);
foreach my $item (@$related) {
my ($table, $column) = @$item;
$self->bz_drop_fk($table, $column);
}
return $related;
}
sub bz_drop_index {

View File

@ -249,7 +249,9 @@ sub _throw_error
# higher than 999, but we do this to avoid conflicts with
# the internal JSON::RPC error codes.
$server->raise_error(code => 100000 + $code,
message => $message);
message => $message,
id => $server->{_bz_request_id},
version => $server->version);
# We die with no message. JSON::RPC checks raise_error before
# it checks $@, so it returns the proper error.
die;

View File

@ -298,11 +298,344 @@ package name in a string, and it will be loaded automatically.
=head2 Hooks
A hook is a place in the code into which other code parts can be inserted.
In Bugzilla, there are code hooks and template hooks.
Extensions should use hooks for extending the functionality. The best
is if you use predefined hooks, but you can also add your own and publish
the patch which adds this hooks somewhere on L<http://wiki.4intra.net/>.
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 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/>.
Hook functions always get arguments through single hashref parameter ($args).
Their return value is always a boolean value: when it's TRUE, other hooks

View File

@ -184,7 +184,8 @@ use constant DEFAULT_FIELDS => (
buglist => 1},
{name => 'rep_platform', desc => 'Platform', in_new_bugmail => 1,
type => FIELD_TYPE_SINGLE_SELECT, buglist => 1},
{name => 'bug_file_loc', desc => 'URL', in_new_bugmail => 1},
{name => 'bug_file_loc', desc => 'URL', in_new_bugmail => 1,
buglist => 1},
{name => 'op_sys', desc => 'OS/Version', in_new_bugmail => 1,
type => FIELD_TYPE_SINGLE_SELECT, buglist => 1},
{name => 'bug_status', desc => 'Status', in_new_bugmail => 1,

View File

@ -230,26 +230,6 @@ sub bug {
=over
=item C<has_flags>
Returns 1 if at least one flag exists in the DB, else 0. This subroutine
is mainly used to decide to display the "(My )Requests" link in the footer.
=back
=cut
sub has_flags {
my $dbh = Bugzilla->dbh;
my $has_flags = $dbh->selectrow_array('SELECT 1 FROM flags ' . $dbh->sql_limit(1));
return $has_flags || 0;
}
=pod
=over
=item C<match($criteria)>
Queries the database for flags matching the given criteria
@ -555,7 +535,7 @@ sub retarget {
my $success = 0;
foreach my $flagtype (@flagtypes) {
next if !$flagtype->is_active;
next if (!$flagtype->is_multiplicable && grep { $_->id != $self->id } @{$flagtype->{flags}});
next if (!$flagtype->is_multiplicable && scalar @{$flagtype->{flags}});
next unless (($self->status eq '?' && $self->setter->can_request_flag($flagtype))
|| $self->setter->can_set_flag($flagtype));

View File

@ -369,6 +369,66 @@ The hash of changed fields. C<< $changes->{field} = [old, new] >>
=back
=head2 bug_check_can_change_field
This hook controls what fields users are allowed to change. You can add code here for
site-specific policy changes and other customizations. This hook is only
executed if the field's new and old values differ. Any denies take priority over any allows.
So, if another extension denies a change but yours allows the change, the other extension's
deny will override your extension's allow.
Params:
=over
=item C<bug>
L<Bugzilla::Bug> - The current bug object that this field is changing on.
=item C<field>
The name (from the C<fielddefs> table) of the field that we are checking.
=item C<new_value>
The new value that the field is being changed to.
=item C<old_value>
The old value that the field is being changed from.
=item C<priv_results>
C<array> - This is how you explicitly allow or deny a change. You should only
push something into this array if you want to explicitly allow or explicitly
deny the change, and thus skip all other permission checks that would otherwise
happen after this hook is called. If you don't care about the field change,
then don't push anything into the array.
The pushed value should be a choice from the following constants:
=over
=item C<PRIVILEGES_REQUIRED_NONE>
No privileges required. This explicitly B<allows> a change.
=item C<PRIVILEGES_REQUIRED_REPORTER>
User is not the reporter, assignee or an empowered user, so B<deny>.
=item C<PRIVILEGES_REQUIRED_ASSIGNEE>
User is not the assignee or an empowered user, so B<deny>.
=item C<PRIVILEGES_REQUIRED_EMPOWERED>
User is not a sufficiently empowered user, so B<deny>.
=back
=back
=head2 bug_fields
Allows the addition of database fields from the bugs table to the standard
@ -546,21 +606,6 @@ spaces.
=back
=head2 colchange_columns
This happens in F<colchange.cgi> right after the list of possible display
columns have been defined and gives you the opportunity to add additional
display columns to the list of selectable columns.
Params:
=over
=item C<columns> - An arrayref containing an array of column IDs. Any IDs
added by this hook must have been defined in the the L</buglist_columns> hook.
=back
=head2 config_add_panels
If you want to add new panels to the Parameters administrative interface,

View File

@ -107,6 +107,8 @@ sub update_fielddefs_definition {
#2008-08-26 elliotte_martin@yahoo.com - Bug 251556
$dbh->bz_add_column('fielddefs', 'reverse_desc', {TYPE => 'TINYTEXT'});
$dbh->do('UPDATE fielddefs SET buglist = 1
WHERE custom = 1 AND type = ' . FIELD_TYPE_MULTI_SELECT);
# Remember, this is not the function for adding general table changes.
# That is below. Add new changes to the fielddefs table above this

View File

@ -150,7 +150,13 @@ sub extension_requirement_packages {
# Bugzilla::Extension->load_all (because stuff has already been loaded).
# (This matters because almost every page calls Bugzilla->feature, which
# calls OPTIONAL_MODULES, which calls this method.)
if (eval { Bugzilla->extensions }) {
#
# We check if Bugzilla.pm is already loaded, instead of doing a "require",
# because we *do* want the code lower down to run during the Requirements
# phase of checksetup.pl, instead of Bugzilla->extensions, and Bugzilla.pm
# actually *can* be loaded during the Requirements phase if all the
# requirements have already been installed.
if ($INC{'Bugzilla.pm'}) {
return Bugzilla->extensions;
}
my $packages = _cache()->{extension_requirement_packages};
@ -262,6 +268,8 @@ sub install_string {
utf8::decode($string_template) if !utf8::is_utf8($string_template);
utf8::decode($string_template) if !utf8::is_utf8($string_template);
$vars ||= {};
my @replace_keys = keys %$vars;
foreach my $key (@replace_keys) {
@ -282,91 +290,85 @@ sub install_string {
return $string_template;
}
sub include_languages {
# If we are in CGI mode (not in checksetup.pl) and if the function has
# been called without any parameter, then we cache the result of this
# function in Bugzilla->request_cache. This is done to improve the
# performance of the template processing.
my $to_be_cached = 0;
if (not @_) {
my $cache = _cache();
if (exists $cache->{include_languages}) {
return @{ $cache->{include_languages} };
}
$to_be_cached = 1;
}
my ($params) = @_;
$params ||= {};
sub _wanted_languages {
my ($requested, @wanted);
# Basically, the way this works is that we have a list of languages
# that we *want*, and a list of languages that Bugzilla actually
# supports. The caller tells us what languages they want, by setting
# $ENV{HTTP_ACCEPT_LANGUAGE}, using the "LANG" cookie or setting
# $params->{only_language}. The languages we support are those
# specified in $params->{use_languages}. Otherwise we support every
# language installed in the template/ directory.
my @wanted;
if ($params->{only_language}) {
# We can pass several languages at once as an arrayref
# or a single language.
if (ref $params->{only_language}) {
@wanted = @{ $params->{only_language} };
}
else {
@wanted = ($params->{only_language});
}
}
# Checking SERVER_SOFTWARE is the same as i_am_cgi() in Bugzilla::Util.
if (exists $ENV{'SERVER_SOFTWARE'}) {
my $cgi = Bugzilla->cgi;
$requested = $cgi->http('Accept-Language') || '';
my $lang = $cgi->cookie('LANG');
push(@wanted, $lang) if $lang;
- }
else {
@wanted = _sort_accept_language($ENV{'HTTP_ACCEPT_LANGUAGE'} || '');
# Don't use the cookie if we are in "checksetup.pl". The test
# with $ENV{'SERVER_SOFTWARE'} is the same as in
# Bugzilla:Util::i_am_cgi.
if (exists $ENV{'SERVER_SOFTWARE'}) {
my $cgi = Bugzilla->cgi;
if (defined (my $lang = $cgi->cookie('LANG'))) {
unshift @wanted, $lang;
}
}
$requested = get_console_locale();
}
my @supported;
if (defined $params->{use_languages}) {
@supported = @{$params->{use_languages}};
}
else {
my @dirs = glob(bz_locations()->{'templatedir'} . "/*");
@dirs = map(basename($_), @dirs);
@supported = grep($_ ne 'CVS', @dirs);
}
my @usedlanguages;
foreach my $wanted (@wanted) {
push(@wanted, _sort_accept_language($requested));
return \@wanted;
}
sub _wanted_to_actual_languages {
my ($wanted, $supported) = @_;
my @actual;
foreach my $lang (@$wanted) {
# If we support the language we want, or *any version* of
# the language we want, it gets pushed into @usedlanguages.
# the language we want, it gets pushed into @actual.
#
# Per RFC 1766 and RFC 2616, things like 'en' match 'en-us' and
# 'en-uk', but not the other way around. (This is unfortunately
# not very clearly stated in those RFC; see comment just over 14.5
# in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4)
if(my @found = grep /^\Q$wanted\E(-.+)?$/i, @supported) {
push (@usedlanguages, @found);
}
my @found = grep(/^\Q$lang\E(-.+)?$/i, @$supported);
push(@actual, @found) if @found;
}
# We always include English at the bottom if it's not there, even if
# somebody removed it from use_languages.
if (!grep($_ eq 'en', @usedlanguages)) {
push(@usedlanguages, 'en');
# it wasn't selected by the user.
if (!grep($_ eq 'en', @actual)) {
push(@actual, 'en');
}
# Cache the result if we are in CGI mode and called without parameter
# (see the comment at the top of this function).
if ($to_be_cached) {
_cache()->{include_languages} = \@usedlanguages;
return \@actual;
}
sub supported_languages {
my $cache = _cache();
return $cache->{supported_languages} if $cache->{supported_languages};
my @dirs = glob(bz_locations()->{'templatedir'} . "/*");
my @languages;
foreach my $dir (@dirs) {
# It's a language directory only if it contains "default" or
# "custom". This auto-excludes CVS directories as well.
next if (!-d "$dir/default" and !-d "$dir/custom");
my $lang = basename($dir);
# Check for language tag format conforming to RFC 1766.
next unless $lang =~ /^[a-zA-Z]{1,8}(-[a-zA-Z]{1,8})?$/;
push(@languages, $lang);
}
return @usedlanguages;
$cache->{supported_languages} = \@languages;
return \@languages;
}
sub include_languages {
my ($params) = @_;
# Basically, the way this works is that we have a list of languages
# that we *want*, and a list of languages that Bugzilla actually
# supports.
my $wanted;
if ($params->{language}) {
$wanted = [$params->{language}];
}
else {
$wanted = _wanted_languages();
}
my $supported = supported_languages();
my $actual = _wanted_to_actual_languages($wanted, $supported);
return @$actual;
}
# Used by template_include_path
@ -551,7 +553,6 @@ sub get_console_locale {
sub init_console {
eval { ON_WINDOWS && require Win32::Console::ANSI; };
$ENV{'ANSI_COLORS_DISABLED'} = 1 if ($@ || !-t *STDOUT);
$ENV{'HTTP_ACCEPT_LANGUAGE'} ||= get_console_locale();
prevent_windows_dialog_boxes();
}

View File

@ -749,8 +749,10 @@ sub groups_mandatory {
# is Mandatory, the group is Mandatory for everybody, regardless of their
# group membership.
my $ids = Bugzilla->dbh->selectcol_arrayref(
"SELECT group_id FROM group_control_map
WHERE product_id = ?
"SELECT group_id
FROM group_control_map
INNER JOIN groups ON group_control_map.group_id = groups.id
WHERE product_id = ? AND isactive = 1
AND (membercontrol = $mandatory
OR (othercontrol = $mandatory
AND group_id NOT IN ($groups)))",
@ -760,8 +762,8 @@ sub groups_mandatory {
}
# We don't just check groups_valid, because we want to know specifically
# if this group is valid for the currently-logged-in user.
sub group_is_valid {
# if this group can be validly set by the currently-logged-in user.
sub group_is_settable {
my ($self, $group) = @_;
my $group_id = blessed($group) ? $group->id : $group;
my $is_mandatory = grep { $group_id == $_->id }
@ -771,6 +773,11 @@ sub group_is_valid {
return ($is_mandatory or $is_available) ? 1 : 0;
}
sub group_is_valid {
my ($self, $group) = @_;
return grep($_->id == $group->id, @{ $self->groups_valid }) ? 1 : 0;
}
sub groups_valid {
my ($self) = @_;
return $self->{groups_valid} if defined $self->{groups_valid};
@ -1102,7 +1109,7 @@ is set to Default for the currently-logged-in user.
Tells you what groups are mandatory for bugs in this product, for the
currently-logged-in user. Returns an arrayref of C<Bugzilla::Group> objects.
=item C<group_is_valid>
=item C<group_is_settable>
=over
@ -1138,7 +1145,9 @@ C<1> if the group is valid in this product, C<0> otherwise.
Returns an arrayref of L<Bugzilla::Group> objects, representing groups
that bugs could validly be restricted to within this product. Used mostly
by L<Bugzilla::Bug> to assure that you're adding valid groups to a bug.
when you need the list of all possible groups that could be set in a product
by anybody, disregarding whether or not the groups are active or who the
currently logged-in user is.
B<Note>: This doesn't check whether or not the current user can add/remove
bugs to/from these groups. It just tells you that bugs I<could be in> these
@ -1150,6 +1159,13 @@ groups, in this product.
=back
=item C<group_is_valid>
Returns C<1> if the passed-in L<Bugzilla::Group> or group id could be set
on a bug by I<anybody>, in this product. Even inactive groups are considered
valid. (This is a shortcut for searching L</groups_valid> to find out if
a group is valid in a particular product.)
=item C<versions>
Description: Returns all valid versions for that product.

View File

@ -1,3 +1,4 @@
# Totally Rewritten Bugzilla4Intranet search engine
# License: Dual-license GPL 3.0+ or MPL 1.1+
# Contributor(s): Vitaliy Filippov <vitalif@mail.ru>
@ -2095,6 +2096,9 @@ sub _long_desc_changedby
sub _long_desc_changedbefore_after
{
my $self = shift;
my %func_args = @_;
my ($chartid, $supptables, $term, $groupby, $fields, $t, $v) =
@func_args{qw(chartid supptables term groupby fields t v)};
my $dbh = Bugzilla->dbh;
my $operator = ($self->{type} =~ /before/) ? '<' : '>';
my $table = "longdescs_".$self->{sequence};

View File

@ -31,6 +31,7 @@ use Bugzilla::Field;
use Bugzilla::Util;
use List::Util qw(min max);
use Text::ParseWords qw(quotewords);
use base qw(Exporter);
@Bugzilla::Search::Quicksearch::EXPORT = qw(quicksearch);
@ -164,6 +165,8 @@ sub quicksearch {
$self->{content} = '';
$self->{unknown_fields} = [];
$self->{ambiguous_fields} = {};
my @words = quotewords('\s+', 0, $searchstring);
_handle_status_and_resolution(\@words);
$self->_handle_status_and_resolution;
@ -655,7 +658,7 @@ sub makeChart {
my $cgi = Bugzilla->cgi;
$cgi->param("field$expr", $field);
$cgi->param("type$expr", $type);
$cgi->param("value$expr", url_decode($value));
$cgi->param("value$expr", $value);
}
1;

View File

@ -89,12 +89,11 @@ sub _load_constants {
# 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 {
sub _include_path {
my $lang = shift || '';
my $cache = Bugzilla->request_cache;
my $lang = $cache->{'language'} || '';
$cache->{"template_include_path_$lang"} ||= template_include_path({
use_languages => Bugzilla->languages,
only_language => $lang });
$cache->{"template_include_path_$lang"} ||=
template_include_path({ language => $lang });
return $cache->{"template_include_path_$lang"};
}
@ -350,7 +349,7 @@ sub quoteUrls {
# we have to do this in one pattern, and so this is semi-messy.
# Also, we can't use $bug_re?$comment_re? because that will match the
# empty string
my $bug_word = Bugzilla->messages->{terms}->{bug};
my $bug_word = template_var('terms')->{bug};
my $bug_re = qr/\Q$bug_word\E\s*\#?\s*(\d+)/i;
my $comment_re = qr/comment\s*\#?\s*(\d+)/i;
$text =~ s~\b($bug_re(?:\s*,?\s*$comment_re)?|$comment_re)
@ -546,6 +545,17 @@ $Template::Stash::SCALAR_OPS->{ truncate } =
###############################################################################
sub process {
my $self = shift;
# All of this current_langs stuff allows template_inner to correctly
# determine what-language Template object it should instantiate.
my $current_langs = Bugzilla->request_cache->{template_current_lang} ||= [];
unshift(@$current_langs, $self->context->{bz_language});
my $retval = $self->SUPER::process(@_);
shift @$current_langs;
return $retval;
}
# Construct the Template object
# Note that all of the failure cases here can't use templateable errors,
@ -560,7 +570,8 @@ sub create {
my $config = {
# Colon-separated list of directories containing templates.
INCLUDE_PATH => $opts{'include_path'} || getTemplateIncludePath(),
INCLUDE_PATH => $opts{'include_path'}
|| _include_path($opts{'language'}),
# Remove white-space before template directives (PRE_CHOMP) and at the
# beginning and end of templates and template blocks (TRIM) for better
@ -830,6 +841,15 @@ sub create {
# Now remove extra whitespace...
my $collapse_filter = $Template::Filters::FILTERS->{collapse};
$var = $collapse_filter->($var);
# And if we're not in the WebService, wrap the message.
# (Wrapping the message in the WebService is unnecessary
# and causes awkward things like \n's appearing in error
# messages in JSON-RPC.)
unless (Bugzilla->usage_mode == USAGE_MODE_JSON
or Bugzilla->usage_mode == USAGE_MODE_XMLRPC)
{
$var = wrap_comment($var, 72);
}
return $var;
},
@ -1012,6 +1032,11 @@ sub create {
'feature_enabled' => sub { return Bugzilla->feature(@_); },
# field_descs can be somewhat slow to generate, so we generate
# it only once per-language no matter how many times
# $template->process() is called.
'field_descs' => sub { return template_var('field_descs') },
'install_string' => \&Bugzilla::Install::Util::install_string,
# These don't work as normal constants.
@ -1022,9 +1047,7 @@ sub create {
my @optional = @{OPTIONAL_MODULES()};
foreach my $item (@optional) {
my @features;
my $feat = $item->{feature};
ref $feat or $feat = [ $feat ];
foreach my $feat_id (@$feat) {
foreach my $feat_id (@{ $item->{feature} }) {
push(@features, install_string("feature_$feat_id"));
}
$item->{feature} = \@features;
@ -1039,6 +1062,11 @@ sub create {
Bugzilla::Hook::process('template_before_create', { config => $config });
my $template = $class->new($config)
|| die("Template creation failed: " . $class->error());
# Pass on our current language to any template hooks or inner templates
# called by this Template object.
$template->context->{bz_language} = $opts{language} || '';
return $template;
}
@ -1071,8 +1099,7 @@ sub precompile_templates {
print install_string('template_precompile') if $output;
my $paths = template_include_path({ use_languages => Bugzilla->languages,
only_language => Bugzilla->languages });
my $paths = template_include_path();
foreach my $dir (@$paths) {
my $template = Bugzilla::Template->create(include_path => [$dir]);

View File

@ -27,7 +27,7 @@ use strict;
use base qw(Template::Plugin);
use Bugzilla::Constants;
use Bugzilla::Install::Util qw(include_languages template_include_path);
use Bugzilla::Install::Util qw(template_include_path);
use Bugzilla::Util;
use Bugzilla::Error;
@ -65,7 +65,7 @@ sub process {
# Get the hooks out of the cache if they exist. Otherwise, read them
# from the disk.
my $cache = Bugzilla->request_cache->{template_plugin_hook_cache} ||= {};
my $lang = Bugzilla->request_cache->{language} || '';
my $lang = $context->{bz_language} || '';
$cache->{"${lang}__$extension_template"}
||= $self->_get_hooks($extension_template);
@ -78,7 +78,7 @@ sub process {
sub _get_hooks {
my ($self, $extension_template) = @_;
my $template_sets = _template_hook_include_path();
my $template_sets = $self->_template_hook_include_path();
my @hooks;
foreach my $dir_set (@$template_sets) {
foreach my $template_dir (@$dir_set) {
@ -96,13 +96,13 @@ sub _get_hooks {
}
sub _template_hook_include_path {
my $self = shift;
my $cache = Bugzilla->request_cache;
my $language = $cache->{language} || '';
my $language = $self->_context->{bz_language} || '';
my $cache_key = "template_plugin_hook_include_path_$language";
$cache->{$cache_key} ||= template_include_path({
use_languages => Bugzilla->languages,
only_language => $language,
hook => 1,
language => $language,
hook => 1,
});
return $cache->{$cache_key};
}

View File

@ -122,7 +122,6 @@ sub IssueEmailChangeToken {
$template->process("account/email/change-new.txt.tmpl", $vars, \$message)
|| ThrowTemplateError($template->error());
Bugzilla->template_inner("");
MessageToMTA($message);
}
@ -160,7 +159,6 @@ sub IssuePasswordToken {
$vars, \$message)
|| ThrowTemplateError($template->error());
Bugzilla->template_inner("");
MessageToMTA($message);
}
@ -300,7 +298,6 @@ sub Cancel {
$template->process("account/cancel-token.txt.tmpl", $vars, \$message)
|| ThrowTemplateError($template->error());
Bugzilla->template_inner("");
MessageToMTA($message);
# Delete the token from the database.

View File

@ -32,21 +32,21 @@ use utf8;
use strict;
use base qw(Exporter);
@Bugzilla::Util::EXPORT = qw(
trick_taint detaint_natural trick_taint_copy detaint_signed
html_quote url_quote url_quote_noslash xml_quote css_class_quote html_light_quote url_decode
i_am_cgi correct_urlbase remote_ip lsearch
do_ssl_redirect_if_required use_attachbase
diff_arrays list
trim wrap_hard wrap_comment find_wrap_point makeCitations
format_time format_time_decimal validate_date validate_time datetime_from
file_mod_time is_7bit_clean
bz_crypt generate_random_password
validate_email_syntax clean_text
stem_text intersect union
get_text disable_utf8 bz_encode_json
xml_element xml_element_quote xml_dump_simple xml_simple
);
@Bugzilla::Util::EXPORT = qw(trick_taint detaint_natural trick_taint_copy
detaint_signed
html_quote url_quote url_quote_noslash xml_quote
css_class_quote html_light_quote url_decode
i_am_cgi correct_urlbase remote_ip
lsearch do_ssl_redirect_if_required use_attachbase
diff_arrays list
trim wrap_hard wrap_comment find_wrap_point makeCitations
format_time format_time_decimal validate_date
validate_time datetime_from
file_mod_time is_7bit_clean
bz_crypt generate_random_password
validate_email_syntax clean_text stem_text bz_encode_json
xml_element xml_element_quote xml_dump_simple xml_simple
get_text template_var disable_utf8);
use Bugzilla::Constants;
@ -743,6 +743,26 @@ sub get_text
return $message;
}
sub template_var {
my $name = shift;
my $cache = Bugzilla->request_cache->{util_template_var} ||= {};
my $template = Bugzilla->template_inner;
my $lang = $template->context->{bz_language};
return $cache->{$lang}->{$name} if defined $cache->{$lang};
my %vars;
# Note: If we suddenly start needing a lot of template_var variables,
# they should move into their own template, not field-descs.
my $result = $template->process('global/field-descs.none.tmpl',
{ vars => \%vars, in_template_var => 1 });
# Bugzilla::Error can't be "use"d in Bugzilla::Util.
if (!$result) {
require Bugzilla::Error;
Bugzilla::Error::ThrowTemplateError($template->error);
}
$cache->{$lang} = \%vars;
return $vars{$name};
}
sub disable_utf8 {
if (Bugzilla->params->{'utf8'}) {
binmode STDOUT, ':bytes'; # Turn off UTF8 encoding.
@ -1221,6 +1241,14 @@ A string.
=back
=item C<template_var>
This is a method of getting the value of a variable from a template in
Perl code. The available variables are in the C<global/field-descs.none.tmpl>
template. Just pass in the name of the variable that you want the value of.
=back
=head2 Formatting Time

View File

@ -94,7 +94,7 @@ sub fields {
if (defined $params->{names}) {
my $names = $params->{names};
foreach my $field_name (@$names) {
my $loop_field = Bugzilla->get_field($field_name, THROW_ERROR);
my $loop_field = Bugzilla::Field->check($field_name);
# Don't push in duplicate fields if we also asked for this field
# in "ids".
if (!grep($_->id == $loop_field->id, @fields)) {
@ -111,7 +111,7 @@ sub fields {
foreach my $field (@fields) {
my $visibility_field = $field->visibility_field
? $field->visibility_field->name : undef;
my $vis_values = $field->visibility_values;
my $vis_value = $field->visibility_value;
my $value_field = $field->value_field
? $field->value_field->name : undef;
@ -137,7 +137,9 @@ sub fields {
# is_mandatory => $self->type('boolean', $field->is_mandatory),
is_on_bug_entry => $self->type('boolean', $field->enter_bug),
visibility_field => $self->type('string', $visibility_field),
visibility_values => [ map { $self->type('string', $_->name) } @{ $vis_values || [] } ],
visibility_values => [
defined $vis_value ? $self->type('string', $vis_value->name) : ()
],
);
if ($has_values) {
$field_data{value_field} = $self->type('string', $value_field);
@ -212,11 +214,14 @@ sub _legal_field_values {
else {
my @values = Bugzilla::Field::Choice->type($field)->get_all();
foreach my $value (@values) {
my $vis_values = $value->visibility_values;
my $vis_val = $value->visibility_value;
push(@result, {
name => $self->type('string', $value->name),
sortkey => $self->type('int' , $value->sortkey),
visibility_values => [ map { $self->type('string', $_->name) } @{ $vis_values || [] } ],
visibility_values => [
defined $vis_val ? $self->type('string', $vis_val->name)
: ()
],
});
}
}
@ -1653,26 +1658,13 @@ take.
If you are not in the time-tracking group, this field will not be included
in the return value.
=item C<groups>
=item internals B<DEPRECATED>
C<array> of C<string>s. The names of all the groups that this bug is in.
=item C<id>
This will be disappearing in a future version of Bugzilla.
C<int> The unique numeric id of this bug.
=item C<is_cc_accessible>
C<boolean> If true, this bug can be accessed by members of the CC list,
even if they are not in the groups the bug is restricted to.
=item C<is_confirmed>
C<boolean> True if the bug has been confirmed. Usually this means that
the bug has at some point been moved out of the C<UNCONFIRMED> status
and into another open status.
=item C<is_open>
=item is_open
C<boolean> True if this bug is open, false if it is closed.
@ -2552,7 +2544,7 @@ code of 32000.
=item C<update>
B<UNSTABLE>
B<EXPERIMENTAL>
=over

View File

@ -107,6 +107,9 @@ use constant WS_ERROR_CODE => {
# Except, historically, AUTH_NODATA, which is 410.
login_required => 410,
# Except, historically, AUTH_NODATA, which is 410.
login_required => 410,
# User errors are 500-600.
account_exists => 500,
illegal_email_address => 501,

View File

@ -42,7 +42,6 @@ sub datetime_format_inbound {
sub datetime_format_outbound {
my ($self, $date) = @_;
return '' if !$date;
my $time = $date;
if (blessed($date)) {

View File

@ -27,7 +27,7 @@ use base qw(JSON::RPC::Legacy::Server::CGI Bugzilla::WebService::Server);
use Bugzilla::Error;
use Bugzilla::WebService::Constants;
use Bugzilla::WebService::Util qw(taint_data);
use Bugzilla::Util qw(datetime_from correct_urlbase trim);
use Bugzilla::Util qw(correct_urlbase trim);
use MIME::Base64 qw(decode_base64 encode_base64);
sub new {
@ -44,33 +44,21 @@ sub create_json_coder {
my $json = $self->SUPER::create_json_coder(@_);
$json->allow_blessed(1);
$json->convert_blessed(1);
#*********************************************************************#
# ïÒÉÇÉÎÁÌØÎÏÅ ÒÅÛÅÎÉÅ, ÎÏ ÐÒÉ×ÏÄÉÔ Ë ÏÛÉÂËÁÍ #
# JSON::RPC::Server at line 170 (HTTP::Message content must be bytes) #
## This may seem a little backwards, but what this really means is #
## "don't convert our utf8 into byte strings, just leave it as a #
## utf8 string." #
#$json->utf8(0) if Bugzilla->params->{'utf8'}; #
#*********************************************************************#
# This may seem a little backwards, but what this really means is
# "don't convert our utf8 into byte strings, just leave it as a
# utf8 string."
$json->utf8(0) if Bugzilla->params->{'utf8'};
return $json;
}
# Override the JSON::RPC method to return our CGI object instead of theirs.
sub cgi { return Bugzilla->cgi; }
# Override the JSON::RPC method to use $cgi->header properly instead of
# just printing text directly. This fixes various problems, including
# sending Bugzilla's cookies properly.
sub response {
my ($self, $response) = @_;
# Implement JSONP.
if (my $callback = $self->_bz_callback) {
my $content = $response->content;
$response->content("$callback($content)");
}
# Use $cgi->header properly instead of just printing text directly.
# This fixes various problems, including sending Bugzilla's cookies
# properly.
my $headers = $response->headers;
my @header_args;
foreach my $name ($headers->header_field_names) {
@ -81,89 +69,10 @@ sub response {
}
}
my $cgi = $self->cgi;
$cgi->send_header(-status => $response->code, @header_args);
print $cgi->header(-status => $response->code, @header_args);
print $response->content;
}
# The JSON-RPC 1.1 GET specification is not so great--you can't specify
# data structures as parameters. However, the JSON-RPC 2.0 "JSON-RPC over
# HTTP" spec is excellent, so we are using that for GET requests, instead.
# Spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
#
# The one exception is that we don't require the "params" argument to be
# Base64 encoded, because that is ridiculous and obnoxious for JavaScript
# clients.
sub retrieve_json_from_get {
my $self = shift;
my $cgi = $self->cgi;
my %input;
# Both version and id must be set before any errors are thrown.
if ($cgi->param('version')) {
$self->version(scalar $cgi->param('version'));
$input{version} = $cgi->param('version');
}
else {
$self->version('1.0');
}
# The JSON-RPC 2.0 spec says that any request that omits an id doesn't
# want a response. However, in an HTTP GET situation, it's stupid to
# expect all clients to specify some id parameter just to get a response,
# so we don't require it.
my $id;
if (defined $cgi->param('id')) {
$id = $cgi->param('id');
}
# However, JSON::RPC does require that an id exist in most cases, in
# order to throw proper errors. We use the installation's urlbase as
# the id, in this case.
else {
$id = correct_urlbase();
}
# Setting _bz_request_id here is required in case we throw errors early,
# before _handle.
$self->{_bz_request_id} = $input{id} = $id;
# _bz_callback can throw an error, so we have to set it here, after we're
# ready to throw errors.
$self->_bz_callback(scalar $cgi->param('callback'));
if (!$cgi->param('method')) {
ThrowUserError('json_rpc_get_method_required');
}
$input{method} = $cgi->param('method');
my $params;
if (defined $cgi->param('params')) {
local $@;
$params = eval {
$self->json->decode(scalar $cgi->param('params'))
};
if ($@) {
ThrowUserError('json_rpc_invalid_params',
{ params => scalar $cgi->param('params'),
err_msg => $@ });
}
}
elsif (!$self->version or $self->version ne '1.1') {
$params = [];
}
else {
$params = {};
}
$input{params} = $params;
my $json = $self->json->encode(\%input);
return $json;
}
#######################################
# Bugzilla::WebService Implementation #
#######################################
sub type {
my ($self, $type, $value) = @_;
@ -188,7 +97,7 @@ sub type {
}
elsif ($type eq 'dateTime') {
# ISO-8601 "YYYYMMDDTHH:MM:SS" with a literal T
$retval = $self->datetime_format($value);
$retval = $self->datetime_format_outbound($value);
}
elsif ($type eq 'base64') {
utf8::encode($value) if utf8::is_utf8($value);
@ -198,13 +107,19 @@ sub type {
return $retval;
}
sub datetime_format {
my ($self, $date_string) = @_;
sub datetime_format_outbound {
my $self = shift;
# YUI expects ISO8601 in UTC time; including TZ specifier
return $self->SUPER::datetime_format_outbound(@_) . 'Z';
}
# YUI expects ISO8601 in UTC time; uncluding TZ specifier
my $time = datetime_from($date_string, 'UTC');
my $iso_datetime = $time->iso8601() . 'Z';
return $iso_datetime;
# Store the ID of the current call, because Bugzilla::Error will need it.
sub _handle {
my $self = shift;
my ($obj) = @_;
$self->{_bz_request_id} = $obj->{id};
return $self->SUPER::_handle(@_);
}
# Make all error messages returned by JSON::RPC go into the 100000
@ -228,7 +143,7 @@ sub _error {
# We want to always send the JSON-RPC 1.1 error format, although
# If we're not in JSON-RPC 1.1, we don't need the silly "name" parameter.
if (!$self->version) {
if (!$self->version or $self->version ne '1.1') {
my $object = $self->json->decode($json);
my $message = $object->{error};
# Just assure that future versions of JSON::RPC don't change the
@ -411,14 +326,6 @@ sub _argument_type_check {
return $params;
}
sub _bz_convert_datetime {
my ($self, $time) = @_;
my $converted = datetime_from($time, Bugzilla->local_timezone);
$time = $converted->ymd() . ' ' . $converted->hms();
return $time
}
sub handle_login {
my $self = shift;

View File

@ -356,30 +356,19 @@ sub view {
Encode::from_to($filename, 'utf-8', 'cp1251');
}
# Don't send a charset header with attachments--they might not be UTF-8.
# However, we do allow people to explicitly specify a charset if they
# want.
my $data = $attachment->data;
if ($contenttype !~ /\bcharset=/i)
{
# Detect UTF-8 encoding
my $is_utf8 = 0;
if ($contenttype =~ m!^text/!iso)
{
Encode::_utf8_on($data);
$is_utf8 = utf8::valid($data);
Encode::_utf8_off($data);
}
# In order to prevent Apache from adding a charset, we have to send a
# charset that's a single space.
$cgi->charset($is_utf8 ? 'utf-8' : ' ');
}
$cgi->send_header(-type=>"$contenttype; name=\"$filename\"",
-content_disposition=> "$disposition; filename=\"$filename\"",
-content_length => $attachment->datasize);
disable_utf8();
print $data;
# Don't send a charset header with attachments--they might not be UTF-8.
# However, we do allow people to explicitly specify a charset if they
# want.
if ($contenttype !~ /\bcharset=/i) {
# In order to prevent Apache from adding a charset, we have to send a
# charset that's a single space.
$cgi->charset(' ');
}
print $cgi->header(-type=>"$contenttype; name=\"$filename\"",
-content_disposition=> "$disposition; filename=\"$filename\"",
-content_length => $attachment->datasize);
disable_utf8();
print $attachment->data;
}
sub interdiff {

View File

@ -470,10 +470,9 @@ if ($cmdtype eq "dorem") {
}
# If we are here, then we can safely remove the saved search
my ($query_id) = $dbh->selectrow_array('SELECT id FROM namedqueries
WHERE userid = ?
AND name = ?',
undef, ($user->id, $qname));
my $query_id;
($buffer, $query_id) = LookupNamedQuery(scalar $cgi->param("namedcmd"),
$user->id);
if (!$query_id) {
# The user has no query of this name. Play along.
}
@ -499,7 +498,7 @@ if ($cmdtype eq "dorem") {
# Generate and return the UI (HTML page) from the appropriate template.
$vars->{'message'} = "buglist_query_gone";
$vars->{'namedcmd'} = $qname;
$vars->{'url'} = "query.cgi";
$vars->{'url'} = "buglist.cgi?newquery=" . url_quote($buffer) . "&cmdtype=doit&remtype=asnamed&newqueryname=" . url_quote($qname);
$template->process("global/message.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
exit;
@ -727,19 +726,19 @@ if ($format->{'extension'} eq 'atom') {
# This is the list of fields that are needed by the Atom filter.
my @required_atom_columns = (
'short_desc',
'creation_ts',
'delta_ts',
'reporter',
'reporter_realname',
'priority',
'bug_severity',
'assigned_to',
'assigned_to_realname',
'bug_status',
'product',
'component',
'resolution'
'short_desc',
'creation_ts',
'delta_ts',
'reporter',
'reporter_realname',
'priority',
'bug_severity',
'assigned_to',
'assigned_to_realname',
'bug_status',
'product',
'component',
'resolution'
);
push(@required_atom_columns, 'target_milestone') if Bugzilla->params->{'usetargetmilestone'};

View File

@ -24,7 +24,6 @@
# Pascal Held <paheld@gmail.com>
use strict;
use lib qw(. lib);
use Bugzilla;
@ -35,7 +34,24 @@ use Bugzilla::Search;
use Bugzilla::Search::Saved;
use Bugzilla::Error;
use Bugzilla::User;
use Bugzilla::Keyword;
use Storable qw(dclone);
# Maps parameters that control columns to the names of columns.
use constant COLUMN_PARAMS => {
'useclassification' => ['classification'],
'usebugaliases' => ['alias'],
'usetargetmilestone' => ['target_milestone'],
'useqacontact' => ['qa_contact', 'qa_contact_realname'],
'usestatuswhiteboard' => ['status_whiteboard'],
};
# We only show these columns if an object of this type exists in the
# database.
use constant COLUMN_CLASSES => {
'Bugzilla::Flag' => 'flagtypes.name',
'Bugzilla::Keyword' => 'keywords',
};
Bugzilla->login();
@ -43,15 +59,31 @@ my $cgi = Bugzilla->cgi;
my $template = Bugzilla->template;
my $vars = {};
my @masterlist =
map { $_->{id} }
sort { $a->{title} cmp $b->{title} }
grep { !$_->{nobuglist} }
values %{ Bugzilla::Search->COLUMNS };
my $columns = dclone(Bugzilla::Search::COLUMNS);
Bugzilla::Hook::process('colchange_columns', {'columns' => \@masterlist} );
# You can't manually select "relevance" as a column you want to see.
delete $columns->{'relevance'};
$vars->{'masterlist'} = \@masterlist;
foreach my $param (keys %{ COLUMN_PARAMS() }) {
next if Bugzilla->params->{$param};
foreach my $column (@{ COLUMN_PARAMS->{$param} }) {
delete $columns->{$column};
}
}
foreach my $class (keys %{ COLUMN_CLASSES() }) {
eval("use $class; 1;") || die $@;
my $column = COLUMN_CLASSES->{$class};
delete $columns->{$column} if !$class->any_exist;
}
if (!Bugzilla->user->is_timetracker) {
foreach my $column (TIMETRACKING_FIELDS) {
delete $columns->{$column};
}
}
$vars->{'columns'} = $columns;
my @collist;
if (defined $cgi->param('rememberedquery')) {
@ -60,8 +92,8 @@ if (defined $cgi->param('rememberedquery')) {
@collist = DEFAULT_COLUMN_LIST;
} else {
if (defined $cgi->param("selected_columns")) {
my %legal_list = map { $_ => 1 } @masterlist;
@collist = grep { exists $legal_list{$_} } $cgi->param("selected_columns");
@collist = grep { exists $columns->{$_} }
$cgi->param("selected_columns");
}
if (defined $cgi->param('splitheader')) {
$splitheader = $cgi->param('splitheader')? 1: 0;

View File

@ -0,0 +1,28 @@
#!/usr/bin/perl -w
#
# 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 Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2010 the
# Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
use strict;
use warnings;
use lib qw(. lib);
use Bugzilla;
use Bugzilla::Install::Filesystem qw(fix_all_file_permissions);
fix_all_file_permissions(1);

View File

@ -121,20 +121,13 @@ if ($old_id == $new_id) {
# where fooN is the column to update, and barN1, barN2, ... are
# the columns to take into account to avoid duplicated entries.
# Note that the barNM columns are optional.
my $changes = {
# Tables affecting bugs.
bugs => ['assigned_to', 'reporter', 'qa_contact'],
bugs_activity => ['who'],
attachments => ['submitter_id'],
flags => ['setter_id', 'requestee_id'],
#
# We set the tables that require custom stuff (multiple columns to check)
# here, but the simple stuff is all handled below by bz_get_related_fks.
my %changes = (
cc => ['who bug_id'],
longdescs => ['who'],
# Tables affecting global behavior / other users.
components => ['initialowner', 'initialqacontact'],
component_cc => ['user_id component_id'],
quips => ['userid'],
series => ['creator'],
whine_events => ['owner_userid'],
watch => ['watcher watched', 'watched watcher'],
# Tables affecting the user directly.
namedqueries => ['userid name'],
@ -142,17 +135,23 @@ my $changes = {
user_group_map => ['user_id group_id isbless grant_type'],
email_setting => ['user_id relationship event'],
profile_setting => ['user_id setting_name'],
profiles_activity => ['userid', 'who'], # Should activity be migrated?
# Only do it if mailto_type = 0, i.e is pointing to a user account!
# This requires to be done separately due to this condition.
whine_schedules => [], # ['mailto'],
);
# Delete all old records for these tables; no migration.
logincookies => [], # ['userid'],
tokens => [], # ['userid'],
profiles => [], # ['userid'],
};
my $userid_fks = $dbh->bz_get_related_fks('profiles', 'userid');
foreach my $item (@$userid_fks) {
my ($table, $column) = @$item;
$changes{$table} ||= [];
push(@{ $changes{$table} }, $column);
}
# Delete all old records for these tables; no migration.
foreach my $table (qw(logincookies tokens profiles)) {
$changes{$table} = [];
}
# Start the transaction
$dbh->bz_start_transaction();
@ -162,8 +161,8 @@ $dbh->do('DELETE FROM logincookies WHERE userid = ?', undef, $old_id);
$dbh->do('DELETE FROM tokens WHERE userid = ?', undef, $old_id);
# Migrate records from old user to new user.
foreach my $table (keys(%$changes)) {
foreach my $column_list (@{$changes->{$table}}) {
foreach my $table (keys %changes) {
foreach my $column_list (@{ $changes{$table} }) {
# Get all columns to consider. There is always at least
# one column given: the one to update.
my @columns = split(/[\s]+/, $column_list);

View File

@ -3019,10 +3019,10 @@ known to us after the Bugzilla 2.14 release.
*** USERS UPGRADING FROM 2.8 OR EARLIER ***
*******************************************
Release notes were not compiled for versions of Bugzilla before
2.12.
The file 'UPGRADING-pre-2.8' contains instructions you may
need to perform in addition to running 'checksetup.pl' if you
are running a pre 2.8 version.
This version of Bugzilla cannot upgrade from version 2.8 (released
November 19, 1999). You will first have to upgrade to Bugzilla 3.6 and
then upgrade to the latest release.
If you are upgrading from a version earlier than 2.8, See the
PGRADING-pre-2.8 file in Bugzilla 3.0 for information
on upgrading from a version that is earlier than 2.8.

View File

@ -367,7 +367,8 @@
<listitem>
<para>
Template (&min-template-ver;)
<link linkend="install-modules-template">Template</link>
(&min-template-ver;)
</para>
</listitem>
@ -477,6 +478,20 @@
</para>
</listitem>
<listitem>
<para>
JSON::RPC
(&min-json-rpc-ver;) for the JSON-RPC interface
</para>
</listitem>
<listitem>
<para>
Test::Taint
(&min-test-taint-ver;) for the web service interface
</para>
</listitem>
<listitem>
<para>
HTML::Parser

View File

@ -68,7 +68,7 @@ elsif ($action eq 'new')
clone_bug => scalar $cgi->param('clone_bug'),
obsolete => scalar $cgi->param('obsolete'),
custom => 1,
buglist => 1, # FIXME remove non-editable 'buglist' field spec
buglist => 1,
visibility_field_id => scalar $cgi->param('visibility_field_id'),
value_field_id => scalar $cgi->param('value_field_id'),
add_to_deps => scalar $cgi->param('add_to_deps'),

View File

@ -29,6 +29,7 @@ use Bugzilla::Error;
use Bugzilla::Group;
use Bugzilla::User;
use Bugzilla::Util qw(diff_arrays html_quote);
use Bugzilla::Status qw(is_open_state);
# This is extensions/Example/lib/Util.pm. I can load this here in my
# Extension.pm only because I have a Config.pm.
@ -655,6 +656,44 @@ sub template_before_process {
}
}
sub bug_check_can_change_field {
my ($self, $args) = @_;
my ($bug, $field, $new_value, $old_value, $priv_results)
= @$args{qw(bug field new_value old_value priv_results)};
my $user = Bugzilla->user;
# Disallow a bug from being reopened if currently closed unless user
# is in 'admin' group
if ($field eq 'bug_status' && $bug->product_obj->name eq 'Example') {
if (!is_open_state($old_value) && is_open_state($new_value)
&& !$user->in_group('admin'))
{
push(@$priv_results, PRIVILEGES_REQUIRED_EMPOWERED);
return;
}
}
# Disallow a bug's keywords from being edited unless user is the
# reporter of the bug
if ($field eq 'keywords' && $bug->product_obj->name eq 'Example'
&& $user->login ne $bug->reporter->login)
{
push(@$priv_results, PRIVILEGES_REQUIRED_REPORTER);
return;
}
# Allow updating of priority even if user cannot normally edit the bug
# and they are in group 'engineering'
if ($field eq 'priority' && $bug->product_obj->name eq 'Example'
&& $user->in_group('engineering'))
{
push(@$priv_results, PRIVILEGES_REQUIRED_NONE);
return;
}
}
sub webservice {
my ($self, $args) = @_;

View File

@ -800,7 +800,6 @@ sub _remove_votes {
$template->process("voting/votes-removed.txt.tmpl", $vars, \$msg);
push(@messages, $msg);
}
Bugzilla->template_inner("");
my $votes = $dbh->selectrow_array("SELECT SUM(vote_count) " .
"FROM votes WHERE bug_id = ?",

View File

@ -18,5 +18,6 @@
# Max Kanat-Alexander <mkanat@bugzilla.org>
#%]
[% field_descs.votes = "Votes" %]
[% IF in_template_var %]
[% vars.field_descs.votes = "Votes" %]
[% END %]

View File

@ -35,7 +35,7 @@ use Bugzilla::Install::CPAN;
use Bugzilla::Constants;
use Bugzilla::Install::Requirements;
use Bugzilla::Install::Util qw(bin_loc init_console vers_cmp);
use Bugzilla::Install::Util qw(bin_loc);
use Data::Dumper;
use Getopt::Long;

View File

@ -19,6 +19,7 @@
* Joel Peshkin <bugreport@peshkin.net>
* Erik Stambaugh <erik@dasbistro.com>
* Marc Schumann <wurblzap@gmail.com>
* Guy Pyrzak <guy.pyrzak@gmail.com>
*/
var SUPA_JAVA_DISABLED_ERROR = 'You cannot paste images from clipboard because Java support'+
@ -439,3 +440,15 @@ function normalizeComments()
big.parentNode.removeChild(big);
}
}
function toggle_attachment_details_visibility ( )
{
// show hide classes
var container = document.getElementById('attachment_info');
if( YAHOO.util.Dom.hasClass(container, 'read') ){
YAHOO.util.Dom.replaceClass(container, 'read', 'edit');
}else{
YAHOO.util.Dom.replaceClass(container, 'edit', 'read');
}
}

View File

@ -149,9 +149,8 @@ Bugzilla::User::match_field({
if (defined $cgi->param('delta_ts'))
{
my $delta_ts_z = datetime_from($cgi->param('delta_ts'));
my $first_delta_tz_z = datetime_from($first_bug->delta_ts);
if ($first_delta_tz_z ne $delta_ts_z)
{
my $first_delta_tz_z = datetime_from($first_bug->delta_ts);
if ($first_delta_tz_z ne $delta_ts_z) {
($vars->{'operations'}) =
Bugzilla::Bug::GetBugActivity($first_bug->id, undef,
scalar $cgi->param('delta_ts'));
@ -201,7 +200,7 @@ if (defined $cgi->param('delta_ts'))
$vars->{'start_at'} = $cgi->param('longdesclength');
# Always sort midair collision comments oldest to newest,
# regardless of the user's personal preference.
$vars->{'comments'} = $first_bug->comments({ order => "oldest_to_newest", start_at => $vars->{start_at} });
$vars->{'comments'} = $first_bug->comments({ order => "oldest_to_newest" });
$vars->{'bug'} = $first_bug;
# The token contains the old delta_ts. We need a new one.

View File

@ -166,7 +166,6 @@ elsif ($action eq 'begin-sudo') {
my $message;
my $mail_template = Bugzilla->template_inner($target_user->settings->{'lang'}->{'value'});
$mail_template->process('email/sudo.txt.tmpl', { reason => $reason }, \$message);
Bugzilla->template_inner("");
MessageToMTA($message);
$vars->{'message'} = 'sudo_started';

View File

@ -220,8 +220,7 @@ sub DotInto
# The dot mapdata lines have the following format (\nsummary is optional):
# rectangle (LEFTX,TOPY) (RIGHTX,BOTTOMY) URLBASE/show_bug.cgi?id=BUGNUM BUGNUM[\nSUMMARY]
sub CreateImagemap
{
sub CreateImagemap {
my ($mapfilename, $mapid, $bugtitles) = @_;
$mapid ||= 'imagemap';
my $map = "<map name=\"$mapid\">\n";

View File

@ -17,4 +17,25 @@
margin-top: .5em;
}
.links { display: inline; }
form#Create #comp_desc {
margin: .5em 1em;
}
#footer #useful-links li {
padding-bottom: 0.8ex;
}
#footer .label {
display: block;
float: left;
width: 8.2em;
padding-bottom: 0.1ex;
}
#footer #links-actions .label {
padding-top: 0.35em;
}
#footer .links {
display: inline;
}

View File

@ -14,6 +14,7 @@
* Joel Peshkin <bugreport@peshkin.net>
* Erik Stambaugh <erik@dasbistro.com>
* Marc Schumann <wurblzap@gmail.com>
* Guy Pyrzak <guy.pyrzak@gmail.com>
*/
table.attachment_entry th {
@ -121,10 +122,6 @@ table.attachment_info td {
vertical-align: middle;
}
#attachment_attributes {
width: 25%;
}
#attachment_attributes div {
padding-bottom: 0.4em;
}
@ -140,8 +137,109 @@ table.attachment_info td {
display: block;
}
#attachment_attributes table#flags {
padding-top: 1em;
#smallCommentFrame, #attachment_flags {
float: left;
}
#smallCommentFrame {
margin-right: 1.5em;
}
#attachment_comments_and_flags, #attachment_actions {
clear: both;
margin-bottom: 1ex;
}
#attachment_information_read_only .title {
font-weight: bold;
font-size: 1.5em;
padding: 0;
margin: 0;
}
#attachment_information_read_only .title #bz_edit {
font-size: 0.7em;
}
#attachment_information_read_only .details {
font-family: monospace;
}
#attachment_info.read #attachment_information_edit {
display: none;
}
#attachment_info.edit #attachment_information_read_only {
display: none;
}
#attachment_info.edit #attachment_view_window {
float: left;
width: 80%;
}
#attachment_info.edit #attachment_information_edit {
width: 20%;
}
<<<<<<< HEAD:skins/standard/create_attachment.css
#comment_textarea { width: 50em; font-size: medium; font-family: monospace; }
=======
#attachment_info.edit #attachment_information_edit input.text,
#attachment_info.edit #attachment_information_edit textarea {
width: 90%;
}
#attachment_isobsolete {
padding-right: 1em;
}
#attachment_information_edit {
float: left;
}
#smallCommentFrame textarea {
display: block;
}
textarea.bz_private {
border: 1px solid #F8C8BA;
}
#update {
clear: both;
display: block;
}
textarea {
font-family: monospace;
}
div#update_container {
clear: both;
padding: 1.5em 0;
}
#attachment_flags {
margin-bottom: 1em;
}
#attachment_flags p {
padding-bottom: 0;
margin-bottom: 0;
}
#editFrame, #viewDiffFrame, #viewFrame {
height: 400px;
width: 100%;
}
.details span.bz_private{
border-left: 1px solid darkred;
padding-left: 0.5em;
}
.no_javascript .bz_hide, .no_javascript .bz_edit {
display: none;
}
>>>>>>> d5cc8b05e7f234c7cc6fe3428ad9e5928236de9a:skins/standard/attachment.css

View File

@ -461,18 +461,44 @@ input.required, select.required, span.required_explanation {
list-style-type: none;
}
/*************/
/* enter_bug */
/*************/
form#Create table {
border-spacing: 0;
border-width: 0;
}
form#Create td, form#Create th {
padding: .25em;
}
form#Create th {
text-align: right;
}
/* This makes the "component" column as small as possible (since it
* contains only fixed-width content) and the Reporter column
* as large as possible, which makes the form not jump around
* when the Component Description changes size. This works
* pretty well on all browsers except IE 8.
*/
form#Create #field_container_component { width: 1px; }
form#Create #field_container_reporter { width: 100%; }
form#Create .comment {
vertical-align: top;
overflow: auto;
color: green;
margin: 0 0.5em;
padding: 0.3em;
height: 8ex;
}
form#Create #comp_desc_container td { padding: 0; }
form#Create #comp_desc { height: 11ex; }
form#Create #os_guess_note {
padding-top: 0;
}
form#Create #os_guess_note div {
max-width: 35em;
}
.image_button {
background-repeat: no-repeat;

View File

@ -24,26 +24,9 @@
<form method=post action="editclassifications.cgi">
<table border=0 cellpadding=4 cellspacing=0>
<tr>
<th align="right">Classification:</th>
<td><input size=64 maxlength=64 name="classification"></td>
</tr>
<tr>
<th align="right">Description:</th>
<td>
[% INCLUDE global/textarea.html.tmpl
name = 'description'
minrows = 4
cols = 64
wrap = 'virtual'
%]
</td>
</tr>
<tr>
<th align="right"><label for="sortkey">Sortkey:</label></th>
<td><input id="sortkey" size="20" maxlength="20" name="sortkey"
value=""></td>
</tr>
[% PROCESS "admin/classifications/edit-common.html.tmpl" %]
</table>
<hr>
<input type=submit value="Add">

View File

@ -0,0 +1,47 @@
[%# 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): Tiago Rodrigues de Mello <timello@linux.vnet.ibm.com>
#%]
[%# INTERFACE:
# classification: Bugzilla::Classifiation object.
#%]
<tr>
<th align="right">Classification:</th>
<td><input size=64 maxlength=64 name="classification"
value="[% classification.name FILTER html %]"></td>
</tr>
<tr>
<th align="right">Description:</th>
<td>
[% INCLUDE global/textarea.html.tmpl
name = 'description'
minrows = 4
cols = 64
defaultcontent = classification.description
%]
</td>
</tr>
<tr>
<th align="right"><label for="sortkey">Sortkey:</label></th>
<td><input id="sortkey" size="20" maxlength="20" name="sortkey"
value="[%- classification.sortkey FILTER html %]"></td>
</tr>
[% Hook.process('rows') %]

View File

@ -24,27 +24,9 @@
<form method=post action="editclassifications.cgi">
<table border=0 cellpadding=4 cellspacing=0>
<tr>
<th align="right">Classification:</th>
<td><input size=64 maxlength=64 name="classification"
value="[% classification.name FILTER html %]"></td>
</tr>
<tr>
<th align="right">Description:</th>
<td>
[% INCLUDE global/textarea.html.tmpl
name = 'description'
minrows = 4
cols = 64
defaultcontent = classification.description
%]
</td>
</tr>
<tr>
<th align="right"><label for="sortkey">Sortkey:</label></th>
<td><input id="sortkey" size="20" maxlength="20" name="sortkey" value="
[%- classification.sortkey FILTER html %]"></td>
</tr>
[% PROCESS "admin/classifications/edit-common.html.tmpl" %]
<tr valign=top>
<th align="right">
<a href="editproducts.cgi?classification=[% classification.name FILTER url_quote %]">

View File

@ -33,92 +33,9 @@
<form method="post" action="editcomponents.cgi">
<table border="0" cellpadding="4" cellspacing="0">
<tr>
<th align="right">Component:</th>
<td><input size="64" maxlength="64" name="component" value=""></td>
</tr>
<tr>
<th align="right">Description:</th>
<td>
[% INCLUDE global/textarea.html.tmpl
name = 'description'
minrows = 4
cols = 64
wrap = 'virtual'
%]
</td>
</tr>
<tr>
<th align="right"><label for="initialowner">Default Assignee:</label></th>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialowner"
id => "initialowner"
value => ""
size => 64
%]
</td>
</tr>
[% IF Param('useqacontact') %]
<tr>
<th align="right">
<label for="initialqacontact">Default QA Contact:</label></th>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialqacontact"
id => "initialqacontact"
value => ""
size => 64
emptyok => 1
%]
</td>
</tr>
[% END %]
<tr>
<th align="right">
<label for="initialcc">Default CC List:</label>
</th>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialcc"
id => "initialcc"
value => ""
size => 64
multiple => 5
%]
<br>
[% IF !Param("usemenuforusers") %]
<em>Enter user names for the CC list as a comma-separated list.</em>
[% END %]
</td>
</tr>
<tr>
<th align="right">
<label for="default_version">Default version:</label>
</td>
<td>
<select id="default_version" name="default_version">
<option value="">---</option>
[% FOREACH x = product.versions %]
<option value="[% x.name FILTER html %]">[% x.name FILTER html %]</option>
[% END %]
</select>
</td>
</tr>
<tr>
<td valign="top">Open for [% terms.bug %] entry:</td>
<td><input type="checkbox" name="is_active" value="1" checked="checked" />
</td>
</tr>
<tr>
<th align="right">
<label for="wiki_url">Wiki URL:</label>
</td>
<td>
<input size="64" maxlength="64" name="wiki_url" value="" />
<br><em>Or use product setting when empty.</em>
</td>
</tr>
[% PROCESS "admin/components/edit-common.html.tmpl" %]
</table>
<hr>
<input type="submit" id="create" value="Add">

View File

@ -0,0 +1,86 @@
[%# 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): Tiago Rodrigues de Mello <timello@linux.vnet.ibm.com>
#%]
[%# INTERFACE:
# comp: object; Bugzilla::Component object.
#%]
<tr>
<td valign="top">Component:</td>
<td><input size="64" maxlength="64" name="component"
value="[%- comp.name FILTER html %]"></td>
</tr>
<tr>
<td valign="top">Component Description:</td>
<td>
[% INCLUDE global/textarea.html.tmpl
name = 'description'
minrows = 4
cols = 64
wrap = 'virtual'
defaultcontent = comp.description
%]
</td>
</tr>
<tr>
<td valign="top"><label for="initialowner">Default Assignee:</label></td>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialowner"
id => "initialowner"
value => comp.default_assignee.login
size => 64
%]
</td>
</tr>
[% IF Param('useqacontact') %]
<tr>
<td valign="top"><label for="initialqacontact">Default QA contact:</label></td>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialqacontact"
id => "initialqacontact"
value => comp.default_qa_contact.login
size => 64
emptyok => 1
%]
</td>
</tr>
[% END %]
<tr>
<td valign="top">
<label for="initialcc">Default CC List:</label>
</td>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialcc"
id => "initialcc"
value => initial_cc_names
size => 64
multiple => 5
%]
<br>
[% IF !Param("usemenuforusers") %]
<em>Enter user names for the CC list as a comma-separated list.</em>
[% END %]
</td>
</tr>
[% Hook.process('rows') %]

View File

@ -38,68 +38,8 @@
<form method="post" action="editcomponents.cgi">
<table border="0" cellpadding="4" cellspacing="0">
<tr>
<td valign="top">Component:</td>
<td><input size="64" maxlength="64" name="component" value="
[%- comp.name FILTER html %]"></td>
</tr>
<tr>
<td valign="top">Component Description:</td>
<td>
[% INCLUDE global/textarea.html.tmpl
name = 'description'
minrows = 4
cols = 64
wrap = 'virtual'
defaultcontent = comp.description
%]
</td>
</tr>
<tr>
<td valign="top"><label for="initialowner">Default Assignee:</label></td>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialowner"
id => "initialowner"
value => comp.default_assignee.login
size => 64
%]
</td>
[% IF Param('useqacontact') %]
</tr>
<tr>
<td valign="top"><label for="initialqacontact">Default QA contact:</label></td>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialqacontact"
id => "initialqacontact"
value => comp.default_qa_contact.login
size => 64
emptyok => 1
%]
</td>
[% END %]
</tr>
<tr>
<td valign="top">
<label for="initialcc">Default CC List:</label>
</td>
<td>
[% INCLUDE global/userselect.html.tmpl
name => "initialcc"
id => "initialcc"
value => initial_cc_names
size => 64
multiple => 5
%]
<br>
[% IF !Param("usemenuforusers") %]
<em>Enter user names for the CC list as a comma-separated list.</em>
[% END %]
</td>
</tr>
[% PROCESS "admin/components/edit-common.html.tmpl" %]
<tr>
<td valign="top">
<label for="default_version">Default version:</label>

View File

@ -31,8 +31,8 @@
title = title
header = header
subheader = subheader
style_urls = [ 'skins/standard/create_attachment.css' ]
javascript_urls = [ "js/attachment.js", "js/field.js" ]
style_urls = [ 'skins/standard/attachment.css' ]
javascript_urls = [ "js/attachment.js", "js/util.js" ]
doc_section = "attachments.html"
%]

View File

@ -51,11 +51,11 @@ Interdiff of #[% oldid %] and #[% newid %] for [% terms.bug %] #[% bugid %]
[% END %]
[% PROCESS global/header.html.tmpl doc_section = "attachments.html#patchviewer"
javascript_urls = "js/attachment.js"
style_urls = ['skins/standard/create_attachment.css'] %]
style_urls = ['skins/standard/attachment.css'] %]
[% ELSE %]
<html>
<head>
<link href="skins/standard/create_attachment.css" rel="stylesheet" type="text/css">
<link href="skins/standard/attachment.css" rel="stylesheet" type="text/css">
<script src="js/attachment.js" type="text/javascript"></script>
</head>
<body onload="[% onload FILTER html %]">

View File

@ -17,6 +17,7 @@
#
# Contributor(s): Myk Melez <myk@mozilla.org>
# Frédéric Buclin <LpSolit@gmail.com>
# Guy Pyrzak <guy.pyrzak@gmail.com>
#%]
[%# Define strings that will serve as the title and header of this page %]
@ -35,7 +36,8 @@
subheader = subheader
doc_section = "attachments.html"
javascript_urls = ['js/attachment.js']
style_urls = ['skins/standard/create_attachment.css']
style_urls = ['skins/standard/attachment.css']
bodyclasses = "no_javascript"
%]
[%# No need to display the Diff button and iframe if the attachment is not a patch. %]
@ -53,12 +55,38 @@
<input type="hidden" name="token" value="[% issue_hash_token([attachment.id, attachment.modification_time]) FILTER html %]">
[% END %]
<table class="attachment_info" width="100%">
<tr>
<td id="attachment_attributes">
<div id="attachment_info" class="attachment_info [% IF can_edit %] edit[% ELSE %] read[% END%]">
<div id="attachment_attributes">
<div id="attachment_information_read_only" class="[% "bz_private" IF attachment.isprivate %]">
<div class="title">
[% "[patch]" IF attachment.ispatch%]
<span class="[% "bz_obsolete" IF attachment.isobsolete %]" title="[% "obsolete" IF attachment.isobsolete %]">
[% attachment.description FILTER html %]
</span>
[% IF can_edit %]
<span class="bz_edit">(<a href="javascript:toggle_attachment_details_visibility()">edit</a>)</span>
[% END %]
</div>
[% IF NOT attachment.isurl %]
<div class="details">
[% attachment.filename FILTER html %] ([% attachment.contenttype FILTER html %])
[% IF attachment.datasize %]
[%+ attachment.datasize FILTER unitconvert %]
[% ELSE %]
<em>deleted</em>
[% END %], created by [%+ INCLUDE global/user.html.tmpl who = attachment.attacher %]
[% IF attachment.isprivate %]
<span class="bz_private">Only visible to <strong>[% Param('insidergroup') FILTER html %]</strong></span>
[% END %]
</div>
[% END %]
</div>
<div id="attachment_information_edit">
<span class="bz_hide">
(<a href="javascript:toggle_attachment_details_visibility();">hide</a>)
</span>
<div id="attachment_description">
<label for="description">Description:</label>
<label for="description">Description:</label>&nbsp;
[% INCLUDE global/textarea.html.tmpl
id = 'description'
name = 'description'
@ -76,31 +104,30 @@
</div>
[% IF attachment.isurl %]
<input type="hidden" name="filename"
value="[% attachment.filename FILTER html %]">
<input type="hidden" name="contenttypeentry"
value="[% attachment.contenttype FILTER html %]">
<input type="hidden" name="filename"
value="[% attachment.filename FILTER html %]">
<input type="hidden" name="contenttypeentry"
value="[% attachment.contenttype FILTER html %]">
[% ELSE %]
<div id="attachment_filename">
<label for="filename">Filename:</label>
<input type="text" size="20" class="block[% editable_or_hide %]"
<input type="text" size="20" class="text block[% editable_or_hide %]"
id="filename" name="filename"
value="[% attachment.filename FILTER html %]">
[% IF !can_edit %]
[%+ attachment.filename FILTER truncate(25) FILTER html %]
[% END %]
value="[% attachment.filename FILTER html %]">
</div>
<div id="attachment_mimetype">
<label for="contenttypeentry">MIME Type:</label>
<input type="text" size="20" class="block[% editable_or_hide %]"
<input type="text" size="20" class="text block[% editable_or_hide %]"
id="contenttypeentry" name="contenttypeentry"
value="[% attachment.contenttype FILTER html %]">
[% IF !can_edit %]
[%+ attachment.contenttype FILTER truncate(25) FILTER html %]
[% END %]
value="[% attachment.contenttype FILTER html %]">
</div>
<div id="attachment_creator">
<span class="label">Creator:</span>
[%+ INCLUDE global/user.html.tmpl who = attachment.attacher %]
</div>
<div id="attachment_size">
<span class="label">Size:</span>
[% IF attachment.datasize %]
@ -110,162 +137,149 @@
[% END %]
</div>
<div id="attachment_creator">
<span class="label">Creator:</span>
[%+ INCLUDE global/user.html.tmpl who = attachment.attacher %]
</div>
<div id="attachment_ispatch">
<input type="checkbox" id="ispatch" name="ispatch" value="1"
[%+ IF !can_edit %]class="bz_hidden_option"[% END %]
[%+ 'checked="checked"' IF attachment.ispatch %]>
[% IF can_edit %]
<label for="ispatch">patch</label>
[% ELSE %]
<span class="label">Is Patch:</span>
[%+ attachment.ispatch ? "yes" : "no" %]
[% END %]
<label for="ispatch">patch</label>
</div>
[% END %]
<div class="readonly">
<div class="checkboxes">
<div id="attachment_isobsolete">
<input type="checkbox" id="isobsolete" name="isobsolete" value="1"
[%+ 'checked="checked"' IF attachment.isobsolete %]>
<label for="isobsolete">obsolete</label>
</div>
<div id="attachment_isobsolete">
<input type="checkbox" id="isobsolete" name="isobsolete" value="1"
[%+ IF !can_edit %]class="bz_hidden_option"[% END %]
[%+ 'checked="checked"' IF attachment.isobsolete %]>
[% IF can_edit %]
<label for="isobsolete">obsolete</label>
[% ELSE %]
<span class="label">Is Obsolete:</span>
[%+ attachment.isobsolete ? "yes" : "no" %]
[% END %]
</div>
[% IF user.is_insider %]
<div id="attachment_isprivate">
<input type="checkbox" id="isprivate" name="isprivate" value="1"
[%+ IF !can_edit %]class="bz_hidden_option"[% END %]
[%+ 'checked="checked"' IF attachment.isprivate %]>
[% IF can_edit %]
<label for="isprivate">private (only visible to
<strong>[% Param('insidergroup') FILTER html %]</strong>)
</label>
[% ELSE %]
<span class="label">Is Private:</span>
[%+ attachment.isprivate ? "yes" : "no" %]
[% IF user.is_insider %]
<div id="attachment_isprivate">
<input type="checkbox" id="isprivate" name="isprivate" value="1"
[%+ 'checked="checked"' IF attachment.isprivate %]>
[% IF can_edit %]
<label for="isprivate">private (only visible to
<strong>[% Param('insidergroup') FILTER html %]</strong>)
</label>
[% ELSE %]
<span class="label">Is Private:</span>
[%+ attachment.isprivate ? "yes" : "no" %]
[% END %]
</div>
[% END %]
</div>
[% END %]
</div>
</div>
[% IF attachment.flag_types.size > 0 %]
<div id="attachment_flags">
[% PROCESS "flag/list.html.tmpl" bug_id = attachment.bug_id
attach_id = attachment.id
flag_types = attachment.flag_types
read_only_flags = !can_edit
<div id="attachment_view_window">
[% IF !attachment.datasize %]
<div><b>The content of this attachment has been deleted.</b></div>
[% ELSIF attachment.isurl %]
<div>
<a href="[% attachment.data FILTER html %]">
[% IF attachment.datasize < 120 %]
[% attachment.data FILTER html %]
[% ELSE %]
[% attachment.data FILTER truncate(80) FILTER html %]
&nbsp;...
[% attachment.data.match(".*(.{20})$").0 FILTER html %]
[% END %]
</a>
</div>
[% ELSIF !Param("allow_attachment_display") %]
<div id="view_disabled" >
<p><b>
The attachment is not viewable in your browser due to security
restrictions enabled by your [% terms.Bugzilla %] administrator.
</b></p>
<p><b>
In order to view the attachment, you first have to
<a href="attachment.cgi?id=[% attachment.id %]">download it</a>.
</b></p>
</div>
[% ELSIF attachment.is_viewable %]
<div >
[% INCLUDE global/textarea.html.tmpl
id = 'editFrame'
name = 'comment'
classes = 'bz_default_hidden'
minrows = 10
cols = 80
wrap = 'soft'
defaultcontent = (attachment.contenttype.match('^text\/')) ?
attachment.data.replace('(.*\n|.+)', '>$1') : undef
%]
<iframe id="viewFrame" src="attachment.cgi?id=[% attachment.id %]">
<b>You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
<a href="attachment.cgi?id=[% attachment.id %]">View the attachment on a separate page</a>.</b>
</iframe>
<script type="text/javascript">
<!--
var patchviewerinstalled = 0;
var attachment_id = [% attachment.id %];
if (typeof document.getElementById == "function") {
[% IF use_patchviewer %]
var patchviewerinstalled = 1;
document.write('<iframe id="viewDiffFrame" class="bz_default_hidden"><\/iframe>');
[% END %]
[% IF user.id %]
document.write('<button type="button" id="editButton" onclick="editAsComment(patchviewerinstalled);">Edit Attachment As Comment<\/button>');
document.write('<button type="button" id="undoEditButton" onclick="undoEditAsComment(patchviewerinstalled);" class="bz_default_hidden">Undo Edit As Comment<\/button>');
document.write('<button type="button" id="redoEditButton" onclick="redoEditAsComment(patchviewerinstalled);" class="bz_default_hidden">Redo Edit As Comment<\/button>');
[% END %]
[% IF use_patchviewer %]
document.write('<button type="button" id="viewDiffButton" onclick="viewDiff(attachment_id, patchviewerinstalled);">View Attachment As Diff<\/button>');
[% END %]
document.write('<button type="button" id="viewRawButton" onclick="viewRaw(patchviewerinstalled);" class="bz_default_hidden">View Attachment As Raw<\/button>');
}
//-->
</script>
</div>
[% ELSE %]
<div id="noview">
<p><b>
Attachment is not viewable in your browser because its MIME type
([% attachment.contenttype FILTER html %]) is not one that your browser is
able to display.
</b></p>
<p><b>
<a href="attachment.cgi?id=[% attachment.id %]">Download the attachment</a>.
</b></p>
</div>
[% END %]
</div>
<div id="attachment_comments_and_flags">
[% IF user.id %]
<div id="smallCommentFrame">
<div id="smallCommentFrame" >
<label for="comment">Comment (on the [% terms.bug %]):</label>
<input type="checkbox" name="commentsilent" value="1" /> <label for="commentsilent">Silent</label></small><br />
[% INCLUDE global/textarea.html.tmpl
id = 'comment_textarea'
name = 'comment'
minrows = 5
cols = 25
minrows = 10
cols = 80
wrap = 'soft'
classes = 'block'
classes = classNames
%]
</div>
<input type="submit" value="Submit" id="update" />
<input type="button" value="Preview" onclick="showcommentpreview()" tabindex="-1" /><br><br>
[% PROCESS "bug/comment-preview-div.html.tmpl" %]
[% END %]
</td>
[% IF !attachment.datasize %]
<td width="75%"><b>The content of this attachment has been deleted.</b></td>
[% ELSIF attachment.isurl %]
<td width="75%">
<a href="[% attachment.data FILTER html %]">
[% IF attachment.datasize < 120 %]
[% attachment.data FILTER html %]
[% ELSE %]
[% attachment.data FILTER truncate(80) FILTER html %]
&nbsp;...
[% attachment.data.match(".*(.{20})$").0 FILTER html %]
[% END %]
</a>
</td>
[% ELSIF !Param("allow_attachment_display") %]
<td id="view_disabled" width="50%">
<p><b>
The attachment is not viewable in your browser due to security
restrictions enabled by [% terms.Bugzilla %].
</b></p>
<p><b>
In order to view the attachment, you first have to
<a href="attachment.cgi?id=[% attachment.id %]">download it</a>.
</b></p>
</td>
[% ELSIF attachment.is_viewable %]
<td width="75%">
[% INCLUDE global/textarea.html.tmpl
id = 'editFrame'
name = 'comment'
style = 'height: 400px; width: 100%; display: none'
minrows = 10
cols = 80
wrap = 'soft'
defaultcontent = (attachment.contenttype.match('^text\/')) ?
attachment.data.replace('(.*\n|.+)', '>$1') : undef
%]
<iframe id="viewFrame" src="attachment.cgi?id=[% attachment.id %]" style="height: 400px; width: 100%;">
<b>You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
<a href="attachment.cgi?id=[% attachment.id %]">View the attachment on a separate page</a>.</b>
</iframe>
<script type="text/javascript">
<!--
var patchviewerinstalled = 0;
var attachment_id = [% attachment.id %];
if (typeof document.getElementById == "function") {
[% IF use_patchviewer %]
var patchviewerinstalled = 1;
document.write('<iframe id="viewDiffFrame" style="height: 400px; width: 100%; display: none;"><\/iframe>');
[% END %]
document.write('<button type="button" id="editButton" onclick="editAsComment(patchviewerinstalled);">Edit Attachment As Comment<\/button>');
document.write('<button type="button" id="undoEditButton" onclick="undoEditAsComment(patchviewerinstalled);" style="display: none;">Undo Edit As Comment<\/button>');
document.write('<button type="button" id="redoEditButton" onclick="redoEditAsComment(patchviewerinstalled);" style="display: none;">Redo Edit As Comment<\/button>');
[% IF use_patchviewer %]
document.write('<button type="button" id="viewDiffButton" onclick="viewDiff(attachment_id, patchviewerinstalled);">View Attachment As Diff<\/button>');
[% END %]
document.write('<button type="button" id="viewRawButton" onclick="viewRaw(patchviewerinstalled);" style="display: none;">View Attachment As Raw<\/button>');
}
//-->
</script>
</td>
[% ELSE %]
<td id="noview" width="50%">
<p><b>
Attachment is not viewable in your browser because its MIME type
([% attachment.contenttype FILTER html %]) is not one that your browser is
able to display.
</b></p>
<p><b>
<a href="attachment.cgi?id=[% attachment.id %]">Download the attachment</a>.
[% IF attachment.isOfficeDocument() == 1 %]
<br /><a href="attachment.cgi?id=[% attachment.id %]&amp;action=online_view" target="_blank">Online-view</a>
[% END %]
</b></p>
</td>
[% END %]
</tr>
</table>
[% END %]
<div id="attachment_flags">
[% IF attachment.flag_types.size > 0 %]
[% PROCESS "flag/list.html.tmpl" bug_id = attachment.bug_id
attach_id = attachment.id
flag_types = attachment.flag_types
read_only_flags = !can_edit
%]
[% END %]
</div>
[% IF user.id %]
<div id="update_container">
<input type="submit" value="Submit" id="update">
</div>
[% END %]
</div>
</div>
</div>
</form>
<div id="attachment_actions">
@ -292,7 +306,14 @@
[% " |" UNLESS loop.last() %]
[% END %]
</div>
[% IF can_edit %]
<script type="text/javascript">
<!--
YAHOO.util.Dom.removeClass( document.body, "no_javascript" );
toggle_attachment_details_visibility( );
-->
</script>
[% END %]
[% Hook.process('end') %]
[% PROCESS global/footer.html.tmpl %]

View File

@ -107,10 +107,22 @@ function toggle_display(link)
<i>no flags</i>
[% ELSE %]
[% FOREACH flag = attachment.flags %]
[% flag.setter.nick FILTER html %]:
[% IF user.id %]
<span title="[% flag.setter.identity FILTER html %]">[% flag.setter.nick FILTER html %]</span>:
[% ELSIF flag.setter.name %]
<span title="[% flag.setter.name FILTER html %]">[% flag.setter.nick FILTER html %]</span>:
[% ELSE %]
[% flag.setter.nick FILTER html %]:
[% END %]
[%+ flag.type.name FILTER html FILTER no_break %][% flag.status %]
[%+ IF flag.status == "?" && flag.requestee %]
([% flag.requestee.nick FILTER html %])
[% IF user.id %]
(<span title="[% flag.requestee.identity FILTER html %]">[% flag.requestee.nick FILTER html %]</span>)
[% ELSIF flag.requestee.name %]
(<span title="[% flag.requestee.name FILTER html %]">[% flag.requestee.nick FILTER html %]</span>)
[% ELSE %]
([% flag.requestee.nick FILTER html %])
[% END %]
[% END %]<br>
[% END %]
[% END %]

View File

@ -29,7 +29,7 @@
title = title
header = header
subheader = filtered_summary
style_urls = ['skins/standard/create_attachment.css']
style_urls = ['skins/standard/attachment.css']
%]
<br>

View File

@ -302,16 +302,17 @@ function checkWorktime(inp)
<p>(<span class="required_star">*</span> = <span class="required_explanation">Required Field</span>)</p>
<table cellspacing="4" cellpadding="2" border="0">
<table>
<tbody>
<tr><td colspan="4"><hr /></td></tr>
<tr>
<th>Product:</th>
<td width="10%">[% product.name FILTER html %]</td>
<th>Reporter:</th>
<td width="100%">[% user.login FILTER html %]</td>
[% INCLUDE bug/field.html.tmpl
bug = default, field = bug_fields.product, editable = 0,
value = product.name %]
[% INCLUDE bug/field.html.tmpl
bug = default, field = bug_fields.reporter, editable = 0,
value = user.login %]
</tr>
[%# We can't use the select block in these two cases for various reasons. %]
@ -320,9 +321,9 @@ function checkWorktime(inp)
<a href="describecomponents.cgi?product=[% product.name FILTER url_quote %]">
Component</a>:
</th>
<td>
<select name="component" onchange="set_assign_to();" size="7"
aria-required="true" class="required">
<td id="field_container_component">
<select name="component" id="component" onchange="set_assign_to();"
size="7" aria-required="true" class="required">
[%# Build the lists of assignees and QA contacts if "usemenuforusers" is enabled. %]
[% IF Param("usemenuforusers") %]
[% assignees_list = user.get_userlist.clone %]
@ -344,7 +345,7 @@ function checkWorktime(inp)
</select>
</td>
<td colspan="2">
<td colspan="2" id="comp_desc_container">
[%# Enclose the fieldset in a nested table so that its width changes based
# on the length on the component description. %]
<table>
@ -361,9 +362,10 @@ function checkWorktime(inp)
</tr>
<tr>
<th>Version:</th>
<td>
<select name="version">
<th rowspan="3"><a href="page.cgi?id=fields.html#version">
[%- field_descs.version FILTER html %]:</a></th>
<td rowspan="3">
<select name="version" size="5">
[%- FOREACH v = version %]
<option value="[% v FILTER html %]"
[% ' selected="selected"' IF v == default.version %]>[% v FILTER html -%]
@ -392,6 +394,22 @@ function checkWorktime(inp)
value = default.op_sys %]
</tr>
[% END %]
[% IF !Param('defaultplatform') || !Param('defaultopsys') %]
<tr>
<td colspan="3">&nbsp;</th>
<td id="os_guess_note" class="comment">
<div>We've made a guess at your
[% IF Param('defaultplatform') %]
operating system. Please check it
[% ELSIF Param('defaultopsys') %]
platform. Please check it
[% ELSE %]
operating system and platform. Please check them
[% END %]
and make any corrections if necessary.</div>
</td>
</tr>
[% END %]
</tbody>
<tbody class="expert_fields">
@ -413,25 +431,6 @@ function checkWorktime(inp)
</tr>
</tbody>
[% IF !Param('defaultplatform') || !Param('defaultopsys') %]
<tbody>
<tr>
<th>&nbsp;</th>
<td colspan="3" class="comment">
We've made a guess at your
[% IF Param('defaultplatform') %]
operating system. Please check it
[% ELSIF Param('defaultopsys') %]
platform. Please check it
[% ELSE %]
operating system and platform. Please check them
[% END %]
and make any corrections if necessary.
</td>
</tr>
</tbody>
[% END %]
<tbody class="expert_fields">
<tr>
@ -521,8 +520,9 @@ function checkWorktime(inp)
[% IF Param("useqacontact") %]
<tr>
<th>QA Contact:</th>
<td>
<th><a href="page.cgi?id=fields.html#qa_contact">
[%- field_descs.qa_contact FILTER html %]:</a></th>
<td colspan="2">
[% INCLUDE global/userselect.html.tmpl
id => "qa_contact"
name => "qa_contact"
@ -538,8 +538,9 @@ function checkWorktime(inp)
[% END %]
<tr>
<th>CC:</th>
<td>
<th><a href="page.cgi?id=fields.html#cc">
[%- field_descs.cc FILTER html %]:</a></th>
<td colspan="2">
[% INCLUDE global/userselect.html.tmpl
id => "cc"
name => "cc"
@ -551,7 +552,17 @@ function checkWorktime(inp)
</td>
</tr>
<tr><td colspan="4"><hr /></td></tr>
<tr>
<th>Default [% field_descs.cc FILTER html %]:</th>
<td colspan="2">
<div id="initial_cc">
</div>
</td>
</tr>
<tr>
<td colspan="3">&nbsp;</td>
</tr>
<tr>
[% IF user.is_timetracker %]
@ -581,8 +592,12 @@ function checkWorktime(inp)
</tr>
<tr>
<th>URL:</th>
<td colspan="2"><input name="bug_file_loc" size="40" value="[% bug_file_loc FILTER html %]"></td>
<th><a href="page.cgi?id=fields.html#bug_file_loc">
[%- field_descs.bug_file_loc FILTER html %]:</a></th>
<td colspan="2">
<input name="bug_file_loc" size="40"
value="[% bug_file_loc FILTER html %]">
</td>
</tr>
</tbody>
@ -604,7 +619,8 @@ function checkWorktime(inp)
<tr><td colspan="4"><hr /></td></tr>
<tr>
<th class="required">Summary:</th>
<th class="required"><a href="page.cgi?id=fields.html#short_desc">
[%- field_descs.short_desc FILTER html %]:</a></th>
<td colspan="3">
<input name="short_desc" id="short_desc" size="70" value="[% short_desc FILTER html %]"
maxlength="255" spellcheck="true" aria-required="true"

View File

@ -50,7 +50,7 @@
[% END %]
[% IF desc_url %]
<a href="[% desc_url FILTER html %]">
[% ELSIF !field.custom %]
[% ELSE %]
<a href="page.cgi?id=fields.html#[% field.name FILTER url_quote %]">
[% END -%]
[% field_descs.${field.name} || field.description FILTER html %]:

View File

@ -179,7 +179,7 @@
[% FOREACH flag_type = flag_types %]
[% NEXT UNLESS flag_type.is_active %]
[% all_visible_flag_types.${flag_type.id} = flag_type %]
<li resource="[% escaped_urlbase %]flag.cgi?id=[% flag_type.id FILTER url_quote
<li resource="[% urlbase FILTER xml %]flag.cgi?id=[% flag_type.id FILTER url_quote
%]&amp;name=[% flag_type.name FILTER url_quote %]" />
[% END %]
</Seq>
@ -227,7 +227,7 @@
<Seq>
[% FOREACH flag_type = all_visible_flag_types.values.sort('name') %]
<li>
<bz:flag_type rdf:about="[% escaped_urlbase %]flag.cgi?id=[% flag_type.id FILTER url_quote
<bz:flag_type rdf:about="[% urlbase FILTER xml %]flag.cgi?id=[% flag_type.id FILTER url_quote
%]&amp;name=[% flag_type.name FILTER url_quote %]">
<bz:id>[% flag_type.id FILTER html %]</bz:id>
<bz:name>[% flag_type.name FILTER html %]</bz:name>

View File

@ -53,14 +53,18 @@
[% FOREACH flag = type.flags %]
<tr>
<td>
<a href="#[% flag.creation_date | timestamp %]">[% flag.setter.nick | html %]</a>:
<label title="[% type.description | html %]" for="flag-[% flag.id %]" style="white-space: nowrap">[%- type.name | html -%]</label>
<span title="[% flag.setter.identity FILTER html %]">[% flag.setter.nick FILTER html %]</span>:
</td>
<td>
<label title="[% type.description FILTER html %]"
for="flag-[% flag.id %]">
[%- type.name FILTER html FILTER no_break -%]</label>
</td>
<td>
<select id="flag-[% flag.id %]" name="flag-[% flag.id %]"
title="[% type.description | html %]"
onchange="toggleRequesteeField(this);"
class="flag_select">
class="flag_select flag_type-[% type.id %]">
[%# Only display statuses the user is allowed to set. %]
[% IF user.can_request_flag(type) || flag.setter_id == user.id %]
<option value="X"></option>
@ -115,10 +119,18 @@
<p><b>Flags:</b></p>
[% header_displayed = 1 %]
[% END %]
[% flag.setter.nick FILTER html %]:
[% IF flag.setter.name %]
<span title="[% flag.setter.name FILTER html %]">[% flag.setter.nick FILTER html %]</span>:
[% ELSE %]
[% flag.setter.nick FILTER html %]:
[% END %]
[%+ type.name FILTER html FILTER no_break %][% flag.status %]
[% IF flag.requestee %]
([% flag.requestee.nick FILTER html %])
[% IF flag.requestee.name %]
(<span title="[% flag.requestee.name FILTER html %]">[% flag.requestee.nick FILTER html %]</span>)
[% ELSE %]
([% flag.requestee.nick FILTER html %])
[% END %]
[% END %]<br>
[% END %]
[% END %]

View File

@ -23,80 +23,6 @@
[% PROCESS global/variables.none.tmpl %]
[% field_descs = { "[Bug creation]" => "[$terms.Bug creation]",
"actual_time" => "Actual Hours",
"alias" => "Alias",
"assigned_to" => "Assignee",
"attach_data.thedata" => "Attachment data",
"attachments.description" => "Attachment description",
"attachments.filename" => "Attachment filename",
"attachments.mimetype" => "Attachment mime type",
"attachments.ispatch" => "Attachment is patch",
"attachments.isobsolete" => "Attachment is obsolete",
"attachments.isprivate" => "Attachment is private",
"attachments.isurl" => "Attachment is a URL",
"attachments.submitter" => "Attachment creator",
"blocked" => "Blocks",
"bug_file_loc" => "URL",
"bug_group" => "Group",
"bug_id" => "$terms.Bug ID",
"bug_severity" => "Severity",
"bug_status" => "Status",
"changeddate" => "Changed",
"cc" => "CC",
"classification" => "Classification",
"cclist_accessible" => "CC list accessible",
"commenter" => "Commenter",
"component_id" => "Component ID",
"component" => "Component",
"content" => "Content",
"creation_ts" => "Creation date",
"deadline" => "Deadline",
"delta_ts" => "Changed",
"dependson" => "Depends on",
"dup_id" => "Duplicate",
"estimated_time" => "Orig. Est.",
"everconfirmed" => "Ever confirmed",
"flagtypes.name" => "Flags",
"keywords" => "Keywords",
"longdesc" => "Comment",
"longdescs.isprivate" => "Comment is private",
"newcc" => "CC",
"op_sys" => "OS",
"opendate" => "Opened",
"owner_idle_time" => "Time Since Assignee Touched",
"percentage_complete" => "%Complete",
"priority" => "Priority",
"product_id" => "Product ID",
"product" => "Product",
"qa_contact" => "QA Contact",
"remaining_time" => "Hours Left",
"rep_platform" => "Hardware",
"reporter" => "Reporter",
"reporter_accessible" => "Reporter accessible",
"requestees.login_name" => "Flag Requestee",
"resolution" => "Resolution",
"see_also" => "See Also",
"setters.login_name" => "Flag Setter",
"setting" => "Setting",
"settings" => "Settings",
"short_desc" => "Summary",
"status_whiteboard" => "Whiteboard",
"target_milestone" => "Target Milestone",
"version" => "Version",
"work_time" => "Hours Worked"} %]
[%# Also include any custom fields or fields which don't have a
Description here, by copying their Description from the
database. If you want to override this for your language
or your installation, just use a hook. %]
[% UNLESS Param('shutdownhtml') %]
[% FOREACH bz_field = bug_fields.values %]
[% SET field_descs.${bz_field.name} = bz_field.description
IF !field_descs.${bz_field.name}.defined %]
[% END %]
[% END %]
[% SET search_descs = {
"noop" => "---",
"equals" => "is equal to",
@ -123,6 +49,7 @@
"changedto" => "changed to",
"changedby" => "changed by",
"matches" => "matches",
"notmatches" => "does not match",
} %]
[% field_types = { ${constants.FIELD_TYPE_UNKNOWN} => "Unknown Type",
@ -162,4 +89,87 @@
[% END %]
[% END %][% END %]
[% IF in_template_var %]
[% vars.terms = terms %]
[%# field_descs is loaded as a global template variable and cached
# across all templates--see VARIABLES in Bugzilla/Template.pm.
#%]
[% vars.field_descs = {
"[Bug creation]" => "[$terms.Bug creation]",
"actual_time" => "Actual Hours",
"alias" => "Alias",
"assigned_to" => "Assignee",
"attach_data.thedata" => "Attachment data",
"attachments.description" => "Attachment description",
"attachments.filename" => "Attachment filename",
"attachments.mimetype" => "Attachment mime type",
"attachments.ispatch" => "Attachment is patch",
"attachments.isobsolete" => "Attachment is obsolete",
"attachments.isprivate" => "Attachment is private",
"attachments.isurl" => "Attachment is a URL",
"attachments.submitter" => "Attachment creator",
"blocked" => "Blocks",
"bug_file_loc" => "URL",
"bug_group" => "Group",
"bug_id" => "$terms.Bug ID",
"bug_severity" => "Severity",
"bug_status" => "Status",
"changeddate" => "Changed",
"cc" => "CC",
"classification" => "Classification",
"cclist_accessible" => "CC list accessible",
"commenter" => "Commenter",
"component_id" => "Component ID",
"component" => "Component",
"content" => "Content",
"creation_ts" => "Creation date",
"deadline" => "Deadline",
"delta_ts" => "Changed",
"dependson" => "Depends on",
"dup_id" => "Duplicate",
"estimated_time" => "Orig. Est.",
"everconfirmed" => "Ever confirmed",
"flagtypes.name" => "Flags",
"keywords" => "Keywords",
"longdesc" => "Comment",
"longdescs.isprivate" => "Comment is private",
"newcc" => "CC",
"op_sys" => "OS",
"opendate" => "Opened",
"owner_idle_time" => "Time Since Assignee Touched",
"percentage_complete" => "%Complete",
"priority" => "Priority",
"product_id" => "Product ID",
"product" => "Product",
"qa_contact" => "QA Contact",
"remaining_time" => "Hours Left",
"rep_platform" => "Hardware",
"reporter" => "Reporter",
"reporter_accessible" => "Reporter accessible",
"requestees.login_name" => "Flag Requestee",
"resolution" => "Resolution",
"see_also" => "See Also",
"setters.login_name" => "Flag Setter",
"setting" => "Setting",
"settings" => "Settings",
"short_desc" => "Summary",
"status_whiteboard" => "Whiteboard",
"target_milestone" => "Target Milestone",
"version" => "Version",
"work_time" => "Hours Worked",
} %]
[%# Also include any custom fields or fields which don't have a
Description here, by copying their Description from the
database. If you want to override this for your language
or your installation, just use a hook. %]
[% UNLESS Param('shutdownhtml') %]
[% FOREACH bz_field = bug_fields.values %]
[% SET vars.field_descs.${bz_field.name} = bz_field.description
IF !vars.field_descs.${bz_field.name}.defined %]
[% END %]
[% END %]
[% END %]
[% Hook.process("end") %]

View File

@ -31,10 +31,23 @@
</p>
[% field_descs.short_short_desc = "Summary (first 60 characters)" %]
[% field_descs.short_desc = "Full Summary" %]
[% field_descs.assigned_to_realname = "Assignee Realname" %]
[% field_descs.reporter_realname = "Reporter Realname" %]
[% field_descs.qa_contact_realname = "QA Contact Realname" %]
[% field_descs.short_desc = "Summary (Full)" %]
[% field_descs.assigned_to_realname = "$field_descs.assigned_to Real Name" %]
[% field_descs.reporter_realname = "$field_descs.reporter Real Name" %]
[% field_descs.qa_contact_realname = "$field_descs.qa_contact Real Name" %]
[%# Create a mapping of field descriptions to field names, so that
# the "Available Columns" list can be sorted alphabetically by
# field description.
#%]
[% SET available_columns = {} %]
[% FOREACH column = columns.keys %]
[% NEXT IF collist.contains(column) %]
[%# We lowecase the keys so that the sort happens case-insensitively. %]
[% SET column_desc = field_descs.$column || column FILTER lower %]
[% available_columns.$column_desc = column %]
[% END %]
<form name="changecolumns" action="colchange.cgi" onsubmit="change_submit();">
<input type="hidden" name="rememberedquery" value="[% buffer FILTER html %]" />
<table>
@ -66,12 +79,13 @@
[% Bugzilla.COLUMNS.${column}.title || column | html %]
</option>
[% END %]
[% FOREACH column = masterlist %]
[% IF lsearch(collist, column) == -1 %]
<option value="[% column FILTER html %]">
[% Bugzilla.COLUMNS.${column}.title || column | html %]
</option>
[% END %]
[% FOREACH key = available_columns.keys.sort %]
[% SET column = available_columns.$key %]
<option value="[% column FILTER html %]">
[%# Don't display the lower-cased column description,
# display the correct-case one. %]
[% (field_descs.$column || column) FILTER html %]
</option>
[% END %]
</select>
</td>

View File

@ -41,6 +41,35 @@
[% field_descs.assigned_to_realname = field_descs.assigned_to %]
[% field_descs.reporter_realname = field_descs.reporter %]
[% field_descs.qa_contact_realname = field_descs.qa_contact %]
[% abbrev =
{
"bug_severity" => { maxlength => 3 , title => "Sev" } ,
"priority" => { maxlength => 3 , title => "Pri" } ,
"rep_platform" => { maxlength => 3 , title => "Plt" } ,
"bug_status" => { maxlength => 4 } ,
"assigned_to" => { maxlength => 30 , ellipsis => "..." } ,
"assigned_to_realname" => { maxlength => 20 , ellipsis => "..." } ,
"reporter" => { maxlength => 30 , ellipsis => "..." } ,
"reporter_realname" => { maxlength => 20 , ellipsis => "..." } ,
"qa_contact" => { maxlength => 30 , ellipsis => "..." , title => "QAContact" } ,
"qa_contact_realname" => { maxlength => 20 , ellipsis => "..." , title => "QAContact" } ,
"resolution" => { maxlength => 4 } ,
"short_desc" => { wrap => 1 } ,
"short_short_desc" => { maxlength => 60 , ellipsis => "..." , wrap => 1 } ,
"status_whiteboard" => { title => "Whiteboard" , wrap => 1 } ,
"keywords" => { wrap => 1 } ,
"flagtypes.name" => { wrap => 1 } ,
"component" => { maxlength => 8 , title => "Comp" } ,
"product" => { maxlength => 8 } ,
"version" => { maxlength => 5 , title => "Vers" } ,
"op_sys" => { maxlength => 4 } ,
"bug_file_loc" => { maxlength => 30 } ,
"target_milestone" => { title => "TargetM" } ,
"percentage_complete" => { format_value => "%d %%" } ,
}
%]
[% PROCESS bug/time.html.tmpl %]
[% Hook.process("before_table") %]

View File

@ -25,7 +25,6 @@
<ul class="bz_toc">
<li><a href="#v36_introduction">Introduction</a></li>
<li><a href="#v36_point">Updates in this 3.6.x Release</a></li>
<li><a href="#v36_req">Minimum Requirements</a></li>
<li><a href="#v36_feat">New Features and Improvements</a></li>
<li><a href="#v36_issues">Outstanding Issues</a></li>
@ -57,187 +56,6 @@
of various features and improvements in this release of
[%+ terms.Bugzilla %].</p>
<h2 id="v36_point">Updates in this 3.6.x Release</h2>
<h3>3.6.4</h3>
<p>This release fixes several security issues, some of which are
<strong>highly critical</strong>. See the
<a href="http://www.bugzilla.org/security/3.2.9/">Security Advisory</a>
for details.</p>
<p>In addition, the following important fixes/changes have been made in
this release:</p>
<ul>
<li>Due to one of the security fixes, [% terms.Bugzilla %] 3.6.4 now
requires a newer version of the CGI.pm Perl module than previous
releases of [% terms.Bugzilla %] did. When you run
<kbd>checksetup.pl</kbd>, it will inform you how to upgrade your CGI.pm
module.
</li>
<li>When replying to a comment with a link like "attachment 1234 [details]",
the "[details]" link will no longer be duplicated in your reply.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=474766">[% terms.Bug %] 474766</a>)
</li>
<li>Using Quicksearch no longer requires that the <kbd>List::MoreUtils</kbd>
module be installed.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=611129">[% terms.Bug %] 611129</a>)
</li>
<li>When using <a href="config.cgi?ctype=rdf">config.cgi?ctype=rdf</a>,
information about products now includes <kbd>allows_unconfirmed</kbd>.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=610217">[% terms.Bug %] 610217</a>)
</li>
<li>When using tabular reports, any value whose name started with a period
or an underscore wasn't being displayed.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=617684">[% terms.Bug %] 617684</a>)
</li>
</ul>
<h3>3.6.3</h3>
<p>This release fixes various important security issues. See the
<a href="http://www.bugzilla.org/security/3.2.8/">Security Advisory</a>
for details.</p>
<p>In addition, the following important fixes/changes have been made in
this release:</p>
<ul>
<li>Clicking the "Submit only my new comment" button on the mid-air
collision page will no longer result in a "Form field longdesclength
was not defined" error.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=591218">[% terms.Bug %] 591218</a>)
</li>
<li>Saving a search with either of the deadline fields set to "Now" would
cause that deadline field to be removed from the saved search.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=590144">[% terms.Bug %] 590144</a>)
</li>
<li>Searching for [% terms.bugs %] "with at least X votes" was instead
returning [% terms.bugs %] with <em>exactly</em> that many votes.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=584414">[% terms.Bug %] 584414</a>)
</li>
<li>Typing something like "P1-5" in the quicksearch box should have
been searching the [% field_descs.priority FILTER html %] field,
but it was not.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=585028">[% terms.Bug %] 585028</a>)
</li>
<li>Users who had passwords less than 6 characters long couldn't log in.
Such users could only exist before 3.6, so it looked like after upgrading
to 3.6, certain users couldn't log in.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=575947">[% terms.Bug %] 575947</a>)
</li>
<li>Loading <a href="config.cgi?ctype=rdf">config.cgi?ctype=rdf</a> should
now be faster, particularly on installations that have many flags.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=553266">[% terms.Bug %] 553266</a>
and <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=605693">[% terms.Bug %] 605693</a>)
</li>
<li>Non-english templates were not being precompiled by checksetup.pl,
leading to reduced performance for localized [% terms.Bugzilla %]
installations.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=605425">[% terms.Bug %] 605425</a>)
</li>
</ul>
<h3>3.6.2</h3>
<p>This release fixes various security issues. See the
<a href="http://www.bugzilla.org/security/3.2.7/">Security Advisory</a>
for details.</p>
<p>In addition, the following important fixes/changes have been made in
this release:</p>
<ul>
<li>[% terms.Bugzilla %] installations running on older versions of IIS
will no longer experience the "Undef to trick_taint" errors that would
sometimes occur.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=521416">[% terms.Bug %] 521416</a>)
</li>
<li>Email notifications were missing the dates that comments were made.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=578003">[% terms.Bug %] 578003</a>)
</li>
<li>Putting a phrase in quotes in the Quicksearch box now works properly,
again.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=578494">[% terms.Bug %] 578494</a>
and <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=553884">[% terms.Bug %] 553884</a>)
</li>
<li>Quicksearch was usually (incorrectly) being limited to 200 results.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=581622">[% terms.Bug %] 581622</a>)
</li>
<li>On Windows, <kbd>install-module.pl</kbd> can now properly install
DateTime and certain other Perl modules that didn't install properly
before.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=576105">[% terms.Bug %] 576105</a>)
</li>
<li>Searching "keywords" for "contains none of the words" or "does not
match regular expression" now works properly.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=562014">[% terms.Bug %] 562014</a>)
</li>
<li>Doing <kbd>collectstats.pl --regenerate</kbd> now works on installations
using PostgreSQL.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=577058">[% terms.Bug %] 577058</a>)
</li>
<li>The "Field Values" administrative control panel was sometimes denying
admins the ability to delete field values when there was no reason
to deny the deletion.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=577054">[% terms.Bug %] 577054</a>)
</li>
<li>Eliminate the "uninitialized value" warnings that would happen when
editing a product's components.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=576911">[% terms.Bug %] 576911</a>)
</li>
<li>The updating of bugs_fulltext that happens during
<kbd>checksetup.pl</kbd> for upgrades to 3.6 should now be MUCH faster.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=577754">[% terms.Bug %] 577754</a>)
</li>
<li><kbd>email_in.pl</kbd> was not allowing the setting of time-tracking
fields via inbound emails.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=583622">[% terms.Bug %] 583622</a>)
</li>
</ul>
<h3>3.6.1</h3>
<p>This release fixes two security issues. See the
<a href="http://www.bugzilla.org/security/3.2.6/">Security Advisory</a>
for details.</p>
<p>In addition, the following important fixes/changes have been made in
this release:</p>
<ul>
<li>Using the "Change Columns" page would sometimes result in a
plain-text page instead of HTML.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=376044">[% terms.Bug %] 376044</a>)
</li>
<li>Extensions that have only templates and no code are now working.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=562551">[% terms.Bug %] 562551</a>)
</li>
<li><kbd>install-module.pl</kbd> has been fixed so that it installs
modules properly on both new and old versions of Perl.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=560318">[% terms.Bug %] 560318</a>
and <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=560330">[% terms.Bug %] 560330</a>)
</li>
<li>It is now possible to upgrade from 3.4 to 3.6 when using Oracle.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=561379">[% terms.Bug %] 561379</a>)
</li>
<li>Editing a field value's name (using the Field Values admin control
panel) wasn't working if the value was set as the default for that
field.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=561296">[% terms.Bug %] 561296</a>)
</li>
<li>If you had the <kbd>noresolveonopenblockers</kbd> parameter set,
[%+ terms.bugs %] couldn't be edited at all if they were marked FIXED
and had any open blockers. (The parameter is only supposed to prevent
<em>changing</em> [% terms.bugs %] to FIXED, not modifying already-FIXED
[%+ terms.bugs %].)
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=565314">[% terms.Bug %] 565314</a>)
</li>
<li>Some minor issues with Perl 5.12 were fixed (mostly warnings that Perl
5.12 was throwing). [% terms.Bugzilla %] now supports Perl 5.12.</li>
</ul>
<h2 id="v36_req">Minimum Requirements</h2>
<p>Any requirements that are new since 3.4.5 will look like
@ -266,7 +84,7 @@
<h3 id="v36_req_modules">Required Perl Modules</h3>
[% INCLUDE req_table reqs = REQUIRED_MODULES
new = [] updated = ['CGI.pm'] %]
new = [] updated = [] %]
<h3 id="v36_req_optional_mod">Optional Perl Modules</h3>
@ -274,7 +92,7 @@
features of [% terms.Bugzilla %]:</p>
[% INCLUDE req_table reqs = OPTIONAL_MODULES
new = ['JSON-RPC', 'Test-Taint', 'Math-Random-Secure']
new = ['JSON-RPC', 'Test-Taint']
updated = ['Chart']
include_feature = 1 %]
@ -298,7 +116,7 @@
<p>A <a href="https://wiki.mozilla.org/Bugzilla:CMU_HCI_Research_2008">scientific
usability study</a> was done on [% terms.Bugzilla %] by researchers
from Carnegie-Mellon University. As a result of this study,
<a href="https://bugzilla.mozilla.org/showdependencytree.cgi?id=490786&amp;hide_resolved=0">several
<a href="https://bugzilla.mozilla.org/showdependencytree.cgi?id=490786&hide_resolved=0">several
usability issues</a> were prioritized to be fixed, based on specific data
from the study.</p>
@ -379,7 +197,7 @@
on shared hosting). SUExec support shows up as an option
in the <kbd>localconfig</kbd> file during installation.</p>
<h3 id="v36_feat_mpwindows">Experimental mod_perl Support on Windows</h3>
<h3 id="36_feat_mpwindows">Experimental mod_perl Support on Windows</h3>
<p>There is now experimental support for running [% terms.Bugzilla %]
under mod_perl on Windows, for a significant performance enhancement
@ -456,12 +274,6 @@
<li><b>[% terms.Bug %] Editing:</b> The "Collapse All Comments"
and "Expand All Comments" links now appear to the right of the
comment list instead of above it.</li>
<li><b>[% terms.Bug %] Editing:</b> The See Also field now supports
URLs for Google Code Issues and the Debian B[% %]ug-Tracking System.</li>
<li><b>[% terms.Bug %] Editing:</b> There have been significant performance
improvements in <kbd>show_bug.cgi</kbd> (the script that displays the
[% terms.bug %]-editing form), particularly for [% terms.bugs %] that
have lots of comments or attachments.</li>
<li><b>Attachments:</b> The "Details" page of an attachment
now displays itself as uneditable if you can't edit the fields
@ -474,9 +286,6 @@
<li><b>Attachments:</b> When you click on an "attachment 12345" link
in a comment, if the attachment is a patch, you will now see the
formatted "Diff" view instead of the raw patch.</li>
<li><b>Attachments</b>: For text attachments, we now let the browser
auto-detect the character encoding, instead of forcing the browser to
always assume the attachment is in UTF-8.</li>
<li><b>Search:</b> You can now display [% terms.bug %] flags as a column
in search results.</li>
@ -573,11 +382,6 @@
faster at upgrading installations, particularly older installations.
Also, it's been made faster to run for the case where it's not
doing an upgrade.</li>
<li><b>Installation:</b> If you install [% terms.Bugzilla %] using the
tarball, the <kbd>CGI.pm</kbd> module from CPAN is now included in
the <kbd>lib/</kbd> dir. If you would rather use the CGI.pm from your
global Perl installation, you can delete <kbd>CGI.pm</kbd> and the
<kbd>CGI</kbd> directory from the <kbd>lib/</kbd> directory.</li>
<li>When editing a group, you can now specify that members of a group
are allowed to grant others membership in that group itself.</li>
@ -588,12 +392,14 @@
words instead of P1, P2, etc.</li>
<li>There is now a system in place so that all field values can be
localized. See the <kbd>value_descs</kbd> variable in
<kbd>template/en/default/global/field-descs.none.tmpl</kbd>.</li>
<li><kbd>config.cgi</kbd> now returns an ETag header and understands
the If-None-Match header in HTTP requests.</li>
<li>The XML format of <kbd>show_bug.cgi</kbd> now returns more information:
the numeric id of each comment, whether an attachment is a URL,
the modification time of an attachment, the numeric id of a flag,
and the numeric id of a flag's type.</li>
<li><b>Parameters:</b> Parameters that aren't actually required are no longer
in the "Required" section of the Parameters page. Instead, some are in the
new "General" section, and some are in the new "Advanced" section.</li>
@ -664,9 +470,6 @@
that parameter will be ignored. Mostly this just affects
customizers--[% terms.Bugzilla %]'s WebService is not functionally
affected by these changes.</li>
<li>In previous versions of [% terms.Bugzilla %], error messages were
sent word-wrapped to the client, from the WebService. Error messages
are now sent as one unbroken line.</li>
</ul>
<h2 id="v36_issues">Outstanding Issues</h2>
@ -718,9 +521,6 @@
<h2 id="v36_code_changes">Code Changes Which May Affect Customizations</h2>
<ul>
<li>There is no longer a SendBugMail method in the templates, and bugmail
is no longer sent by processing a template. Instead, it is sent
by using <kbd>Bugzilla::BugMail::Send</kbd>.</li>
<li>Comments are now represented as a
<a href="[% docs_urlbase FILTER html %]api/Bugzilla/Comment.html">Bugzilla::Comment</a>
object instead of just being hashes.</li>
@ -733,13 +533,13 @@
<li>All of the various template header information required to display
the [% terms.bug %] form is now in one template,
<kbd>template/en/default/bug/show-header.html.tmpl</kbd>.</li>
<li><s>You should now use <kbd>display_value</kbd> instead of
<li>You should now use <kbd>display_value</kbd> instead of
<kbd>get_status</kbd> or <kbd>get_resolution</kbd> in templates.
<kbd>display_value</kbd> should be used anywhere that a
&lt;select&gt;-type field has its values displayed.</s>
Долбанутое решение убрано.</li>
&lt;select&gt;-type field has its values displayed.</li>
</ul>
<h1 id="v36_previous">[% terms.Bugzilla %] 3.4 Release Notes</h1>
<ul class="bz_toc">
@ -776,6 +576,52 @@
<h2 id="v34_point">Updates In This 3.4.x Release</h2>
<h3>3.4.6</h3>
<ul>
<li>When doing a search that involves "not equals" or "does not contain the
string" or similar "negative" search types, the search description that
appears at the top of the resulting [% terms.bug %] list will indicate
that the search was of that type.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=474738">[% terms.Bug %] 474738</a>)
</li>
<li>In Internet Explorer, users couldn't easily mark a RESOLVED DUPLICATE
[%+ terms.bug %] as REOPENED, due to a JavaScript error.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=546719">[% terms.Bug %] 546719</a>)
</li>
<li>If you use a "bookmarkable template" to pre-fill forms on
the [% terms.bug %]-filing page, and you have custom fields
that are only supposed to appear (or only supposed to have certain
values) based on the values of other fields, those custom fields will
now work properly.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=538211">[% terms.Bug %] 538211</a>)
</li>
<li>If you have a custom field that's only supposed to appear when
a [% terms.bug %]'s resolution is FIXED, it will now behave properly
on the [% terms.bug %]-editing form when a user sets the [% terms.bug %]'s
status to RESOLVED.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=520993">[% terms.Bug %] 520993</a>)
</li>
<li>If you are logged-out and using <kbd>request.cgi</kbd>, the Requester
and Requestee fields no longer respect the <kbd>usermatching</kbd>
parameter--they always require full usernames.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=533018">[% terms.Bug %] 533018</a>)
</li>
<li>If you tried to do a search with too many terms (resulting in a URL
that was longer than about 7000 characters), Apache would return a
500 error instead of your search results.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=513989">[% terms.Bug %] 513989</a>)
</li>
<li>[% terms.Bugzilla %] would sometimes lose fields from your sort order
when you added new fields to your sort order.
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=470214">[% terms.Bug %] 470214</a>)
</li>
<li>The Atom format of search results would sometimes be missing the
Reporter or Assignee field for some [% terms.bugs %].
(<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=537834">[% terms.Bug %] 537834</a>)
</li>
</ul>
<h3>3.4.5</h3>
<p>This release contains fixes for multiple security issues. See the
@ -929,20 +775,19 @@
<p>Perl v5.8.1</p>
[% INCLUDE db_req db='mysql' %]
<h3 id="v34_req_mysql">For MySQL Users</h3>
<ul>
<li>MySQL v4.1.2</li>
<li><strong>perl module:</strong> DBD::mysql v4.00</li>
</ul>
[% INCLUDE db_req db='pg' %]
[% INCLUDE db_req db='oracle' %]
<h3 id="v34_req_modules">Required Perl Modules</h3>
[% INCLUDE req_table reqs = REQUIRED_MODULES
new = ['URI', 'DateTime', 'DateTime-TimeZone',
'Digest-SHA']
updated = ['Template-Toolkit'] %]
<h3 id="v34_req_optional_mod">Optional Perl Modules</h3>
<h3 id="v34_req_pg">For PostgreSQL Users</h3>
<ul>
<li>PostgreSQL v8.00.0000</li>
<li><strong>perl module:</strong> DBD::Pg v1.45</li>
</ul>
<h3 id="v34_req_oracle">For Oracle Users</h3>
@ -1012,10 +857,118 @@
<p>The following perl modules, if installed, enable various
features of [% terms.Bugzilla %]:</p>
[% INCLUDE req_table reqs = OPTIONAL_MODULES
new = ['TheSchwartz', 'Daemon-Generic']
updated = []
include_feature = 1 %]
<table class="req_table" border="0" cellspacing="0" cellpadding="0">
<tr>
<th>Module</th>
<th>Version</th>
<th>Enables Feature</th>
</tr>
<tr>
<td>LWP::UserAgent</td>
<td>(Any)</td>
<td>Automatic Update Notifications</td>
</tr>
<tr>
<td>Template::Plugin::GD::Image</td>
<td>(Any)</td>
<td>Graphical Reports</td>
</tr>
<tr>
<td>GD::Text</td>
<td>(Any)</td>
<td>Graphical Reports</td>
</tr>
<tr>
<td>GD::Graph</td>
<td>(Any)</td>
<td>Graphical Reports</td>
</tr>
<tr>
<td>GD</td>
<td>1.20</td>
<td>Graphical Reports, New Charts, Old Charts</td>
</tr>
<tr>
<td>Email::MIME::Attachment::Stripper</td>
<td>(Any)</td>
<td>Inbound Email</td>
</tr>
<tr>
<td>Email::Reply</td>
<td>(Any)</td>
<td>Inbound Email</td>
</tr>
<tr>
<td>Net::LDAP</td>
<td>(Any)</td>
<td>LDAP Authentication</td>
</tr>
<tr>
<td class="req_new">TheSchwartz</td>
<td class="req_new">(Any)</td>
<td>Mail Queueing</td>
</tr>
<tr>
<td class="req_new">Daemon::Generic</td>
<td class="req_new">(Any)</td>
<td>Mail Queueing</td>
</tr>
<tr>
<td>HTML::Parser</td>
<td>3.40</td>
<td>More HTML in Product/Group Descriptions</td>
</tr>
<tr>
<td>HTML::Scrubber</td>
<td>(Any)</td>
<td>More HTML in Product/Group Descriptions</td>
</tr>
<tr>
<td>XML::Twig</td>
<td>(Any)</td>
<td>Move [% terms.Bugs %] Between Installations</td>
</tr>
<tr>
<td>MIME::Parser</td>
<td>5.406</td>
<td>Move [% terms.Bugs %] Between Installations</td>
</tr>
<tr>
<td>Chart::Base</td>
<td>1.0</td>
<td>New Charts, Old Charts</td>
</tr>
<tr>
<td>Image::Magick</td>
<td>(Any)</td>
<td>Optionally Convert BMP Attachments to PNGs</td>
</tr>
<tr>
<td>PatchReader</td>
<td>0.9.4</td>
<td>Patch Viewer</td>
</tr>
<tr>
<td>Authen::Radius</td>
<td>(Any)</td>
<td>RADIUS Authentication</td>
</tr>
<tr>
<td>Authen::SASL</td>
<td>(Any)</td>
<td>SMTP Authentication</td>
</tr>
<tr>
<td>SOAP::Lite</td>
<td>0.710.06</td>
<td>XML-RPC Interface</td>
</tr>
<tr>
<td>mod_perl2</td>
<td>1.999022</td>
<td>mod_perl</td>
</tr>
</table>
<h2 id="v34_feat">New Features and Improvements</h2>
@ -3119,7 +3072,7 @@ sub y { $var++ }</pre>
[% BLOCK db_req %]
[% SET m = DB_MODULE.$db %]
<h3 id="v34_req_[% db FILTER html %]">For [% m.name FILTER html %] Users</h3>
<h3 id="v36_req_[% db FILTER html %]">For [% m.name FILTER html %] Users</h3>
<ul>
<li>[% m.name FILTER html %]

View File

@ -46,6 +46,7 @@
"changedto",
"changedby",
"matches",
"notmatches",
] %]
<h3 id="chart">

View File

@ -85,7 +85,7 @@ sub SaveAccount {
my $pwd1 = $cgi->param('new_password1');
my $pwd2 = $cgi->param('new_password2');
my $old_login_name = $user->login;
my $old_login_name = $cgi->param('old_login');
my $new_login_name = trim($cgi->param('new_login_name'));
if ($user->authorizer->can_change_password

View File

@ -395,7 +395,6 @@ sub mail {
$template->process("whine/multipart-mime.txt.tmpl", $args, \$msg)
or die($template->error());
Bugzilla->template_inner("");
MessageToMTA($msg);
delete $args->{'boundary'};

View File

@ -89,7 +89,6 @@ foreach my $email (sort (keys %bugs)) {
$template->process("email/whine.txt.tmpl", $vars, \$msg)
or die($template->error());
Bugzilla->template_inner("");
MessageToMTA($msg);
print "$email " . join(" ", @{$bugs{$email}}) . "\n";